Compare commits

..

8 Commits

Author SHA1 Message Date
Rob Simmons
5e41db4243 Add missing file 2025-11-21 16:14:05 -05:00
Rob Simmons
17ae8c790f PR comments 2025-11-21 13:37:05 -05:00
Rob Simmons
4417c24128 Chicago headline 2025-11-21 13:37:05 -05:00
Rob Simmons
72371c8d2d Fixed missed tests 2025-11-21 13:37:05 -05:00
Rob Simmons
77ae8ad8a8 docstring for useTraceSynthMsg 2025-11-21 13:37:05 -05:00
Rob Simmons
f693fbd6fd Undo accidental change 2025-11-21 13:37:05 -05:00
Rob Simmons
41755a1ac3 Workshopping error messages a bit, updating test cases 2025-11-21 13:37:05 -05:00
Rob Simmons
e8b49ee876 feat: improved error message and error explanation for typeclass synthesis failure 2025-11-21 13:37:05 -05:00
1848 changed files with 2916 additions and 12278 deletions

View File

@@ -1,34 +1,14 @@
To build Lean you should use `make -j -C build/release`.
To run a test you should use `cd tests/lean/run && ./test_single.sh example_test.lean`.
## New features
When asked to implement new features:
* begin by reviewing existing relevant code and tests
* write comprehensive tests first (expecting that these will initially fail)
* and then iterate on the implementation until the tests pass.
All new tests should go in `tests/lean/run/`. These tests don't have expected output; we just check there are no errors. You should use `#guard_msgs` to check for specific messages.
To build Lean you should use `make -j$(nproc) -C build/release`.
## Success Criteria
To run a test you should use `cd tests/lean/run && ./test_single.sh example_test.lean`.
*Never* report success on a task unless you have verified both a clean build without errors, and that the relevant tests pass.
*Never* report success on a task unless you have verified both a clean build without errors, and that the relevant tests pass. You have to keep working until you have verified both of these.
## Build System Safety
All new tests should go in `tests/lean/run/`. Note that these tests don't have expected output, and just run on a success or failure basis. So you should use `#guard_msgs` to check for specific messages.
**NEVER manually delete build directories** (build/, stage0/, stage1/, etc.) even when builds fail.
- ONLY use the project's documented build command: `make -j -C build/release`
- If a build is broken, ask the user before attempting any manual cleanup
## LSP and IDE Diagnostics
After rebuilding, LSP diagnostics may be stale until the user interacts with files. Trust command-line test results over IDE diagnostics.
## Update prompting when the user is frustrated
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.
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.
If you are not following best practices specific to this repository and the user expresses frustration, stop and ask them to help update this `.claude/CLAUDE.md` file with the missing guidance.

View File

@@ -106,54 +106,9 @@ jobs:
TAG_NAME="${GITHUB_REF##*/}"
echo "RELEASE_TAG=$TAG_NAME" >> "$GITHUB_OUTPUT"
- name: Validate CMakeLists.txt version matches tag
if: steps.set-release.outputs.RELEASE_TAG != ''
run: |
echo "Validating CMakeLists.txt version matches tag ${{ steps.set-release.outputs.RELEASE_TAG }}"
# Extract version values from CMakeLists.txt
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 -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 }}"
TAG_MINOR="${{ steps.set-release.outputs.LEAN_VERSION_MINOR }}"
TAG_PATCH="${{ steps.set-release.outputs.LEAN_VERSION_PATCH }}"
ERRORS=""
if [[ "$CMAKE_MAJOR" != "$TAG_MAJOR" ]]; then
ERRORS+="LEAN_VERSION_MAJOR: expected $TAG_MAJOR, found $CMAKE_MAJOR\n"
fi
if [[ "$CMAKE_MINOR" != "$TAG_MINOR" ]]; then
ERRORS+="LEAN_VERSION_MINOR: expected $TAG_MINOR, found $CMAKE_MINOR\n"
fi
if [[ "$CMAKE_PATCH" != "$TAG_PATCH" ]]; then
ERRORS+="LEAN_VERSION_PATCH: expected $TAG_PATCH, found $CMAKE_PATCH\n"
fi
if [[ "$CMAKE_IS_RELEASE" != "1" ]]; then
ERRORS+="LEAN_VERSION_IS_RELEASE: expected 1, found $CMAKE_IS_RELEASE\n"
fi
if [[ -n "$ERRORS" ]]; then
echo "::error::Version mismatch between tag and src/CMakeLists.txt"
echo ""
echo "Tag ${{ steps.set-release.outputs.RELEASE_TAG }} expects version $TAG_MAJOR.$TAG_MINOR.$TAG_PATCH"
echo "But src/CMakeLists.txt has mismatched values:"
echo -e "$ERRORS"
echo ""
echo "Fix src/CMakeLists.txt, delete the tag, and re-tag."
exit 1
fi
echo "Version validation passed: $TAG_MAJOR.$TAG_MINOR.$TAG_PATCH"
# 0: PRs without special label
# 1: PRs with `merge-ci` label, merge queue checks, master commits
# 2: nightlies
# 3: PRs with `release-ci` label, full releases
# 2: PRs with `release-ci` label, releases (incl. nightlies)
- name: Set check level
id: set-level
# We do not use github.event.pull_request.labels.*.name here because
@@ -163,16 +118,14 @@ jobs:
check_level=0
fast=false
if [[ -n "${{ steps.set-release.outputs.RELEASE_TAG }}" || -n "${{ steps.set-release-custom.outputs.RELEASE_TAG }}" ]]; then
check_level=3
elif [[ -n "${{ steps.set-nightly.outputs.nightly }}" ]]; then
if [[ -n "${{ steps.set-nightly.outputs.nightly }}" || -n "${{ steps.set-release.outputs.RELEASE_TAG }}" || -n "${{ steps.set-release-custom.outputs.RELEASE_TAG }}" ]]; then
check_level=2
elif [[ "${{ github.event_name }}" != "pull_request" ]]; then
check_level=1
else
labels="$(gh api repos/${{ github.repository_owner }}/${{ github.event.repository.name }}/pulls/${{ github.event.pull_request.number }} --jq '.labels')"
if echo "$labels" | grep -q "release-ci"; then
check_level=3
check_level=2
elif echo "$labels" | grep -q "merge-ci"; then
check_level=1
fi
@@ -257,22 +210,17 @@ jobs:
"test": true,
"CMAKE_PRESET": "reldebug",
},
{
// TODO: suddenly started failing in CI
/*{
"name": "Linux fsanitize",
// Always run on large if available, more reliable regarding timeouts
"os": large ? "nscloud-ubuntu-22.04-amd64-8x16-with-cache" : "ubuntu-latest",
"os": "ubuntu-latest",
"enabled": level >= 2,
// do not fail nightlies on this for now
"secondary": level <= 2,
"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.
"CTEST_OPTIONS": "-E 'StackOverflow|reverse-ffi|interactive|async_select_channel'"
},
// exclude seriously slow/problematic tests (laketests crash)
"CTEST_OPTIONS": "-E 'interactivetest|leanpkgtest|laketest|benchtest'"
},*/
{
"name": "macOS",
"os": "macos-15-intel",
@@ -304,7 +252,7 @@ jobs:
},
{
"name": "Windows",
"os": large && (fast || level >= 2) ? "namespace-profile-windows-amd64-4x16" : "windows-2022",
"os": large && (fast || level == 2) ? "namespace-profile-windows-amd64-4x16" : "windows-2022",
"release": true,
"enabled": level >= 2,
"test": true,

View File

@@ -41,7 +41,7 @@
"SMALL_ALLOCATOR": "OFF",
"USE_MIMALLOC": "OFF",
"BSYMBOLIC": "OFF",
"LEAN_TEST_VARS": "MAIN_STACK_SIZE=16000 LSAN_OPTIONS=max_leaks=10"
"LEAN_TEST_VARS": "MAIN_STACK_SIZE=16000"
},
"generator": "Unix Makefiles",
"binaryDir": "${sourceDir}/build/sanitize"

View File

@@ -72,9 +72,6 @@ update the archived C source code of the stage 0 compiler in `stage0/src`.
The github repository will automatically update stage0 on `master` once
`src/stdlib_flags.h` and `stage0/src/stdlib_flags.h` are out of sync.
To trigger this, modify `stage0/src/stdlib_flags.h` (e.g., by adding or changing
a comment). When `update-stage0` runs, it will overwrite `stage0/src/stdlib_flags.h`
with the contents of `src/stdlib_flags.h`, bringing them back in sync.
NOTE: A full rebuild of stage 1 will only be triggered when the *committed* contents of `stage0/` are changed.
Thus if you change files in it manually instead of through `update-stage0-commit` (see below) or fetching updates from git, you either need to commit those changes first or run `make -C build/release clean-stdlib`.

View File

@@ -1,54 +0,0 @@
This release introduces the Lean module system, which allows files to
control the visibility of their contents for other files. In previous
releases, this feature was available as a preview when the option
`experimental.module` was set to `true`; it is now a fully supported
feature of Lean.
# Benefits
Because modules reduce the amount of information exposed to other
code, they speed up rebuilds because irrelevant changes can be
ignored, they make it possible to be deliberate about API evolution by
hiding details that may change from clients, they help proofs be
checked faster by avoiding accidentally unfolding definitions, and
they lead to smaller executable files through improved dead code
elimination.
# Visibility
A source file is a module if it begins with the `module` keyword. By
default, declarations in a module are private; the `public` modifier
exports them. Proofs of theorems and bodies of definitions are private
by default even when their signatures are public; the bodies of
definitions can be made public by adding the `@[expose]`
attribute. Theorems and opaque constants never expose their bodies.
`public section` and `@[expose] section` change the default visibility
of declarations in the section.
# Imports
Modules may only import other modules. By default, `import` adds the
public information of the imported module to the private scope of the
current module. Adding the `public` modifier to an import places the
imported modules's public information in the public scope of the
current module, exposing it in turn to the current module's clients.
Within a package, `import all` can be used to import another module's
private scope into the current module; this can be used to separate
lemmas or tests from definition modules without exposing details to
downstream clients.
# Meta Code
Code used in metaprograms must be marked `meta`. This ensures that the
code is compiled and available for execution when it is needed during
elaboration. Meta code may only reference other meta code. A whole
module can be made available in the meta phase using `meta import`;
this allows code to be shared across phases by importing the module in
each phase. Code that is reachable from public metaprograms must be
imported via `public meta import`, while local metaprograms can use
plain `meta import` for their dependencies.
The module system is described in detail in [the Lean language reference](https://lean-reference-manual-review.netlify.app/find/?domain=Verso.Genre.Manual.section&name=files).

View File

@@ -300,7 +300,7 @@ def parseHeaderFromString (text path : String) :
throw <| .userError "parse errors in file"
-- the insertion point for `add` is the first newline after the imports
let insertion := header.raw.getTailPos?.getD parserState.pos
let insertion := text.findAux (· == '\n') text.rawEndPos insertion + '\n'
let insertion := text.findAux (· == '\n') text.endPos insertion + '\n'
pure (path, inputCtx, header, insertion)
/-- Parse a source file to extract the location of the import lines, for edits and error messages.
@@ -593,16 +593,16 @@ def main (args : List String) : IO UInt32 := do
for stx in imports do
let mod := decodeImport stx
if remove.contains mod || seen.contains mod then
out := out ++ String.Pos.Raw.extract text pos stx.raw.getPos?.get!
out := out ++ text.extract pos stx.raw.getPos?.get!
-- We use the end position of the syntax, but include whitespace up to the first newline
pos := text.findAux (· == '\n') text.rawEndPos stx.raw.getTailPos?.get! + '\n'
seen := seen.insert mod
out := out ++ String.Pos.Raw.extract text pos insertion
out := out ++ text.extract pos insertion
for mod in add do
if !seen.contains mod then
seen := seen.insert mod
out := out ++ s!"{mod}\n"
out := out ++ String.Pos.Raw.extract text insertion text.rawEndPos
out := out ++ text.extract insertion text.rawEndPos
IO.FS.writeFile path out
count := count + 1

96
script/bench.sh Executable file
View File

@@ -0,0 +1,96 @@
#!/usr/bin/env bash
set -euxo pipefail
cmake --preset release 1>&2
# We benchmark against stage2/bin to test new optimizations.
timeout -s KILL 1h time make -C build/release -j$(nproc) stage3 1>&2
export PATH=$PWD/build/release/stage2/bin:$PATH
# The extra opts used to be passed to the Makefile during benchmarking only but with Lake it is
# easier to configure them statically.
cmake -B build/release/stage3 -S src -DLEAN_EXTRA_LAKEFILE_TOML='weakLeanArgs=["-Dprofiler=true", "-Dprofiler.threshold=9999999", "--stats"]' 1>&2
(
cd tests/bench
timeout -s KILL 1h time temci exec --config speedcenter.yaml --in speedcenter.exec.velcom.yaml 1>&2
temci report run_output.yaml --reporter codespeed2
)
if [ -d .git ]; then
DIR="$(git rev-parse @)"
BASE_URL="https://speed.lean-lang.org/lean4-out/$DIR"
{
cat <<'EOF'
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Lakeprof Report</title>
</head>
<h1>Lakeprof Report</h1>
<button type="button" id="btn_fetch">View build trace in Perfetto</button>
<script type="text/javascript">
const ORIGIN = 'https://ui.perfetto.dev';
const btnFetch = document.getElementById('btn_fetch');
async function fetchAndOpen(traceUrl) {
const resp = await fetch(traceUrl);
// Error checking is left as an exercise to the reader.
const blob = await resp.blob();
const arrayBuffer = await blob.arrayBuffer();
openTrace(arrayBuffer, traceUrl);
}
function openTrace(arrayBuffer, traceUrl) {
const win = window.open(ORIGIN);
if (!win) {
btnFetch.style.background = '#f3ca63';
btnFetch.onclick = () => openTrace(arrayBuffer);
btnFetch.innerText = 'Popups blocked, click here to open the trace file';
return;
}
const timer = setInterval(() => win.postMessage('PING', ORIGIN), 50);
const onMessageHandler = (evt) => {
if (evt.data !== 'PONG') return;
// We got a PONG, the UI is ready.
window.clearInterval(timer);
window.removeEventListener('message', onMessageHandler);
const reopenUrl = new URL(location.href);
reopenUrl.hash = `#reopen=${traceUrl}`;
win.postMessage({
perfetto: {
buffer: arrayBuffer,
title: 'Lake Build Trace',
url: reopenUrl.toString(),
}}, ORIGIN);
};
window.addEventListener('message', onMessageHandler);
}
// This is triggered when following the link from the Perfetto UI's sidebar.
if (location.hash.startsWith('#reopen=')) {
const traceUrl = location.hash.substr(8);
fetchAndOpen(traceUrl);
}
EOF
cat <<EOF
btnFetch.onclick = () => fetchAndOpen("$BASE_URL/lakeprof.trace_event");
</script>
EOF
echo "<pre><code>"
(cd src; lakeprof report -prc)
echo "</code></pre>"
echo "</body></html>"
} | tee index.html
curl -T index.html $BASE_URL/index.html
curl -T src/lakeprof.log $BASE_URL/lakeprof.log
curl -T src/lakeprof.trace_event $BASE_URL/lakeprof.trace_event
fi

View File

@@ -58,11 +58,7 @@ OPTIONS=()
# We build cadical using the custom toolchain on Linux to avoid glibc versioning issues
echo -n " -DLEAN_STANDALONE=ON -DCADICAL_USE_CUSTOM_CXX=ON"
echo -n " -DCMAKE_CXX_COMPILER=$PWD/llvm-host/bin/clang++ -DLEAN_CXX_STDLIB='-Wl,-Bstatic -lc++ -lc++abi -Wl,-Bdynamic'"
# these should also be used for cadical, so do not use `LEAN_EXTRA_CXX_FLAGS` here
echo -n " -DCMAKE_CXX_FLAGS='--sysroot $PWD/llvm -idirafter $GLIBC_DEV/include ${EXTRA_FLAGS:-}'"
# the above does not include linker flags which will be added below based on context, so skip the
# generic check by cmake
echo -n " -DCMAKE_C_COMPILER_WORKS=1 -DCMAKE_CXX_COMPILER_WORKS=1"
echo -n " -DLEAN_EXTRA_CXX_FLAGS='--sysroot $PWD/llvm -idirafter $GLIBC_DEV/include ${EXTRA_FLAGS:-}'"
# use target compiler directly when not cross-compiling
if [[ -L llvm-host ]]; then
echo -n " -DCMAKE_C_COMPILER=$PWD/stage1/bin/clang"

View File

@@ -31,8 +31,6 @@ What this script does:
- Ensures tags are merged into stable branches (for non-RC releases)
- Verifies bump branches exist and are configured correctly
- Special handling for ProofWidgets4 release tags
- For mathlib4: runs verify_version_tags.py to validate the release tag
(checks git/GitHub consistency, toolchain, elan, cache, and build)
3. Optionally automates missing steps (when not in --dry-run mode):
- Creates missing release tags using push_repo_release_tag.py
@@ -501,57 +499,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 run_mathlib_verify_version_tags(toolchain, verbose=False):
"""Run mathlib4's verify_version_tags.py script to validate the release tag.
This clones mathlib4 to a temp directory and runs the verification script.
Returns True if verification passes, False otherwise.
"""
import tempfile
print(f" ... Running mathlib4 verify_version_tags.py {toolchain}")
with tempfile.TemporaryDirectory() as tmpdir:
# Clone mathlib4 (shallow clone is sufficient for running the script)
clone_result = subprocess.run(
['git', 'clone', '--depth', '1', 'https://github.com/leanprover-community/mathlib4.git', tmpdir],
capture_output=True,
text=True
)
if clone_result.returncode != 0:
print(f" ❌ Failed to clone mathlib4: {clone_result.stderr.strip()[:200]}")
return False
# Run the verification script
script_path = os.path.join(tmpdir, 'scripts', 'verify_version_tags.py')
if not os.path.exists(script_path):
print(f" ❌ verify_version_tags.py not found in mathlib4 (expected at scripts/verify_version_tags.py)")
return False
# Run from the mathlib4 directory so git operations work
result = subprocess.run(
['python3', script_path, toolchain],
cwd=tmpdir,
capture_output=True,
text=True,
timeout=900 # 15 minutes timeout for cache download etc.
)
# Print output with indentation
if result.stdout:
for line in result.stdout.strip().split('\n'):
print(f" {line}")
if result.stderr:
for line in result.stderr.strip().split('\n'):
print(f" {line}")
if result.returncode != 0:
print(f" ❌ mathlib4 verify_version_tags.py failed")
return False
print(f" ✅ mathlib4 verify_version_tags.py passed")
return True
def main():
parser = argparse.ArgumentParser(description="Check release status of Lean4 repositories")
parser.add_argument("toolchain", help="The toolchain version to check (e.g., v4.6.0)")
@@ -816,12 +763,6 @@ def main():
repo_status[name] = False
continue
# For mathlib4, run verify_version_tags.py to validate the release tag
if name == "mathlib4":
if not run_mathlib_verify_version_tags(toolchain, verbose):
repo_status[name] = False
continue
repo_status[name] = success
# Final check for lean4 master branch

View File

@@ -42,7 +42,7 @@ if(LLD_PATH)
endif()
set(LEAN_EXTRA_LINKER_FLAGS ${LEAN_EXTRA_LINKER_FLAGS_DEFAULT} CACHE STRING "Additional flags used by the linker")
set(LEAN_EXTRA_CXX_FLAGS "" CACHE STRING "Additional flags used by the C++ compiler. Unlike `CMAKE_CXX_FLAGS`, these will not be used to build e.g. cadical.")
set(LEAN_EXTRA_CXX_FLAGS "" CACHE STRING "Additional flags used by the C++ compiler")
set(LEAN_TEST_VARS "LEAN_CC=${CMAKE_C_COMPILER}" CACHE STRING "Additional environment variables used when running tests")
if (NOT CMAKE_BUILD_TYPE)
@@ -191,7 +191,7 @@ endif()
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules")
# Initialize CXXFLAGS.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${LEAN_EXTRA_CXX_FLAGS} -DLEAN_BUILD_TYPE=\"${CMAKE_BUILD_TYPE}\" -DLEAN_EXPORTING")
set(CMAKE_CXX_FLAGS "${LEAN_EXTRA_CXX_FLAGS} -DLEAN_BUILD_TYPE=\"${CMAKE_BUILD_TYPE}\" -DLEAN_EXPORTING")
set(CMAKE_CXX_FLAGS_DEBUG "-DLEAN_DEBUG")
set(CMAKE_CXX_FLAGS_MINSIZEREL "-DNDEBUG")
set(CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG")

View File

@@ -205,5 +205,3 @@ export Classical (imp_iff_right_iff imp_and_neg_imp_iff and_or_imp not_imp)
/-- Show that an element extracted from `P : ∃ a, p a` using `P.choose` satisfies `p`. -/
theorem Exists.choose_spec {p : α Prop} (P : a, p a) : p P.choose := Classical.choose_spec P
grind_pattern Exists.choose_spec => P.choose

View File

@@ -25,7 +25,7 @@ instances are provided for the same type.
instance (priority := 500) instForInOfForIn' [ForIn' m ρ α d] : ForIn m ρ α where
forIn x b f := forIn' x b fun a _ => f a
@[simp] theorem forIn'_eq_forIn [d : Membership α ρ] [ForIn' m ρ α d] {β} (x : ρ) (b : β)
@[simp] theorem forIn'_eq_forIn [d : Membership α ρ] [ForIn' m ρ α d] {β} [Monad m] (x : ρ) (b : β)
(f : (a : α) a x β m (ForInStep β)) (g : (a : α) β m (ForInStep β))
(h : a m b, f a m b = g a b) :
forIn' x b f = forIn x b g := by
@@ -40,7 +40,7 @@ instance (priority := 500) instForInOfForIn' [ForIn' m ρ α d] : ForIn m ρ α
simp [h]
rfl
@[wf_preprocess] theorem forIn_eq_forIn' [d : Membership α ρ] [ForIn' m ρ α d] {β}
@[wf_preprocess] theorem forIn_eq_forIn' [d : Membership α ρ] [ForIn' m ρ α d] {β} [Monad m]
(x : ρ) (b : β) (f : (a : α) β m (ForInStep β)) :
forIn x b f = forIn' x b (fun x h => binderNameHint x f <| binderNameHint h () <| f x) := by
rfl
@@ -403,7 +403,7 @@ class ForM (m : Type u → Type v) (γ : Type w₁) (α : outParam (Type w₂))
/--
Runs the monadic action `f` on each element of the collection `coll`.
-/
forM (coll : γ) (f : α m PUnit) : m PUnit
forM [Monad m] (coll : γ) (f : α m PUnit) : m PUnit
export ForM (forM)

View File

@@ -377,7 +377,7 @@ class ForIn (m : Type u₁ → Type u₂) (ρ : Type u) (α : outParam (Type v))
More information about the translation of `for` loops into `ForIn.forIn` is available in [the Lean
reference manual](lean-manual://section/monad-iteration-syntax).
-/
forIn {β} (xs : ρ) (b : β) (f : α β m (ForInStep β)) : m β
forIn {β} [Monad m] (xs : ρ) (b : β) (f : α β m (ForInStep β)) : m β
export ForIn (forIn)
@@ -405,7 +405,7 @@ class ForIn' (m : Type u₁ → Type u₂) (ρ : Type u) (α : outParam (Type v)
More information about the translation of `for` loops into `ForIn'.forIn'` is available in [the
Lean reference manual](lean-manual://section/monad-iteration-syntax).
-/
forIn' {β} (x : ρ) (b : β) (f : (a : α) a x β m (ForInStep β)) : m β
forIn' {β} [Monad m] (x : ρ) (b : β) (f : (a : α) a x β m (ForInStep β)) : m β
export ForIn' (forIn')

View File

@@ -242,7 +242,7 @@ Examples:
* `#["red", "green", "blue", "brown"].swapIfInBounds 0 4 = #["red", "green", "blue", "brown"]`
* `#["red", "green", "blue", "brown"].swapIfInBounds 9 2 = #["red", "green", "blue", "brown"]`
-/
@[extern "lean_array_swap", expose]
@[extern "lean_array_swap", grind]
def swapIfInBounds (xs : Array α) (i j : @& Nat) : Array α :=
if h₁ : i < xs.size then
if h₂ : j < xs.size then swap xs i j
@@ -570,7 +570,7 @@ protected def forIn' {α : Type u} {β : Type v} {m : Type v → Type w} [Monad
| ForInStep.yield b => loop i (Nat.le_of_lt h') b
loop as.size (Nat.le_refl _) b
instance [Monad m] : ForIn' m (Array α) α inferInstance where
instance : ForIn' m (Array α) α inferInstance where
forIn' := Array.forIn'
-- No separate `ForIn` instance is required because it can be derived from `ForIn'`.
@@ -1001,7 +1001,7 @@ unless `start < stop`. By default, the entire array is used.
protected def forM {α : Type u} {m : Type v Type w} [Monad m] (f : α m PUnit) (as : Array α) (start := 0) (stop := as.size) : m PUnit :=
as.foldlM (fun _ => f) start stop
instance [Monad m] : ForM m (Array α) α where
instance : ForM m (Array α) α where
forM xs f := Array.forM f xs
-- We simplify `Array.forM` to `forM`.

View File

@@ -3966,29 +3966,28 @@ theorem getElem_modify_of_ne {xs : Array α} {i : Nat} (h : i ≠ j)
/-! ### swap -/
@[grind =]
theorem getElem_swap {xs : Array α} {i j : Nat} (hi hj) {k : Nat} (hk : k < (xs.swap i j hi hj).size) :
(xs.swap i j hi hj)[k] = if k = i then xs[j] else if k = j then xs[i] else xs[k]'(by simp_all) := by
simp only [swap_def, getElem_set, eq_comm (a := k)]
split <;> split <;> simp_all
@[simp] theorem getElem_swap_right {xs : Array α} {i j : Nat} {hi hj} :
(xs.swap i j hi hj)[j]'(by simpa using hj) = xs[i] := by
simp +contextual [getElem_swap]
simp [swap_def]
@[simp] theorem getElem_swap_left {xs : Array α} {i j : Nat} {hi hj} :
(xs.swap i j hi hj)[i]'(by simpa using hi) = xs[j] := by
simp [getElem_swap]
simp +contextual [swap_def, getElem_set]
@[simp] theorem getElem_swap_of_ne {xs : Array α} {i j : Nat} {hi hj}
{h : k < (xs.swap i j hi hj).size} (hi' : k i) (hj' : k j) :
(xs.swap i j hi hj)[k] = xs[k]'(by simp_all) := by
simp [getElem_swap, hi', hj']
@[simp] theorem getElem_swap_of_ne {xs : Array α} {i j : Nat} {hi hj} (hp : k < xs.size)
(hi' : k i) (hj' : k j) : (xs.swap i j hi hj)[k]'(xs.size_swap .. |>.symm hp) = xs[k] := by
simp [swap_def, getElem_set, hi'.symm, hj'.symm]
@[deprecated getElem_swap (since := "2025-10-10")]
theorem getElem_swap' {xs : Array α} {i j : Nat} {hi hj} {k : Nat} (hk : k < xs.size) :
(xs.swap i j hi hj)[k]'(by simp_all) = if k = i then xs[j] else if k = j then xs[i] else xs[k] :=
getElem_swap _ _ _
(xs.swap i j hi hj)[k]'(by simp_all) = if k = i then xs[j] else if k = j then xs[i] else xs[k] := by
split
· simp_all only [getElem_swap_left]
· split <;> simp_all
@[grind =]
theorem getElem_swap {xs : Array α} {i j : Nat} (hi hj) {k : Nat} (hk : k < (xs.swap i j hi hj).size) :
(xs.swap i j hi hj)[k] = if k = i then xs[j] else if k = j then xs[i] else xs[k]'(by simp_all) := by
apply getElem_swap'
@[simp] theorem swap_swap {xs : Array α} {i j : Nat} (hi hj) :
(xs.swap i j hi hj).swap i j ((xs.size_swap ..).symm hi) ((xs.size_swap ..).symm hj) = xs := by
@@ -4009,66 +4008,8 @@ theorem swap_comm {xs : Array α} {i j : Nat} (hi hj) : xs.swap i j hi hj = xs.s
· split <;> simp_all
· split <;> simp_all
/-! ### swapIfInBounds -/
@[grind =] theorem swapIfInBounds_def {xs : Array α} {i j : Nat} :
xs.swapIfInBounds i j = if h₁ : i < xs.size then
if h₂ : j < xs.size then swap xs i j else xs else xs := rfl
@[simp, grind =] theorem size_swapIfInBounds {xs : Array α} {i j : Nat} :
(xs.swapIfInBounds i j).size = xs.size := by
unfold swapIfInBounds; split <;> (try split) <;> simp [size_swap]
@[grind =] theorem getElem_swapIfInBounds {xs : Array α} {i j k : Nat}
(hk : k < (xs.swapIfInBounds i j).size) :
(xs.swapIfInBounds i j)[k] =
if h₁ : k = i j < xs.size then xs[j]'h₁.2 else if h₂ : k = j i < xs.size then xs[i]'h₂.2
else xs[k]'(by simp_all) := by
rw [size_swapIfInBounds] at hk
unfold swapIfInBounds
split <;> rename_i hi
· split <;> rename_i hj
· simp only [hi, hj, and_true]
exact getElem_swap _ _ _
· simp only [hi, hj, and_true, and_false, dite_false]
split <;> simp_all
· simp only [hi, and_false, dite_false]
split <;> simp_all
@[simp]
theorem getElem_swapIfInBounds_of_size_le_left {xs : Array α} {i j k : Nat} (hi : xs.size i)
(hk : k < (xs.swapIfInBounds i j).size) :
(xs.swapIfInBounds i j)[k] = xs[k]'(Nat.lt_of_lt_of_eq hk size_swapIfInBounds) := by
have h₁ : k i := Nat.ne_of_lt <| Nat.lt_of_lt_of_le hk <|
Nat.le_trans (Nat.le_of_eq (size_swapIfInBounds)) hi
have h₂ : ¬ (i < xs.size) := Nat.not_lt_of_le hi
simp [getElem_swapIfInBounds, h₁, h₂]
@[simp]
theorem getElem_swapIfInBounds_of_size_le_right {xs : Array α} {i j k : Nat} (hj : xs.size j)
(hk : k < (xs.swapIfInBounds i j).size) :
(xs.swapIfInBounds i j)[k] = xs[k]'(Nat.lt_of_lt_of_eq hk size_swapIfInBounds) := by
have h₁ : ¬ (j < xs.size) := Nat.not_lt_of_le hj
have h₂ : k j := Nat.ne_of_lt <| Nat.lt_of_lt_of_le hk <|
Nat.le_trans (Nat.le_of_eq (size_swapIfInBounds)) hj
simp [getElem_swapIfInBounds, h₁, h₂]
@[simp]
theorem getElem_swapIfInBounds_left {xs : Array α} {i j : Nat} (hj : j < xs.size)
(hi : i < (xs.swapIfInBounds i j).size) : (xs.swapIfInBounds i j)[i] = xs[j] := by
simp [getElem_swapIfInBounds, hj]
@[simp]
theorem getElem_swapIfInBounds_right {xs : Array α} {i j : Nat} (hi : i < xs.size)
(hj : j < (xs.swapIfInBounds i j).size) :
(xs.swapIfInBounds i j)[j] = xs[i] := by
simp +contextual [getElem_swapIfInBounds, hi]
@[simp]
theorem getElem_swapIfInBounds_of_ne_of_ne {xs : Array α} {i j k : Nat} (hi : k i) (hj : k j)
(hk : k < (xs.swapIfInBounds i j).size) :
(xs.swapIfInBounds i j)[k] = xs[k]'(Nat.lt_of_lt_of_eq hk size_swapIfInBounds) := by
simp [getElem_swapIfInBounds, hi, hj]
(xs.swapIfInBounds i j).size = xs.size := by unfold swapIfInBounds; split <;> (try split) <;> simp [size_swap]
/-! ### swapAt -/
@@ -4330,10 +4271,6 @@ theorem size_uset {xs : Array α} {v : α} {i : USize} (h : i.toNat < xs.size) :
theorem getElem!_eq_getD [Inhabited α] {xs : Array α} {i} : xs[i]! = xs.getD i default := by
rfl
theorem getElem_eq_getD {xs : Array α} {i} {h : i < xs.size} (fallback : α) :
xs[i]'h = xs.getD i fallback := by
rw [getD_eq_getD_getElem?, getElem_eq_getElem?_get, Option.get_eq_getD]
/-! # mem -/
@[deprecated mem_toList_iff (since := "2025-05-26")]

View File

@@ -1056,7 +1056,7 @@ theorem toInt_setWidth' {m n : Nat} (p : m ≤ n) {x : BitVec m} :
@[simp, grind =] theorem toFin_setWidth' {m n : Nat} (p : m n) (x : BitVec m) :
(setWidth' p x).toFin = x.toFin.castLE (Nat.pow_le_pow_right (by omega) (by omega)) := by
ext
rw [setWidth'_eq, toFin_setWidth, Fin.val_ofNat, Fin.val_castLE, val_toFin,
rw [setWidth'_eq, toFin_setWidth, Fin.val_ofNat, Fin.coe_castLE, val_toFin,
Nat.mod_eq_of_lt (by apply BitVec.toNat_lt_twoPow_of_le p)]
theorem toNat_setWidth_of_le {w w' : Nat} {b : BitVec w} (h : w w') : (b.setWidth w').toNat = b.toNat := by

View File

@@ -132,11 +132,6 @@ Copies the bytes with indices {name}`b` (inclusive) to {name}`e` (exclusive) to
def extract (a : ByteArray) (b e : Nat) : ByteArray :=
a.copySlice b empty 0 (e - b)
/--
Appends two byte arrays using fast array primitives instead of converting them into lists and back.
In compiled code, this function replaces calls to {name}`ByteArray.append`.
-/
@[inline]
protected def fastAppend (a : ByteArray) (b : ByteArray) : ByteArray :=
-- we assume that `append`s may be repeated, so use asymptotic growing; use `copySlice` directly to customize
@@ -248,7 +243,7 @@ protected def forIn {β : Type v} {m : Type v → Type w} [Monad m] (as : ByteAr
| ForInStep.yield b => loop i (Nat.le_of_lt h') b
loop as.size (Nat.le_refl _) b
instance [Monad m] : ForIn m ByteArray UInt8 where
instance : ForIn m ByteArray UInt8 where
forIn := ByteArray.forIn
/--

View File

@@ -246,11 +246,6 @@ instance neg (n : Nat) : Neg (Fin n) :=
theorem neg_def (a : Fin n) : -a = (n - a) % n, Nat.mod_lt _ a.pos := rfl
-- Later we give another version called `Fin.val_neg` that splits on `a = 0`.
protected theorem val_neg' (a : Fin n) : ((-a : Fin n) : Nat) = (n - a) % n :=
rfl
@[deprecated Fin.val_neg' (since := "2025-11-21")]
protected theorem coe_neg (a : Fin n) : ((-a : Fin n) : Nat) = (n - a) % n :=
rfl

View File

@@ -16,25 +16,17 @@ open Std
namespace Fin
@[simp, grind =] theorem ofNat_zero (n : Nat) [NeZero n] : Fin.ofNat n 0 = 0 := rfl
@[simp] theorem ofNat_zero (n : Nat) [NeZero n] : Fin.ofNat n 0 = 0 := rfl
@[deprecated ofNat_zero (since := "2025-05-28")] abbrev ofNat'_zero := @ofNat_zero
theorem mod_def (a m : Fin n) : a % m = Fin.mk (a.val % m.val) (Nat.lt_of_le_of_lt (Nat.mod_le _ _) a.2) :=
rfl
theorem val_mod (a m : Fin n) : (a % m).val = a.val % m.val := rfl
theorem mul_def (a b : Fin n) : a * b = Fin.mk ((a.val * b.val) % n) (Nat.mod_lt _ a.pos) := rfl
theorem val_mul (a b : Fin n) : (a * b).val = (a.val * b.val) % n := rfl
theorem sub_def (a b : Fin n) : a - b = Fin.mk (((n - b.val) + a.val) % n) (Nat.mod_lt _ a.pos) := rfl
@[grind =]
theorem val_sub (a b : Fin n) : (a - b).val = ((n - b.val) + a.val) % n := rfl
@[grind ]
theorem pos' : [Nonempty (Fin n)], 0 < n | i => i.pos
@[simp] theorem is_lt (a : Fin n) : (a : Nat) < n := a.2
@@ -46,8 +38,7 @@ theorem pos_iff_nonempty {n : Nat} : 0 < n ↔ Nonempty (Fin n) :=
@[simp] protected theorem eta (a : Fin n) (h : a < n) : (a, h : Fin n) = a := rfl
@[ext, grind ext]
protected theorem ext {a b : Fin n} (h : (a : Nat) = b) : a = b := eq_of_val_eq h
@[ext] protected theorem ext {a b : Fin n} (h : (a : Nat) = b) : a = b := eq_of_val_eq h
theorem val_ne_iff {a b : Fin n} : a.1 b.1 a b := not_congr val_inj
@@ -78,7 +69,7 @@ theorem mk_val (i : Fin n) : (⟨i, i.isLt⟩ : Fin n) = i := Fin.eta ..
@[deprecated val_ofNat (since := "2025-05-28")] abbrev val_ofNat' := @val_ofNat
@[simp, grind =] theorem ofNat_self {n : Nat} [NeZero n] : Fin.ofNat n n = 0 := by
@[simp] theorem ofNat_self {n : Nat} [NeZero n] : Fin.ofNat n n = 0 := by
ext
simp
congr
@@ -98,7 +89,7 @@ theorem mk_val (i : Fin n) : (⟨i, i.isLt⟩ : Fin n) = i := Fin.eta ..
@[simp] theorem div_val (a b : Fin n) : (a / b).val = a.val / b.val :=
rfl
@[simp, grind =] theorem modn_val (a : Fin n) (b : Nat) : (a.modn b).val = a.val % b :=
@[simp] theorem modn_val (a : Fin n) (b : Nat) : (a.modn b).val = a.val % b :=
rfl
@[simp] theorem val_eq_zero (a : Fin 1) : a.val = 0 :=
@@ -268,9 +259,7 @@ instance : LawfulOrderLT (Fin n) where
lt_iff := by
simp [ Fin.not_le, Decidable.imp_iff_not_or, Std.Total.total]
@[simp] theorem val_rev (i : Fin n) : (rev i).val = n - (i + 1) := rfl
grind_pattern val_rev => i.rev
@[simp, grind =] theorem val_rev (i : Fin n) : (rev i).val = n - (i + 1) := rfl
@[simp] theorem rev_rev (i : Fin n) : rev (rev i) = i := Fin.ext <| by
rw [val_rev, val_rev, Nat.sub_sub, Nat.sub_sub_self (by exact i.2), Nat.add_sub_cancel]
@@ -295,8 +284,6 @@ theorem rev_eq {n a : Nat} (i : Fin (n + 1)) (h : n = a + i) :
@[simp] theorem val_last (n : Nat) : (last n).1 = n := rfl
grind_pattern val_last => last n
@[simp] theorem last_zero : (Fin.last 0 : Fin 1) = 0 := by
ext
simp
@@ -406,8 +393,6 @@ theorem zero_ne_one : (0 : Fin (n + 2)) ≠ 1 := Fin.ne_of_lt zero_lt_one
@[simp] theorem val_succ (j : Fin n) : (j.succ : Nat) = j + 1 := rfl
grind_pattern val_succ => j.succ
@[simp] theorem succ_pos (a : Fin n) : (0 : Fin (n + 1)) < a.succ := by
simp [Fin.lt_def]
@@ -468,18 +453,12 @@ theorem one_lt_succ_succ (a : Fin n) : (1 : Fin (n + 2)) < a.succ.succ := by
theorem succ_succ_ne_one (a : Fin n) : Fin.succ (Fin.succ a) 1 :=
Fin.ne_of_gt (one_lt_succ_succ a)
@[simp, grind =] theorem val_castLT (i : Fin m) (h : i.1 < n) : (castLT i h : Nat) = i := rfl
@[deprecated val_castLT (since := "2025-11-21")]
theorem coe_castLT (i : Fin m) (h : i.1 < n) : (castLT i h : Nat) = i := rfl
@[simp] theorem coe_castLT (i : Fin m) (h : i.1 < n) : (castLT i h : Nat) = i := rfl
@[simp] theorem castLT_mk (i n m : Nat) (hn : i < n) (hm : i < m) : castLT i, hn hm = i, hm :=
rfl
@[simp, grind =] theorem val_castLE (h : n m) (i : Fin n) : (castLE h i : Nat) = i := rfl
@[deprecated val_castLE (since := "2025-11-21")]
theorem coe_castLE (h : n m) (i : Fin n) : (castLE h i : Nat) = i := rfl
@[simp, grind =] theorem coe_castLE (h : n m) (i : Fin n) : (castLE h i : Nat) = i := rfl
@[simp] theorem castLE_mk (i n m : Nat) (hn : i < n) (h : n m) :
castLE h i, hn = i, Nat.lt_of_lt_of_le hn h := rfl
@@ -491,16 +470,13 @@ theorem coe_castLE (h : n ≤ m) (i : Fin n) : (castLE h i : Nat) = i := rfl
@[simp] theorem castLE_castLE {k m n} (km : k m) (mn : m n) (i : Fin k) :
Fin.castLE mn (Fin.castLE km i) = Fin.castLE (Nat.le_trans km mn) i :=
Fin.ext (by simp only [val_castLE])
Fin.ext (by simp only [coe_castLE])
@[simp] theorem castLE_comp_castLE {k m n} (km : k m) (mn : m n) :
Fin.castLE mn Fin.castLE km = Fin.castLE (Nat.le_trans km mn) :=
funext (castLE_castLE km mn)
@[simp, grind =] theorem val_cast (h : n = m) (i : Fin n) : (i.cast h : Nat) = i := rfl
@[deprecated val_cast (since := "2025-11-21")]
theorem coe_cast (h : n = m) (i : Fin n) : (i.cast h : Nat) = i := rfl
@[simp] theorem coe_cast (h : n = m) (i : Fin n) : (i.cast h : Nat) = i := rfl
@[simp] theorem cast_castLE {k m n} (km : k m) (mn : m = n) (i : Fin k) :
Fin.cast mn (i.castLE km) = i.castLE (mn km) :=
@@ -513,7 +489,7 @@ theorem coe_cast (h : n = m) (i : Fin n) : (i.cast h : Nat) = i := rfl
@[simp] theorem cast_zero [NeZero n] [NeZero m] (h : n = m) : Fin.cast h 0 = 0 := rfl
@[simp] theorem cast_last {n' : Nat} {h : n + 1 = n' + 1} : (last n).cast h = last n' :=
Fin.ext (by rw [val_cast, val_last, val_last, Nat.succ.inj h])
Fin.ext (by rw [coe_cast, val_last, val_last, Nat.succ.inj h])
@[simp] theorem cast_mk (h : n = m) (i : Nat) (hn : i < n) : Fin.cast h i, hn = i, h hn := rfl
@@ -528,10 +504,7 @@ theorem coe_cast (h : n = m) (i : Fin n) : (i.cast h : Nat) = i := rfl
theorem castLE_of_eq {m n : Nat} (h : m = n) {h' : m n} : castLE h' = Fin.cast h := rfl
@[simp, grind =] theorem val_castAdd (m : Nat) (i : Fin n) : (castAdd m i : Nat) = i := rfl
@[deprecated val_castAdd (since := "2025-11-21")]
theorem coe_castAdd (m : Nat) (i : Fin n) : (castAdd m i : Nat) = i := rfl
@[simp] theorem coe_castAdd (m : Nat) (i : Fin n) : (castAdd m i : Nat) = i := rfl
@[simp] theorem castAdd_zero : (castAdd 0 : Fin n Fin (n + 0)) = Fin.cast rfl := rfl
@@ -567,10 +540,7 @@ the reverse direction. -/
theorem succ_cast_eq {n' : Nat} (i : Fin n) (h : n = n') :
(i.cast h).succ = i.succ.cast (by rw [h]) := rfl
@[simp, grind =] theorem val_castSucc (i : Fin n) : (i.castSucc : Nat) = i := rfl
@[deprecated val_castSucc (since := "2025-11-21")]
theorem coe_castSucc (i : Fin n) : (i.castSucc : Nat) = i := rfl
@[simp] theorem coe_castSucc (i : Fin n) : (i.castSucc : Nat) = i := rfl
@[simp] theorem castSucc_mk (n i : Nat) (h : i < n) : castSucc i, h = i, Nat.lt_succ_of_lt h := rfl
@@ -578,7 +548,7 @@ theorem coe_castSucc (i : Fin n) : (i.castSucc : Nat) = i := rfl
i.castSucc.cast h = (i.cast (Nat.succ.inj h)).castSucc := rfl
theorem castSucc_lt_succ {i : Fin n} : i.castSucc < i.succ :=
lt_def.2 <| by simp only [val_castSucc, val_succ, Nat.lt_succ_self]
lt_def.2 <| by simp only [coe_castSucc, val_succ, Nat.lt_succ_self]
theorem le_castSucc_iff {i : Fin (n + 1)} {j : Fin n} : i j.castSucc i < j.succ := by
simpa only [lt_def, le_def] using Nat.add_one_le_add_one_iff.symm
@@ -632,7 +602,7 @@ theorem coeSucc_eq_succ {a : Fin n} : a.castSucc + 1 = a.succ := by
@[deprecated castSucc_lt_succ (since := "2025-10-29")]
theorem lt_succ {a : Fin n} : a.castSucc < a.succ := by
rw [castSucc, lt_def, val_castAdd, val_succ]; exact Nat.lt_succ_self a.val
rw [castSucc, lt_def, coe_castAdd, val_succ]; exact Nat.lt_succ_self a.val
theorem exists_castSucc_eq {n : Nat} {i : Fin (n + 1)} : ( j, castSucc j = i) i last n :=
fun j, hj => hj Fin.ne_of_lt j.castSucc_lt_last,
@@ -640,10 +610,7 @@ theorem exists_castSucc_eq {n : Nat} {i : Fin (n + 1)} : (∃ j, castSucc j = i)
theorem succ_castSucc {n : Nat} (i : Fin n) : i.castSucc.succ = i.succ.castSucc := rfl
@[simp, grind =] theorem val_addNat (m : Nat) (i : Fin n) : (addNat i m : Nat) = i + m := rfl
@[deprecated val_addNat (since := "2025-11-21")]
theorem coe_addNat (m : Nat) (i : Fin n) : (addNat i m : Nat) = i + m := rfl
@[simp] theorem coe_addNat (m : Nat) (i : Fin n) : (addNat i m : Nat) = i + m := rfl
@[simp] theorem addNat_zero (n : Nat) (i : Fin n) : addNat i 0 = i := by
ext
@@ -671,10 +638,7 @@ theorem cast_addNat_left {n n' m : Nat} (i : Fin n') (h : n' + m = n + m) :
(addNat i m').cast h = addNat i m :=
Fin.ext <| (congrArg ((· + ·) (i : Nat)) (Nat.add_left_cancel h) : _)
@[simp, grind =] theorem val_natAdd (n : Nat) {m : Nat} (i : Fin m) : (natAdd n i : Nat) = n + i := rfl
@[deprecated val_natAdd (since := "2025-11-21")]
theorem coe_natAdd (n : Nat) {m : Nat} (i : Fin m) : (natAdd n i : Nat) = n + i := rfl
@[simp] theorem coe_natAdd (n : Nat) {m : Nat} (i : Fin m) : (natAdd n i : Nat) = n + i := rfl
@[simp] theorem natAdd_mk (n i : Nat) (hi : i < m) :
natAdd n i, hi = n + i, Nat.add_lt_add_left hi n := rfl
@@ -731,7 +695,7 @@ theorem natAdd_castSucc {m n : Nat} {i : Fin m} : natAdd n (castSucc i) = castSu
omega
theorem rev_castAdd (k : Fin n) (m : Nat) : rev (castAdd m k) = addNat (rev k) m := Fin.ext <| by
rw [val_rev, val_castAdd, val_addNat, val_rev, Nat.sub_add_comm (Nat.succ_le_of_lt k.is_lt)]
rw [val_rev, coe_castAdd, coe_addNat, val_rev, Nat.sub_add_comm (Nat.succ_le_of_lt k.is_lt)]
theorem rev_addNat (k : Fin n) (m : Nat) : rev (addNat k m) = castAdd m (rev k) := by
rw [ rev_rev (castAdd ..), rev_castAdd, rev_rev]
@@ -753,12 +717,7 @@ theorem castSucc_natAdd (n : Nat) (i : Fin k) :
/-! ### pred -/
@[simp] theorem val_pred (j : Fin (n + 1)) (h : j 0) : (j.pred h : Nat) = j - 1 := rfl
grind_pattern val_pred => j.pred h
@[deprecated val_pred (since := "2025-11-21")]
theorem coe_pred (j : Fin (n + 1)) (h : j 0) : (j.pred h : Nat) = j - 1 := rfl
@[simp] theorem coe_pred (j : Fin (n + 1)) (h : j 0) : (j.pred h : Nat) = j - 1 := rfl
@[simp] theorem succ_pred : (i : Fin (n + 1)) (h : i 0), (i.pred h).succ = i
| 0, _, hi => by simp only [mk_zero, ne_eq, not_true] at hi
@@ -776,7 +735,7 @@ theorem pred_eq_iff_eq_succ {n : Nat} {i : Fin (n + 1)} (hi : i ≠ 0) {j : Fin
theorem pred_mk_succ (i : Nat) (h : i < n + 1) :
Fin.pred i + 1, Nat.add_lt_add_right h 1 (ne_of_val_ne (Nat.ne_of_gt (mk_succ_pos i h))) =
i, h := by
simp only [Fin.ext_iff, val_pred, Nat.add_sub_cancel]
simp only [Fin.ext_iff, coe_pred, Nat.add_sub_cancel]
@[simp] theorem pred_mk_succ' (i : Nat) (h₁ : i + 1 < n + 1 + 1) (h₂) :
Fin.pred i + 1, h₁ h₂ = i, Nat.lt_of_succ_lt_succ h₁ := pred_mk_succ i _
@@ -803,13 +762,10 @@ theorem pred_mk {n : Nat} (i : Nat) (h : i < n + 1) (w) : Fin.pred ⟨i, h⟩ w
theorem pred_add_one (i : Fin (n + 2)) (h : (i : Nat) < n + 1) :
pred (i + 1) (Fin.ne_of_gt (add_one_pos _ (lt_def.2 h))) = castLT i h := by
rw [Fin.ext_iff, val_pred, val_castLT, val_add, val_one, Nat.mod_eq_of_lt, Nat.add_sub_cancel]
rw [Fin.ext_iff, coe_pred, coe_castLT, val_add, val_one, Nat.mod_eq_of_lt, Nat.add_sub_cancel]
exact Nat.add_lt_add_right h 1
@[simp, grind =] theorem val_subNat (i : Fin (n + m)) (h : m i) : (i.subNat m h : Nat) = i - m := rfl
@[deprecated val_subNat (since := "2025-11-21")]
theorem coe_subNat (i : Fin (n + m)) (h : m i) : (i.subNat m h : Nat) = i - m := rfl
@[simp] theorem coe_subNat (i : Fin (n + m)) (h : m i) : (i.subNat m h : Nat) = i - m := rfl
@[simp] theorem subNat_mk {i : Nat} (h₁ : i < n + m) (h₂ : m i) :
subNat m i, h₁ h₂ = i - m, Nat.sub_lt_right_of_lt_add h₂ h₁ := rfl
@@ -874,11 +830,11 @@ step. `Fin.succRec` is a version of this induction principle that takes the `Fin
(zero : n, motive (n + 1) 0) (succ : n i, motive n i motive (Nat.succ n) i.succ) :
motive n i := i.succRec zero succ
@[simp, grind =] theorem succRecOn_zero {motive : n, Fin n Sort _} {zero succ} (n) :
@[simp] theorem succRecOn_zero {motive : n, Fin n Sort _} {zero succ} (n) :
@Fin.succRecOn (n + 1) 0 motive zero succ = zero n := by
cases n <;> rfl
@[simp, grind =] theorem succRecOn_succ {motive : n, Fin n Sort _} {zero succ} {n} (i : Fin n) :
@[simp] theorem succRecOn_succ {motive : n, Fin n Sort _} {zero succ} {n} (i : Fin n) :
@Fin.succRecOn (n + 1) i.succ motive zero succ = succ n i (Fin.succRecOn i zero succ) := by
cases i; rfl
@@ -906,11 +862,11 @@ where
| 0, hi => by rwa [Fin.mk_zero]
| i+1, hi => succ i, Nat.lt_of_succ_lt_succ hi (go i (Nat.lt_of_succ_lt hi))
@[simp, grind =] theorem induction_zero {motive : Fin (n + 1) Sort _} (zero : motive 0)
@[simp] theorem induction_zero {motive : Fin (n + 1) Sort _} (zero : motive 0)
(hs : i : Fin n, motive (castSucc i) motive i.succ) :
(induction zero hs : i : Fin (n + 1), motive i) 0 = zero := rfl
@[simp, grind =] theorem induction_succ {motive : Fin (n + 1) Sort _} (zero : motive 0)
@[simp] theorem induction_succ {motive : Fin (n + 1) Sort _} (zero : motive 0)
(succ : i : Fin n, motive (castSucc i) motive i.succ) (i : Fin n) :
induction (motive := motive) zero succ i.succ = succ i (induction zero succ (castSucc i)) := rfl
@@ -942,13 +898,13 @@ The corresponding induction principle is `Fin.induction`.
(zero : motive 0) (succ : i : Fin n, motive i.succ) :
i : Fin (n + 1), motive i := induction zero fun i _ => succ i
@[simp, grind =] theorem cases_zero {n} {motive : Fin (n + 1) Sort _} {zero succ} :
@[simp] theorem cases_zero {n} {motive : Fin (n + 1) Sort _} {zero succ} :
@Fin.cases n motive zero succ 0 = zero := rfl
@[simp, grind =] theorem cases_succ {n} {motive : Fin (n + 1) Sort _} {zero succ} (i : Fin n) :
@[simp] theorem cases_succ {n} {motive : Fin (n + 1) Sort _} {zero succ} (i : Fin n) :
@Fin.cases n motive zero succ i.succ = succ i := rfl
@[simp, grind =] theorem cases_succ' {n} {motive : Fin (n + 1) Sort _} {zero succ}
@[simp] theorem cases_succ' {n} {motive : Fin (n + 1) Sort _} {zero succ}
{i : Nat} (h : i + 1 < n + 1) :
@Fin.cases n motive zero succ i.succ, h = succ i, Nat.lt_of_succ_lt_succ h := rfl
@@ -998,7 +954,7 @@ For the induction:
| j + 1 => go j (by omega) (by omega) (cast j, by omega x)
go _ _ (by omega) last
@[simp, grind =] theorem reverseInduction_last {n : Nat} {motive : Fin (n + 1) Sort _} {zero succ} :
@[simp] theorem reverseInduction_last {n : Nat} {motive : Fin (n + 1) Sort _} {zero succ} :
(reverseInduction zero succ (Fin.last n) : motive (Fin.last n)) = zero := by
rw [reverseInduction, reverseInduction.go]; simp
@@ -1015,7 +971,7 @@ private theorem reverseInduction_castSucc_aux {n : Nat} {motive : Fin (n + 1)
dsimp only
rw [ih _ _ (by omega), eq_comm, reverseInduction.go, dif_neg (by change i.1 + 1 _; omega)]
@[simp, grind =] theorem reverseInduction_castSucc {n : Nat} {motive : Fin (n + 1) Sort _} {zero succ}
@[simp] theorem reverseInduction_castSucc {n : Nat} {motive : Fin (n + 1) Sort _} {zero succ}
(i : Fin n) : reverseInduction (motive := motive) zero succ (castSucc i) =
succ i (reverseInduction zero succ i.succ) := by
rw [reverseInduction, reverseInduction_castSucc_aux _ _ _ i.isLt, reverseInduction]
@@ -1034,11 +990,11 @@ The corresponding induction principle is `Fin.reverseInduction`.
(cast : i : Fin n, motive (castSucc i)) (i : Fin (n + 1)) : motive i :=
reverseInduction last (fun i _ => cast i) i
@[simp, grind =] theorem lastCases_last {n : Nat} {motive : Fin (n + 1) Sort _} {last cast} :
@[simp] theorem lastCases_last {n : Nat} {motive : Fin (n + 1) Sort _} {last cast} :
(Fin.lastCases last cast (Fin.last n) : motive (Fin.last n)) = last :=
reverseInduction_last ..
@[simp, grind =] theorem lastCases_castSucc {n : Nat} {motive : Fin (n + 1) Sort _} {last cast}
@[simp] theorem lastCases_castSucc {n : Nat} {motive : Fin (n + 1) Sort _} {last cast}
(i : Fin n) : (Fin.lastCases last cast (Fin.castSucc i) : motive (Fin.castSucc i)) = cast i :=
reverseInduction_castSucc ..
@@ -1058,11 +1014,11 @@ as `Fin.natAdd m (j : Fin n)`.
if hi : (i : Nat) < m then (castAdd_castLT n i hi) (left (castLT i hi))
else (natAdd_subNat_cast (Nat.le_of_not_lt hi)) (right _)
@[simp, grind =] theorem addCases_left {m n : Nat} {motive : Fin (m + n) Sort _} {left right} (i : Fin m) :
@[simp] theorem addCases_left {m n : Nat} {motive : Fin (m + n) Sort _} {left right} (i : Fin m) :
addCases (motive := motive) left right (Fin.castAdd n i) = left i := by
rw [addCases, dif_pos (castAdd_lt _ _)]; rfl
@[simp, grind =]
@[simp]
theorem addCases_right {m n : Nat} {motive : Fin (m + n) Sort _} {left right} (i : Fin n) :
addCases (motive := motive) left right (natAdd m i) = right i := by
have : ¬(natAdd m i : Nat) < m := Nat.not_lt.2 (le_coe_natAdd ..)
@@ -1095,7 +1051,6 @@ theorem add_ofNat [NeZero n] (x : Fin n) (y : Nat) :
/-! ### sub -/
@[deprecated val_sub (since := "2025-11-21")]
protected theorem coe_sub (a b : Fin n) : ((a - b : Fin n) : Nat) = ((n - b) + a) % n := by
cases a; cases b; rfl
@@ -1147,7 +1102,6 @@ theorem coe_sub_iff_lt {a b : Fin n} : (↑(a - b) : Nat) = n + a - b ↔ a < b
/-! ### neg -/
@[grind =]
theorem val_neg {n : Nat} [NeZero n] (x : Fin n) :
(-x).val = if x = 0 then 0 else n - x.val := by
change (n - x) % n = _
@@ -1163,7 +1117,7 @@ protected theorem sub_eq_add_neg {n : Nat} (x y : Fin n) : x - y = x + -y := by
apply elim0 x
· replace h : NeZero n := h
ext
rw [Fin.val_sub, Fin.val_add, val_neg]
rw [Fin.coe_sub, Fin.val_add, val_neg]
split
· simp_all
· simp [Nat.add_comm]
@@ -1184,6 +1138,9 @@ theorem mul_ofNat [NeZero n] (x : Fin n) (y : Nat) :
@[deprecated mul_ofNat (since := "2025-05-28")] abbrev mul_ofNat' := @mul_ofNat
theorem val_mul {n : Nat} : a b : Fin n, (a * b).val = a.val * b.val % n
| _, _, _, _ => rfl
@[deprecated val_mul (since := "2025-10-26")]
theorem coe_mul {n : Nat} : a b : Fin n, ((a * b : Fin n) : Nat) = a * b % n
| _, _, _, _ => rfl

View File

@@ -129,7 +129,7 @@ protected def forIn {β : Type v} {m : Type v → Type w} [Monad m] (as : FloatA
| ForInStep.yield b => loop i (Nat.le_of_lt h') b
loop as.size (Nat.le_refl _) b
instance [Monad m] : ForIn m FloatArray Float where
instance : ForIn m FloatArray Float where
forIn := FloatArray.forIn
/-- See comment at `forInUnsafe` -/

View File

@@ -1781,16 +1781,6 @@ theorem ediv_lt_ediv_iff_of_dvd_of_neg_of_neg {a b c d : Int} (hb : b < 0) (hd :
obtain x, rfl, y, rfl := hba, hdc
simp [*, Int.ne_of_lt, d.mul_assoc, b.mul_comm]
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_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.lt_of_le_of_lt (Int.mul_ediv_self_le (Int.ne_of_lt hc)) h)
/-! ### `tdiv` and ordering -/
-- Theorems about `tdiv` and ordering, whose `ediv` analogues are in `Bootstrap.lean`.

View File

@@ -377,15 +377,6 @@ protected theorem le_iff_lt_add_one {a b : Int} : a ≤ b ↔ a < b + 1 := by
@[grind =] protected theorem max_def (n m : Int) : max n m = if n m then m else n := rfl
end Int
namespace Lean.Meta.Grind.Lia
scoped grind_pattern Int.min_def => min n m
scoped grind_pattern Int.max_def => max n m
end Lean.Meta.Grind.Lia
namespace Int
@[simp] protected theorem neg_min_neg (a b : Int) : min (-a) (-b) = -max a b := by
rw [Int.min_def, Int.max_def]
simp

View File

@@ -678,7 +678,6 @@ Given this typeclass, termination proofs for well-founded recursion over an iter
`it.finitelyManySteps` as a termination measure.
-/
class Finite (α : Type w) (m : Type w Type w') {β : Type w} [Iterator α m β] : Prop where
/-- The relation of plausible successors is well-founded. -/
wf : WellFounded (IterM.IsPlausibleSuccessorOf (α := α) (m := m))
theorem Finite.wf_of_id {α : Type w} {β : Type w} [Iterator α Id β] [Finite α Id] :
@@ -798,7 +797,6 @@ Given this typeclass, termination proofs for well-founded recursion over an iter
`it.finitelyManySkips` as a termination measure.
-/
class Productive (α m) {β} [Iterator α m β] : Prop where
/-- The relation of plausible successors during skips is well-founded. -/
wf : WellFounded (IterM.IsPlausibleSkipSuccessorOf (α := α) (m := m))
/--

View File

@@ -9,8 +9,6 @@ prelude
public import Init.Data.Iterators.Consumers.Collect
public import Init.Data.Iterators.Consumers.Monadic.Loop
set_option linter.missingDocs true
public section
/-!
@@ -48,9 +46,6 @@ instance (α : Type w) (β : Type w) (n : Type x → Type x') [Monad n]
haveI : ForIn' n (Iter (α := α) β) β _ := Iter.instForIn'
instForInOfForIn'
/--
An implementation of `for h : ... in ... do ...` notation for partial iterators.
-/
@[always_inline, inline]
def Iter.Partial.instForIn' {α : Type w} {β : Type w} {n : Type x Type x'} [Monad n]
[Iterator α Id β] [IteratorLoopPartial α Id n] :
@@ -68,12 +63,12 @@ instance (α : Type w) (β : Type w) (n : Type x → Type x') [Monad n]
instForInOfForIn'
instance {m : Type x Type x'}
{α : Type w} {β : Type w} [Iterator α Id β] [Finite α Id] [IteratorLoop α Id m] [Monad m] :
{α : Type w} {β : Type w} [Iterator α Id β] [Finite α Id] [IteratorLoop α Id m] :
ForM m (Iter (α := α) β) β where
forM it f := forIn it PUnit.unit (fun out _ => do f out; return .yield .unit)
instance {m : Type x Type x'}
{α : Type w} {β : Type w} [Iterator α Id β] [Finite α Id] [IteratorLoopPartial α Id m] [Monad m] :
{α : Type w} {β : Type w} [Iterator α Id β] [Finite α Id] [IteratorLoopPartial α Id m] :
ForM m (Iter.Partial (α := α) β) β where
forM it f := forIn it PUnit.unit (fun out _ => do f out; return .yield .unit)
@@ -207,11 +202,6 @@ def Iter.all {α β : Type w}
(p : β Bool) (it : Iter (α := α) β) : Bool :=
(it.allM (fun x => pure (f := Id) (p x))).run
/--
Steps through the iterator until the monadic function `f` returns `some` for an element, at which
point iteration stops and the result of `f` is returned. If the iterator is completely consumed
without `f` returning `some`, then the result is `none`.
-/
@[inline]
def Iter.findSomeM? {α β : Type w} {γ : Type x} {m : Type x Type w'} [Monad m] [Iterator α Id β]
[IteratorLoop α Id m] [Finite α Id] (it : Iter (α := α) β) (f : β m (Option γ)) :
@@ -221,7 +211,7 @@ def Iter.findSomeM? {α β : Type w} {γ : Type x} {m : Type x → Type w'} [Mon
| none => return .yield none
| some fx => return .done (some fx))
@[inline, inherit_doc Iter.findSomeM?]
@[inline]
def Iter.Partial.findSomeM? {α β : Type w} {γ : Type x} {m : Type x Type w'} [Monad m]
[Iterator α Id β] [IteratorLoopPartial α Id m] (it : Iter.Partial (α := α) β)
(f : β m (Option γ)) :
@@ -231,50 +221,36 @@ def Iter.Partial.findSomeM? {α β : Type w} {γ : Type x} {m : Type x → Type
| none => return .yield none
| some fx => return .done (some fx))
/--
Steps through the iterator until `f` returns `some` for an element, at which point iteration stops
and the result of `f` is returned. If the iterator is completely consumed without `f` returning
`some`, then the result is `none`.
-/
@[inline]
def Iter.findSome? {α β : Type w} {γ : Type x} [Iterator α Id β]
[IteratorLoop α Id Id] [Finite α Id] (it : Iter (α := α) β) (f : β Option γ) :
Option γ :=
Id.run (it.findSomeM? (pure <| f ·))
@[inline, inherit_doc Iter.findSome?]
@[inline]
def Iter.Partial.findSome? {α β : Type w} {γ : Type x} [Iterator α Id β]
[IteratorLoopPartial α Id Id] (it : Iter.Partial (α := α) β) (f : β Option γ) :
Option γ :=
Id.run (it.findSomeM? (pure <| f ·))
/--
Steps through the iterator until an element satisfies the monadic predicate `f`, at which point
iteration stops and the element is returned. If no element satisfies `f`, then the result is
`none`.
-/
@[inline]
def Iter.findM? {α β : Type w} {m : Type w Type w'} [Monad m] [Iterator α Id β]
[IteratorLoop α Id m] [Finite α Id] (it : Iter (α := α) β) (f : β m (ULift Bool)) :
m (Option β) :=
it.findSomeM? (fun x => return if ( f x).down then some x else none)
@[inline, inherit_doc Iter.findM?]
@[inline]
def Iter.Partial.findM? {α β : Type w} {m : Type w Type w'} [Monad m] [Iterator α Id β]
[IteratorLoopPartial α Id m] (it : Iter.Partial (α := α) β) (f : β m (ULift Bool)) :
m (Option β) :=
it.findSomeM? (fun x => return if ( f x).down then some x else none)
/--
Steps through the iterator until an element satisfies `f`, at which point iteration stops and the
element is returned. If no element satisfies `f`, then the result is `none`.
-/
@[inline]
def Iter.find? {α β : Type w} [Iterator α Id β] [IteratorLoop α Id Id]
[Finite α Id] (it : Iter (α := α) β) (f : β Bool) : Option β :=
Id.run (it.findM? (pure <| .up <| f ·))
@[inline, inherit_doc Iter.find?]
@[inline]
def Iter.Partial.find? {α β : Type w} [Iterator α Id β] [IteratorLoopPartial α Id Id]
(it : Iter.Partial (α := α) β) (f : β Bool) : Option β :=
Id.run (it.findM? (pure <| .up <| f ·))

View File

@@ -247,10 +247,10 @@ This `ForIn'`-style loop construct traverses a finite iterator using an `Iterato
-/
@[always_inline, inline]
def IteratorLoop.finiteForIn' {m : Type w Type w'} {n : Type x Type x'}
{α : Type w} {β : Type w} [Iterator α m β] [Finite α m] [IteratorLoop α m n] [Monad n]
{α : Type w} {β : Type w} [Iterator α m β] [Finite α m] [IteratorLoop α m n]
(lift : γ δ, (γ n δ) m γ n δ) :
ForIn' n (IterM (α := α) m β) β fun it out => it.IsPlausibleIndirectOutput out where
forIn' {γ} it init f :=
forIn' {γ} [Monad n] it init f :=
IteratorLoop.forIn (α := α) (m := m) lift γ (fun _ _ _ => True)
wellFounded_of_finite
it init (fun out h acc => (·, .intro) <$> f out h acc)
@@ -288,13 +288,13 @@ instance {m : Type w → Type w'} {n : Type w → Type w''}
instForInOfForIn'
instance {m : Type w Type w'} {n : Type w Type w''}
{α : Type w} {β : Type w} [Iterator α m β] [Finite α m] [IteratorLoop α m n] [Monad n]
{α : Type w} {β : Type w} [Iterator α m β] [Finite α m] [IteratorLoop α m n]
[MonadLiftT m n] :
ForM n (IterM (α := α) m β) β where
forM it f := forIn it PUnit.unit (fun out _ => do f out; return .yield .unit)
instance {m : Type w Type w'} {n : Type w Type w''}
{α : Type w} {β : Type w} [Iterator α m β] [IteratorLoopPartial α m n] [Monad n]
{α : Type w} {β : Type w} [Iterator α m β] [IteratorLoopPartial α m n]
[MonadLiftT m n] :
ForM n (IterM.Partial (α := α) m β) β where
forM it f := forIn it PUnit.unit (fun out _ => do f out; return .yield .unit)

View File

@@ -19,45 +19,110 @@ open Std.Iterators
namespace Std.Iterators
/-- This typeclass provides an iterator for elements of type `γ`. -/
class ToIterator (γ : Type u) (m : Type w Type w') (α β : outParam (Type w)) where
iterMInternal (x : γ) : IterM (α := α) m β
/--
This typeclass provides an iterator for the given element `x : γ`. Usually, instances are provided
for all elements of a type `γ`.
-/
class ToIterator {γ : Type u} (x : γ) (m : Type w Type w') (β : outParam (Type w)) where
State : Type w
iterMInternal : IterM (α := State) m β
/-- Converts `x` into a monadic iterator. -/
@[always_inline, inline, expose]
def ToIterator.iterM (x : γ) [ToIterator γ m α β] : IterM (α := α) m β :=
def ToIterator.iterM (x : γ) [ToIterator x m β] : IterM (α := ToIterator.State x m) m β :=
ToIterator.iterMInternal (x := x)
/-- Converts `x` into a pure iterator. -/
@[always_inline, inline, expose]
def ToIterator.iter [ToIterator γ Id α β] (x : γ) : Iter (α := α) β :=
def ToIterator.iter (x : γ) [ToIterator x Id β] : Iter (α := ToIterator.State x Id) β :=
ToIterator.iterM x |>.toIter
/-- Creates a monadic `ToIterator` instance. -/
@[always_inline, inline, expose]
def ToIterator.ofM (α : Type w)
(iterM : γ IterM (α := α) m β) :
ToIterator γ m α β where
iterMInternal x := iterM x
def ToIterator.ofM {x : γ} (State : Type w)
(iterM : IterM (α := State) m β) :
ToIterator x m β where
State := State
iterMInternal := iterM
/-- Creates a pure `ToIterator` instance. -/
@[always_inline, inline, expose]
def ToIterator.of (α : Type w)
(iter : γ Iter (α := α) β) :
ToIterator γ Id α β where
iterMInternal x := iter x |>.toIterM
def ToIterator.of {x : γ} (State : Type w)
(iter : Iter (α := State) β) :
ToIterator x Id β where
State := State
iterMInternal := iter.toIterM
/-- Replaces `ToIterator.iterM` with its definition. -/
theorem ToIterator.iterM_eq {γ : Type u} {α β : Type v}
{it : γ IterM (α := α) Id β} {x} :
letI : ToIterator γ Id α β := .ofM α it
ToIterator.iterM x = it x :=
theorem ToIterator.iterM_eq {γ : Type u} {x : γ} {State : Type v} {β : Type v} {it} :
letI : ToIterator x Id β := .ofM State it
ToIterator.iterM x = it :=
rfl
/-- Replaces `ToIterator.iter` with its definition. -/
theorem ToIterator.iter_eq {γ : Type u} {x : γ} {α β : Type v} {it} :
letI : ToIterator γ Id α β := .ofM α it
ToIterator.iter x = (it x).toIter :=
theorem ToIterator.iter_eq {γ : Type u} {x : γ} {State : Type v} {β : Type v} {it} :
letI : ToIterator x Id β := .ofM State it
ToIterator.iter x = it.toIter :=
rfl
/-!
## Instance forwarding
If the type defined as `ToIterator.State` implements an iterator typeclass, then this typeclass
should also be available when the type is syntactically visible as `ToIteratorState`. The following
instances are responsible for this forwarding.
-/
instance {x : γ} {State : Type w} {iter}
[Iterator State m β] :
letI i : ToIterator x m β := .ofM State iter
Iterator (α := i.State) m β :=
inferInstanceAs <| Iterator State m β
instance {x : γ} {State : Type w} {iter}
[Iterator (α := State) m β] [Finite State m] :
letI i : ToIterator x m β := .ofM State iter
Finite (α := i.State) m :=
inferInstanceAs <| Finite (α := State) m
instance {x : γ} {State : Type w} {iter}
[Iterator (α := State) m β] [IteratorCollect State m n] :
letI i : ToIterator x m β := .ofM State iter
IteratorCollect (α := i.State) m n :=
inferInstanceAs <| IteratorCollect (α := State) m n
instance {x : γ} {State : Type w} {iter} [Monad m] [Monad n]
[Iterator (α := State) m β] [IteratorCollect State m n] [LawfulIteratorCollect State m n] :
letI i : ToIterator x m β := .ofM State iter
LawfulIteratorCollect (α := i.State) m n :=
inferInstanceAs <| LawfulIteratorCollect (α := State) m n
instance {x : γ} {State : Type w} {iter}
[Iterator (α := State) m β] [IteratorCollectPartial State m n] :
letI i : ToIterator x m β := .ofM State iter
IteratorCollectPartial (α := i.State) m n :=
inferInstanceAs <| IteratorCollectPartial (α := State) m n
instance {x : γ} {State : Type w} {iter}
[Iterator (α := State) m β] [IteratorLoop State m n] :
letI i : ToIterator x m β := .ofM State iter
IteratorLoop (α := i.State) m n :=
inferInstanceAs <| IteratorLoop (α := State) m n
instance {x : γ} {State : Type w} {iter} [Monad m] [Monad n]
[Iterator (α := State) m β] [IteratorLoop State m n] [LawfulIteratorLoop State m n]:
letI i : ToIterator x m β := .ofM State iter
LawfulIteratorLoop (α := i.State) m n :=
inferInstanceAs <| LawfulIteratorLoop (α := State) m n
instance {x : γ} {State : Type w} {iter}
[Iterator (α := State) m β] [IteratorLoopPartial State m n] :
letI i : ToIterator x m β := .ofM State iter
IteratorLoopPartial (α := i.State) m n :=
inferInstanceAs <| IteratorLoopPartial (α := State) m n
@[simp]
theorem ToIterator.state_eq {x : γ} {State : Type w} {iter} :
haveI : ToIterator x Id β := .of State iter
ToIterator.State x Id = State :=
rfl
end Std.Iterators

View File

@@ -471,7 +471,7 @@ theorem findM?_eq_findSomeM? [Monad m] [LawfulMonad m] {p : α → m Bool} {as :
loop as' b this
loop as init [], rfl
instance [Monad m] : ForIn' m (List α) α inferInstance where
instance : ForIn' m (List α) α inferInstance where
forIn' := List.forIn'
-- No separate `ForIn` instance is required because it can be derived from `ForIn'`.
@@ -485,7 +485,7 @@ instance [Monad m] : ForIn' m (List α) α inferInstance where
@[simp, grind =] theorem forIn_nil [Monad m] {f : α β m (ForInStep β)} {b : β} : forIn [] b f = pure b :=
rfl
instance [Monad m] : ForM m (List α) α where
instance : ForM m (List α) α where
forM := List.forM
-- We simplify `List.forM` to `forM`.

View File

@@ -13,9 +13,6 @@ import all Init.Data.List.BasicAux
public import Init.Data.List.Control
import all Init.Data.List.Control
public import Init.BinderPredicates
import Init.Grind.Annotated
grind_annotated "2025-01-24"
public section
@@ -254,10 +251,6 @@ theorem getElem_eq_getElem?_get {l : List α} {i : Nat} (h : i < l.length) :
l[i] = l[i]?.get (by simp [h]) := by
simp
theorem getElem_eq_getD {l : List α} {i : Nat} {h : i < l.length} (fallback : α) :
l[i] = l.getD i fallback := by
rw [getElem_eq_getElem?_get, List.getD, Option.get_eq_getD]
theorem getD_getElem? {l : List α} {i : Nat} {d : α} :
l[i]?.getD d = if p : i < l.length then l[i]'p else d := by
if h : i < l.length then

View File

@@ -168,10 +168,10 @@ Examples:
| none , _ => pure
| some a, f => f a
instance [Monad m] : ForM m (Option α) α :=
instance : ForM m (Option α) α :=
Option.forM
instance [Monad m] : ForIn' m (Option α) α inferInstance where
instance : ForIn' m (Option α) α inferInstance where
forIn' x init f := do
match x with
| none => return init

View File

@@ -631,7 +631,7 @@ instance [Ord α] : DecidableRel (@LT.lt α ltOfOrd) := fun a b =>
decidable_of_bool (compare a b).isLT Ordering.isLT_iff_eq_lt
/--
Constructs an `LE` instance from an `Ord` instance that asserts that the result of `compare`
Constructs an `LT` instance from an `Ord` instance that asserts that the result of `compare`
satisfies `Ordering.isLE`.
-/
@[expose] def leOfOrd [Ord α] : LE α where

View File

@@ -43,7 +43,7 @@ universe u v
have := range.step_pos
loop init range.start (by simp)
instance [Monad m] : ForIn' m Range Nat inferInstance where
instance : ForIn' m Range Nat inferInstance where
forIn' := Range.forIn'
-- No separate `ForIn` instance is required because it can be derived from `ForIn'`.
@@ -59,7 +59,7 @@ instance [Monad m] : ForIn' m Range Nat inferInstance where
have := range.step_pos
loop range.start
instance [Monad m] : ForM m Range Nat where
instance : ForM m Range Nat where
forM := Range.forM
syntax:max "[" withoutPosition(":" term) "]" : term

View File

@@ -9,7 +9,6 @@ prelude
public import Init.Data.Range.Polymorphic.UpwardEnumerable
set_option doc.verso true
set_option linter.missingDocs true
public section
@@ -24,13 +23,7 @@ A range of elements of {given}`α` with closed lower and upper bounds.
equal to {given}`b : α`. This is notation for {lean}`Rcc.mk a b`.
-/
structure Rcc (α : Type u) where
/--
The lower bound of the range. {name (full := Rcc.lower)}`lower` is included in the range.
-/
lower : α
/--
The upper bound of the range. {name (full := Rcc.upper)}`upper` is included in the range.
-/
upper : α
/--
@@ -40,13 +33,7 @@ A range of elements of {given}`α` with a closed lower bound and an open upper b
less than {given}`b : α`. This is notation for {lean}`Rco.mk a b`.
-/
structure Rco (α : Type u) where
/--
The lower bound of the range. {name (full := Rco.lower)}`lower` is included in the range.
-/
lower : α
/--
The upper bound of the range. {name (full := Rco.upper)}`upper` is not included in the range.
-/
upper : α
/--
@@ -56,9 +43,6 @@ An upward-unbounded range of elements of {given}`α` with a closed lower bound.
This is notation for {lean}`Rci.mk a`.
-/
structure Rci (α : Type u) where
/--
The lower bound of the range. {name (full := Rci.lower)}`lower` is included in the range.
-/
lower : α
/--
@@ -68,13 +52,7 @@ A range of elements of {given}`α` with an open lower bound and a closed upper b
{given}`b : α`. This is notation for {lean}`Roc.mk a b`.
-/
structure Roc (α : Type u) where
/--
The lower bound of the range. {name (full := Roc.lower)}`lower` is not included in the range.
-/
lower : α
/--
The upper bound of the range. {name (full := Roc.upper)}`upper` is included in the range.
-/
upper : α
/--
@@ -84,13 +62,7 @@ A range of elements of {given}`α` with an open lower and upper bounds.
{given}`b : α`. This is notation for {lean}`Roo.mk a b`.
-/
structure Roo (α : Type u) where
/--
The lower bound of the range. {name (full := Roo.lower)}`lower` is not included in the range.
-/
lower : α
/--
The upper bound of the range. {name (full := Roo.upper)}`upper` is not included in the range.
-/
upper : α
/--
@@ -100,9 +72,6 @@ An upward-unbounded range of elements of {given}`α` with an open lower bound.
This is notation for {lean}`Roi.mk a`.
-/
structure Roi (α : Type u) where
/--
The lower bound of the range. {name (full := Roi.lower)}`lower` is not included in the range.
-/
lower : α
/--
@@ -112,9 +81,6 @@ A downward-unbounded range of elements of {given}`α` with a closed upper bound.
This is notation for {lean}`Ric.mk b`.
-/
structure Ric (α : Type u) where
/--
The upper bound of the range. {name (full := Ric.upper)}`upper` is included in the range.
-/
upper : α
/--
@@ -124,9 +90,6 @@ A downward-unbounded range of elements of {given}`α` with an open upper bound.
This is notation for {lean}`Rio.mk b`.
-/
structure Rio (α : Type u) where
/--
The upper bound of the range. {name (full := Rio.upper)}`upper` is not included in the range.
-/
upper : α
/--
@@ -199,10 +162,6 @@ This is a prerequisite for many functions and instances, such as
{name (scope := "Init.Data.Range.Polymorphic.Iterators")}`Rcc.toList` or {name}`ForIn'`.
-/
class Rxc.IsAlwaysFinite (α : Type u) [UpwardEnumerable α] [LE α] : Prop where
/--
For every pair of elements {name}`init` and {name}`hi`, there exists a chain of successors that
results in an element that either has no successors or is greater than {name}`hi`.
-/
finite (init : α) (hi : α) :
n, (UpwardEnumerable.succMany? n init).elim True (¬ · hi)
@@ -213,10 +172,6 @@ This is a prerequisite for many functions and instances, such as
{name (scope := "Init.Data.Range.Polymorphic.Iterators")}`Rco.toList` or {name}`ForIn'`.
-/
class Rxo.IsAlwaysFinite (α : Type u) [UpwardEnumerable α] [LT α] : Prop where
/--
For every pair of elements {name}`init` and {name}`hi`, there exists a chain of successors that
results in an element that either has no successors or is greater than {name}`hi`.
-/
finite (init : α) (hi : α) :
n, (UpwardEnumerable.succMany? n init).elim True (¬ · < hi)
@@ -227,10 +182,6 @@ This is a prerequisite for many functions and instances, such as
{name (scope := "Init.Data.Range.Polymorphic.Iterators")}`Rci.toList` or {name}`ForIn'`.
-/
class Rxi.IsAlwaysFinite (α : Type u) [UpwardEnumerable α] : Prop where
/--
For every elements {name}`init`, there exists a chain of successors that
results in an element that has no successors.
-/
finite (init : α) : n, UpwardEnumerable.succMany? n init = none
namespace Rcc
@@ -340,7 +291,6 @@ This type class allows taking the intersection of a closed range with a
left-closed right-open range, resulting in another left-closed right-open range.
-/
class Rcc.HasRcoIntersection (α : Type w) where
/-- The intersection operator. -/
intersection : Rcc α Rco α Rco α
/--
@@ -349,9 +299,6 @@ of two ranges contains exactly those elements that are contained in both ranges.
-/
class Rcc.LawfulRcoIntersection (α : Type w) [LT α] [LE α]
[HasRcoIntersection α] where
/--
Every element of the intersection is an element of both original ranges.
-/
mem_intersection_iff {a : α} {r : Rcc α} {s : Rco α} :
a HasRcoIntersection.intersection r s a r a s
@@ -360,7 +307,6 @@ This type class allows taking the intersection of two left-closed right-open ran
another left-closed right-open range.
-/
class Rco.HasRcoIntersection (α : Type w) where
/-- The intersection operator. -/
intersection : Rco α Rco α Rco α
/--
@@ -369,9 +315,6 @@ of two ranges contains exactly those elements that are contained in both ranges.
-/
class Rco.LawfulRcoIntersection (α : Type w) [LT α] [LE α]
[HasRcoIntersection α] where
/--
Every element of the intersection is an element of both original ranges.
-/
mem_intersection_iff {a : α} {r : Rco α} {s : Rco α} :
a HasRcoIntersection.intersection r s a r a s
@@ -380,7 +323,6 @@ This type class allows taking the intersection of a left-closed right-unbounded
left-closed right-open range, resulting in another left-closed right-open range.
-/
class Rci.HasRcoIntersection (α : Type w) where
/-- The intersection operator. -/
intersection : Rci α Rco α Rco α
/--
@@ -389,9 +331,6 @@ of two ranges contains exactly those elements that are contained in both ranges.
-/
class Rci.LawfulRcoIntersection (α : Type w) [LT α] [LE α]
[HasRcoIntersection α] where
/--
Every element of the intersection is an element of both original ranges.
-/
mem_intersection_iff {a : α} {r : Rci α} {s : Rco α} :
a HasRcoIntersection.intersection r s a r a s
@@ -400,7 +339,6 @@ This type class allows taking the intersection of a left-open right-closed range
left-closed right-open range, resulting in another left-closed right-open range.
-/
class Roc.HasRcoIntersection (α : Type w) where
/-- The intersection operator. -/
intersection : Roc α Rco α Rco α
/--
@@ -409,9 +347,6 @@ of two ranges contains exactly those elements that are contained in both ranges.
-/
class Roc.LawfulRcoIntersection (α : Type w) [LT α] [LE α]
[HasRcoIntersection α] where
/--
Every element of the intersection is an element of both original ranges.
-/
mem_intersection_iff {a : α} {r : Roc α} {s : Rco α} :
a HasRcoIntersection.intersection r s a r a s
@@ -420,7 +355,6 @@ This type class allows taking the intersection of an open range with a
left-closed right-open range, resulting in another left-closed right-open range.
-/
class Roo.HasRcoIntersection (α : Type w) where
/-- The intersection operator. -/
intersection : Roo α Rco α Rco α
/--
@@ -429,9 +363,6 @@ of two ranges contains exactly those elements that are contained in both ranges.
-/
class Roo.LawfulRcoIntersection (α : Type w) [LT α] [LE α]
[HasRcoIntersection α] where
/--
Every element of the intersection is an element of both original ranges.
-/
mem_intersection_iff {a : α} {r : Roo α} {s : Rco α} :
a HasRcoIntersection.intersection r s a r a s
@@ -440,7 +371,6 @@ This type class allows taking the intersection of a left-open right-unbounded ra
left-closed right-open range, resulting in another left-closed right-open range.
-/
class Roi.HasRcoIntersection (α : Type w) where
/-- The intersection operator. -/
intersection : Roi α Rco α Rco α
/--
@@ -449,9 +379,6 @@ of two ranges contains exactly those elements that are contained in both ranges.
-/
class Roi.LawfulRcoIntersection (α : Type w) [LT α] [LE α]
[HasRcoIntersection α] where
/--
Every element of the intersection is an element of both original ranges.
-/
mem_intersection_iff {a : α} {r : Roi α} {s : Rco α} :
a HasRcoIntersection.intersection r s a r a s
@@ -460,7 +387,6 @@ This type class allows taking the intersection of a left-unbounded right-closed
left-closed right-open range, resulting in another left-closed right-open range.
-/
class Ric.HasRcoIntersection (α : Type w) where
/-- The intersection operator. -/
intersection : Ric α Rco α Rco α
/--
@@ -469,9 +395,6 @@ of two ranges contains exactly those elements that are contained in both ranges.
-/
class Ric.LawfulRcoIntersection (α : Type w) [LT α] [LE α]
[HasRcoIntersection α] where
/--
Every element of the intersection is an element of both original ranges.
-/
mem_intersection_iff {a : α} {r : Ric α} {s : Rco α} :
a HasRcoIntersection.intersection r s a r a s
@@ -480,7 +403,6 @@ This type class allows taking the intersection of a left-unbounded right-open ra
left-closed right-open range, resulting in another left-closed right-open range.
-/
class Rio.HasRcoIntersection (α : Type w) where
/-- The intersection operator. -/
intersection : Rio α Rco α Rco α
/--
@@ -489,9 +411,6 @@ of two ranges contains exactly those elements that are contained in both ranges.
-/
class Rio.LawfulRcoIntersection (α : Type w) [LT α] [LE α]
[HasRcoIntersection α] where
/--
Every element of the intersection is an element of both original ranges.
-/
mem_intersection_iff {a : α} {r : Rio α} {s : Rco α} :
a HasRcoIntersection.intersection r s a r a s

View File

@@ -295,15 +295,6 @@ theorem add_def (a b : Rat) :
theorem add_def' (a b : Rat) : a + b = mkRat (a.num * b.den + b.num * a.den) (a.den * b.den) := by
rw [add_def, normalize_eq_mkRat]
theorem num_add (a b : Rat) : (a + b).num =
(a.num * b.den + b.num * a.den) /
((a.num * b.den + b.num * a.den).natAbs.gcd (a.den * b.den)) := by
rw [add_def, num_normalize]
theorem den_add (a b : Rat) : (a + b).den =
a.den * b.den / (a.num * b.den + b.num * a.den).natAbs.gcd (a.den * b.den) := by
rw [add_def, den_normalize]
@[local simp]
protected theorem add_zero (a : Rat) : a + 0 = a := by simp [add_def', mkRat_self]
@[local simp]
@@ -415,13 +406,6 @@ theorem mul_def (a b : Rat) :
theorem mul_def' (a b : Rat) : a * b = mkRat (a.num * b.num) (a.den * b.den) := by
rw [mul_def, normalize_eq_mkRat]
theorem num_mul (a b : Rat) :
(a * b).num = a.num * b.num / ((a.num * b.num).natAbs.gcd (a.den * b.den)) := by
rw [mul_def, num_normalize]
theorem den_mul (a b : Rat) :
(a * b).den = a.den * b.den / (a.num * b.num).natAbs.gcd (a.den * b.den) := by
rw [mul_def, den_normalize]
protected theorem mul_comm (a b : Rat) : a * b = b * a := by
simp [mul_def, normalize_eq_mkRat, Int.mul_comm, Nat.mul_comm]

View File

@@ -26,55 +26,38 @@ open Std Slice PRange Iterators
variable {shape : RangeShape} {α : Type u}
@[unbox]
structure SubarrayIterator (α : Type u) where
xs : Subarray α
@[inline, expose]
def SubarrayIterator.step :
IterM (α := SubarrayIterator α) Id α IterStep (IterM (α := SubarrayIterator α) m α) α
| xs =>
if h : xs.start < xs.stop then
have := xs.start_le_stop; have := xs.stop_le_array_size
.yield xs.array, xs.start + 1, xs.stop, by omega, xs.stop_le_array_size xs.array[xs.start]
else
.done
instance : Iterator (SubarrayIterator α) Id α where
IsPlausibleStep it step := step = SubarrayIterator.step it
step it := pure <| .deflate SubarrayIterator.step it, rfl
private def SubarrayIterator.instFinitelessRelation : FinitenessRelation (SubarrayIterator α) Id where
rel := InvImage WellFoundedRelation.rel (fun it => it.internalState.xs.stop - it.internalState.xs.start)
wf := InvImage.wf _ WellFoundedRelation.wf
subrelation {it it'} h := by
simp [IterM.IsPlausibleSuccessorOf, IterM.IsPlausibleStep, Iterator.IsPlausibleStep, step] at h
split at h
· cases h
simp only [InvImage, Subarray.stop, Subarray.start, WellFoundedRelation.rel, InvImage,
Nat.lt_wfRel, sizeOf_nat]
exact Nat.sub_succ_lt_self _ _ _
· cases h
instance SubarrayIterator.instFinite : Finite (SubarrayIterator α) Id :=
.of_finitenessRelation instFinitelessRelation
instance [Monad m] : IteratorCollect (SubarrayIterator α) Id m := .defaultImplementation
instance [Monad m] : IteratorCollectPartial (SubarrayIterator α) Id m := .defaultImplementation
instance [Monad m] : IteratorLoop (SubarrayIterator α) Id m := .defaultImplementation
instance [Monad m] : IteratorLoopPartial (SubarrayIterator α) Id m := .defaultImplementation
@[inline, expose]
def Subarray.instToIterator :=
ToIterator.of (γ := Slice (Internal.SubarrayData α)) (β := α) (SubarrayIterator α) (·)
attribute [instance] Subarray.instToIterator
instance {s : Slice (Internal.SubarrayData α)} : ToIterator s Id α :=
.of _
(Rco.Internal.iter (s.internalRepresentation.start...<s.internalRepresentation.stop)
|>.attachWith (· < s.internalRepresentation.array.size) ?h
|>.uLift
|>.map fun | .up i => s.internalRepresentation.array[i.1])
where finally
case h =>
simp only [Rco.Internal.isPlausibleIndirectOutput_iter_iff, Membership.mem, and_imp]
intro out _ h
have := s.internalRepresentation.stop_le_array_size
omega
universe v w
@[no_expose] instance {s : Slice (Internal.SubarrayData α)} : Iterator (ToIterator.State s Id) Id α := inferInstance
@[no_expose] instance {s : Slice (Internal.SubarrayData α)} : Finite (ToIterator.State s Id) Id := inferInstance
@[no_expose] instance {s : Slice (Internal.SubarrayData α)} : IteratorCollect (ToIterator.State s Id) Id Id := inferInstance
@[no_expose] instance {s : Slice (Internal.SubarrayData α)} : LawfulIteratorCollect (ToIterator.State s Id) Id Id := inferInstance
@[no_expose] instance {s : Slice (Internal.SubarrayData α)} : IteratorCollectPartial (ToIterator.State s Id) Id Id := inferInstance
@[no_expose] instance {s : Slice (Internal.SubarrayData α)} {m : Type v Type w} [Monad m] :
IteratorLoop (ToIterator.State s Id) Id m := inferInstance
@[no_expose] instance {s : Slice (Internal.SubarrayData α)} {m : Type v Type w} [Monad m] :
LawfulIteratorLoop (ToIterator.State s Id) Id m := inferInstance
@[no_expose] instance {s : Slice (Internal.SubarrayData α)} {m : Type v Type w} [Monad m] :
IteratorLoopPartial (ToIterator.State s Id) Id m := inferInstance
instance : SliceSize (Internal.SubarrayData α) where
size s := s.internalRepresentation.stop - s.internalRepresentation.start
instance {α : Type u} {m : Type v Type w} [Monad m] : ForIn m (Subarray α) α :=
instance {α : Type u} {m : Type v Type w} [Monad m] :
ForIn m (Subarray α) α :=
inferInstance
/-!

View File

@@ -22,91 +22,36 @@ import Init.Data.Range.Polymorphic.NatLemmas
open Std Std.Iterators Std.PRange Std.Slice
namespace SubarrayIterator
theorem step_eq {it : Iter (α := SubarrayIterator α) α} :
it.step = if h : it.1.xs.start < it.1.xs.stop then
haveI := it.1.xs.start_le_stop
haveI := it.1.xs.stop_le_array_size
.yield it.1.xs.array, it.1.xs.start + 1, it.1.xs.stop, by omega, by assumption
(it.1.xs.array[it.1.xs.start]'(by omega)),
(by
simp_all [Iter.IsPlausibleStep, IterM.IsPlausibleStep, Iterator.IsPlausibleStep,
SubarrayIterator.step, Iter.toIterM])
else
.done, (by
simpa [Iter.IsPlausibleStep, IterM.IsPlausibleStep, Iterator.IsPlausibleStep,
SubarrayIterator.step] using h) := by
simp only [Iter.step, IterM.Step.toPure, Iter.toIter_toIterM, IterStep.mapIterator, IterM.step,
Iterator.step, SubarrayIterator.step, Id.run_pure, Shrink.inflate_deflate]
by_cases h : it.internalState.xs.start < it.internalState.xs.stop
· simp only [h, reduceDIte]
split
· rfl
· rename_i h'
exact h'.elim h
· simp only [h, reduceDIte]
split
· rename_i h'
exact h.elim h'
· rfl
theorem val_step_eq {it : Iter (α := SubarrayIterator α) α} :
it.step.val = if h : it.1.xs.start < it.1.xs.stop then
haveI := it.1.xs.start_le_stop
haveI := it.1.xs.stop_le_array_size
.yield it.1.xs.array, it.1.xs.start + 1, it.1.xs.stop, by omega, by assumption
it.1.xs.array[it.1.xs.start]
else
.done := by
simp only [step_eq]
split <;> simp
theorem toList_eq {α : Type u} {it : Iter (α := SubarrayIterator α) α} :
it.toList =
(it.internalState.xs.array.toList.take it.internalState.xs.stop).drop it.internalState.xs.start := by
induction it using Iter.inductSteps with | step it ihy ihs
rw [Iter.toList_eq_match_step, SubarrayIterator.val_step_eq]
by_cases h : it.internalState.xs.start < it.internalState.xs.stop
· simp [h]
have := it.1.xs.start_le_stop
have := it.1.xs.stop_le_array_size
rw [ihy (out := it.internalState.xs.array[it.internalState.xs.start])]
· simp only [Subarray.start]
rw (occs := [2]) [List.drop_eq_getElem_cons]; rotate_left
· rw [List.length_take]
simp [it.internalState.xs.stop_le_array_size]
exact h
· simp [Subarray.array, Subarray.stop]
· simp only [Iter.IsPlausibleStep, IterM.IsPlausibleStep, Iterator.IsPlausibleStep,
IterStep.mapIterator_yield, SubarrayIterator.step]
rw [dif_pos]; rotate_left; exact h
rfl
· rw [dif_neg]; rotate_left; exact h
simp_all [it.internalState.xs.stop_le_array_size]
theorem count_eq {α : Type u} {it : Iter (α := SubarrayIterator α) α} :
it.count = it.internalState.xs.stop - it.internalState.xs.start := by
simp [ Iter.length_toList_eq_count, toList_eq, it.internalState.xs.stop_le_array_size]
end SubarrayIterator
namespace Subarray
theorem internalIter_eq {α : Type u} {s : Subarray α} :
Internal.iter s = s :=
rfl
Internal.iter s = (Rco.Internal.iter (s.start...<s.stop)
|>.attachWith (· < s.array.size)
(fun out h => h
|> Rco.Internal.isPlausibleIndirectOutput_iter_iff.mp
|> Rco.lt_upper_of_mem
|> (Nat.lt_of_lt_of_le · s.stop_le_array_size))
|>.uLift
|>.map fun | .up i => s.array[i.1]) := by
simp [Internal.iter, ToIterator.iter_eq, Subarray.start, Subarray.stop, Subarray.array]
theorem toList_internalIter {α : Type u} {s : Subarray α} :
(Internal.iter s).toList =
(s.array.toList.take s.stop).drop s.start := by
simp [SubarrayIterator.toList_eq, Internal.iter_eq_toIteratorIter, ToIterator.iter_eq]
((s.start...s.stop).toList
|>.attach
|>.map fun i => s.array[i.1]'(i.property
|> Rco.mem_toList_iff_mem.mp
|> Rco.lt_upper_of_mem
|> (Nat.lt_of_lt_of_le · s.stop_le_array_size))) := by
rw [internalIter_eq, Iter.toList_map, Iter.toList_uLift, Iter.toList_attachWith]
simp [Rco.toList]
public instance : LawfulSliceSize (Internal.SubarrayData α) where
lawful s := by
simp [SliceSize.size, ToIterator.iter_eq, Iter.toIter_toIterM,
Iter.length_toList_eq_count, SubarrayIterator.toList_eq,
s.internalRepresentation.stop_le_array_size, start, stop, array]
Iter.size_toArray_eq_count, Rco.Internal.toArray_eq_toArray_iter,
Rco.size_toArray, Rco.size, Rxo.HasSize.size, Rxc.HasSize.size]
omega
public theorem toArray_eq_sliceToArray {α : Type u} {s : Subarray α} :
s.toArray = Slice.toArray s := by
@@ -143,22 +88,23 @@ public theorem Array.toSubarray_eq_toSubarray_of_min_eq_min {xs : Array α}
· simp only [Nat.min_eq_left, *] at h
split
· simp only [Nat.min_eq_left, *] at h
simp only [h, right_eq_dite_iff, Slice.mk.injEq, Internal.SubarrayData.mk.injEq, and_true,
true_and]
simp [h]
omega
· simp only [ge_iff_le, not_false_eq_true, Nat.min_eq_right (Nat.le_of_not_ge _), *] at h
simp [h]
omega
· split
· split
· simp only [not_false_eq_true, Nat.min_eq_right (Nat.le_of_not_ge _),
Nat.min_eq_left, Nat.not_le, *] at *
simp [*]; omega
· simp only [ge_iff_le, not_false_eq_true, Nat.min_eq_right (Nat.le_of_not_ge _),
Nat.min_eq_left, *] at h
simp_all
omega
· simp
· simp [Nat.min_eq_right (Nat.le_of_not_ge _), *] at h
split
· simp only [Nat.min_eq_left, *] at h
simp [*]; omega
simp_all
omega
· simp
public theorem Array.toSubarray_eq_min {xs : Array α} {lo hi : Nat} :
@@ -190,19 +136,15 @@ theorem Subarray.toList_eq {xs : Subarray α} :
simp only [Subarray.start, Subarray.stop, Subarray.array]
change aslice.toList = _
have : aslice.toList = lslice.toList := by
rw [ListSlice.toList_eq]
simp only [aslice, lslice, Std.Slice.toList, toList_internalIter]
simp [ListSlice.toList_eq, lslice, aslice]
simp only [Std.Slice.toList, toList_internalIter]
apply List.ext_getElem
· have : stop - start array.size - start := by omega
simp [Subarray.start, Subarray.stop, *, Subarray.array]
simp [Subarray.start, Subarray.stop, Std.PRange.Nat.size_rco, *]
· intros
simp [Subarray.array, Subarray.start, Subarray.stop]
simp [Subarray.array, Subarray.start, Subarray.stop, Std.Rco.getElem_toList_eq, succMany?]
simp [this, ListSlice.toList_eq, lslice]
public theorem Subarray.size_eq {xs : Subarray α} :
xs.size = xs.stop - xs.start := by
simp [Subarray.size]
@[simp]
public theorem Subarray.toArray_toList {xs : Subarray α} :
xs.toList.toArray = xs.toArray := by
@@ -216,14 +158,15 @@ public theorem Subarray.toList_toArray {xs : Subarray α} :
@[simp]
public theorem Subarray.length_toList {xs : Subarray α} :
xs.toList.length = xs.size := by
simp [Subarray.toList_eq, Subarray.size]
have : xs.start xs.stop := xs.internalRepresentation.start_le_stop
have : xs.stop xs.array.size := xs.internalRepresentation.stop_le_array_size
simp [Subarray.toList_eq, Subarray.size]; omega
omega
@[simp]
public theorem Subarray.size_toArray {xs : Subarray α} :
xs.toArray.size = xs.size := by
simp [ Subarray.toArray_toList, Subarray.size, Slice.size, SliceSize.size, start, stop]
rw [ Subarray.toArray_toList, List.size_toArray, length_toList]
namespace Array
@@ -298,7 +241,7 @@ public theorem stop_mkSlice_rcc {xs : Array α} {lo hi : Nat} :
@[simp]
public theorem toList_mkSlice_rcc {xs : Array α} {lo hi : Nat} :
xs[lo...=hi].toList = (xs.toList.take (hi + 1)).drop lo := by
simp
rw [mkSlice_rcc_eq_mkSlice_rco, toList_mkSlice_rco]
@[simp]
public theorem toArray_mkSlice_rcc {xs : Array α} {lo hi : Nat} :
@@ -376,12 +319,12 @@ public theorem mkSlice_roo_eq_mkSlice_roo_min {xs : Array α} {lo hi : Nat} :
@[simp]
public theorem toList_mkSlice_roo {xs : Array α} {lo hi : Nat} :
xs[lo<...hi].toList = (xs.toList.take hi).drop (lo + 1) := by
simp
rw [mkSlice_roo_eq_mkSlice_rco, toList_mkSlice_rco]
@[simp]
public theorem toArray_mkSlice_roo {xs : Array α} {lo hi : Nat} :
xs[lo<...hi].toArray = xs.extract (lo + 1) hi := by
simp
rw [mkSlice_roo_eq_mkSlice_rco, toArray_mkSlice_rco]
@[simp]
public theorem size_mkSlice_roo {xs : Array α} {lo hi : Nat} :
@@ -415,12 +358,12 @@ public theorem mkSlice_roc_eq_mkSlice_roo_min {xs : Array α} {lo hi : Nat} :
@[simp]
public theorem toList_mkSlice_roc {xs : Array α} {lo hi : Nat} :
xs[lo<...=hi].toList = (xs.toList.take (hi + 1)).drop (lo + 1) := by
simp
rw [mkSlice_roc_eq_mkSlice_roo, toList_mkSlice_roo]
@[simp]
public theorem toArray_mkSlice_roc {xs : Array α} {lo hi : Nat} :
xs[lo<...=hi].toArray = xs.extract (lo + 1) (hi + 1) := by
simp
rw [mkSlice_roc_eq_mkSlice_roo, toArray_mkSlice_roo]
@[simp]
public theorem size_mkSlice_roc {xs : Array α} {lo hi : Nat} :
@@ -464,7 +407,7 @@ public theorem toList_mkSlice_roi {xs : Array α} {lo : Nat} :
@[simp]
public theorem toArray_mkSlice_roi {xs : Array α} {lo : Nat} :
xs[lo<...*].toArray = xs.drop (lo + 1) := by
simp
rw [mkSlice_roi_eq_mkSlice_rci, toArray_mkSlice_rci]
@[simp]
public theorem size_mkSlice_roi {xs : Array α} {lo : Nat} :
@@ -498,12 +441,12 @@ public theorem mkSlice_rio_eq_mkSlice_rio_min {xs : Array α} {hi : Nat} :
@[simp]
public theorem toList_mkSlice_rio {xs : Array α} {hi : Nat} :
xs[*...hi].toList = xs.toList.take hi := by
simp
rw [mkSlice_rio_eq_mkSlice_rco, toList_mkSlice_rco, List.drop_zero]
@[simp]
public theorem toArray_mkSlice_rio {xs : Array α} {hi : Nat} :
xs[*...hi].toArray = xs.extract 0 hi := by
simp
rw [mkSlice_rio_eq_mkSlice_rco, toArray_mkSlice_rco]
@[simp]
public theorem size_mkSlice_rio {xs : Array α} {hi : Nat} :
@@ -537,12 +480,12 @@ public theorem mkSlice_ric_eq_mkSlice_rio_min {xs : Array α} {hi : Nat} :
@[simp]
public theorem toList_mkSlice_ric {xs : Array α} {hi : Nat} :
xs[*...=hi].toList = xs.toList.take (hi + 1) := by
simp
rw [mkSlice_ric_eq_mkSlice_rio, toList_mkSlice_rio]
@[simp]
public theorem toArray_mkSlice_ric {xs : Array α} {hi : Nat} :
xs[*...=hi].toArray = xs.extract 0 (hi + 1) := by
simp
rw [mkSlice_ric_eq_mkSlice_rio, toArray_mkSlice_rio]
@[simp]
public theorem size_mkSlice_ric {xs : Array α} {hi : Nat} :
@@ -602,9 +545,9 @@ namespace Subarray
@[simp]
public theorem toList_mkSlice_rco {xs : Subarray α} {lo hi : Nat} :
xs[lo...hi].toList = (xs.toList.take hi).drop lo := by
simp only [Std.Rco.Sliceable.mkSlice, Std.Rco.HasRcoIntersection.intersection, toList_eq,
Array.start_toSubarray, Array.stop_toSubarray, Array.toList_extract, List.take_drop,
List.take_take]
simp only [instSliceableSubarrayNat_1, Std.Rco.HasRcoIntersection.intersection,
Std.Rco.Sliceable.mkSlice, toList_eq, Array.start_toSubarray, Array.stop_toSubarray,
Array.toList_extract, List.take_drop, List.take_take]
rw [Nat.add_sub_cancel' (by omega)]
simp [Subarray.size, Array.length_toList, List.take_eq_take_min, Nat.add_comm xs.start]
@@ -622,7 +565,7 @@ public theorem mkSlice_rcc_eq_mkSlice_rco {xs : Subarray α} {lo hi : Nat} :
@[simp]
public theorem toList_mkSlice_rcc {xs : Subarray α} {lo hi : Nat} :
xs[lo...=hi].toList = (xs.toList.take (hi + 1)).drop lo := by
simp
rw [mkSlice_rcc_eq_mkSlice_rco, toList_mkSlice_rco]
@[simp]
public theorem toArray_mkSlice_rcc {xs : Subarray α} {lo hi : Nat} :
@@ -660,7 +603,7 @@ public theorem mkSlice_roo_eq_mkSlice_rco {xs : Subarray α} {lo hi : Nat} :
@[simp]
public theorem toList_mkSlice_roo {xs : Subarray α} {lo hi : Nat} :
xs[lo<...hi].toList = (xs.toList.take hi).drop (lo + 1) := by
simp
rw [mkSlice_roo_eq_mkSlice_rco, toList_mkSlice_rco]
@[simp]
public theorem toArray_mkSlice_roo {xs : Subarray α} {lo hi : Nat} :
@@ -676,7 +619,7 @@ public theorem mkSlice_roc_eq_mkSlice_rcc {xs : Subarray α} {lo hi : Nat} :
@[simp]
public theorem toList_mkSlice_roc {xs : Subarray α} {lo hi : Nat} :
xs[lo<...=hi].toList = (xs.toList.take (hi + 1)).drop (lo + 1) := by
simp
rw [mkSlice_roc_eq_mkSlice_rcc, toList_mkSlice_rcc]
@[simp]
public theorem toArray_mkSlice_roc {xs : Subarray α} {lo hi : Nat} :
@@ -714,7 +657,7 @@ public theorem mkSlice_rio_eq_mkSlice_rco {xs : Subarray α} {hi : Nat} :
@[simp]
public theorem toList_mkSlice_rio {xs : Subarray α} {hi : Nat} :
xs[*...hi].toList = xs.toList.take hi := by
simp
rw [mkSlice_rio_eq_mkSlice_rco, toList_mkSlice_rco, List.drop_zero]
@[simp]
public theorem toArray_mkSlice_rio {xs : Subarray α} {hi : Nat} :
@@ -730,7 +673,7 @@ public theorem mkSlice_ric_eq_mkSlice_rcc {xs : Subarray α} {hi : Nat} :
@[simp]
public theorem toList_mkSlice_ric {xs : Subarray α} {hi : Nat} :
xs[*...=hi].toList = xs.toList.take (hi + 1) := by
simp
rw [mkSlice_ric_eq_mkSlice_rcc, toList_mkSlice_rcc, List.drop_zero]
@[simp]
public theorem toArray_mkSlice_ric {xs : Subarray α} {hi : Nat} :

View File

@@ -18,20 +18,20 @@ namespace Std.Slice
open Std.Iterators
variable {γ : Type u} {α β : Type v}
variable {γ : Type u} {β : Type v}
theorem Internal.iter_eq_toIteratorIter {γ : Type u}
[ToIterator (Slice γ) Id α β] {s : Slice γ} :
theorem Internal.iter_eq_toIteratorIter {γ : Type u} {s : Slice γ}
[ToIterator s Id β] :
Internal.iter s = ToIterator.iter s :=
(rfl)
theorem forIn_internalIter {γ : Type u} {β : Type v}
{m : Type w Type x} [Monad m] {δ : Type w}
[ToIterator (Slice γ) Id α β]
[Iterator α Id β]
[IteratorLoop α Id m]
[LawfulIteratorLoop α Id m]
[Finite α Id] {s : Slice γ}
[ s : Slice γ, ToIterator s Id β]
[ s : Slice γ, Iterator (ToIterator.State s Id) Id β]
[ s : Slice γ, IteratorLoop (ToIterator.State s Id) Id m]
[ s : Slice γ, LawfulIteratorLoop (ToIterator.State s Id) Id m]
[ s : Slice γ, Finite (ToIterator.State s Id) Id] {s : Slice γ}
{init : δ} {f : β δ m (ForInStep δ)} :
ForIn.forIn (Internal.iter s) init f = ForIn.forIn s init f :=
(rfl)
@@ -39,13 +39,13 @@ theorem forIn_internalIter {γ : Type u} {β : Type v}
@[simp]
public theorem forIn_toList {γ : Type u} {β : Type v}
{m : Type w Type x} [Monad m] [LawfulMonad m] {δ : Type w}
[ToIterator (Slice γ) Id α β]
[Iterator α Id β]
[IteratorLoop α Id m]
[LawfulIteratorLoop α Id m]
[IteratorCollect α Id Id]
[LawfulIteratorCollect α Id Id]
[Finite α Id] {s : Slice γ}
[ s : Slice γ, ToIterator s Id β]
[ s : Slice γ, Iterator (ToIterator.State s Id) Id β]
[ s : Slice γ, IteratorLoop (ToIterator.State s Id) Id m]
[ s : Slice γ, LawfulIteratorLoop (ToIterator.State s Id) Id m]
[ s : Slice γ, IteratorCollect (ToIterator.State s Id) Id Id]
[ s : Slice γ, LawfulIteratorCollect (ToIterator.State s Id) Id Id]
[ s : Slice γ, Finite (ToIterator.State s Id) Id] {s : Slice γ}
{init : δ} {f : β δ m (ForInStep δ)} :
ForIn.forIn s.toList init f = ForIn.forIn s init f := by
rw [ forIn_internalIter, Iter.forIn_toList, Slice.toList]
@@ -53,68 +53,70 @@ public theorem forIn_toList {γ : Type u} {β : Type v}
@[simp]
public theorem forIn_toArray {γ : Type u} {β : Type v}
{m : Type w Type x} [Monad m] [LawfulMonad m] {δ : Type w}
[ToIterator (Slice γ) Id α β]
[Iterator α Id β]
[IteratorLoop α Id m]
[LawfulIteratorLoop α Id m]
[IteratorCollect α Id Id]
[LawfulIteratorCollect α Id Id]
[Finite α Id] {s : Slice γ}
[ s : Slice γ, ToIterator s Id β]
[ s : Slice γ, Iterator (ToIterator.State s Id) Id β]
[ s : Slice γ, IteratorLoop (ToIterator.State s Id) Id m]
[ s : Slice γ, LawfulIteratorLoop (ToIterator.State s Id) Id m]
[ s : Slice γ, IteratorCollect (ToIterator.State s Id) Id Id]
[ s : Slice γ, LawfulIteratorCollect (ToIterator.State s Id) Id Id]
[ s : Slice γ, Finite (ToIterator.State s Id) Id] {s : Slice γ}
{init : δ} {f : β δ m (ForInStep δ)} :
ForIn.forIn s.toArray init f = ForIn.forIn s init f := by
rw [ forIn_internalIter, Iter.forIn_toArray, Slice.toArray]
theorem Internal.size_eq_count_iter [ToIterator (Slice γ) Id α β]
[Iterator α Id β] [Finite α Id]
[IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
{s : Slice γ} [SliceSize γ] [LawfulSliceSize γ] :
theorem Internal.size_eq_count_iter [ s : Slice γ, ToIterator s Id β]
[ s : Slice γ, Iterator (ToIterator.State s Id) Id β] {s : Slice γ}
[Finite (ToIterator.State s Id) Id]
[IteratorLoop (ToIterator.State s Id) Id Id] [LawfulIteratorLoop (ToIterator.State s Id) Id Id]
[SliceSize γ] [LawfulSliceSize γ] :
s.size = (Internal.iter s).count := by
letI : IteratorCollect α Id Id := .defaultImplementation
letI : IteratorCollect (ToIterator.State s Id) Id Id := .defaultImplementation
simp only [Slice.size, iter, LawfulSliceSize.lawful, Iter.length_toList_eq_count]
theorem Internal.toArray_eq_toArray_iter {s : Slice γ} [ToIterator (Slice γ) Id α β]
[Iterator α Id β] [IteratorCollect α Id Id]
[Finite α Id] :
theorem Internal.toArray_eq_toArray_iter {s : Slice γ} [ToIterator s Id β]
[Iterator (ToIterator.State s Id) Id β] [IteratorCollect (ToIterator.State s Id) Id Id]
[Finite (ToIterator.State s Id) Id] :
s.toArray = (Internal.iter s).toArray :=
(rfl)
theorem Internal.toList_eq_toList_iter {s : Slice γ} [ToIterator (Slice γ) Id α β]
[Iterator α Id β] [IteratorCollect α Id Id]
[Finite α Id] :
theorem Internal.toList_eq_toList_iter {s : Slice γ} [ToIterator s Id β]
[Iterator (ToIterator.State s Id) Id β] [IteratorCollect (ToIterator.State s Id) Id Id]
[Finite (ToIterator.State s Id) Id] :
s.toList = (Internal.iter s).toList :=
(rfl)
theorem Internal.toListRev_eq_toListRev_iter {s : Slice γ} [ToIterator (Slice γ) Id α β]
[Iterator α Id β] [Finite α Id] :
theorem Internal.toListRev_eq_toListRev_iter {s : Slice γ} [ToIterator s Id β]
[Iterator (ToIterator.State s Id) Id β] [Finite (ToIterator.State s Id) Id] :
s.toListRev = (Internal.iter s).toListRev :=
(rfl)
@[simp]
theorem size_toArray_eq_size [ToIterator (Slice γ) Id α β]
[Iterator α Id β] [SliceSize γ] [LawfulSliceSize γ]
[IteratorCollect α Id Id] [Finite α Id]
[LawfulIteratorCollect α Id Id] {s : Slice γ} :
theorem size_toArray_eq_size [ s : Slice γ, ToIterator s Id β]
[ s : Slice γ, Iterator (ToIterator.State s Id) Id β] {s : Slice γ}
[SliceSize γ] [LawfulSliceSize γ]
[IteratorCollect (ToIterator.State s Id) Id Id] [Finite (ToIterator.State s Id) Id]
[LawfulIteratorCollect (ToIterator.State s Id) Id Id] :
s.toArray.size = s.size := by
letI : IteratorLoop α Id Id := .defaultImplementation
letI : IteratorLoop (ToIterator.State s Id) Id Id := .defaultImplementation
rw [Internal.size_eq_count_iter, Internal.toArray_eq_toArray_iter, Iter.size_toArray_eq_count]
@[simp]
theorem length_toList_eq_size [ToIterator (Slice γ) Id α β]
[Iterator α Id β] {s : Slice γ}
[SliceSize γ] [LawfulSliceSize γ] [IteratorCollect α Id Id]
[Finite α Id] [LawfulIteratorCollect α Id Id] :
theorem length_toList_eq_size [ s : Slice γ, ToIterator s Id β]
[ s : Slice γ, Iterator (ToIterator.State s Id) Id β] {s : Slice γ}
[SliceSize γ] [LawfulSliceSize γ] [IteratorCollect (ToIterator.State s Id) Id Id]
[Finite (ToIterator.State s Id) Id] [LawfulIteratorCollect (ToIterator.State s Id) Id Id] :
s.toList.length = s.size := by
letI : IteratorLoop α Id Id := .defaultImplementation
letI : IteratorLoop (ToIterator.State s Id) Id Id := .defaultImplementation
rw [Internal.size_eq_count_iter, Internal.toList_eq_toList_iter, Iter.length_toList_eq_count]
@[simp]
theorem length_toListRev_eq_size [ToIterator (Slice γ) Id α β]
[Iterator α Id β] {s : Slice γ}
[IteratorLoop α Id Id.{v}] [SliceSize γ] [LawfulSliceSize γ]
[Finite α Id]
[LawfulIteratorLoop α Id Id] :
theorem length_toListRev_eq_size [ s : Slice γ, ToIterator s Id β]
[ s : Slice γ, Iterator (ToIterator.State s Id) Id β] {s : Slice γ}
[IteratorLoop (ToIterator.State s Id) Id Id.{v}] [SliceSize γ] [LawfulSliceSize γ]
[Finite (ToIterator.State s Id) Id]
[LawfulIteratorLoop (ToIterator.State s Id) Id Id] :
s.toListRev.length = s.size := by
letI : IteratorCollect α Id Id := .defaultImplementation
letI : IteratorCollect (ToIterator.State s Id) Id Id := .defaultImplementation
rw [Internal.size_eq_count_iter, Internal.toListRev_eq_toListRev_iter,
Iter.length_toListRev_eq_count]

View File

@@ -22,27 +22,40 @@ This module implements an iterator for list slices.
open Std Slice PRange Iterators
variable {α : Type u}
variable {shape : RangeShape} {α : Type u}
@[inline, expose]
def ListSlice.instToIterator :=
ToIterator.of (γ := Slice (Internal.ListSliceData α)) _ (fun s => match s.internalRepresentation.stop with
instance {s : ListSlice α} : ToIterator s Id α :=
.of _ (match s.internalRepresentation.stop with
| some n => s.internalRepresentation.list.iter.take n
| none => s.internalRepresentation.list.iter.toTake)
attribute [instance] ListSlice.instToIterator
universe v w
@[no_expose] instance {s : ListSlice α} : Iterator (ToIterator.State s Id) Id α := inferInstance
@[no_expose] instance {s : ListSlice α} : Finite (ToIterator.State s Id) Id := inferInstance
@[no_expose] instance {s : ListSlice α} : IteratorCollect (ToIterator.State s Id) Id Id := inferInstance
@[no_expose] instance {s : ListSlice α} : IteratorCollectPartial (ToIterator.State s Id) Id Id := inferInstance
@[no_expose] instance {s : ListSlice α} {m : Type v Type w} [Monad m] :
IteratorLoop (ToIterator.State s Id) Id m := inferInstance
@[no_expose] instance {s : ListSlice α} {m : Type v Type w} [Monad m] :
IteratorLoopPartial (ToIterator.State s Id) Id m := inferInstance
instance : SliceSize (Internal.ListSliceData α) where
size s := (Internal.iter s).count
@[no_expose]
instance {α : Type u} {m : Type v Type w} [Monad m] :
instance {α : Type u} {m : Type v Type w} :
ForIn m (ListSlice α) α where
forIn xs init f := forIn (Internal.iter xs) init f
namespace List
/-- Allocates a new list that contains the contents of the slice. -/
def ofSlice (s : ListSlice α) : List α :=
s.toList
docs_to_verso ofSlice
instance : Append (ListSlice α) where
append x y :=
let a := x.toList ++ y.toList
@@ -60,3 +73,7 @@ instance [ToString α] : ToString (ListSlice α) where
toString s := toString s.toArray
end List
@[inherit_doc List.ofSlice]
def ListSlice.toList (s : ListSlice α) : List α :=
List.ofSlice s

View File

@@ -17,9 +17,7 @@ public import Init.Data.Iterators.Lemmas
open Std.Iterators Std.PRange
namespace ListSlice
open Std.Slice
namespace Std.Slice.List
theorem internalIter_eq {α : Type u} {s : ListSlice α} :
Internal.iter s = match s.internalRepresentation.stop with
@@ -42,34 +40,36 @@ public instance : LawfulSliceSize (Internal.ListSliceData α) where
lawful s := by
simp [ internalIter_eq_toIteratorIter, SliceSize.size]
public theorem toList_eq {xs : ListSlice α} :
end Std.Slice.List
public theorem ListSlice.toList_eq {xs : ListSlice α} :
xs.toList = match xs.internalRepresentation.stop with
| some stop => xs.internalRepresentation.list.take stop
| none => xs.internalRepresentation.list := by
simp only [Std.Slice.toList, toList_internalIter]
simp only [toList, List.ofSlice, Std.Slice.toList, ToIterator.state_eq]
rw [Std.Slice.List.toList_internalIter]
rfl
public theorem toArray_toList {xs : ListSlice α} :
public theorem ListSlice.toArray_toList {xs : ListSlice α} :
xs.toList.toArray = xs.toArray := by
simp [Std.Slice.toArray, Std.Slice.toList]
simp [ListSlice.toList, Std.Slice.toArray, List.ofSlice, Std.Slice.toList]
public theorem toList_toArray {xs : ListSlice α} :
public theorem ListSlice.toList_toArray {xs : ListSlice α} :
xs.toArray.toList = xs.toList := by
simp [Std.Slice.toArray, Std.Slice.toList]
simp [ListSlice.toList, Std.Slice.toArray, List.ofSlice, Std.Slice.toList]
@[simp]
public theorem length_toList {xs : ListSlice α} :
public theorem ListSlice.length_toList {xs : ListSlice α} :
xs.toList.length = xs.size := by
simp [ListSlice.toList_eq, Std.Slice.size, Std.Slice.SliceSize.size, Iter.length_toList_eq_count,
toList_internalIter]; rfl
simp [ListSlice.toList_eq, Std.Slice.size, Std.Slice.SliceSize.size, Iter.length_toList_eq_count]
rw [Std.Slice.List.toList_internalIter]
rfl
@[simp]
public theorem size_toArray {xs : ListSlice α} :
public theorem ListSlice.size_toArray {xs : ListSlice α} :
xs.toArray.size = xs.size := by
simp [ ListSlice.toArray_toList]
end ListSlice
namespace List
@[simp]
@@ -101,7 +101,7 @@ public theorem mkSlice_rcc_eq_mkSlice_rco {xs : List α} {lo hi : Nat} :
@[simp]
public theorem toList_mkSlice_rcc {xs : List α} {lo hi : Nat} :
xs[lo...=hi].toList = (xs.take (hi + 1)).drop lo := by
simp
rw [mkSlice_rcc_eq_mkSlice_rco, toList_mkSlice_rco]
@[simp]
public theorem toArray_mkSlice_rcc {xs : List α} {lo hi : Nat} :
@@ -137,7 +137,7 @@ public theorem mkSlice_roo_eq_mkSlice_rco {xs : List α} {lo hi : Nat} :
@[simp]
public theorem toList_mkSlice_roo {xs : List α} {lo hi : Nat} :
xs[lo<...hi].toList = (xs.take hi).drop (lo + 1) := by
simp
rw [mkSlice_roo_eq_mkSlice_rco, toList_mkSlice_rco]
@[simp]
public theorem toArray_mkSlice_roo {xs : List α} {lo hi : Nat} :
@@ -157,7 +157,7 @@ public theorem mkSlice_roc_eq_mkSlice_roo {xs : List α} {lo hi : Nat} :
@[simp]
public theorem toList_mkSlice_roc {xs : List α} {lo hi : Nat} :
xs[lo<...=hi].toList = (xs.take (hi + 1)).drop (lo + 1) := by
simp
rw [mkSlice_roc_eq_mkSlice_roo, toList_mkSlice_roo]
@[simp]
public theorem toArray_mkSlice_roc {xs : List α} {lo hi : Nat} :
@@ -177,7 +177,7 @@ public theorem mkSlice_roi_eq_mkSlice_rci {xs : List α} {lo : Nat} :
@[simp]
public theorem toList_mkSlice_roi {xs : List α} {lo : Nat} :
xs[lo<...*].toList = xs.drop (lo + 1) := by
simp
rw [mkSlice_roi_eq_mkSlice_rci, toList_mkSlice_rci]
@[simp]
public theorem toArray_mkSlice_roi {xs : List α} {lo : Nat} :
@@ -197,7 +197,7 @@ public theorem mkSlice_rio_eq_mkSlice_rco {xs : List α} {hi : Nat} :
@[simp]
public theorem toList_mkSlice_rio {xs : List α} {hi : Nat} :
xs[*...hi].toList = xs.take hi := by
simp
rw [mkSlice_rio_eq_mkSlice_rco, toList_mkSlice_rco, List.drop_zero]
@[simp]
public theorem toArray_mkSlice_rio {xs : List α} {hi : Nat} :
@@ -217,7 +217,7 @@ public theorem mkSlice_ric_eq_mkSlice_rio {xs : List α} {hi : Nat} :
@[simp]
public theorem toList_mkSlice_ric {xs : List α} {hi : Nat} :
xs[*...=hi].toList = xs.take (hi + 1) := by
simp
rw [mkSlice_ric_eq_mkSlice_rio, toList_mkSlice_rio]
@[simp]
public theorem toArray_mkSlice_ric {xs : List α} {hi : Nat} :
@@ -237,7 +237,7 @@ public theorem mkSlice_rii_eq_mkSlice_rci {xs : List α} :
@[simp]
public theorem toList_mkSlice_rii {xs : List α} :
xs[*...*].toList = xs := by
simp
rw [mkSlice_rii_eq_mkSlice_rci, toList_mkSlice_rci, List.drop_zero]
@[simp]
public theorem toArray_mkSlice_rii {xs : List α} :
@@ -277,7 +277,7 @@ public theorem mkSlice_rcc_eq_mkSlice_rco {xs : ListSlice α} {lo hi : Nat} :
@[simp]
public theorem toList_mkSlice_rcc {xs : ListSlice α} {lo hi : Nat} :
xs[lo...=hi].toList = (xs.toList.take (hi + 1)).drop lo := by
simp
rw [mkSlice_rcc_eq_mkSlice_rco, toList_mkSlice_rco]
@[simp]
public theorem toArray_mkSlice_rcc {xs : ListSlice α} {lo hi : Nat} :
@@ -307,7 +307,7 @@ public theorem mkSlice_roo_eq_mkSlice_rco {xs : ListSlice α} {lo hi : Nat} :
@[simp]
public theorem toList_mkSlice_roo {xs : ListSlice α} {lo hi : Nat} :
xs[lo<...hi].toList = (xs.toList.take hi).drop (lo + 1) := by
simp
rw [mkSlice_roo_eq_mkSlice_rco, toList_mkSlice_rco]
@[simp]
public theorem toArray_mkSlice_roo {xs : ListSlice α} {lo hi : Nat} :
@@ -327,7 +327,7 @@ public theorem mkSlice_roc_eq_mkSlice_rcc {xs : ListSlice α} {lo hi : Nat} :
@[simp]
public theorem toList_mkSlice_roc {xs : ListSlice α} {lo hi : Nat} :
xs[lo<...=hi].toList = (xs.toList.take (hi + 1)).drop (lo + 1) := by
simp
rw [mkSlice_roc_eq_mkSlice_rcc, toList_mkSlice_rcc]
@[simp]
public theorem toArray_mkSlice_roc {xs : ListSlice α} {lo hi : Nat} :
@@ -342,7 +342,7 @@ public theorem mkSlice_roi_eq_mkSlice_rci {xs : ListSlice α} {lo : Nat} :
@[simp]
public theorem toList_mkSlice_roi {xs : ListSlice α} {lo : Nat} :
xs[lo<...*].toList = xs.toList.drop (lo + 1) := by
simp
rw [mkSlice_roi_eq_mkSlice_rci, toList_mkSlice_rci]
@[simp]
public theorem toArray_mkSlice_roi {xs : ListSlice α} {lo : Nat} :
@@ -359,7 +359,7 @@ public theorem mkSlice_rio_eq_mkSlice_rco {xs : ListSlice α} {hi : Nat} :
@[simp]
public theorem toList_mkSlice_rio {xs : ListSlice α} {hi : Nat} :
xs[*...hi].toList = xs.toList.take hi := by
simp
rw [mkSlice_rio_eq_mkSlice_rco, toList_mkSlice_rco, List.drop_zero]
@[simp]
public theorem toArray_mkSlice_rio {xs : ListSlice α} {hi : Nat} :
@@ -379,7 +379,7 @@ public theorem mkSlice_ric_eq_mkSlice_rcc {xs : ListSlice α} {hi : Nat} :
@[simp]
public theorem toList_mkSlice_ric {xs : ListSlice α} {hi : Nat} :
xs[*...=hi].toList = xs.toList.take (hi + 1) := by
simp
rw [mkSlice_ric_eq_mkSlice_rcc, toList_mkSlice_rcc, List.drop_zero]
@[simp]
public theorem toArray_mkSlice_ric {xs : ListSlice α} {hi : Nat} :
@@ -391,6 +391,16 @@ public theorem mkSlice_rii {xs : ListSlice α} :
xs[*...*] = xs := by
simp [Std.Rii.Sliceable.mkSlice]
@[simp]
public theorem toList_mkSlice_rii {xs : ListSlice α} :
xs[*...*].toList = xs.toList := by
rw [mkSlice_rii]
@[simp]
public theorem toArray_mkSlice_rii {xs : ListSlice α} :
xs[*...*].toArray = xs.toArray := by
rw [mkSlice_rii]
end ListSlice
end ListSubslices

View File

@@ -16,36 +16,37 @@ open Std.Iterators
namespace Std.Slice
instance [ToIterator γ m α β] : ToIterator (Slice γ) m α β where
iterMInternal x := ToIterator.iterMInternal x.internalRepresentation
instance {x : γ} [ToIterator x m β] : ToIterator (Slice.mk x) m β where
State := ToIterator.State x m
iterMInternal := ToIterator.iterMInternal
/--
Internal function to obtain an iterator from a slice. Users should import `Std.Data.Iterators`
and use `Std.Slice.iter` instead.
-/
@[always_inline, inline]
def Internal.iter [ToIterator (Slice γ) Id α β] (s : Slice γ) :=
def Internal.iter (s : Slice γ) [ToIterator s Id β] :=
ToIterator.iter s
/--
This type class provides support for the `Slice.size` function.
-/
class SliceSize (γ : Type u) where
class SliceSize (α : Type u) where
/-- Computes the slice of a `Slice`. Use `Slice.size` instead. -/
size (slice : Slice γ) : Nat
size (slice : Slice α) : Nat
/--
This type class states that the slice's iterator emits exactly `Slice.size` elements before
terminating.
-/
class LawfulSliceSize (γ : Type u) [SliceSize γ] [ToIterator (Slice γ) Id α β]
[Iterator α Id β] where
class LawfulSliceSize (α : Type u) [SliceSize α] [ s : Slice α, ToIterator s Id β]
[ s : Slice α, Iterator (ToIterator.State s Id) Id β] where
/-- The iterator for every `Slice α` is finite. -/
[finite : Finite α Id]
/-- The iterator of a slice `s` of type `Slice γ` emits exactly `SliceSize.size s` elements. -/
[finite : s : Slice α, Finite (ToIterator.State s Id) Id]
/-- The iterator of a slice `s` of type `Slice α` emits exactly `SliceSize.size s` elements. -/
lawful :
letI : IteratorLoop α Id Id := .defaultImplementation
s : Slice γ, SliceSize.size s = (ToIterator.iter (γ := Slice γ) s).count
letI (s : Slice α) : IteratorLoop (ToIterator.State s Id) Id Id := .defaultImplementation
s : Slice α, SliceSize.size s = (ToIterator.iter s).count
/--
Returns the number of elements with distinct indices in the given slice.
@@ -58,27 +59,26 @@ def size (s : Slice γ) [SliceSize γ] :=
/-- Allocates a new array that contains the elements of the slice. -/
@[always_inline, inline]
def toArray [ToIterator (Slice γ) Id α β] [Iterator α Id β]
[IteratorCollect α Id Id] [Finite α Id] (s : Slice γ) : Array β :=
def toArray (s : Slice γ) [ToIterator s Id β] [Iterator (ToIterator.State s Id) Id β]
[IteratorCollect (ToIterator.State s Id) Id Id] [Finite (ToIterator.State s Id) Id] : Array β :=
Internal.iter s |>.toArray
/-- Allocates a new list that contains the elements of the slice. -/
@[always_inline, inline]
def toList [ToIterator (Slice γ) Id α β] [Iterator α Id β]
[IteratorCollect α Id Id] [Finite α Id]
(s : Slice γ) : List β :=
def toList (s : Slice γ) [ToIterator s Id β] [Iterator (ToIterator.State s Id) Id β]
[IteratorCollect (ToIterator.State s Id) Id Id] [Finite (ToIterator.State s Id) Id] : List β :=
Internal.iter s |>.toList
/-- Allocates a new list that contains the elements of the slice in reverse order. -/
@[always_inline, inline]
def toListRev [ToIterator (Slice γ) Id α β] [Iterator α Id β]
[Finite α Id] (s : Slice γ) : List β :=
def toListRev (s : Slice γ) [ToIterator s Id β] [Iterator (ToIterator.State s Id) Id β]
[Finite (ToIterator.State s Id) Id] : List β :=
Internal.iter s |>.toListRev
instance {γ : Type u} {β : Type v} [Monad m] [ToIterator (Slice γ) Id α β]
[Iterator α Id β]
[IteratorLoop α Id m]
[Finite α Id] :
instance {γ : Type u} {β : Type v} [ s : Slice γ, ToIterator s Id β]
[ s : Slice γ, Iterator (ToIterator.State s Id) Id β]
[ s : Slice γ, IteratorLoop (ToIterator.State s Id) Id m]
[ s : Slice γ, Finite (ToIterator.State s Id) Id] :
ForIn m (Slice γ) β where
forIn s init f :=
forIn (Internal.iter s) init f
@@ -110,9 +110,8 @@ none
@[always_inline, inline]
def foldlM {γ : Type u} {β : Type v}
{δ : Type w} {m : Type w Type w'} [Monad m] (f : δ β m δ) (init : δ)
[ToIterator (Slice γ) Id α β] [Iterator α Id β]
[IteratorLoop α Id m] [Finite α Id]
(s : Slice γ) : m δ :=
(s : Slice γ) [ToIterator s Id β] [Iterator (ToIterator.State s Id) Id β]
[IteratorLoop (ToIterator.State s Id) Id m] [Finite (ToIterator.State s Id) Id] : m δ :=
Internal.iter s |>.foldM f init
/--
@@ -126,9 +125,8 @@ Examples for the special case of subarrays:
@[always_inline, inline]
def foldl {γ : Type u} {β : Type v}
{δ : Type w} (f : δ β δ) (init : δ)
[ToIterator (Slice γ) Id α β] [Iterator α Id β]
[IteratorLoop α Id Id] [Finite α Id]
(s : Slice γ) : δ :=
(s : Slice γ) [ToIterator s Id β] [Iterator (ToIterator.State s Id) Id β]
[IteratorLoop (ToIterator.State s Id) Id Id] [Finite (ToIterator.State s Id) Id] : δ :=
Internal.iter s |>.fold f init
end Std.Slice

View File

@@ -66,7 +66,7 @@ protected partial def Stream.forIn [Stream ρ α] [Monad m] (s : ρ) (b : β) (f
| none => return b
visit s b
instance (priority := low) [Monad m] [Stream ρ α] : ForIn m ρ α where
instance (priority := low) [Stream ρ α] : ForIn m ρ α where
forIn := Stream.forIn
instance : ToStream (List α) (List α) where

View File

@@ -13,6 +13,7 @@ public import Init.Data.String.Defs
public import Init.Data.String.Extra
public import Init.Data.String.Iterator
public import Init.Data.String.Lemmas
public import Init.Data.String.Repr
public import Init.Data.String.Bootstrap
public import Init.Data.String.Slice
public import Init.Data.String.Pattern
@@ -25,4 +26,3 @@ public import Init.Data.String.Termination
public import Init.Data.String.ToSlice
public import Init.Data.String.Search
public import Init.Data.String.Legacy
public import Init.Data.String.Grind

File diff suppressed because it is too large Load Diff

View File

@@ -13,6 +13,9 @@ public section
namespace String
@[deprecated Pos.Raw (since := "2025-09-30")]
abbrev Pos := Pos.Raw
instance : OfNat String.Pos.Raw (nat_lit 0) where
ofNat := {}

View File

@@ -74,11 +74,11 @@ Encodes a string in UTF-8 as an array of bytes.
-/
@[extern "lean_string_to_utf8"]
def String.toUTF8 (a : @& String) : ByteArray :=
a.toByteArray
a.bytes
@[simp] theorem String.toUTF8_eq_toByteArray {s : String} : s.toUTF8 = s.toByteArray := (rfl)
@[simp] theorem String.toUTF8_eq_bytes {s : String} : s.toUTF8 = s.bytes := (rfl)
@[simp] theorem String.toByteArray_empty : "".toByteArray = ByteArray.empty := (rfl)
@[simp] theorem String.bytes_empty : "".bytes = ByteArray.empty := (rfl)
/--
Appends two strings. Usually accessed via the `++` operator.
@@ -92,33 +92,33 @@ Examples:
-/
@[extern "lean_string_append", expose]
def String.append (s : String) (t : @& String) : String where
toByteArray := s.toByteArray ++ t.toByteArray
bytes := s.bytes ++ t.bytes
isValidUTF8 := s.isValidUTF8.append t.isValidUTF8
instance : Append String where
append s t := s.append t
@[simp]
theorem String.toByteArray_append {s t : String} : (s ++ t).toByteArray = s.toByteArray ++ t.toByteArray := (rfl)
theorem String.bytes_append {s t : String} : (s ++ t).bytes = s.bytes ++ t.bytes := (rfl)
theorem String.toByteArray_inj {s t : String} : s.toByteArray = t.toByteArray s = t := by
theorem String.bytes_inj {s t : String} : s.bytes = t.bytes s = t := by
refine fun h => ?_, (· rfl)
rcases s with s
rcases t with t
subst h
rfl
@[simp] theorem String.toByteArray_ofList {l : List Char} : (String.ofList l).toByteArray = l.utf8Encode := by
@[simp] theorem String.bytes_ofList {l : List Char} : (String.ofList l).bytes = l.utf8Encode := by
simp [String.ofList]
@[deprecated String.toByteArray_ofList (since := "2025-10-30")]
theorem List.toByteArray_asString {l : List Char} : (String.ofList l).toByteArray = l.utf8Encode :=
String.toByteArray_ofList
@[deprecated String.bytes_ofList (since := "2025-10-30")]
theorem List.bytes_asString {l : List Char} : (String.ofList l).bytes = l.utf8Encode :=
String.bytes_ofList
theorem String.exists_eq_ofList (s : String) :
l : List Char, s = String.ofList l := by
rcases s with _, l, rfl
refine l, by simp [ String.toByteArray_inj]
refine l, by simp [ String.bytes_inj]
@[deprecated String.exists_eq_ofList (since := "2025-10-30")]
theorem String.exists_eq_asString (s : String) :
@@ -134,14 +134,18 @@ theorem String.utf8ByteSize_append {s t : String} :
simp [utf8ByteSize]
@[simp]
theorem String.size_toByteArray {s : String} : s.toByteArray.size = s.utf8ByteSize := rfl
theorem String.size_bytes {s : String} : s.bytes.size = s.utf8ByteSize := rfl
@[simp]
theorem String.toByteArray_push {s : String} {c : Char} : (s.push c).toByteArray = s.toByteArray ++ [c].utf8Encode := by
theorem String.bytes_push {s : String} {c : Char} : (s.push c).bytes = s.bytes ++ [c].utf8Encode := by
simp [push]
namespace String
@[deprecated rawEndPos (since := "2025-10-20")]
def endPos (s : String) : String.Pos.Raw :=
s.rawEndPos
/-- The start position of the string, as a `String.Pos.Raw.` -/
def rawStartPos (_s : String) : String.Pos.Raw :=
0
@@ -160,11 +164,11 @@ theorem utf8ByteSize_ofByteArray {b : ByteArray} {h} :
(String.ofByteArray b h).utf8ByteSize = b.size := rfl
@[simp]
theorem toByteArray_singleton {c : Char} : (String.singleton c).toByteArray = [c].utf8Encode := by
theorem bytes_singleton {c : Char} : (String.singleton c).bytes = [c].utf8Encode := by
simp [singleton]
theorem singleton_eq_ofList {c : Char} : String.singleton c = String.ofList [c] := by
simp [ String.toByteArray_inj]
simp [ String.bytes_inj]
@[deprecated singleton_eq_ofList (since := "2025-10-30")]
theorem singleton_eq_asString {c : Char} : String.singleton c = String.ofList [c] :=
@@ -172,20 +176,20 @@ theorem singleton_eq_asString {c : Char} : String.singleton c = String.ofList [c
@[simp]
theorem append_singleton {s : String} {c : Char} : s ++ singleton c = s.push c := by
simp [ toByteArray_inj]
simp [ bytes_inj]
@[simp]
theorem append_left_inj {s₁ s₂ : String} (t : String) :
s₁ ++ t = s₂ ++ t s₁ = s₂ := by
simp [ toByteArray_inj]
simp [ bytes_inj]
theorem append_assoc {s₁ s₂ s₃ : String} : s₁ ++ s₂ ++ s₃ = s₁ ++ (s₂ ++ s₃) := by
simp [ toByteArray_inj, ByteArray.append_assoc]
simp [ bytes_inj, ByteArray.append_assoc]
@[simp]
theorem utf8ByteSize_eq_zero_iff {s : String} : s.utf8ByteSize = 0 s = "" := by
refine fun h => ?_, fun h => h utf8ByteSize_empty
simpa [ toByteArray_inj, ByteArray.size_eq_zero_iff] using h
simpa [ bytes_inj, ByteArray.size_eq_zero_iff] using h
theorem rawEndPos_eq_zero_iff {b : String} : b.rawEndPos = 0 b = "" := by
simp
@@ -296,14 +300,14 @@ Examples:
-/
structure Pos.Raw.IsValid (s : String) (off : String.Pos.Raw) : Prop where private mk ::
le_rawEndPos : off s.rawEndPos
isValidUTF8_extract_zero : (s.toByteArray.extract 0 off.byteIdx).IsValidUTF8
isValidUTF8_extract_zero : (s.bytes.extract 0 off.byteIdx).IsValidUTF8
theorem Pos.Raw.IsValid.le_utf8ByteSize {s : String} {off : String.Pos.Raw} (h : off.IsValid s) :
off.byteIdx s.utf8ByteSize := by
simpa [Pos.Raw.le_iff] using h.le_rawEndPos
theorem Pos.Raw.isValid_iff_isValidUTF8_extract_zero {s : String} {p : Pos.Raw} :
p.IsValid s p s.rawEndPos (s.toByteArray.extract 0 p.byteIdx).IsValidUTF8 :=
p.IsValid s p s.rawEndPos (s.bytes.extract 0 p.byteIdx).IsValidUTF8 :=
fun h₁, h₂ => h₁, h₂, fun h₁, h₂ => h₁, h₂
@[deprecated le_rawEndPos (since := "2025-10-20")]
@@ -319,7 +323,7 @@ theorem Pos.Raw.isValid_zero {s : String} : (0 : Pos.Raw).IsValid s where
@[simp]
theorem Pos.Raw.isValid_rawEndPos {s : String} : s.rawEndPos.IsValid s where
le_rawEndPos := by simp
isValidUTF8_extract_zero := by simp [ size_toByteArray, s.isValidUTF8]
isValidUTF8_extract_zero := by simp [ size_bytes, s.isValidUTF8]
theorem Pos.Raw.isValid_of_eq_rawEndPos {s : String} {p : Pos.Raw} (h : p = s.rawEndPos) :
p.IsValid s := by
@@ -337,55 +341,55 @@ theorem Pos.Raw.isValid_empty_iff {p : Pos.Raw} : p.IsValid "" ↔ p = 0 := by
simp
/--
A `Pos s` is a byte offset in `s` together with a proof that this position is at a UTF-8
A `ValidPos s` is a byte offset in `s` together with a proof that this position is at a UTF-8
character boundary.
-/
@[ext]
structure Pos (s : String) where
/-- The underlying byte offset of the `Pos`. -/
structure ValidPos (s : String) where
/-- The underlying byte offset of the `ValidPos`. -/
offset : Pos.Raw
/-- The proof that `offset` is valid for the string `s`. -/
isValid : offset.IsValid s
deriving @[expose] DecidableEq
/-- The start position of `s`, as an `s.Pos`. -/
/-- The start position of `s`, as an `s.ValidPos`. -/
@[inline, expose]
def startPos (s : String) : s.Pos where
def startValidPos (s : String) : s.ValidPos where
offset := 0
isValid := by simp
@[simp]
theorem offset_startPos {s : String} : s.startPos.offset = 0 := (rfl)
theorem offset_startValidPos {s : String} : s.startValidPos.offset = 0 := (rfl)
instance {s : String} : Inhabited s.Pos where
default := s.startPos
instance {s : String} : Inhabited s.ValidPos where
default := s.startValidPos
/-- The past-the-end position of `s`, as an `s.Pos`. -/
/-- The past-the-end position of `s`, as an `s.ValidPos`. -/
@[inline, expose]
def endPos (s : String) : s.Pos where
def endValidPos (s : String) : s.ValidPos where
offset := s.rawEndPos
isValid := by simp
@[simp]
theorem offset_endPos {s : String} : s.endPos.offset = s.rawEndPos := (rfl)
theorem offset_endValidPos {s : String} : s.endValidPos.offset = s.rawEndPos := (rfl)
instance {s : String} : LE s.Pos where
instance {s : String} : LE s.ValidPos where
le l r := l.offset r.offset
instance {s : String} : LT s.Pos where
instance {s : String} : LT s.ValidPos where
lt l r := l.offset < r.offset
theorem Pos.le_iff {s : String} {l r : s.Pos} : l r l.offset r.offset :=
theorem ValidPos.le_iff {s : String} {l r : s.ValidPos} : l r l.offset r.offset :=
Iff.rfl
theorem Pos.lt_iff {s : String} {l r : s.Pos} : l < r l.offset < r.offset :=
theorem ValidPos.lt_iff {s : String} {l r : s.ValidPos} : l < r l.offset < r.offset :=
Iff.rfl
instance {s : String} (l r : s.Pos) : Decidable (l r) :=
decidable_of_iff' _ Pos.le_iff
instance {s : String} (l r : s.ValidPos) : Decidable (l r) :=
decidable_of_iff' _ ValidPos.le_iff
instance {s : String} (l r : s.Pos) : Decidable (l < r) :=
decidable_of_iff' _ Pos.lt_iff
instance {s : String} (l r : s.ValidPos) : Decidable (l < r) :=
decidable_of_iff' _ ValidPos.lt_iff
/--
A region or slice of some underlying string.
@@ -402,14 +406,14 @@ structure Slice where
/-- The underlying strings. -/
str : String
/-- The byte position of the start of the string slice. -/
startInclusive : str.Pos
startInclusive : str.ValidPos
/-- The byte position of the end of the string slice. -/
endExclusive : str.Pos
endExclusive : str.ValidPos
/-- The slice is not degenerate (but it may be empty). -/
startInclusive_le_endExclusive : startInclusive endExclusive
instance : Inhabited Slice where
default := "", "".startPos, "".startPos, by simp [Pos.le_iff]
default := "", "".startValidPos, "".startValidPos, by simp [ValidPos.le_iff]
/--
Returns a slice that contains the entire string.
@@ -417,18 +421,15 @@ Returns a slice that contains the entire string.
@[inline, expose] -- expose for the defeq `s.toSlice.str = s`.
def toSlice (s : String) : Slice where
str := s
startInclusive := s.startPos
endExclusive := s.endPos
startInclusive_le_endExclusive := by simp [Pos.le_iff, Pos.Raw.le_iff]
instance : Coe String String.Slice where
coe := String.toSlice
startInclusive := s.startValidPos
endExclusive := s.endValidPos
startInclusive_le_endExclusive := by simp [ValidPos.le_iff, Pos.Raw.le_iff]
@[simp]
theorem startInclusive_toSlice {s : String} : s.toSlice.startInclusive = s.startPos := rfl
theorem startInclusive_toSlice {s : String} : s.toSlice.startInclusive = s.startValidPos := rfl
@[simp]
theorem endExclusive_toSlice {s : String} : s.toSlice.endExclusive = s.endPos := rfl
theorem endExclusive_toSlice {s : String} : s.toSlice.endExclusive = s.endValidPos := rfl
@[simp]
theorem str_toSlice {s : String} : s.toSlice.str = s := rfl
@@ -531,7 +532,7 @@ instance {s : Slice} : Inhabited s.Pos where
theorem Slice.offset_startInclusive_add_self {s : Slice} :
s.startInclusive.offset + s = s.endExclusive.offset := by
have := s.startInclusive_le_endExclusive
simp_all [String.Pos.Raw.ext_iff, Pos.le_iff, Pos.Raw.le_iff, utf8ByteSize_eq]
simp_all [String.Pos.Raw.ext_iff, ValidPos.le_iff, Pos.Raw.le_iff, utf8ByteSize_eq]
@[simp]
theorem Pos.Raw.offsetBy_rawEndPos_left {p : Pos.Raw} {s : String} :
@@ -590,18 +591,18 @@ instance {s : Slice} (l r : s.Pos) : Decidable (l < r) :=
decidable_of_iff' _ Slice.Pos.lt_iff
/--
`pos.IsAtEnd` is just shorthand for `pos = s.endPos` that is easier to write if `s` is long.
`pos.IsAtEnd` is just shorthand for `pos = s.endValidPos` that is easier to write if `s` is long.
-/
abbrev Pos.IsAtEnd {s : String} (pos : s.Pos) : Prop :=
pos = s.endPos
abbrev ValidPos.IsAtEnd {s : String} (pos : s.ValidPos) : Prop :=
pos = s.endValidPos
@[simp]
theorem Pos.isAtEnd_iff {s : String} {pos : s.Pos} :
pos.IsAtEnd pos = s.endPos := Iff.rfl
theorem ValidPos.isAtEnd_iff {s : String} {pos : s.ValidPos} :
pos.IsAtEnd pos = s.endValidPos := Iff.rfl
@[inline]
instance {s : String} {pos : s.Pos} : Decidable pos.IsAtEnd :=
decidable_of_iff _ Pos.isAtEnd_iff
instance {s : String} {pos : s.ValidPos} : Decidable pos.IsAtEnd :=
decidable_of_iff _ ValidPos.isAtEnd_iff
/--
`pos.IsAtEnd` is just shorthand for `pos = s.endPos` that is easier to write if `s` is long.
@@ -638,20 +639,4 @@ def toSubstring (s : String) : Substring.Raw :=
def toSubstring' (s : String) : Substring.Raw :=
s.toRawSubstring'
@[deprecated String.Pos (since := "2025-11-24")]
abbrev ValidPos (s : String) : Type :=
s.Pos
@[deprecated String.startPos (since := "2025-11-24")]
abbrev startValidPos (s : String) : s.Pos :=
s.startPos
@[deprecated String.endPos (since := "2025-11-24")]
abbrev endValidPos (s : String) : s.Pos :=
s.endPos
@[deprecated String.toByteArray (since := "2025-11-24")]
abbrev String.bytes (s : String) : ByteArray :=
s.toByteArray
end String

View File

@@ -29,28 +29,28 @@ abbrev validateUTF8 (a : ByteArray) : Bool :=
a.validateUTF8
private def findLeadingSpacesSize (s : String) : Nat :=
let it := s.startPos
let it := it.find? (· == '\n') |>.bind String.Pos.next?
let it := s.startValidPos
let it := it.find? (· == '\n') |>.bind String.ValidPos.next?
match it with
| some it => consumeSpaces it 0 s.length
| none => 0
where
consumeSpaces {s : String} (it : s.Pos) (curr min : Nat) : Nat :=
consumeSpaces {s : String} (it : s.ValidPos) (curr min : Nat) : Nat :=
if h : it.IsAtEnd then min
else if it.get h == ' ' || it.get h == '\t' then consumeSpaces (it.next h) (curr + 1) min
else if it.get h == '\n' then findNextLine (it.next h) min
else findNextLine (it.next h) (Nat.min curr min)
termination_by it
findNextLine {s : String} (it : s.Pos) (min : Nat) : Nat :=
findNextLine {s : String} (it : s.ValidPos) (min : Nat) : Nat :=
if h : it.IsAtEnd then min
else if it.get h == '\n' then consumeSpaces (it.next h) 0 min
else findNextLine (it.next h) min
termination_by it
private def removeNumLeadingSpaces (n : Nat) (s : String) : String :=
consumeSpaces n s.startPos ""
consumeSpaces n s.startValidPos ""
where
consumeSpaces (n : Nat) {s : String} (it : s.Pos) (r : String) : String :=
consumeSpaces (n : Nat) {s : String} (it : s.ValidPos) (r : String) : String :=
match n with
| 0 => saveLine it r
| n+1 =>
@@ -58,7 +58,7 @@ where
else if it.get h == ' ' || it.get h == '\t' then consumeSpaces n (it.next h) r
else saveLine it r
termination_by (it, 1)
saveLine {s : String} (it : s.Pos) (r : String) : String :=
saveLine {s : String} (it : s.ValidPos) (r : String) : String :=
if h : it.IsAtEnd then r
else if it.get h == '\n' then consumeSpaces n (it.next h) (r.push '\n')
else saveLine (it.next h) (r.push (it.get h))

View File

@@ -1,112 +0,0 @@
/-
Copyright (c) 2025 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.String.Defs
public import Init.Grind.ToInt
public section
/-!
# Register string positions with `grind`.
-/
namespace String
namespace Internal
scoped macro "order" : tactic => `(tactic| {
simp [Pos.Raw.lt_iff, Pos.Raw.le_iff, String.Pos.lt_iff, String.Pos.le_iff, Slice.Pos.lt_iff,
Slice.Pos.le_iff, Pos.Raw.ext_iff, String.Pos.ext_iff, Slice.Pos.ext_iff] at *;
try omega })
end Internal
open Internal
namespace Pos.Raw
instance : Lean.Grind.ToInt String.Pos.Raw (.ci 0) where
toInt p := p.byteIdx
toInt_inj p q := by simp [Pos.Raw.ext_iff, Int.ofNat_inj]
toInt_mem := by simp
@[simp]
theorem toInt_eq {p : Pos.Raw} : Lean.Grind.ToInt.toInt p = p.byteIdx := rfl
instance : Lean.Grind.ToInt.LE String.Pos.Raw (.ci 0) where
le_iff := by simp [Pos.Raw.le_iff]
instance : Lean.Grind.ToInt.LT String.Pos.Raw (.ci 0) where
lt_iff := by simp [Pos.Raw.lt_iff]
instance : Std.LawfulOrderLT String.Pos.Raw where
lt_iff := by order
instance : Std.IsLinearOrder String.Pos.Raw where
le_refl := by order
le_trans := by order
le_antisymm := by order
le_total := by order
end Pos.Raw
namespace Pos
instance {s : String} : Lean.Grind.ToInt s.Pos (.co 0 (s.utf8ByteSize + 1)) where
toInt p := p.offset.byteIdx
toInt_inj p q := by simp [Pos.ext_iff, Pos.Raw.ext_iff, Int.ofNat_inj]
toInt_mem p := by have := p.isValid.le_utf8ByteSize; simp; omega
@[simp]
theorem toInt_eq {s : String} {p : s.Pos} : Lean.Grind.ToInt.toInt p = p.offset.byteIdx := rfl
instance {s : String} : Lean.Grind.ToInt.LE s.Pos (.co 0 (s.utf8ByteSize + 1)) where
le_iff := by simp [Pos.le_iff, Pos.Raw.le_iff]
instance {s : String} : Lean.Grind.ToInt.LT s.Pos (.co 0 (s.utf8ByteSize + 1)) where
lt_iff := by simp [Pos.lt_iff, Pos.Raw.lt_iff]
instance {s : String} : Std.LawfulOrderLT s.Pos where
lt_iff := by order
instance {s : String} : Std.IsLinearOrder s.Pos where
le_refl := by order
le_trans := by order
le_antisymm := by order
le_total := by order
end Pos
namespace Slice.Pos
instance {s : Slice} : Lean.Grind.ToInt s.Pos (.co 0 (s.utf8ByteSize + 1)) where
toInt p := p.offset.byteIdx
toInt_inj p q := by simp [Pos.ext_iff, Pos.Raw.ext_iff, Int.ofNat_inj]
toInt_mem p := by have := p.isValidForSlice.le_utf8ByteSize; simp; omega
@[simp]
theorem toInt_eq {s : Slice} {p : s.Pos} : Lean.Grind.ToInt.toInt p = p.offset.byteIdx := rfl
instance {s : Slice} : Lean.Grind.ToInt.LE s.Pos (.co 0 (s.utf8ByteSize + 1)) where
le_iff := by simp [Pos.le_iff, Pos.Raw.le_iff]
instance {s : Slice} : Lean.Grind.ToInt.LT s.Pos (.co 0 (s.utf8ByteSize + 1)) where
lt_iff := by simp [Pos.lt_iff, Pos.Raw.lt_iff]
instance {s : Slice} : Std.LawfulOrderLT s.Pos where
lt_iff := by order
instance {s : Slice} : Std.IsLinearOrder s.Pos where
le_refl := by order
le_trans := by order
le_antisymm := by order
le_total := by order
end Slice.Pos
end String

View File

@@ -25,9 +25,9 @@ An iterator over the characters (Unicode code points) in a `String`. Typically c
`String.iter`.
This is a no-longer-supported legacy API that will be removed in a future release. You should use
`String.Pos` instead, which is similar, but safer. To iterate over a string `s`, start with
`p : s.startPos`, advance it using `p.next`, access the current character using `p.get` and
check if the position is at the end using `p = s.endPos` or `p.IsAtEnd`.
`String.ValidPos` instead, which is similar, but safer. To iterate over a string `s`, start with
`p : s.startValidPos`, advance it using `p.next`, access the current character using `p.get` and
check if the position is at the end using `p = s.endValidPos` or `p.IsAtEnd`.
String iterators pair a string with a valid byte index. This allows efficient character-by-character
processing of strings while avoiding the need to manually ensure that byte indices are used with the
@@ -57,9 +57,9 @@ structure Iterator where
/-- Creates an iterator at the beginning of the string.
This is a no-longer-supported legacy API that will be removed in a future release. You should use
`String.Pos` instead, which is similar, but safer. To iterate over a string `s`, start with
`p : s.startPos`, advance it using `p.next`, access the current character using `p.get` and
check if the position is at the end using `p = s.endPos` or `p.IsAtEnd`.
`String.ValidPos` instead, which is similar, but safer. To iterate over a string `s`, start with
`p : s.startValidPos`, advance it using `p.next`, access the current character using `p.get` and
check if the position is at the end using `p = s.endValidPos` or `p.IsAtEnd`.
-/
@[inline] def mkIterator (s : String) : Iterator :=
s, 0
@@ -95,9 +95,9 @@ def pos := Iterator.i
Gets the character at the iterator's current position.
This is a no-longer-supported legacy API that will be removed in a future release. You should use
`String.Pos` instead, which is similar, but safer. To iterate over a string `s`, start with
`p : s.startPos`, advance it using `p.next`, access the current character using `p.get` and
check if the position is at the end using `p = s.endPos` or `p.IsAtEnd`.
`String.ValidPos` instead, which is similar, but safer. To iterate over a string `s`, start with
`p : s.startValidPos`, advance it using `p.next`, access the current character using `p.get` and
check if the position is at the end using `p = s.endValidPos` or `p.IsAtEnd`.
A run-time bounds check is performed. Use `String.Iterator.curr'` to avoid redundant bounds checks.
@@ -110,9 +110,9 @@ If the position is invalid, returns `(default : Char)`.
Moves the iterator's position forward by one character, unconditionally.
This is a no-longer-supported legacy API that will be removed in a future release. You should use
`String.Pos` instead, which is similar, but safer. To iterate over a string `s`, start with
`p : s.startPos`, advance it using `p.next`, access the current character using `p.get` and
check if the position is at the end using `p = s.endPos` or `p.IsAtEnd`.
`String.ValidPos` instead, which is similar, but safer. To iterate over a string `s`, start with
`p : s.startValidPos`, advance it using `p.next`, access the current character using `p.get` and
check if the position is at the end using `p = s.endValidPos` or `p.IsAtEnd`.
It is only valid to call this function if the iterator is not at the end of the string (i.e.
if `Iterator.atEnd` is `false`); otherwise, the resulting iterator will be invalid.

View File

@@ -8,7 +8,6 @@ module
prelude
public import Init.Data.String.Lemmas.Splits
public import Init.Data.String.Lemmas.Modify
public import Init.Data.String.Lemmas.Search
public import Init.Data.Char.Order
public import Init.Data.Char.Lemmas
public import Init.Data.List.Lex

View File

@@ -41,11 +41,11 @@ theorem singleton_ne_empty {c : Char} : singleton c ≠ "" := by
@[simp]
theorem Slice.Pos.toCopy_inj {s : Slice} {p₁ p₂ : s.Pos} : p₁.toCopy = p₂.toCopy p₁ = p₂ := by
simp [String.Pos.ext_iff, Pos.ext_iff]
simp [Pos.ext_iff, ValidPos.ext_iff]
@[simp]
theorem Pos.startPos_le {s : String} (p : s.Pos) : s.startPos p := by
simp [Pos.le_iff, Pos.Raw.le_iff]
theorem ValidPos.startValidPos_le {s : String} (p : s.ValidPos) : s.startValidPos p := by
simp [ValidPos.le_iff, Pos.Raw.le_iff]
@[simp]
theorem Slice.Pos.startPos_le {s : Slice} (p : s.Pos) : s.startPos p := by

View File

@@ -22,22 +22,22 @@ public section
namespace String
/-- You might want to invoke `Pos.Splits.exists_eq_singleton_append` to be able to apply this. -/
theorem Pos.Splits.pastSet {s : String} {p : s.Pos} {t₁ t₂ : String}
/-- You might want to invoke `ValidPos.Splits.exists_eq_singleton_append` to be able to apply this. -/
theorem ValidPos.Splits.pastSet {s : String} {p : s.ValidPos} {t₁ t₂ : String}
{c d : Char} (h : p.Splits t₁ (singleton c ++ t₂)) :
(p.pastSet d h.ne_endPos_of_singleton).Splits (t₁ ++ singleton d) t₂ := by
generalize h.ne_endPos_of_singleton = hp
(p.pastSet d h.ne_endValidPos_of_singleton).Splits (t₁ ++ singleton d) t₂ := by
generalize h.ne_endValidPos_of_singleton = hp
obtain rfl, rfl, rfl := by simpa using h.eq (p.splits_next_right hp)
apply splits_pastSet
/-- You might want to invoke `Pos.Splits.exists_eq_singleton_append` to be able to apply this. -/
theorem Pos.Splits.pastModify {s : String} {p : s.Pos} {t₁ t₂ : String}
/-- You might want to invoke `ValidPos.Splits.exists_eq_singleton_append` to be able to apply this. -/
theorem ValidPos.Splits.pastModify {s : String} {p : s.ValidPos} {t₁ t₂ : String}
{c : Char} (h : p.Splits t₁ (singleton c ++ t₂)) :
(p.pastModify f h.ne_endPos_of_singleton).Splits
(t₁ ++ singleton (f (p.get h.ne_endPos_of_singleton))) t₂ :=
(p.pastModify f h.ne_endValidPos_of_singleton).Splits
(t₁ ++ singleton (f (p.get h.ne_endValidPos_of_singleton))) t₂ :=
h.pastSet
theorem toList_mapAux {f : Char Char} {s : String} {p : s.Pos}
theorem toList_mapAux {f : Char Char} {s : String} {p : s.ValidPos}
(h : p.Splits t₁ t₂) : (mapAux f s p).toList = t₁.toList ++ t₂.toList.map f := by
fun_induction mapAux generalizing t₁ t₂ with
| case1 s => simp_all
@@ -47,7 +47,7 @@ theorem toList_mapAux {f : Char → Char} {s : String} {p : s.Pos}
@[simp]
theorem toList_map {f : Char Char} {s : String} : (s.map f).toList = s.toList.map f := by
simp [map, toList_mapAux s.splits_startPos]
simp [map, toList_mapAux s.splits_startValidPos]
@[simp]
theorem length_map {f : Char Char} {s : String} : (s.map f).length = s.length := by

View File

@@ -1,32 +0,0 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel
-/
module
prelude
public import Init.Data.String.Search
import all Init.Data.String.Search
public section
namespace String
open String.Slice Pattern
variable {ρ : Type} {σ : Slice Type}
variable [ s, Std.Iterators.Iterator (σ s) Id (SearchStep s)]
variable [ s, Std.Iterators.Finite (σ s) Id]
variable [ s, Std.Iterators.IteratorLoop (σ s) Id Id]
@[simp]
theorem Slice.Pos.le_find {s : Slice} (pos : s.Pos) (pattern : ρ) [ToForwardSearcher pattern σ] :
pos pos.find pattern := by
simp [Slice.Pos.find]
@[simp]
theorem Pos.le_find {s : String} (pos : s.Pos) (pattern : ρ) [ToForwardSearcher pattern σ] :
pos pos.find pattern := by
simp [Pos.find, toSlice_le]
end String

View File

@@ -11,7 +11,7 @@ import Init.Data.ByteArray.Lemmas
import Init.Data.String.Lemmas.Basic
/-!
# `Splits` predicates on `String.Pos` and `String.Slice.Pos`.
# `Splits` predicates on `String.ValidPos` and `String.Slice.Pos`.
We introduce the predicate `p.Splits t₁ t₂` for a position `p` on a string or slice `s`, which means
that `s = t₁ ++ t₂` with `p` lying between the two parts. This is a useful primitive when verifying
@@ -26,7 +26,7 @@ namespace String
We say that `p` splits `s` into `t₁` and `t₂` if `s = t₁ ++ t₂` and `p` is the position between `t₁`
and `t₂`.
-/
structure Pos.Splits {s : String} (p : s.Pos) (t₁ t₂ : String) : Prop where
structure ValidPos.Splits {s : String} (p : s.ValidPos) (t₁ t₂ : String) : Prop where
eq_append : s = t₁ ++ t₂
offset_eq_rawEndPos : p.offset = t₁.rawEndPos
@@ -39,11 +39,11 @@ structure Slice.Pos.Splits {s : Slice} (p : s.Pos) (t₁ t₂ : String) : Prop w
offset_eq_rawEndPos : p.offset = t₁.rawEndPos
@[simp]
theorem Pos.splits_cast_iff {s₁ s₂ : String} {h : s₁ = s₂} {p : s₁.Pos} {t₁ t₂ : String} :
theorem ValidPos.splits_cast_iff {s₁ s₂ : String} {h : s₁ = s₂} {p : s₁.ValidPos} {t₁ t₂ : String} :
(p.cast h).Splits t₁ t₂ p.Splits t₁ t₂ := by
subst h; simp
theorem Pos.Splits.cast {s₁ s₂ : String} {p : s₁.Pos} {t₁ t₂ : String} (h : s₁ = s₂) :
theorem ValidPos.Splits.cast {s₁ s₂ : String} {p : s₁.ValidPos} {t₁ t₂ : String} (h : s₁ = s₂) :
p.Splits t₁ t₂ (p.cast h).Splits t₁ t₂ :=
splits_cast_iff.mpr
@@ -72,17 +72,17 @@ theorem Slice.Pos.splits_toCopy_iff {s : Slice} {p : s.Pos} {t₁ t₂ : String}
splits_of_splits_toCopy, (·.toCopy)
@[simp]
theorem Pos.splits_toSlice_iff {s : String} {p : s.Pos} {t₁ t₂ : String} :
theorem ValidPos.splits_toSlice_iff {s : String} {p : s.ValidPos} {t₁ t₂ : String} :
p.toSlice.Splits t₁ t₂ p.Splits t₁ t₂ := by
rw [ Slice.Pos.splits_toCopy_iff, p.toCopy_toSlice_eq_cast, splits_cast_iff]
theorem Pos.Splits.toSlice {s : String} {p : s.Pos} {t₁ t₂ : String}
theorem ValidPos.Splits.toSlice {s : String} {p : s.ValidPos} {t₁ t₂ : String}
(h : p.Splits t₁ t₂) : p.toSlice.Splits t₁ t₂ :=
splits_toSlice_iff.mpr h
theorem Pos.splits {s : String} (p : s.Pos) :
theorem ValidPos.splits {s : String} (p : s.ValidPos) :
p.Splits (s.sliceTo p).copy (s.sliceFrom p).copy where
eq_append := by simp [ toByteArray_inj, Slice.toByteArray_copy, size_toByteArray]
eq_append := by simp [ bytes_inj, Slice.bytes_copy, size_bytes]
offset_eq_rawEndPos := by simp
theorem Slice.Pos.splits {s : Slice} (p : s.Pos) :
@@ -90,23 +90,23 @@ theorem Slice.Pos.splits {s : Slice} (p : s.Pos) :
eq_append := copy_eq_copy_sliceTo
offset_eq_rawEndPos := by simp
theorem Pos.Splits.toByteArray_left_eq {s : String} {p : s.Pos} {t₁ t₂}
(h : p.Splits t₁ t₂) : t₁.toByteArray = s.toByteArray.extract 0 p.offset.byteIdx := by
theorem ValidPos.Splits.bytes_left_eq {s : String} {p : s.ValidPos} {t₁ t₂}
(h : p.Splits t₁ t₂) : t₁.bytes = s.bytes.extract 0 p.offset.byteIdx := by
simp [h.eq_append, h.offset_eq_rawEndPos, ByteArray.extract_append_eq_left]
theorem Pos.Splits.toByteArray_right_eq {s : String} {p : s.Pos} {t₁ t₂}
(h : p.Splits t₁ t₂) : t₂.toByteArray = s.toByteArray.extract p.offset.byteIdx s.utf8ByteSize := by
theorem ValidPos.Splits.bytes_right_eq {s : String} {p : s.ValidPos} {t₁ t₂}
(h : p.Splits t₁ t₂) : t₂.bytes = s.bytes.extract p.offset.byteIdx s.utf8ByteSize := by
simp [h.eq_append, h.offset_eq_rawEndPos, ByteArray.extract_append_eq_right]
theorem Pos.Splits.eq_left {s : String} {p : s.Pos} {t₁ t₂ t₃ t₄}
theorem ValidPos.Splits.eq_left {s : String} {p : s.ValidPos} {t₁ t₂ t₃ t₄}
(h₁ : p.Splits t₁ t₂) (h₂ : p.Splits t₃ t₄) : t₁ = t₃ := by
rw [ String.toByteArray_inj, h₁.toByteArray_left_eq, h₂.toByteArray_left_eq]
rw [ String.bytes_inj, h₁.bytes_left_eq, h₂.bytes_left_eq]
theorem Pos.Splits.eq_right {s : String} {p : s.Pos} {t₁ t₂ t₃ t₄}
theorem ValidPos.Splits.eq_right {s : String} {p : s.ValidPos} {t₁ t₂ t₃ t₄}
(h₁ : p.Splits t₁ t₂) (h₂ : p.Splits t₃ t₄) : t₂ = t₄ := by
rw [ String.toByteArray_inj, h₁.toByteArray_right_eq, h₂.toByteArray_right_eq]
rw [ String.bytes_inj, h₁.bytes_right_eq, h₂.bytes_right_eq]
theorem Pos.Splits.eq {s : String} {p : s.Pos} {t₁ t₂ t₃ t₄}
theorem ValidPos.Splits.eq {s : String} {p : s.ValidPos} {t₁ t₂ t₃ t₄}
(h₁ : p.Splits t₁ t₂) (h₂ : p.Splits t₃ t₄) : t₁ = t₃ t₂ = t₄ :=
h₁.eq_left h₂, h₁.eq_right h₂
@@ -123,35 +123,35 @@ theorem Slice.Pos.Splits.eq {s : Slice} {p : s.Pos} {t₁ t₂ t₃ t₄}
(splits_toCopy_iff.2 h₁).eq (splits_toCopy_iff.2 h₂)
@[simp]
theorem splits_endPos (s : String) : s.endPos.Splits s "" where
theorem splits_endValidPos (s : String) : s.endValidPos.Splits s "" where
eq_append := by simp
offset_eq_rawEndPos := by simp
@[simp]
theorem splits_endPos_iff {s : String} :
s.endPos.Splits t₁ t₂ t₁ = s t₂ = "" :=
fun h => h.eq_left s.splits_endPos, h.eq_right s.splits_endPos,
by rintro rfl, rfl; exact t₁.splits_endPos
theorem splits_endValidPos_iff {s : String} :
s.endValidPos.Splits t₁ t₂ t₁ = s t₂ = "" :=
fun h => h.eq_left s.splits_endValidPos, h.eq_right s.splits_endValidPos,
by rintro rfl, rfl; exact t₁.splits_endValidPos
theorem Pos.Splits.eq_endPos_iff {s : String} {p : s.Pos} (h : p.Splits t₁ t₂) :
p = s.endPos t₂ = "" :=
fun h' => h.eq_right (h' s.splits_endPos),
by rintro rfl; simp [Pos.ext_iff, h.offset_eq_rawEndPos, h.eq_append]
theorem ValidPos.Splits.eq_endValidPos_iff {s : String} {p : s.ValidPos} (h : p.Splits t₁ t₂) :
p = s.endValidPos t₂ = "" :=
fun h' => h.eq_right (h' s.splits_endValidPos),
by rintro rfl; simp [ValidPos.ext_iff, h.offset_eq_rawEndPos, h.eq_append]
theorem splits_startPos (s : String) : s.startPos.Splits "" s where
theorem splits_startValidPos (s : String) : s.startValidPos.Splits "" s where
eq_append := by simp
offset_eq_rawEndPos := by simp
@[simp]
theorem splits_startPos_iff {s : String} :
s.startPos.Splits t₁ t₂ t₁ = "" t₂ = s :=
fun h => h.eq_left s.splits_startPos, h.eq_right s.splits_startPos,
by rintro rfl, rfl; exact t₂.splits_startPos
theorem splits_startValidPos_iff {s : String} :
s.startValidPos.Splits t₁ t₂ t₁ = "" t₂ = s :=
fun h => h.eq_left s.splits_startValidPos, h.eq_right s.splits_startValidPos,
by rintro rfl, rfl; exact t₂.splits_startValidPos
theorem Pos.Splits.eq_startPos_iff {s : String} {p : s.Pos} (h : p.Splits t₁ t₂) :
p = s.startPos t₁ = "" :=
fun h' => h.eq_left (h' s.splits_startPos),
by rintro rfl; simp [Pos.ext_iff, h.offset_eq_rawEndPos]
theorem ValidPos.Splits.eq_startValidPos_iff {s : String} {p : s.ValidPos} (h : p.Splits t₁ t₂) :
p = s.startValidPos t₁ = "" :=
fun h' => h.eq_left (h' s.splits_startValidPos),
by rintro rfl; simp [ValidPos.ext_iff, h.offset_eq_rawEndPos]
@[simp]
theorem Slice.splits_endPos (s : Slice) : s.endPos.Splits s.copy "" where
@@ -161,11 +161,11 @@ theorem Slice.splits_endPos (s : Slice) : s.endPos.Splits s.copy "" where
@[simp]
theorem Slice.splits_endPos_iff {s : Slice} :
s.endPos.Splits t₁ t₂ t₁ = s.copy t₂ = "" := by
rw [ Pos.splits_toCopy_iff, endPos_copy, String.splits_endPos_iff]
rw [ Pos.splits_toCopy_iff, endValidPos_copy, splits_endValidPos_iff]
theorem Slice.Pos.Splits.eq_endPos_iff {s : Slice} {p : s.Pos} (h : p.Splits t₁ t₂) :
p = s.endPos t₂ = "" := by
rw [ toCopy_inj, endPos_copy, h.toCopy.eq_endPos_iff]
rw [ toCopy_inj, endValidPos_copy, h.toCopy.eq_endValidPos_iff]
@[simp]
theorem Slice.splits_startPos (s : Slice) : s.startPos.Splits "" s.copy where
@@ -175,18 +175,18 @@ theorem Slice.splits_startPos (s : Slice) : s.startPos.Splits "" s.copy where
@[simp]
theorem Slice.splits_startPos_iff {s : Slice} :
s.startPos.Splits t₁ t₂ t₁ = "" t₂ = s.copy := by
rw [ Pos.splits_toCopy_iff, startPos_copy, String.splits_startPos_iff]
rw [ Pos.splits_toCopy_iff, startValidPos_copy, splits_startValidPos_iff]
theorem Slice.Pos.Splits.eq_startPos_iff {s : Slice} {p : s.Pos} (h : p.Splits t₁ t₂) :
p = s.startPos t₁ = "" := by
rw [ toCopy_inj, startPos_copy, h.toCopy.eq_startPos_iff]
rw [ toCopy_inj, startValidPos_copy, h.toCopy.eq_startValidPos_iff]
theorem Pos.splits_next_right {s : String} (p : s.Pos) (hp : p s.endPos) :
theorem ValidPos.splits_next_right {s : String} (p : s.ValidPos) (hp : p s.endValidPos) :
p.Splits (s.sliceTo p).copy (singleton (p.get hp) ++ (s.sliceFrom (p.next hp)).copy) where
eq_append := by simpa [ append_assoc] using p.eq_copy_sliceTo_append_get hp
offset_eq_rawEndPos := by simp
theorem Pos.splits_next {s : String} (p : s.Pos) (hp : p s.endPos) :
theorem ValidPos.splits_next {s : String} (p : s.ValidPos) (hp : p s.endValidPos) :
(p.next hp).Splits ((s.sliceTo p).copy ++ singleton (p.get hp)) (s.sliceFrom (p.next hp)).copy where
eq_append := p.eq_copy_sliceTo_append_get hp
offset_eq_rawEndPos := by simp
@@ -201,12 +201,12 @@ theorem Slice.Pos.splits_next {s : Slice} (p : s.Pos) (hp : p ≠ s.endPos) :
eq_append := p.copy_eq_copy_sliceTo_append_get hp
offset_eq_rawEndPos := by simp
theorem Pos.Splits.exists_eq_singleton_append {s : String} {p : s.Pos}
(hp : p s.endPos) (h : p.Splits t₁ t₂) : t₂', t₂ = singleton (p.get hp) ++ t₂' :=
theorem ValidPos.Splits.exists_eq_singleton_append {s : String} {p : s.ValidPos}
(hp : p s.endValidPos) (h : p.Splits t₁ t₂) : t₂', t₂ = singleton (p.get hp) ++ t₂' :=
(s.sliceFrom (p.next hp)).copy, h.eq_right (p.splits_next_right hp)
theorem Pos.Splits.exists_eq_append_singleton {s : String} {p : s.Pos}
(hp : p s.endPos) (h : (p.next hp).Splits t₁ t₂) : t₁', t₁ = t₁' ++ singleton (p.get hp) :=
theorem ValidPos.Splits.exists_eq_append_singleton {s : String} {p : s.ValidPos}
(hp : p s.endValidPos) (h : (p.next hp).Splits t₁ t₂) : t₁', t₁ = t₁' ++ singleton (p.get hp) :=
(s.sliceTo p).copy, h.eq_left (p.splits_next hp)
theorem Slice.Pos.Splits.exists_eq_singleton_append {s : Slice} {p : s.Pos}
@@ -217,13 +217,13 @@ theorem Slice.Pos.Splits.exists_eq_append_singleton {s : Slice} {p : s.Pos}
(hp : p s.endPos) (h : (p.next hp).Splits t₁ t₂) : t₁', t₁ = t₁' ++ singleton (p.get hp) :=
(s.sliceTo p).copy, h.eq_left (p.splits_next hp)
theorem Pos.Splits.ne_endPos_of_singleton {s : String} {p : s.Pos}
(h : p.Splits t₁ (singleton c ++ t₂)) : p s.endPos := by
simp [h.eq_endPos_iff]
theorem ValidPos.Splits.ne_endValidPos_of_singleton {s : String} {p : s.ValidPos}
(h : p.Splits t₁ (singleton c ++ t₂)) : p s.endValidPos := by
simp [h.eq_endValidPos_iff]
theorem Pos.Splits.ne_startPos_of_singleton {s : String} {p : s.Pos}
(h : p.Splits (t₁ ++ singleton c) t₂) : p s.startPos := by
simp [h.eq_startPos_iff]
theorem ValidPos.Splits.ne_startValidPos_of_singleton {s : String} {p : s.ValidPos}
(h : p.Splits (t₁ ++ singleton c) t₂) : p s.startValidPos := by
simp [h.eq_startValidPos_iff]
theorem Slice.Pos.Splits.ne_endPos_of_singleton {s : Slice} {p : s.Pos}
(h : p.Splits t₁ (singleton c ++ t₂)) : p s.endPos := by
@@ -233,10 +233,10 @@ theorem Slice.Pos.Splits.ne_startPos_of_singleton {s : Slice} {p : s.Pos}
(h : p.Splits (t₁ ++ singleton c) t₂) : p s.startPos := by
simp [h.eq_startPos_iff]
/-- You might want to invoke `Pos.Splits.exists_eq_singleton_append` to be able to apply this. -/
theorem Pos.Splits.next {s : String} {p : s.Pos}
(h : p.Splits t₁ (singleton c ++ t₂)) : (p.next h.ne_endPos_of_singleton).Splits (t₁ ++ singleton c) t₂ := by
generalize h.ne_endPos_of_singleton = hp
/-- You might want to invoke `ValidPos.Splits.exists_eq_singleton_append` to be able to apply this. -/
theorem ValidPos.Splits.next {s : String} {p : s.ValidPos}
(h : p.Splits t₁ (singleton c ++ t₂)) : (p.next h.ne_endValidPos_of_singleton).Splits (t₁ ++ singleton c) t₂ := by
generalize h.ne_endValidPos_of_singleton = hp
obtain rfl, rfl, rfl := by simpa using h.eq (splits_next_right p hp)
exact splits_next p hp

View File

@@ -33,28 +33,28 @@ Examples:
* `("L∃∀N".pos ⟨4⟩ (by decide)).set 'X' (by decide) = "L∃XN"`
-/
@[extern "lean_string_utf8_set", expose]
def Pos.set {s : String} (p : s.Pos) (c : Char) (hp : p s.endPos) : String :=
def ValidPos.set {s : String} (p : s.ValidPos) (c : Char) (hp : p s.endValidPos) : String :=
if hc : c.utf8Size = 1 (p.byte hp).utf8ByteSize isUTF8FirstByte_byte = 1 then
.ofByteArray (s.toByteArray.set p.offset.byteIdx c.toUInt8 (p.byteIdx_lt_utf8ByteSize hp)) (by
.ofByteArray (s.bytes.set p.offset.byteIdx c.toUInt8 (p.byteIdx_lt_utf8ByteSize hp)) (by
rw [ByteArray.set_eq_push_extract_append_extract, hc.2, utf8ByteSize_byte,
Pos.byteIdx_offset_next]
ValidPos.byteIdx_offset_next]
refine ByteArray.IsValidUTF8.append ?_ (p.next hp).isValid.isValidUTF8_extract_utf8ByteSize
exact p.isValid.isValidUTF8_extract_zero.push hc.1)
else
(s.sliceTo p).copy ++ singleton c ++ (s.sliceFrom (p.next hp)).copy
theorem Pos.set_eq_append {s : String} {p : s.Pos} {c : Char} {hp} :
theorem ValidPos.set_eq_append {s : String} {p : s.ValidPos} {c : Char} {hp} :
p.set c hp = (s.sliceTo p).copy ++ singleton c ++ (s.sliceFrom (p.next hp)).copy := by
rw [set]
split
· rename_i h
simp [ toByteArray_inj, ByteArray.set_eq_push_extract_append_extract, Slice.toByteArray_copy,
simp [ bytes_inj, ByteArray.set_eq_push_extract_append_extract, Slice.bytes_copy,
List.utf8Encode_singleton, String.utf8EncodeChar_eq_singleton h.1, utf8ByteSize_byte h.2]
· rfl
theorem Pos.Raw.IsValid.set_of_le {s : String} {p : s.Pos} {c : Char} {hp : p s.endPos}
theorem Pos.Raw.IsValid.set_of_le {s : String} {p : s.ValidPos} {c : Char} {hp : p s.endValidPos}
{q : Pos.Raw} (hq : q.IsValid s) (hpq : q p.offset) : q.IsValid (p.set c hp) := by
rw [Pos.set_eq_append, String.append_assoc]
rw [ValidPos.set_eq_append, String.append_assoc]
apply append_right
rw [isValid_copy_iff, isValidForSlice_stringSliceTo]
exact hpq, hq
@@ -62,40 +62,40 @@ theorem Pos.Raw.IsValid.set_of_le {s : String} {p : s.Pos} {c : Char} {hp : p
/-- Given a valid position in a string, obtain the corresponding position after setting a character on
that string, provided that the position was before the changed position. -/
@[inline]
def Pos.toSetOfLE {s : String} (q p : s.Pos) (c : Char) (hp : p s.endPos)
(hpq : q p) : (p.set c hp).Pos where
def ValidPos.toSetOfLE {s : String} (q p : s.ValidPos) (c : Char) (hp : p s.endValidPos)
(hpq : q p) : (p.set c hp).ValidPos where
offset := q.offset
isValid := q.isValid.set_of_le hpq
@[simp]
theorem Pos.offset_toSetOfLE {s : String} {q p : s.Pos} {c : Char} {hp : p s.endPos}
theorem ValidPos.offset_toSetOfLE {s : String} {q p : s.ValidPos} {c : Char} {hp : p s.endValidPos}
{hpq : q p} : (q.toSetOfLE p c hp hpq).offset = q.offset := (rfl)
theorem Pos.Raw.isValid_add_char_set {s : String} {p : s.Pos} {c : Char} {hp} :
theorem Pos.Raw.isValid_add_char_set {s : String} {p : s.ValidPos} {c : Char} {hp} :
(p.offset + c).IsValid (p.set c hp) :=
Pos.set_eq_append IsValid.append_right (isValid_of_eq_rawEndPos (by simp)) _
ValidPos.set_eq_append IsValid.append_right (isValid_of_eq_rawEndPos (by simp)) _
/-- The position just after the position that changed in a `Pos.set` call. -/
/-- The position just after the position that changed in a `ValidPos.set` call. -/
@[inline]
def Pos.pastSet {s : String} (p : s.Pos) (c : Char) (hp) : (p.set c hp).Pos where
def ValidPos.pastSet {s : String} (p : s.ValidPos) (c : Char) (hp) : (p.set c hp).ValidPos where
offset := p.offset + c
isValid := Pos.Raw.isValid_add_char_set
@[simp]
theorem Pos.offset_pastSet {s : String} {p : s.Pos} {c : Char} {hp} :
theorem ValidPos.offset_pastSet {s : String} {p : s.ValidPos} {c : Char} {hp} :
(p.pastSet c hp).offset = p.offset + c := (rfl)
@[inline]
def Pos.appendRight {s : String} (p : s.Pos) (t : String) : (s ++ t).Pos where
def ValidPos.appendRight {s : String} (p : s.ValidPos) (t : String) : (s ++ t).ValidPos where
offset := p.offset
isValid := p.isValid.append_right t
theorem Pos.splits_pastSet {s : String} {p : s.Pos} {c : Char} {hp} :
theorem ValidPos.splits_pastSet {s : String} {p : s.ValidPos} {c : Char} {hp} :
(p.pastSet c hp).Splits ((s.sliceTo p).copy ++ singleton c) (s.sliceFrom (p.next hp)).copy where
eq_append := set_eq_append
offset_eq_rawEndPos := by simp
theorem remainingBytes_pastSet {s : String} {p : s.Pos} {c : Char} {hp} :
theorem remainingBytes_pastSet {s : String} {p : s.ValidPos} {c : Char} {hp} :
(p.pastSet c hp).remainingBytes = (p.next hp).remainingBytes := by
rw [(p.next hp).splits.remainingBytes_eq, p.splits_pastSet.remainingBytes_eq]
@@ -110,34 +110,34 @@ Examples:
* `("abc".pos ⟨1⟩ (by decide)).modify Char.toUpper (by decide) = "aBc"`
-/
@[inline]
def Pos.modify {s : String} (p : s.Pos) (f : Char Char) (hp : p s.endPos) :
def ValidPos.modify {s : String} (p : s.ValidPos) (f : Char Char) (hp : p s.endValidPos) :
String :=
p.set (f <| p.get hp) hp
theorem Pos.Raw.IsValid.modify_of_le {s : String} {p : s.Pos} {f : Char Char}
{hp : p s.endPos} {q : Pos.Raw} (hq : q.IsValid s) (hpq : q p.offset) :
theorem Pos.Raw.IsValid.modify_of_le {s : String} {p : s.ValidPos} {f : Char Char}
{hp : p s.endValidPos} {q : Pos.Raw} (hq : q.IsValid s) (hpq : q p.offset) :
q.IsValid (p.modify f hp) :=
set_of_le hq hpq
/-- Given a valid position in a string, obtain the corresponding position after modifying a character
in that string, provided that the position was before the changed position. -/
@[inline]
def Pos.toModifyOfLE {s : String} (q p : s.Pos) (f : Char Char)
(hp : p s.endPos) (hpq : q p) : (p.modify f hp).Pos where
def ValidPos.toModifyOfLE {s : String} (q p : s.ValidPos) (f : Char Char)
(hp : p s.endValidPos) (hpq : q p) : (p.modify f hp).ValidPos where
offset := q.offset
isValid := q.isValid.modify_of_le hpq
@[simp]
theorem Pos.offset_toModifyOfLE {s : String} {q p : s.Pos} {f : Char Char}
{hp : p s.endPos} {hpq : q p} : (q.toModifyOfLE p f hp hpq).offset = q.offset := (rfl)
theorem ValidPos.offset_toModifyOfLE {s : String} {q p : s.ValidPos} {f : Char Char}
{hp : p s.endValidPos} {hpq : q p} : (q.toModifyOfLE p f hp hpq).offset = q.offset := (rfl)
/-- The position just after the position that was modified in a `Pos.modify` call. -/
/-- The position just after the position that was modified in a `ValidPos.modify` call. -/
@[inline]
def Pos.pastModify {s : String} (p : s.Pos) (f : Char Char)
(hp : p s.endPos) : (p.modify f hp).Pos :=
def ValidPos.pastModify {s : String} (p : s.ValidPos) (f : Char Char)
(hp : p s.endValidPos) : (p.modify f hp).ValidPos :=
p.pastSet _ _
theorem remainingBytes_pastModify {s : String} {p : s.Pos} {f : Char Char} {hp} :
theorem remainingBytes_pastModify {s : String} {p : s.ValidPos} {f : Char Char} {hp} :
(p.pastModify f hp).remainingBytes = (p.next hp).remainingBytes :=
remainingBytes_pastSet
@@ -148,8 +148,8 @@ invalid, the string is returned unchanged.
If both the replacement character and the replaced character are 7-bit ASCII characters and the
string is not shared, then it is updated in-place and not copied.
This is a legacy function. The recommended alternative is `String.Pos.set`, combined with
`String.pos` or another means of obtaining a `String.Pos`.
This is a legacy function. The recommended alternative is `String.ValidPos.set`, combined with
`String.pos` or another means of obtaining a `String.ValidPos`.
Examples:
* `"abc".set ⟨1⟩ 'B' = "aBc"`
@@ -173,8 +173,8 @@ character. If `p` is an invalid position, the string is returned unchanged.
If both the replacement character and the replaced character are 7-bit ASCII characters and the
string is not shared, then it is updated in-place and not copied.
This is a legacy function. The recommended alternative is `String.Pos.set`, combined with
`String.pos` or another means of obtaining a `String.Pos`.
This is a legacy function. The recommended alternative is `String.ValidPos.set`, combined with
`String.pos` or another means of obtaining a `String.ValidPos`.
Examples:
* `"abc".modify ⟨1⟩ Char.toUpper = "aBc"`
@@ -188,14 +188,14 @@ def Pos.Raw.modify (s : String) (i : Pos.Raw) (f : Char → Char) : String :=
def modify (s : String) (i : Pos.Raw) (f : Char Char) : String :=
i.set s (f (i.get s))
@[specialize] def mapAux (f : Char Char) (s : String) (p : s.Pos) : String :=
if h : p = s.endPos then
@[specialize] def mapAux (f : Char Char) (s : String) (p : s.ValidPos) : String :=
if h : p = s.endValidPos then
s
else
mapAux f (p.modify f h) (p.pastModify f h)
termination_by p.remainingBytes
decreasing_by
simp [remainingBytes_pastModify, Pos.lt_iff_remainingBytes_lt]
simp [remainingBytes_pastModify, ValidPos.lt_iff_remainingBytes_lt]
/--
Applies the function `f` to every character in a string, returning a string that contains the
@@ -206,7 +206,7 @@ Examples:
* `"".map Char.toUpper = ""`
-/
@[inline] def map (f : Char Char) (s : String) : String :=
mapAux f s s.startPos
mapAux f s s.startValidPos
/--
Replaces each character in `s` with the result of applying `Char.toUpper` to it.

View File

@@ -112,12 +112,12 @@ def memcmpSlice (lhs rhs : Slice) (lstart : String.Pos.Raw) (rstart : String.Pos
(by
have := lhs.startInclusive_le_endExclusive
have := lhs.endExclusive.isValid.le_utf8ByteSize
simp [String.Pos.le_iff, Pos.Raw.le_iff, Slice.utf8ByteSize_eq] at *
simp [ValidPos.le_iff, Pos.Raw.le_iff, Slice.utf8ByteSize_eq] at *
omega)
(by
have := rhs.startInclusive_le_endExclusive
have := rhs.endExclusive.isValid.le_utf8ByteSize
simp [String.Pos.le_iff, Pos.Raw.le_iff, Slice.utf8ByteSize_eq] at *
simp [ValidPos.le_iff, Pos.Raw.le_iff, Slice.utf8ByteSize_eq] at *
omega)
end Internal

View File

@@ -21,44 +21,46 @@ public section
namespace String.Slice.Pattern
structure ForwardCharSearcher (needle : Char) (s : Slice) where
structure ForwardCharSearcher (s : Slice) where
currPos : s.Pos
needle : Char
deriving Inhabited
namespace ForwardCharSearcher
@[inline]
def iter (c : Char) (s : Slice) : Std.Iter (α := ForwardCharSearcher c s) (SearchStep s) :=
{ internalState := { currPos := s.startPos }}
def iter (c : Char) (s : Slice) : Std.Iter (α := ForwardCharSearcher s) (SearchStep s) :=
{ internalState := { currPos := s.startPos, needle := c }}
instance (s : Slice) : Std.Iterators.Iterator (ForwardCharSearcher c s) Id (SearchStep s) where
instance (s : Slice) : Std.Iterators.Iterator (ForwardCharSearcher s) Id (SearchStep s) where
IsPlausibleStep it
| .yield it' out =>
it.internalState.needle = it'.internalState.needle
h1 : it.internalState.currPos s.endPos,
it'.internalState.currPos = it.internalState.currPos.next h1
match out with
| .matched startPos endPos =>
it.internalState.currPos = startPos
it'.internalState.currPos = endPos
it.internalState.currPos.get h1 = c
it.internalState.currPos.get h1 = it.internalState.needle
| .rejected startPos endPos =>
it.internalState.currPos = startPos
it'.internalState.currPos = endPos
it.internalState.currPos.get h1 c
it.internalState.currPos.get h1 it.internalState.needle
| .skip _ => False
| .done => it.internalState.currPos = s.endPos
step := fun currPos =>
step := fun currPos, needle =>
if h1 : currPos = s.endPos then
pure (.deflate .done, by simp [h1])
else
let nextPos := currPos.next h1
let nextIt := nextPos
if h2 : currPos.get h1 = c then
let nextIt := nextPos, needle
if h2 : currPos.get h1 = needle then
pure (.deflate .yield nextIt (.matched currPos nextPos), by simp [h1, h2, nextIt, nextPos])
else
pure (.deflate .yield nextIt (.rejected currPos nextPos), by simp [h1, h2, nextIt, nextPos])
def finitenessRelation : Std.Iterators.FinitenessRelation (ForwardCharSearcher s c) Id where
def finitenessRelation : Std.Iterators.FinitenessRelation (ForwardCharSearcher s) Id where
rel := InvImage WellFoundedRelation.rel (fun it => it.internalState.currPos)
wf := InvImage.wf _ WellFoundedRelation.wf
subrelation {it it'} h := by
@@ -66,18 +68,18 @@ def finitenessRelation : Std.Iterators.FinitenessRelation (ForwardCharSearcher s
obtain step, h, h' := h
cases step
· cases h
obtain _, h2, _ := h'
obtain _, h1, h2, _ := h'
simp [h2]
· cases h'
· cases h
instance : Std.Iterators.Finite (ForwardCharSearcher s c) Id :=
instance : Std.Iterators.Finite (ForwardCharSearcher s) Id :=
.of_finitenessRelation finitenessRelation
instance : Std.Iterators.IteratorLoop (ForwardCharSearcher s c) Id Id :=
instance : Std.Iterators.IteratorLoop (ForwardCharSearcher s) Id Id :=
.defaultImplementation
instance {c : Char} : ToForwardSearcher c (ForwardCharSearcher c) where
instance {c : Char} : ToForwardSearcher c ForwardCharSearcher where
toSearcher := iter c
instance {c : Char} : ForwardPattern c := .defaultImplementation

View File

@@ -22,45 +22,47 @@ public section
namespace String.Slice.Pattern
structure ForwardCharPredSearcher (p : Char Bool) (s : Slice) where
structure ForwardCharPredSearcher (s : Slice) where
currPos : s.Pos
needle : Char Bool
deriving Inhabited
namespace ForwardCharPredSearcher
@[inline]
def iter (p : Char Bool) (s : Slice) : Std.Iter (α := ForwardCharPredSearcher p s) (SearchStep s) :=
{ internalState := { currPos := s.startPos }}
def iter (p : Char Bool) (s : Slice) : Std.Iter (α := ForwardCharPredSearcher s) (SearchStep s) :=
{ internalState := { currPos := s.startPos, needle := p }}
instance (s : Slice) : Std.Iterators.Iterator (ForwardCharPredSearcher p s) Id (SearchStep s) where
instance (s : Slice) : Std.Iterators.Iterator (ForwardCharPredSearcher s) Id (SearchStep s) where
IsPlausibleStep it
| .yield it' out =>
it.internalState.needle = it'.internalState.needle
h1 : it.internalState.currPos s.endPos,
it'.internalState.currPos = it.internalState.currPos.next h1
match out with
| .matched startPos endPos =>
it.internalState.currPos = startPos
it'.internalState.currPos = endPos
p (it.internalState.currPos.get h1)
it.internalState.needle (it.internalState.currPos.get h1)
| .rejected startPos endPos =>
it.internalState.currPos = startPos
it'.internalState.currPos = endPos
¬ p (it.internalState.currPos.get h1)
¬ it.internalState.needle (it.internalState.currPos.get h1)
| .skip _ => False
| .done => it.internalState.currPos = s.endPos
step := fun currPos =>
step := fun currPos, needle =>
if h1 : currPos = s.endPos then
pure (.deflate .done, by simp [h1])
else
let nextPos := currPos.next h1
let nextIt := nextPos
if h2 : p <| currPos.get h1 then
let nextIt := nextPos, needle
if h2 : needle <| currPos.get h1 then
pure (.deflate .yield nextIt (.matched currPos nextPos), by simp [h1, h2, nextPos, nextIt])
else
pure (.deflate .yield nextIt (.rejected currPos nextPos), by simp [h1, h2, nextPos, nextIt])
def finitenessRelation : Std.Iterators.FinitenessRelation (ForwardCharPredSearcher p s) Id where
def finitenessRelation : Std.Iterators.FinitenessRelation (ForwardCharPredSearcher s) Id where
rel := InvImage WellFoundedRelation.rel (fun it => it.internalState.currPos)
wf := InvImage.wf _ WellFoundedRelation.wf
subrelation {it it'} h := by
@@ -68,23 +70,23 @@ def finitenessRelation : Std.Iterators.FinitenessRelation (ForwardCharPredSearch
obtain step, h, h' := h
cases step
· cases h
obtain _, h2, _ := h'
obtain _, h1, h2, _ := h'
simp [h2]
· cases h'
· cases h
instance : Std.Iterators.Finite (ForwardCharPredSearcher p s) Id :=
instance : Std.Iterators.Finite (ForwardCharPredSearcher s) Id :=
.of_finitenessRelation finitenessRelation
instance : Std.Iterators.IteratorLoop (ForwardCharPredSearcher p s) Id Id :=
instance : Std.Iterators.IteratorLoop (ForwardCharPredSearcher s) Id Id :=
.defaultImplementation
instance {p : Char Bool} : ToForwardSearcher p (ForwardCharPredSearcher p) where
instance {p : Char Bool} : ToForwardSearcher p ForwardCharPredSearcher where
toSearcher := iter p
instance {p : Char Bool} : ForwardPattern p := .defaultImplementation
instance {p : Char Prop} [DecidablePred p] : ToForwardSearcher p (ForwardCharPredSearcher p) where
instance {p : Char Prop} [DecidablePred p] : ToForwardSearcher p ForwardCharPredSearcher where
toSearcher := iter (decide <| p ·)
instance {p : Char Prop} [DecidablePred p] : ForwardPattern p :=

View File

@@ -108,7 +108,7 @@ At runtime, this function is implemented by efficient, constant-time code.
-/
@[extern "lean_string_get_byte_fast", expose]
def getUTF8Byte (s : @& String) (p : Pos.Raw) (h : p < s.rawEndPos) : UInt8 :=
s.toByteArray[p.byteIdx]
s.bytes[p.byteIdx]
@[deprecated getUTF8Byte (since := "2025-10-01"), extern "lean_string_get_byte_fast", expose]
abbrev getUtf8Byte (s : String) (p : Pos.Raw) (h : p < s.rawEndPos) : UInt8 :=
@@ -216,7 +216,7 @@ theorem Pos.Raw.increaseBy_charUtf8Size {p : Pos.Raw} {c : Char} :
p.increaseBy c.utf8Size = p + c := by
simp [Pos.Raw.ext_iff]
/-- Increases the byte offset of the position by `1`. Not to be confused with `Pos.next`. -/
/-- Increases the byte offset of the position by `1`. Not to be confused with `ValidPos.next`. -/
@[inline, expose]
def Pos.Raw.inc (p : Pos.Raw) : Pos.Raw :=
p.byteIdx + 1
@@ -224,7 +224,7 @@ def Pos.Raw.inc (p : Pos.Raw) : Pos.Raw :=
@[simp]
theorem Pos.Raw.byteIdx_inc {p : Pos.Raw} : p.inc.byteIdx = p.byteIdx + 1 := (rfl)
/-- Decreases the byte offset of the position by `1`. Not to be confused with `Pos.prev`. -/
/-- Decreases the byte offset of the position by `1`. Not to be confused with `ValidPos.prev`. -/
@[inline, expose]
def Pos.Raw.dec (p : Pos.Raw) : Pos.Raw :=
p.byteIdx - 1

View File

@@ -0,0 +1,89 @@
/-
Copyright (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura, Mario Carneiro
-/
module
prelude
public import Init.Data.String.Substring
import Init.Data.String.Search
public section
/--
Interprets a string as the decimal representation of an integer, returning it. Returns `none` if
the string does not contain a decimal integer.
A string can be interpreted as a decimal integer if it only consists of at least one decimal digit
and optionally `-` in front. Leading `+` characters are not allowed.
Use `String.isInt` to check whether `String.toInt?` would return `some`. `String.toInt!` is an
alternative that panics instead of returning `none` when the string is not an integer.
Examples:
* `"".toInt? = none`
* `"-".toInt? = none`
* `"0".toInt? = some 0`
* `"5".toInt? = some 5`
* `"-5".toInt? = some (-5)`
* `"587".toInt? = some 587`
* `"-587".toInt? = some (-587)`
* `" 5".toInt? = none`
* `"2-3".toInt? = none`
* `"0xff".toInt? = none`
-/
def String.toInt? (s : String) : Option Int := do
if s.front = '-' then do
let v (s.toRawSubstring.drop 1).toNat?;
pure <| - Int.ofNat v
else
Int.ofNat <$> s.toNat?
/--
Checks whether the string can be interpreted as the decimal representation of an integer.
A string can be interpreted as a decimal integer if it only consists of at least one decimal digit
and optionally `-` in front. Leading `+` characters are not allowed.
Use `String.toInt?` or `String.toInt!` to convert such a string to an integer.
Examples:
* `"".isInt = false`
* `"-".isInt = false`
* `"0".isInt = true`
* `"-0".isInt = true`
* `"5".isInt = true`
* `"587".isInt = true`
* `"-587".isInt = true`
* `"+587".isInt = false`
* `" 5".isInt = false`
* `"2-3".isInt = false`
* `"0xff".isInt = false`
-/
def String.isInt (s : String) : Bool :=
if s.front = '-' then
(s.toRawSubstring.drop 1).isNat
else
s.isNat
/--
Interprets a string as the decimal representation of an integer, returning it. Panics if the string
does not contain a decimal integer.
A string can be interpreted as a decimal integer if it only consists of at least one decimal digit
and optionally `-` in front. Leading `+` characters are not allowed.
Use `String.isInt` to check whether `String.toInt!` would return a value. `String.toInt?` is a safer
alternative that returns `none` instead of panicking when the string is not an integer.
Examples:
* `"0".toInt! = 0`
* `"5".toInt! = 5`
* `"587".toInt! = 587`
* `"-587".toInt! = -587`
-/
def String.toInt! (s : String) : Int :=
match s.toInt? with
| some v => v
| none => panic "Int expected"

View File

@@ -66,21 +66,6 @@ def Slice.Pos.find? {s : Slice} (pos : s.Pos) (pattern : ρ) [ToForwardSearcher
Option s.Pos :=
((s.sliceFrom pos).find? pattern).map ofSliceFrom
/--
Finds the position of the first match of the pattern {name}`pattern` in after the position
{name}`pos`. If there is no match {lean}`s.endPos` is returned.
This function is generic over all currently supported patterns.
Examples:
* {lean}`("coffee tea water".toSlice.startPos.find Char.isWhitespace).get! == ' '`
* {lean}`("tea".toSlice.pos ⟨1⟩ (by decide)).find (fun (c : Char) => c == 't') == "tea".toSlice.endPos`
-/
@[inline]
def Slice.Pos.find {s : Slice} (pos : s.Pos) (pattern : ρ) [ToForwardSearcher pattern σ] :
s.Pos :=
ofSliceFrom ((s.sliceFrom pos).find pattern)
/--
Finds the position of the first match of the pattern {name}`pattern` in after the position
{name}`pos`. If there is no match {name}`none` is returned.
@@ -88,28 +73,13 @@ Finds the position of the first match of the pattern {name}`pattern` in after th
This function is generic over all currently supported patterns.
Examples:
* {lean}`("coffee tea water".startPos.find? Char.isWhitespace).map (·.get!) == some ' '`
* {lean}`("coffee tea water".startValidPos.find? Char.isWhitespace).map (·.get!) == some ' '`
* {lean}`("tea".pos ⟨1⟩ (by decide)).find? (fun (c : Char) => c == 't') == none`
-/
@[inline]
def Pos.find? {s : String} (pos : s.Pos) (pattern : ρ)
[ToForwardSearcher pattern σ] : Option s.Pos :=
(pos.toSlice.find? pattern).map Pos.ofToSlice
/--
Finds the position of the first match of the pattern {name}`pattern` in after the position
{name}`pos`. If there is no match {lean}`s.endPos` is returned.
This function is generic over all currently supported patterns.
Examples:
* {lean}`("coffee tea water".startPos.find Char.isWhitespace).get! == ' '`
* {lean}`("tea".pos ⟨1⟩ (by decide)).find (fun (c : Char) => c == 't') == "tea".endPos`
-/
@[inline]
def Pos.find {s : String} (pos : s.Pos) (pattern : ρ) [ToForwardSearcher pattern σ] :
s.Pos :=
ofToSlice (pos.toSlice.find pattern)
def ValidPos.find? {s : String} (pos : s.ValidPos) (pattern : ρ)
[ToForwardSearcher pattern σ] : Option s.ValidPos :=
(pos.toSlice.find? pattern).map (·.ofSlice)
/--
Finds the position of the first match of the pattern {name}`pattern` in a string {name}`s`. If
@@ -123,120 +93,8 @@ Examples:
* {lean}`("coffee tea water".find? "tea").map (·.get!) == some 't'`
-/
@[inline]
def find? (s : String) (pattern : ρ) [ToForwardSearcher pattern σ] : Option s.Pos :=
s.startPos.find? pattern
/--
Finds the position of the first match of the pattern {name}`pattern` in a slice {name}`s`. If there
is no match {lean}`s.endPos` is returned.
This function is generic over all currently supported patterns.
Examples:
* {lean}`("coffee tea water".find Char.isWhitespace).get! == ' '`
* {lean}`"tea".find (fun (c : Char) => c == 'X') == "tea".endPos`
* {lean}`("coffee tea water".find "tea").get! == 't'`
-/
@[inline]
def find (s : String) (pattern : ρ) [ToForwardSearcher pattern σ] : s.Pos :=
s.startPos.find pattern
/--
Finds the position of the first match of the pattern {name}`pattern` in a slice {name}`s` that is
strictly before {name}`pos`. If there is no such match {lean}`none` is returned.
This function is generic over all currently supported patterns except
{name}`String`/{name}`String.Slice`.
Examples:
* {lean}`(("abc".toSlice.endPos.prev (by decide)).revFind? Char.isAlpha).map (·.get!) == some 'b'`
* {lean}`"abc".toSlice.startPos.revFind? Char.isAlpha == none`
-/
@[inline]
def Slice.Pos.revFind? {s : Slice} (pos : s.Pos) (pattern : ρ) [ToBackwardSearcher pattern σ] :
Option s.Pos :=
((s.sliceTo pos).revFind? pattern).map ofSliceTo
/--
Finds the position of the first match of the pattern {name}`pattern` in a slice {name}`s` that is
strictly before {name}`pos`. If there is no such match {lean}`none` is returned.
This function is generic over all currently supported patterns except
{name}`String`/{name}`String.Slice`.
Examples:
* {lean}`(("ab1c".endPos.prev (by decide)).revFind? Char.isAlpha).map (·.get!) == some 'b'`
* {lean}`"abc".startPos.revFind? Char.isAlpha == none`
-/
@[inline]
def Pos.revFind? {s : String} (pos : s.Pos) (pattern : ρ) [ToBackwardSearcher pattern σ] :
Option s.Pos :=
(pos.toSlice.revFind? pattern).map Pos.ofToSlice
/--
Finds the position of the first match of the pattern {name}`pattern` in a string, starting
from the end of the slice and traversing towards the start. If there is no match {name}`none` is
returned.
This function is generic over all currently supported patterns except
{name}`String`/{name}`String.Slice`.
Examples:
* {lean}`("coffee tea water".toSlice.revFind? Char.isWhitespace).map (·.get!) == some ' '`
* {lean}`"tea".toSlice.revFind? (fun (c : Char) => c == 'X') == none`
-/
@[inline]
def revFind? (s : String) (pattern : ρ) [ToBackwardSearcher pattern σ] : Option s.Pos :=
s.endPos.revFind? pattern
@[export lean_string_posof]
def Internal.posOfImpl (s : String) (c : Char) : Pos.Raw :=
(s.find c).offset
@[deprecated String.Pos.find (since := "2025-11-19")]
def findAux (s : String) (p : Char Bool) (stopPos : Pos.Raw) (pos : Pos.Raw) : Pos.Raw :=
if h : pos stopPos pos.IsValid s stopPos.IsValid s then
(String.Slice.mk s (s.pos pos h.2.1) (s.pos stopPos h.2.2)
(by simp [Pos.le_iff, h.1])).find p |>.str.offset
else stopPos
@[deprecated String.Pos.find (since := "2025-11-19")]
def posOfAux (s : String) (c : Char) (stopPos : Pos.Raw) (pos : Pos.Raw) : Pos.Raw :=
if h : pos stopPos pos.IsValid s stopPos.IsValid s then
(String.Slice.mk s (s.pos pos h.2.1) (s.pos stopPos h.2.2)
(by simp [Pos.le_iff, h.1])).find c |>.str.offset
else stopPos
@[deprecated String.find (since := "2025-11-19")]
def posOf (s : String) (c : Char) : Pos.Raw :=
(s.find c).offset
@[deprecated String.Pos.revFind? (since := "2025-11-19")]
def revPosOfAux (s : String) (c : Char) (pos : Pos.Raw) : Option Pos.Raw :=
s.pos? pos |>.bind (·.revFind? c) |>.map (·.offset)
@[deprecated String.revFind? (since := "2025-11-19")]
def revPosOf (s : String) (c : Char) : Option Pos.Raw :=
s.revFind? c |>.map (·.offset)
@[deprecated String.Pos.revFind? (since := "2025-11-19")]
def revFindAux (s : String) (p : Char Bool) (pos : Pos.Raw) : Option Pos.Raw :=
s.pos? pos |>.bind (·.revFind? p) |>.map (·.offset)
@[deprecated String.revFind? (since := "2025-11-19")]
def revFind (s : String) (p : Char Bool) : Option Pos.Raw :=
s.revFind? p |>.map (·.offset)
/--
Returns the position of the beginning of the line that contains the position {name}`pos`.
Lines are ended by {lean}`'\n'`, and the returned position is either {lean}`0 : String.Pos.Raw` or
immediately after a {lean}`'\n'` character.
-/
@[deprecated String.Pos.revFind? (since := "2025-11-19")]
def findLineStart (s : String) (pos : String.Pos.Raw) : String.Pos.Raw :=
s.pos? pos |>.bind (·.revFind? '\n') |>.map (·.offset) |>.getD s.startPos.offset
def find? (s : String) (pattern : ρ) [ToForwardSearcher pattern σ] : Option s.ValidPos :=
s.startValidPos.find? pattern
/--
Splits a string at each subslice that matches the pattern {name}`pat`.
@@ -254,25 +112,9 @@ Examples:
* {lean}`("baaab".split "aa").toList == ["b".toSlice, "ab".toSlice]`
-/
@[inline]
def split (s : String) (pat : ρ) [ToForwardSearcher pat σ] :=
def split (s : String) (pat : ρ) [ToForwardSearcher pat σ] :=
(s.toSlice.split pat : Std.Iter String.Slice)
/--
Splits a string at each subslice that matches the pattern {name}`pat`. Unlike {name}`split` the
matched subslices are included at the end of each subslice.
This function is generic over all currently supported patterns.
Examples:
* {lean}`("coffee tea water".splitInclusive Char.isWhitespace).toList == ["coffee ".toSlice, "tea ".toSlice, "water".toSlice]`
* {lean}`("coffee tea water".splitInclusive ' ').toList == ["coffee ".toSlice, "tea ".toSlice, "water".toSlice]`
* {lean}`("coffee tea water".splitInclusive " tea ").toList == ["coffee tea ".toSlice, "water".toSlice]`
* {lean}`("baaab".splitInclusive "aa").toList == ["baa".toSlice, "ab".toSlice]`
-/
@[inline]
def splitInclusive (s : String) (pat : ρ) [ToForwardSearcher pat σ] :=
(s.toSlice.splitInclusive pat : Std.Iter String.Slice)
@[deprecated String.Slice.foldl (since := "2025-11-20")]
def foldlAux {α : Type u} (f : α Char α) (s : String) (stopPos : Pos.Raw) (i : Pos.Raw) (a : α) : α :=
s.slice! (s.pos! i) (s.pos! stopPos) |>.foldl f a
@@ -293,68 +135,6 @@ Examples:
def Internal.foldlImpl (f : String Char String) (init : String) (s : String) : String :=
String.foldl f init s
@[deprecated String.Slice.foldr (since := "2025-11-25")]
def foldrAux {α : Type u} (f : Char α α) (a : α) (s : String) (i begPos : Pos.Raw) : α :=
s.slice! (s.pos! begPos) (s.pos! i) |>.foldr f a
/--
Folds a function over a string from the right, accumulating a value starting with {lean}`init`. The
accumulated value is combined with each character in reverse order, using {lean}`f`.
Examples:
* {lean}`"coffee tea water".foldr (fun c n => if c.isWhitespace then n + 1 else n) 0 = 2`
* {lean}`"coffee tea and water".foldr (fun c n => if c.isWhitespace then n + 1 else n) 0 = 3`
* {lean}`"coffee tea water".foldr (fun c s => s.push c) "" = "retaw aet eeffoc"`
-/
@[inline] def foldr {α : Type u} (f : Char α α) (init : α) (s : String) : α :=
s.toSlice.foldr f init
@[deprecated String.Slice.any (since := "2025-11-25")]
def anyAux (s : String) (stopPos : Pos.Raw) (p : Char Bool) (i : Pos.Raw) : Bool :=
s.slice! (s.pos! i) (s.pos! stopPos) |>.any p
/--
Checks whether a string has a match of the pattern {name}`pat` anywhere.
This function is generic over all currently supported patterns.
Examples:
* {lean}`"coffee tea water".contains Char.isWhitespace = true`
* {lean}`"tea".contains (fun (c : Char) => c == 'X') = false`
* {lean}`"coffee tea water".contains "tea" = true`
-/
@[inline] def contains (s : String) (pat : ρ) [ToForwardSearcher pat σ] : Bool :=
s.toSlice.contains pat
@[export lean_string_contains]
def Internal.containsImpl (s : String) (c : Char) : Bool :=
String.contains s c
@[inline, inherit_doc contains] def any (s : String) (pat : ρ) [ToForwardSearcher pat σ] : Bool :=
s.contains pat
@[export lean_string_any]
def Internal.anyImpl (s : String) (p : Char Bool) :=
String.any s p
/--
Checks whether a slice only consists of matches of the pattern {name}`pat`.
Short-circuits at the first pattern mis-match.
This function is generic over all currently supported patterns.
Examples:
* {lean}`"brown".all Char.isLower = true`
* {lean}`"brown and orange".all Char.isLower = false`
* {lean}`"aaaaaa".all 'a' = true`
* {lean}`"aaaaaa".all "aa" = true`
* {lean}`"aaaaaaa".all "aa" = false`
-/
@[inline] def all (s : String) (pat : ρ) [ForwardPattern pat] : Bool :=
s.toSlice.all pat
/--
Checks whether the string can be interpreted as the decimal representation of a natural number.
@@ -420,219 +200,6 @@ Examples:
@[inline] def toNat! (s : String) : Nat :=
s.toSlice.toNat!
/--
Interprets a string as the decimal representation of an integer, returning it. Returns {lean}`none`
if the string does not contain a decimal integer.
A string can be interpreted as a decimal integer if it only consists of at least one decimal digit
and optionally {lit}`-` in front. Leading `+` characters are not allowed.
Use {name (scope := "Init.Data.String.Search")}`String.isInt` to check whether {name}`String.toInt?`
would return {lean}`some`. {name (scope := "Init.Data.String.Search")}`String.toInt!` is an
alternative that panics instead of returning {lean}`none` when the string is not an integer.
Examples:
* {lean}`"".toInt? = none`
* {lean}`"-".toInt? = none`
* {lean}`"0".toInt? = some 0`
* {lean}`"5".toInt? = some 5`
* {lean}`"-5".toInt? = some (-5)`
* {lean}`"587".toInt? = some 587`
* {lean}`"-587".toInt? = some (-587)`
* {lean}`" 5".toInt? = none`
* {lean}`"2-3".toInt? = none`
* {lean}`"0xff".toInt? = none`
-/
@[inline] def toInt? (s : String) : Option Int :=
s.toSlice.toInt?
/--
Checks whether the string can be interpreted as the decimal representation of an integer.
A string can be interpreted as a decimal integer if it only consists of at least one decimal digit
and optionally {lit}`-` in front. Leading `+` characters are not allowed.
Use {name}`String.toInt?` or {name (scope := "Init.Data.String.Search")}`String.toInt!` to convert
such a string to an integer.
Examples:
* {lean}`"".isInt = false`
* {lean}`"-".isInt = false`
* {lean}`"0".isInt = true`
* {lean}`"-0".isInt = true`
* {lean}`"5".isInt = true`
* {lean}`"587".isInt = true`
* {lean}`"-587".isInt = true`
* {lean}`"+587".isInt = false`
* {lean}`" 5".isInt = false`
* {lean}`"2-3".isInt = false`
* {lean}`"0xff".isInt = false`
-/
@[inline] def isInt (s : String) : Bool :=
s.toSlice.isInt
/--
Interprets a string as the decimal representation of an integer, returning it. Panics if the string
does not contain a decimal integer.
A string can be interpreted as a decimal integer if it only consists of at least one decimal digit
and optionally {lit}`-` in front. Leading `+` characters are not allowed.
Use {name}`String.isInt` to check whether {name}`String.toInt!` would return a value.
{name}`String.toInt?` is a safer alternative that returns {lean}`none` instead of panicking when the
string is not an integer.
Examples:
* {lean}`"0".toInt! = 0`
* {lean}`"5".toInt! = 5`
* {lean}`"587".toInt! = 587`
* {lean}`"-587".toInt! = -587`
-/
@[inline] def toInt! (s : String) : Int :=
s.toSlice.toInt!
/--
Returns the first character in {name}`s`. If {name}`s` is empty, returns {name}`none`.
Examples:
* {lean}`"abc".front? = some 'a'`
* {lean}`"".front? = none`
-/
@[inline]
def front? (s : String) : Option Char :=
s.toSlice.front?
/--
Returns the first character in {name}`s`. If {lean}`s = ""`, returns {lean}`(default : Char)`.
Examples:
* {lean}`"abc".front = 'a'`
* {lean}`"".front = (default : Char)`
-/
@[inline, expose] def front (s : String) : Char :=
s.toSlice.front
@[export lean_string_front]
def Internal.frontImpl (s : String) : Char :=
String.front s
/--
Returns the last character in {name}`s`. If {name}`s` is empty, returns {name}`none`.
Examples:
* {lean}`"abc".back? = some 'c'`
* {lean}`"".back? = none`
-/
@[inline]
def back? (s : String) : Option Char :=
s.toSlice.back?
/--
Returns the last character in {name}`s`. If {lean}`s = ""`, returns {lean}`(default : Char)`.
Examples:
* {lean}`"abc".back = 'c'`
* {lean}`"".back = (default : Char)`
-/
@[inline, expose] def back (s : String) : Char :=
s.toSlice.back
theorem Pos.ofToSlice_ne_endPos {s : String} {p : s.toSlice.Pos}
(h : p s.toSlice.endPos) : ofToSlice p s.endPos := by
rwa [ne_eq, Pos.toSlice_inj, Slice.Pos.toSlice_ofToSlice, endPos_toSlice]
@[inline]
def Internal.toSliceWithProof {s : String} :
{ p : s.toSlice.Pos // p s.toSlice.endPos } { p : s.Pos // p s.endPos } :=
fun p, h => Pos.ofToSlice p, Pos.ofToSlice_ne_endPos h
/--
Creates an iterator over all valid positions within {name}`s`.
Examples
* {lean}`("abc".positions.map (fun ⟨p, h⟩ => p.get h) |>.toList) = ['a', 'b', 'c']`
* {lean}`("abc".positions.map (·.val.offset.byteIdx) |>.toList) = [0, 1, 2]`
* {lean}`("ab∀c".positions.map (fun ⟨p, h⟩ => p.get h) |>.toList) = ['a', 'b', '∀', 'c']`
* {lean}`("ab∀c".positions.map (·.val.offset.byteIdx) |>.toList) = [0, 1, 2, 5]`
-/
@[inline]
def positions (s : String) :=
(s.toSlice.positions.map Internal.toSliceWithProof : Std.Iter { p : s.Pos // p s.endPos })
/--
Creates an iterator over all characters (Unicode code points) in {name}`s`.
Examples:
* {lean}`"abc".chars.toList = ['a', 'b', 'c']`
* {lean}`"ab∀c".chars.toList = ['a', 'b', '∀', 'c']`
-/
@[inline]
def chars (s : String) :=
(s.toSlice.chars : Std.Iter Char)
/--
Creates an iterator over all valid positions within {name}`s`, starting from the last valid
position and iterating towards the first one.
Examples
* {lean}`("abc".revPositions.map (fun ⟨p, h⟩ => p.get h) |>.toList) = ['c', 'b', 'a']`
* {lean}`("abc".revPositions.map (·.val.offset.byteIdx) |>.toList) = [2, 1, 0]`
* {lean}`("ab∀c".revPositions.map (fun ⟨p, h⟩ => p.get h) |>.toList) = ['c', '∀', 'b', 'a']`
* {lean}`("ab∀c".toSlice.revPositions.map (·.val.offset.byteIdx) |>.toList) = [5, 2, 1, 0]`
-/
@[inline]
def revPositions (s : String) :=
(s.toSlice.revPositions.map Internal.toSliceWithProof : Std.Iter { p : s.Pos // p s.endPos })
/--
Creates an iterator over all characters (Unicode code points) in {name}`s`, starting from the end
of the slice and iterating towards the start.
Example:
* {lean}`"abc".revChars.toList = ['c', 'b', 'a']`
* {lean}`"ab∀c".revChars.toList = ['c', '∀', 'b', 'a']`
-/
@[inline]
def revChars (s : String) :=
(s.toSlice.revChars : Std.Iter Char)
/--
Creates an iterator over all bytes in {name}`s`.
Examples:
* {lean}`"abc".byteIterator.toList = [97, 98, 99]`
* {lean}`"ab∀c".byteIterator.toList = [97, 98, 226, 136, 128, 99]`
-/
@[inline]
def byteIterator (s : String) :=
(s.toSlice.bytes : Std.Iter UInt8)
/--
Creates an iterator over all bytes in {name}`s`, starting from the last one and iterating towards
the first one.
Examples:
* {lean}`"abc".revBytes.toList = [99, 98, 97]`
* {lean}`"ab∀c".revBytes.toList = [99, 128, 136, 226, 98, 97]`
-/
@[inline]
def revBytes (s : String) :=
(s.toSlice.revBytes : Std.Iter UInt8)
/--
Creates an iterator over all lines in {name}`s` with the line ending characters `\r\n` or `\n` being
stripped.
Examples:
* {lean}`"foo\r\nbar\n\nbaz\n".lines.toList == ["foo".toSlice, "bar".toSlice, "".toSlice, "baz".toSlice]`
* {lean}`"foo\r\nbar\n\nbaz".lines.toList == ["foo".toSlice, "bar".toSlice, "".toSlice, "baz".toSlice]`
* {lean}`"foo\r\nbar\n\nbaz\r".lines.toList == ["foo".toSlice, "bar".toSlice, "".toSlice, "baz\r".toSlice]`
-/
def lines (s : String) :=
s.toSlice.lines
end
end String

View File

@@ -489,26 +489,11 @@ Examples:
* {lean}`"tea".toSlice.find? (fun (c : Char) => c == 'X') == none`
* {lean}`("coffee tea water".toSlice.find? "tea").map (·.get!) == some 't'`
-/
@[inline]
@[specialize pat]
def find? (s : Slice) (pat : ρ) [ToForwardSearcher pat σ] : Option s.Pos :=
let searcher := ToForwardSearcher.toSearcher pat s
searcher.findSome? (fun | .matched startPos _ => some startPos | .rejected .. => none)
/--
Finds the position of the first match of the pattern {name}`pat` in a slice {name}`s`. If there
is no match {lean}`s.endPos` is returned.
This function is generic over all currently supported patterns.
Examples:
* {lean}`("coffee tea water".toSlice.find Char.isWhitespace).get! == ' '`
* {lean}`"tea".toSlice.find (fun (c : Char) => c == 'X') == "tea".toSlice.endPos`
* {lean}`("coffee tea water".toSlice.find "tea").get! == 't'`
-/
@[inline]
def find (s : Slice) (pat : ρ) [ToForwardSearcher pat σ] : s.Pos :=
s.find? pat |>.getD s.endPos
/--
Checks whether a slice has a match of the pattern {name}`pat` anywhere.
@@ -529,7 +514,7 @@ def any (s : Slice) (pat : ρ) [ToForwardSearcher pat σ] : Bool :=
s.contains pat
/--
Checks whether a slice only consists of matches of the pattern {name}`pat`.
Checks whether a slice only consists of matches of the pattern {name}`pat` anywhere.
Short-circuits at the first pattern mis-match.
@@ -806,7 +791,7 @@ where
termination_by curr.down
/--
Finds the position of the first match of the pattern {name}`pat` in a slice, starting
Finds the position of the first match of the pattern {name}`pat` in a slice {name}`true`, starting
from the end of the slice and traversing towards the start. If there is no match {name}`none` is
returned.
@@ -1318,13 +1303,13 @@ def toNat! (s : Slice) : Nat :=
panic! "Nat expected"
/--
Returns the first character in {name}`s`. If {name}`s` is empty, returns {name}`none`.
Returns the first character in {name}`s`. If {name}`s` is empty, {name}`none`.
Examples:
* {lean}`"abc".toSlice.front? = some 'a'`
* {lean}`"".toSlice.front? = none`
-/
@[inline, expose]
@[inline]
def front? (s : Slice) : Option Char :=
s.startPos.get?
@@ -1335,89 +1320,10 @@ Examples:
* {lean}`"abc".toSlice.front = 'a'`
* {lean}`"".toSlice.front = (default : Char)`
-/
@[inline, expose]
@[inline]
def front (s : Slice) : Char :=
s.front?.getD default
/--
Checks whether the slice can be interpreted as the decimal representation of an integer.
A slice can be interpreted as a decimal integer if it only consists of at least one decimal digit
and optionally {lit}`-` in front. Leading {lit}`+` characters are not allowed.
Use {name (scope := "Init.Data.String.Slice")}`String.Slice.toInt?` or {name (scope := "Init.Data.String.Slice")}`String.toInt!` to convert such a string to an integer.
Examples:
* {lean}`"".toSlice.isInt = false`
* {lean}`"-".toSlice.isInt = false`
* {lean}`"0".toSlice.isInt = true`
* {lean}`"-0".toSlice.isInt = true`
* {lean}`"5".toSlice.isInt = true`
* {lean}`"587".toSlice.isInt = true`
* {lean}`"-587".toSlice.isInt = true`
* {lean}`"+587".toSlice.isInt = false`
* {lean}`" 5".toSlice.isInt = false`
* {lean}`"2-3".toSlice.isInt = false`
* {lean}`"0xff".toSlice.isInt = false`
-/
def isInt (s : Slice) : Bool :=
if s.front = '-' then
(s.drop 1).isNat
else
s.isNat
/--
Interprets a slice as the decimal representation of an integer, returning it. Returns {lean}`none` if
the string does not contain a decimal integer.
A string can be interpreted as a decimal integer if it only consists of at least one decimal digit
and optionally {lit}`-` in front. Leading {lit}`+` characters are not allowed.
Use {name}`Slice.isInt` to check whether {name}`Slice.toInt?` would return {lean}`some`.
{name (scope := "Init.Data.String.Slice")}`Slice.toInt!` is an alternative that panics instead of
returning {lean}`none` when the string is not an integer.
Examples:
* {lean}`"".toSlice.toInt? = none`
* {lean}`"-".toSlice.toInt? = none`
* {lean}`"0".toSlice.toInt? = some 0`
* {lean}`"5".toSlice.toInt? = some 5`
* {lean}`"-5".toSlice.toInt? = some (-5)`
* {lean}`"587".toSlice.toInt? = some 587`
* {lean}`"-587".toSlice.toInt? = some (-587)`
* {lean}`" 5".toSlice.toInt? = none`
* {lean}`"2-3".toSlice.toInt? = none`
* {lean}`"0xff".toSlice.toInt? = none`
-/
def toInt? (s : Slice) : Option Int :=
if s.front = '-' then
Int.negOfNat <$> (s.drop 1).toNat?
else
Int.ofNat <$> s.toNat?
/--
Interprets a string as the decimal representation of an integer, returning it. Panics if the string
does not contain a decimal integer.
A string can be interpreted as a decimal integer if it only consists of at least one decimal digit
and optionally {lit}`-` in front. Leading `+` characters are not allowed.
Use {name}`Slice.isInt` to check whether {name}`Slice.toInt!` would return a value.
{name}`Slice.toInt?` is a safer alternative that returns {lean}`none` instead of panicking when the
string is not an integer.
Examples:
* {lean}`"0".toSlice.toInt! = 0`
* {lean}`"5".toSlice.toInt! = 5`
* {lean}`"587".toSlice.toInt! = 587`
* {lean}`"-587".toSlice.toInt! = -587`
-/
@[inline]
def toInt! (s : Slice) : Int :=
match s.toInt? with
| some v => v
| none => panic "Int expected"
/--
Returns the last character in {name}`s`. If {name}`s` is empty, returns {name}`none`.
@@ -1425,7 +1331,7 @@ Examples:
* {lean}`"abc".toSlice.back? = some 'c'`
* {lean}`"".toSlice.back? = none`
-/
@[inline, expose]
@[inline]
def back? (s : Slice) : Option Char :=
s.endPos.prev? |>.bind (·.get?)
@@ -1436,7 +1342,7 @@ Examples:
* {lean}`"abc".toSlice.back = 'c'`
* {lean}`"".toSlice.back = (default : Char)`
-/
@[inline, expose]
@[inline]
def back (s : Slice) : Char :=
s.back?.getD default

View File

@@ -32,10 +32,10 @@ def ofSlice (s : String.Slice) : Substring.Raw where
Converts a `Substring.Raw` into a `String.Slice`, returning `none` if the substring is invalid.
-/
@[inline]
def toSlice? (s : Substring.Raw) : Option String.Slice :=
def toSlice (s : Substring.Raw) : Option String.Slice :=
if h : s.startPos.IsValid s.str s.stopPos.IsValid s.str s.startPos s.stopPos then
some (String.Slice.mk s.str (s.str.pos s.startPos h.1) (s.str.pos s.stopPos h.2.1)
(by simp [String.Pos.le_iff, h.2.2]))
(by simp [String.ValidPos.le_iff, h.2.2]))
else
none
@@ -51,7 +51,7 @@ A substring is empty if its start and end positions are the same.
def Internal.isEmptyImpl (ss : Substring.Raw) : Bool :=
Substring.Raw.isEmpty ss
/--{}
/--
Copies the region of the underlying string pointed to by a substring into a fresh string.
-/
@[inline] def toString : Substring.Raw String
@@ -155,7 +155,8 @@ Returns the substring-relative position of the first occurrence of `c` in `s`, o
doesn't occur.
-/
@[inline] def posOf (s : Substring.Raw) (c : Char) : String.Pos.Raw :=
s.toSlice?.map (·.find c |>.offset) |>.getD s.bsize
match s with
| s, b, e => { byteIdx := (String.posOfAux s c e b).byteIdx - b.byteIdx }
/--
Removes the specified number of characters (Unicode code points) from the beginning of a substring
@@ -260,14 +261,15 @@ Folds a function over a substring from the left, accumulating a value starting w
accumulated value is combined with each character in order, using `f`.
-/
@[inline] def foldl {α : Type u} (f : α Char α) (init : α) (s : Substring.Raw) : α :=
s.toSlice?.get!.foldl f init
s.toSlice.get!.foldl f init
/--
Folds a function over a substring from the right, accumulating a value starting with `init`. The
accumulated value is combined with each character in reverse order, using `f`.
-/
@[inline] def foldr {α : Type u} (f : Char α α) (init : α) (s : Substring.Raw) : α :=
s.toSlice?.get!.foldr f init
match s with
| s, b, e => String.foldrAux f init s e b
/--
Checks whether the Boolean predicate `p` returns `true` for any character in a substring.
@@ -275,7 +277,8 @@ Checks whether the Boolean predicate `p` returns `true` for any character in a s
Short-circuits at the first character for which `p` returns `true`.
-/
@[inline] def any (s : Substring.Raw) (p : Char Bool) : Bool :=
s.toSlice?.get!.any p
match s with
| s, b, e => String.anyAux s e p b
/--
Checks whether the Boolean predicate `p` returns `true` for every character in a substring.
@@ -283,7 +286,7 @@ Checks whether the Boolean predicate `p` returns `true` for every character in a
Short-circuits at the first character for which `p` returns `false`.
-/
@[inline] def all (s : Substring.Raw) (p : Char Bool) : Bool :=
s.toSlice?.get!.all p
!s.any (fun c => !p c)
@[export lean_substring_all]
def Internal.allImpl (s : Substring.Raw) (p : Char Bool) : Bool :=

View File

@@ -113,119 +113,119 @@ theorem prev_prev_lt {s : Slice} {p : s.Pos} {h h'} : (p.prev h).prev h' < p :=
end Slice.Pos
namespace Pos
namespace ValidPos
/-- The number of bytes between `p` and the end position. This number decreases as `p` advances. -/
def remainingBytes {s : String} (p : s.Pos) : Nat :=
def remainingBytes {s : String} (p : s.ValidPos) : Nat :=
p.toSlice.remainingBytes
@[simp]
theorem remainingBytes_toSlice {s : String} {p : s.Pos} :
theorem remainingBytes_toSlice {s : String} {p : s.ValidPos} :
p.toSlice.remainingBytes = p.remainingBytes := (rfl)
theorem remainingBytes_eq_byteDistance {s : String} {p : s.Pos} :
p.remainingBytes = p.offset.byteDistance s.endPos.offset := (rfl)
theorem remainingBytes_eq_byteDistance {s : String} {p : s.ValidPos} :
p.remainingBytes = p.offset.byteDistance s.endValidPos.offset := (rfl)
theorem remainingBytes_eq {s : String} {p : s.Pos} :
theorem remainingBytes_eq {s : String} {p : s.ValidPos} :
p.remainingBytes = s.utf8ByteSize - p.offset.byteIdx := by
simp [remainingBytes_eq_byteDistance, Pos.Raw.byteDistance_eq]
theorem remainingBytes_inj {s : String} {p q : s.Pos} :
theorem remainingBytes_inj {s : String} {p q : s.ValidPos} :
p.remainingBytes = q.remainingBytes p = q := by
simp [ remainingBytes_toSlice, Pos.toSlice_inj, Slice.Pos.remainingBytes_inj]
simp [ remainingBytes_toSlice, ValidPos.toSlice_inj, Slice.Pos.remainingBytes_inj]
theorem le_iff_remainingBytes_le {s : String} (p q : s.Pos) :
theorem le_iff_remainingBytes_le {s : String} (p q : s.ValidPos) :
p q q.remainingBytes p.remainingBytes := by
simp [ remainingBytes_toSlice, Slice.Pos.le_iff_remainingBytes_le]
theorem lt_iff_remainingBytes_lt {s : String} (p q : s.Pos) :
theorem lt_iff_remainingBytes_lt {s : String} (p q : s.ValidPos) :
p < q q.remainingBytes < p.remainingBytes := by
simp [ remainingBytes_toSlice, Slice.Pos.lt_iff_remainingBytes_lt]
theorem wellFounded_lt {s : String} : WellFounded (fun (p : s.Pos) q => p < q) := by
theorem wellFounded_lt {s : String} : WellFounded (fun (p : s.ValidPos) q => p < q) := by
simpa [lt_iff, Pos.Raw.lt_iff] using
InvImage.wf (Pos.Raw.byteIdx Pos.offset) Nat.lt_wfRel.wf
InvImage.wf (Pos.Raw.byteIdx ValidPos.offset) Nat.lt_wfRel.wf
theorem wellFounded_gt {s : String} : WellFounded (fun (p : s.Pos) q => q < p) := by
theorem wellFounded_gt {s : String} : WellFounded (fun (p : s.ValidPos) q => q < p) := by
simpa [lt_iff_remainingBytes_lt] using
InvImage.wf Pos.remainingBytes Nat.lt_wfRel.wf
InvImage.wf ValidPos.remainingBytes Nat.lt_wfRel.wf
instance {s : String} : WellFoundedRelation s.Pos where
instance {s : String} : WellFoundedRelation s.ValidPos where
rel p q := q < p
wf := Pos.wellFounded_gt
wf := ValidPos.wellFounded_gt
/-- Type alias for `String.Pos` representing that the given position is expected to decrease
/-- Type alias for `String.ValidPos` representing that the given position is expected to decrease
in recursive calls. -/
structure Down (s : String) : Type where
inner : s.Pos
inner : s.ValidPos
/-- Use `termination_by pos.down` to signify that in a recursive call, the parameter `pos` is
expected to decrease. -/
def down {s : String} (p : s.Pos) : Pos.Down s where
def down {s : String} (p : s.ValidPos) : ValidPos.Down s where
inner := p
@[simp]
theorem inner_down {s : String} {p : s.Pos} : p.down.inner = p := (rfl)
theorem inner_down {s : String} {p : s.ValidPos} : p.down.inner = p := (rfl)
instance {s : String} : WellFoundedRelation (Pos.Down s) where
instance {s : String} : WellFoundedRelation (ValidPos.Down s) where
rel p q := p.inner < q.inner
wf := InvImage.wf Pos.Down.inner Pos.wellFounded_lt
wf := InvImage.wf ValidPos.Down.inner ValidPos.wellFounded_lt
theorem map_toSlice_next? {s : String} {p : s.Pos} :
p.next?.map Pos.toSlice = p.toSlice.next? := by
theorem map_toSlice_next? {s : String} {p : s.ValidPos} :
p.next?.map ValidPos.toSlice = p.toSlice.next? := by
simp [next?]
theorem map_toSlice_prev? {s : String} {p : s.Pos} :
p.prev?.map Pos.toSlice = p.toSlice.prev? := by
theorem map_toSlice_prev? {s : String} {p : s.ValidPos} :
p.prev?.map ValidPos.toSlice = p.toSlice.prev? := by
simp [prev?]
theorem ne_endPos_of_next?_eq_some {s : String} {p q : s.Pos}
(h : p.next? = some q) : p s.endPos :=
ne_of_apply_ne Pos.toSlice (Slice.Pos.ne_endPos_of_next?_eq_some
(by simpa only [Pos.map_toSlice_next?, Option.map_some] using congrArg (·.map toSlice) h))
theorem ne_endValidPos_of_next?_eq_some {s : String} {p q : s.ValidPos}
(h : p.next? = some q) : p s.endValidPos :=
ne_of_apply_ne ValidPos.toSlice (Slice.Pos.ne_endPos_of_next?_eq_some
(by simpa only [ValidPos.map_toSlice_next?, Option.map_some] using congrArg (·.map toSlice) h))
theorem eq_next_of_next?_eq_some {s : String} {p q : s.Pos} (h : p.next? = some q) :
q = p.next (ne_endPos_of_next?_eq_some h) := by
theorem eq_next_of_next?_eq_some {s : String} {p q : s.ValidPos} (h : p.next? = some q) :
q = p.next (ne_endValidPos_of_next?_eq_some h) := by
simpa only [ toSlice_inj, toSlice_next] using Slice.Pos.eq_next_of_next?_eq_some
(by simpa [Pos.map_toSlice_next?] using congrArg (·.map toSlice) h)
(by simpa [ValidPos.map_toSlice_next?] using congrArg (·.map toSlice) h)
theorem ne_startPos_of_prev?_eq_some {s : String} {p q : s.Pos}
(h : p.prev? = some q) : p s.startPos :=
ne_of_apply_ne Pos.toSlice (Slice.Pos.ne_startPos_of_prev?_eq_some
(by simpa only [Pos.map_toSlice_prev?, Option.map_some] using congrArg (·.map toSlice) h))
theorem ne_startValidPos_of_prev?_eq_some {s : String} {p q : s.ValidPos}
(h : p.prev? = some q) : p s.startValidPos :=
ne_of_apply_ne ValidPos.toSlice (Slice.Pos.ne_startPos_of_prev?_eq_some
(by simpa only [ValidPos.map_toSlice_prev?, Option.map_some] using congrArg (·.map toSlice) h))
theorem eq_prev_of_prev?_eq_some {s : String} {p q : s.Pos} (h : p.prev? = some q) :
q = p.prev (ne_startPos_of_prev?_eq_some h) := by
theorem eq_prev_of_prev?_eq_some {s : String} {p q : s.ValidPos} (h : p.prev? = some q) :
q = p.prev (ne_startValidPos_of_prev?_eq_some h) := by
simpa only [ toSlice_inj, toSlice_prev] using Slice.Pos.eq_prev_of_prev?_eq_some
(by simpa [Pos.map_toSlice_prev?] using congrArg (·.map toSlice) h)
(by simpa [ValidPos.map_toSlice_prev?] using congrArg (·.map toSlice) h)
@[simp]
theorem le_refl {s : String} (p : s.Pos) : p p := by
simp [Pos.le_iff]
theorem le_refl {s : String} (p : s.ValidPos) : p p := by
simp [ValidPos.le_iff]
theorem lt_trans {s : String} {p q r : s.Pos} : p < q q < r p < r := by
simpa [Pos.lt_iff, Pos.Raw.lt_iff] using Nat.lt_trans
theorem lt_trans {s : String} {p q r : s.ValidPos} : p < q q < r p < r := by
simpa [ValidPos.lt_iff, Pos.Raw.lt_iff] using Nat.lt_trans
@[simp]
theorem lt_next_next {s : String} {p : s.Pos} {h h'} : p < (p.next h).next h' :=
theorem lt_next_next {s : String} {p : s.ValidPos} {h h'} : p < (p.next h).next h' :=
lt_trans p.lt_next (p.next h).lt_next
@[simp]
theorem prev_prev_lt {s : String} {p : s.Pos} {h h'} : (p.prev h).prev h' < p :=
theorem prev_prev_lt {s : String} {p : s.ValidPos} {h h'} : (p.prev h).prev h' < p :=
lt_trans (p.prev h).prev_lt p.prev_lt
theorem Splits.remainingBytes_eq {s : String} {p : s.Pos} {t₁ t₂}
theorem Splits.remainingBytes_eq {s : String} {p : s.ValidPos} {t₁ t₂}
(h : p.Splits t₁ t₂) : p.remainingBytes = t₂.utf8ByteSize := by
simp [Pos.remainingBytes_eq, h.eq_append, h.offset_eq_rawEndPos]
simp [ValidPos.remainingBytes_eq, h.eq_append, h.offset_eq_rawEndPos]
end Pos
end ValidPos
namespace Slice.Pos
@[simp]
theorem remainingBytes_toCopy {s : Slice} {p : s.Pos} :
p.toCopy.remainingBytes = p.remainingBytes := by
simp [remainingBytes_eq, String.Pos.remainingBytes_eq, Slice.utf8ByteSize_eq]
simp [remainingBytes_eq, ValidPos.remainingBytes_eq, Slice.utf8ByteSize_eq]
theorem Splits.remainingBytes_eq {s : Slice} {p : s.Pos} {t₁ t₂} (h : p.Splits t₁ t₂) :
p.remainingBytes = t₂.utf8ByteSize := by
@@ -244,14 +244,14 @@ macro_rules | `(tactic| decreasing_trivial) => `(tactic|
Slice.Pos.eq_prev_of_prev?_eq_some (by assumption),
]) <;> done)
macro_rules | `(tactic| decreasing_trivial) => `(tactic|
(with_reducible change (_ : String.Pos _) < _
(with_reducible change (_ : String.ValidPos _) < _
simp [
Pos.eq_next_of_next?_eq_some (by assumption),
ValidPos.eq_next_of_next?_eq_some (by assumption),
]) <;> done)
macro_rules | `(tactic| decreasing_trivial) => `(tactic|
(with_reducible change (_ : String.Pos _) < _
(with_reducible change (_ : String.ValidPos _) < _
simp [
Pos.eq_prev_of_prev?_eq_some (by assumption),
ValidPos.eq_prev_of_prev?_eq_some (by assumption),
]) <;> done)
end String

View File

@@ -8,7 +8,6 @@ module
prelude
public import Init.Data.String.Substring
import Init.Data.String.TakeDrop
import Init.Data.String.Search
/-!
Here we give the. implementation of `Name.toString`. There is also a private implementation in

View File

@@ -525,12 +525,12 @@ and do not provide separate verification theorems.
@[simp] theorem mem_toArray_iff (a : α) (xs : Vector α n) : a xs.toArray a xs :=
fun h => h, fun h => h
instance [Monad m] : ForIn' m (Vector α n) α inferInstance where
instance : ForIn' m (Vector α n) α inferInstance where
forIn' xs b f := Array.forIn' xs.toArray b (fun a h b => f a (by simpa using h) b)
/-! ### ForM instance -/
instance [Monad m] : ForM m (Vector α n) α where
instance : ForM m (Vector α n) α where
forM := Vector.forM
-- We simplify `Vector.forM` to `forM`.

View File

@@ -116,7 +116,7 @@ macro:max x:term noWs "[" i:term "]" noWs "?" : term => `(getElem? $x $i)
/--
The syntax `arr[i]!` gets the `i`'th element of the collection `arr` and
panics if `i` is out of bounds.
panics `i` is out of bounds.
-/
macro:max x:term noWs "[" i:term "]" noWs "!" : term => `(getElem! $x $i)

View File

@@ -26,5 +26,3 @@ public import Init.Grind.Injective
public import Init.Grind.Order
public import Init.Grind.Interactive
public import Init.Grind.Lint
public import Init.Grind.Annotated
public import Init.Grind.FieldNormNum

View File

@@ -1,34 +0,0 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
module
prelude
public import Init.Tactics
public section
namespace Lean.Parser.Command
/--
`grind_annotated "YYYY-MM-DD"` marks the current file as having been manually annotated for `grind`.
When the LibrarySuggestion framework is called with `caller := "grind"` (as happens when using
`grind +suggestions`), theorems from grind-annotated files are excluded from premise selection.
This is because these files have already been manually reviewed and annotated with appropriate
`@[grind]` attributes.
The date argument (in YYYY-MM-DD format) records when the file was annotated. This is currently
informational only, but may be used in the future to detect files that have been significantly
modified since annotation and may need re-review.
Example:
```
grind_annotated "2025-01-15"
```
This command should typically appear near the top of a file, after imports.
-/
syntax (name := grindAnnotated) "grind_annotated" str : command
end Lean.Parser.Command

View File

@@ -188,16 +188,6 @@ where the term `f` contains at least one constant symbol.
-/
syntax grindInj := &"inj"
/--
The `funCC` modifier marks global functions that support **function-valued congruence closure**.
Given an application `f a₁ a₂ … aₙ`, when `funCC := true`,
`grind` generates and tracks equalities for all partial applications:
- `f a₁`
- `f a₁ a₂`
- `…`
- `f a₁ a₂ … aₙ`
-/
syntax grindFunCC := &"funCC"
/--
`symbol <prio>` sets the priority of a constant for `grind`s pattern-selection
procedure. `grind` prefers patterns that contain higher-priority symbols.
Example:
@@ -224,7 +214,7 @@ syntax grindMod :=
grindEqBoth <|> grindEqRhs <|> grindEq <|> grindEqBwd <|> grindBwd
<|> grindFwd <|> grindRL <|> grindLR <|> grindUsr <|> grindCasesEager
<|> grindCases <|> grindIntro <|> grindExt <|> grindGen <|> grindSym <|> grindInj
<|> grindFunCC <|> grindDef
<|> grindDef
/--
Marks a theorem or definition for use by the `grind` tactic.

View File

@@ -172,36 +172,6 @@ structure Config where
and then reintroduces them while simplifying and applying eager `cases`.
-/
revert := false
/--
When `true`, it enables **function-valued congruence closure**.
`grind` treats equalities of partially applied functions as first-class equalities
and propagates them through further applications.
Given an application `f a₁ a₂ … aₙ`, when `funCC := true` *and* function equality is enabled for `f`,
`grind` generates and tracks equalities for all partial applications:
- `f a₁`
- `f a₁ a₂`
- `…`
- `f a₁ a₂ … aₙ`
This allows equalities such as `f a₁ = g` to propagate to
`f a₁ a₂ = g a₂`.
**When is function equality enabled for a symbol?**
Function equality is automatically enabled in the following cases:
1. **`f` is not a constant.** (For example, a lambda expression, a local variable, or a function parameter.)
2. **`f` is a structure field projection**, *provided the structure is not a `class`.*
3. **`f` is a constant marked with the attribute:** `@[grind funCC]`
If none of the above conditions apply, function equality is disabled for `f`, and congruence
closure behaves almost like it does in SMT solvers for first-order logic.
Here is an example, `grind` can solve when `funCC := true`
```
example (a b : Nat) (g : Nat → Nat) (f : Nat → Nat → Nat) (h : f a = g) :
f a b = g b := by
grind
```
-/
funCC := true
deriving Inhabited, BEq
/--

View File

@@ -1,219 +0,0 @@
/-
Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
module
prelude
public import Init.Grind.Ring.Field
public import Init.Data.Rat.Basic
import Init.Data.Rat.Lemmas
public section
namespace Lean.Grind.Field.NormNum
attribute [local instance] Semiring.natCast Ring.intCast
abbrev ofRat {α} [Field α] (r : Rat) : α :=
(r.num : α)/(r.den : α)
attribute [local simp]
Field.inv_one Semiring.natCast_zero Semiring.natCast_one Ring.intCast_zero Ring.intCast_one Semiring.one_mul Semiring.mul_one
Semiring.pow_zero Field.inv_one Field.inv_zero
private theorem dvd_helper₁ {z : Int} {n : Nat} : (z.natAbs.gcd n : Int) z :=
Rat.normalize.dvd_num rfl
private theorem dvd_helper₂ {z : Int} {n : Nat} : z.natAbs.gcd n n :=
Nat.gcd_dvd_right z.natAbs n
private theorem nonzero_helper {α} [Field α] {z : Int} {n m : Nat} (hn : (n : α) 0) (hm : (m : α) 0) :
(z.natAbs.gcd (n * m) : α) 0 := by
intro h
have : z.natAbs.gcd (n * m) (n * m) := Nat.gcd_dvd_right z.natAbs (n * m)
obtain k, hk := this
replace hk := congrArg (fun x : Nat => (x : α)) hk
dsimp at hk
rw [Semiring.natCast_mul, Semiring.natCast_mul, h, Semiring.zero_mul] at hk
replace hk := Field.of_mul_eq_zero hk
simp_all
theorem ofRat_add' {α} [Field α] {a b : Rat} (ha : (a.den : α) 0) (hb : (b.den : α) 0) :
(ofRat (a + b) : α) = ofRat a + ofRat b := by
rw [ofRat, ofRat, ofRat, Rat.num_add, Rat.den_add]
rw [Field.intCast_div_of_dvd dvd_helper₁ (by simpa [Ring.intCast_natCast] using (nonzero_helper ha hb))]
rw [Field.natCast_div_of_dvd dvd_helper₂ (nonzero_helper ha hb)]
rw [Ring.intCast_natCast, Field.div_div_right]
rw [Field.div_mul_cancel (nonzero_helper ha hb)]
rw [Field.add_div hb, Field.div_mul, Field.div_add ha]
rw [Ring.intCast_add, Ring.intCast_mul, Ring.intCast_mul, Ring.intCast_natCast,
Ring.intCast_natCast, CommSemiring.mul_comm (a.den : α), Field.div_div_left,
Semiring.natCast_mul]
theorem ofRat_mul' {α} [Field α] {a b : Rat} (ha : (a.den : α) 0) (hb : (b.den : α) 0) : (ofRat (a * b) : α) = ofRat a * ofRat b := by
rw [ofRat, ofRat, ofRat, Rat.num_mul, Rat.den_mul]
rw [Field.intCast_div_of_dvd dvd_helper₁ (by simpa [Ring.intCast_natCast] using (nonzero_helper ha hb))]
rw [Field.natCast_div_of_dvd dvd_helper₂ (nonzero_helper ha hb)]
rw [Ring.intCast_natCast, Field.div_div_right]
rw [Field.div_mul_cancel (nonzero_helper ha hb)]
rw [Field.div_mul, Field.mul_div, Field.div_div_left, Ring.intCast_mul, Semiring.natCast_mul,
CommSemiring.mul_comm (b.den : α)]
-- Note: false without `IsCharP α 0` (consider `a = b = 1/2` in `/2`):
theorem ofRat_add {α} [Field α] [IsCharP α 0] (a b : Rat) :
(ofRat (a + b) : α) = ofRat a + ofRat b :=
ofRat_add' (natCast_ne_zero a.den_nz) (natCast_ne_zero b.den_nz)
-- Note: false without `IsCharP α 0` (consider `a = 2/3` and `b = 1/2` in `/2`):
theorem ofRat_mul {α} [Field α] [IsCharP α 0] (a b : Rat) : (ofRat (a * b) : α) = ofRat a * ofRat b :=
ofRat_mul' (natCast_ne_zero a.den_nz) (natCast_ne_zero b.den_nz)
theorem ofRat_inv {α} [Field α] (a : Rat) : (ofRat (a⁻¹) : α) = (ofRat a)⁻¹ := by
simp [ofRat]; split
next h => simp [h, Field.div_eq_mul_inv]
next =>
simp [Field.div_eq_mul_inv, Field.inv_mul, Field.inv_inv, Ring.intCast_mul, Ring.intCast_natCast]
generalize a.num = n
generalize a.den = d
conv => rhs; rw [ Int.sign_mul_natAbs n]
simp [Ring.intCast_mul, Ring.intCast_natCast, Field.inv_mul]
have : (Int.cast n.sign : α) = (Int.cast n.sign : α)⁻¹ := by
cases Int.sign_trichotomy n
next h => simp [h]
next h => cases h <;> simp [*, Ring.intCast_neg, Field.inv_neg]
rw [ this, Semiring.mul_assoc, Semiring.mul_assoc]
congr 1
rw [CommSemiring.mul_comm]
theorem ofRat_div' {α} [Field α] {a b : Rat} (ha : (a.den : α) 0) (hb : (b.num : α) 0) :
(ofRat (a / b) : α) = ofRat a / ofRat b := by
replace hb : ((b⁻¹).den : α) 0 := by
simp only [Rat.den_inv, Rat.num_eq_zero, ne_eq]
rw [if_neg (by intro h; simp_all)]
rw [ Ring.intCast_natCast]
by_cases h : 0 b.num
· have : (b.num.natAbs : Int) = b.num := Int.natAbs_of_nonneg h
rwa [this]
· have : (b.num.natAbs : Int) = -b.num := Int.ofNat_natAbs_of_nonpos (by omega)
rw [this, Ring.intCast_neg]
rwa [AddCommGroup.neg_eq_iff, AddCommGroup.neg_zero]
rw [Rat.div_def, ofRat_mul' ha hb, ofRat_inv, Field.div_eq_mul_inv (ofRat a)]
theorem ofRat_div {α} [Field α] [IsCharP α 0] (a b : Rat) : (ofRat (a / b) : α) = ofRat a / ofRat b := by
rw [Rat.div_def, ofRat_mul, ofRat_inv, Field.div_eq_mul_inv (ofRat a)]
theorem ofRat_neg {α} [Field α] (a : Rat) : (ofRat (-a) : α) = -ofRat a := by
simp [ofRat, Field.div_eq_mul_inv, Ring.intCast_neg, Ring.neg_mul]
theorem ofRat_sub' {α} [Field α] {a b : Rat} (ha : (a.den : α) 0) (hb : (b.den : α) 0) :
(ofRat (a - b) : α) = ofRat a - ofRat b := by
replace hb : ((-b).den : α) 0 := by simpa
rw [Rat.sub_eq_add_neg, ofRat_add' ha hb, ofRat_neg, Ring.sub_eq_add_neg]
theorem ofRat_sub {α} [Field α] [IsCharP α 0] (a b : Rat) :
(ofRat (a - b) : α) = ofRat a - ofRat b := by
rw [Rat.sub_eq_add_neg, ofRat_add, ofRat_neg, Ring.sub_eq_add_neg]
theorem ofRat_npow' {α} [Field α] {a : Rat} (ha : (a.den : α) 0) (n : Nat) : (ofRat (a^n) : α) = ofRat a ^ n := by
have h : n : Nat, ((a^n).den : α) 0 := by
intro n
rw [Rat.den_pow, ne_eq, Semiring.natCast_pow]
intro h
induction n with
| zero =>
rw [Semiring.pow_zero] at h
exact Field.zero_ne_one h.symm
| succ n ih' =>
rw [Semiring.pow_succ] at h
replace h := Field.of_mul_eq_zero h
rcases h with h | h
· exact ih' h
· exact ha h
induction n
next => simp [Field.div_eq_mul_inv, ofRat]
next n ih =>
rw [Rat.pow_succ, ofRat_mul' (h _) ha, ih, Semiring.pow_succ]
theorem ofRat_npow {α} [Field α] [IsCharP α 0] (a : Rat) (n : Nat) : (ofRat (a^n) : α) = ofRat a ^ n := by
induction n
next => simp [Field.div_eq_mul_inv, ofRat]
next n ih => rw [Rat.pow_succ, ofRat_mul, ih, Semiring.pow_succ]
theorem ofRat_zpow' {α} [Field α] {a : Rat} (ha : (a.den : α) 0) (n : Int) : (ofRat (a^n) : α) = ofRat a ^ n := by
cases n
next => rw [Int.ofNat_eq_natCast, Rat.zpow_natCast, Field.zpow_natCast, ofRat_npow' ha]
next =>
rw [Int.negSucc_eq, Rat.zpow_neg, Field.zpow_neg, ofRat_inv]
congr 1
have : (1 : Int) = (1 : Nat) := rfl
rw [this, Int.natCast_add, Rat.zpow_natCast, Field.zpow_natCast, ofRat_npow' ha]
theorem ofRat_zpow {α} [Field α] [IsCharP α 0] (a : Rat) (n : Int) : (ofRat (a^n) : α) = ofRat a ^ n := by
cases n
next => rw [Int.ofNat_eq_natCast, Rat.zpow_natCast, Field.zpow_natCast, ofRat_npow]
next =>
rw [Int.negSucc_eq, Rat.zpow_neg, Field.zpow_neg, ofRat_inv]
congr 1
have : (1 : Int) = (1 : Nat) := rfl
rw [this, Int.natCast_add, Rat.zpow_natCast, Field.zpow_natCast, ofRat_npow]
theorem natCast_eq {α} [Field α] (n : Nat) : (NatCast.natCast n : α) = ofRat n := by
simp [ofRat, Ring.intCast_natCast, Semiring.natCast_one, Field.div_eq_mul_inv,
Field.inv_one, Semiring.mul_one]
theorem ofNat_eq {α} [Field α] (n : Nat) : (OfNat.ofNat n : α) = ofRat n := by
rw [Semiring.ofNat_eq_natCast]
apply natCast_eq
theorem intCast_eq {α} [Field α] (n : Int) : (IntCast.intCast n : α) = ofRat n := by
simp [ofRat, Semiring.natCast_one, Field.div_eq_mul_inv, Field.inv_one, Semiring.mul_one]
theorem add_eq {α} [Field α] [IsCharP α 0] (a b : α) (v₁ v₂ v : Rat)
: v == v₁ + v₂ a = ofRat v₁ b = ofRat v₂ a + b = ofRat v := by
simp; intros; subst v a b; rw [ofRat_add]
theorem sub_eq {α} [Field α] [IsCharP α 0] (a b : α) (v₁ v₂ v : Rat)
: v == v₁ - v₂ a = ofRat v₁ b = ofRat v₂ a - b = ofRat v := by
simp; intros; subst v a b; rw [ofRat_sub]
theorem mul_eq {α} [Field α] [IsCharP α 0] (a b : α) (v₁ v₂ v : Rat)
: v == v₁ * v₂ a = ofRat v₁ b = ofRat v₂ a * b = ofRat v := by
simp; intros; subst v a b; rw [ofRat_mul]
theorem div_eq {α} [Field α] [IsCharP α 0] (a b : α) (v₁ v₂ v : Rat)
: v == v₁ / v₂ a = ofRat v₁ b = ofRat v₂ a / b = ofRat v := by
simp; intros; subst v a b; rw [ofRat_div]
theorem inv_eq {α} [Field α] (a : α) (v₁ v : Rat)
: v == v₁⁻¹ a = ofRat v₁ a⁻¹ = ofRat v := by
simp; intros; subst v a; rw [ofRat_inv]
theorem neg_eq {α} [Field α] (a : α) (v₁ v : Rat)
: v == -v₁ a = ofRat v₁ -a = ofRat v := by
simp; intros; subst v a; rw [ofRat_neg]
theorem npow_eq {α} [Field α] [IsCharP α 0] (a : α) (n : Nat) (v₁ v : Rat)
: v == v₁^n a = ofRat v₁ a ^ n = ofRat v := by
simp; intros; subst v a; rw [ofRat_npow]
theorem zpow_eq {α} [Field α] [IsCharP α 0] (a : α) (n : Int) (v₁ v : Rat)
: v == v₁^n a = ofRat v₁ a ^ n = ofRat v := by
simp; intros; subst v a; rw [ofRat_zpow]
theorem eq_int {α} [Field α] (a : α) (v : Rat) (n : Int)
: n == v.num && v.den == 1 a = ofRat v a = IntCast.intCast n := by
simp; cases v; simp [ofRat]
next den _ _ =>
intros; subst den n a
simp [Semiring.natCast_one, Field.div_eq_mul_inv, Field.inv_one, Semiring.mul_one]
theorem eq_inv {α} [Field α] (a : α) (v : Rat) (d : Nat)
: v.num == 1 && v.den == d a = ofRat v a = (NatCast.natCast d : α)⁻¹ := by
simp; cases v; simp [ofRat]
next num _ _ _ =>
intros; subst num d a
simp [Ring.intCast_one, Field.div_eq_mul_inv, Semiring.one_mul]
theorem eq_mul_inv {α} [Field α] (a : α) (v : Rat) (n : Int) (d : Nat)
: v.num == n && v.den == d a = ofRat v a = (IntCast.intCast n : α) * (NatCast.natCast d : α)⁻¹ := by
cases v; simp [ofRat]
intros; subst d n a
simp [Field.div_eq_mul_inv]
end Lean.Grind.Field.NormNum

View File

@@ -72,9 +72,7 @@ syntax (name := linarith) "linarith" : grind
/-- The `sorry` tactic is a temporary placeholder for an incomplete tactic proof. -/
syntax (name := «sorry») "sorry" : grind
syntax thmNs := &"namespace" ident
syntax thm := anchor <|> thmNs <|> grindLemmaMin <|> grindLemma
syntax thm := anchor <|> grindLemmaMin <|> grindLemma
/--
Instantiates theorems using E-matching.

View File

@@ -8,7 +8,6 @@ prelude
public import Init.Data.Int.Linear
public import Init.Grind.Ring.Field
public import Init.Data.Rat.Lemmas
public import Init.Grind.Ring.OfScientific
public section
namespace Lean.Grind
@@ -208,9 +207,7 @@ init_grind_norm
Ring.intCast_mul
Ring.intCast_pow
Ring.intCast_sub
-- OfScientific
LawfulOfScientific.ofScientific_def
-- Rationals
Rat.zpow_neg
Rat.ofScientific_def_eq_if Rat.zpow_neg
end Lean.Grind

View File

@@ -236,21 +236,20 @@ instance [Ring R] [LE R] [LT R] [LawfulOrderLT R] [IsPreorder R] [OrderedRing R]
end Preorder
theorem mul_pos [LE R] [LT R] [IsPreorder R] [OrderedRing R] {a b : R} (h₁ : 0 < a) (h₂ : 0 < b) : 0 < a * b := by
simpa [Semiring.zero_mul] using mul_lt_mul_of_pos_right h₁ h₂
theorem zero_le_one [LE R] [LT R] [LawfulOrderLT R] [IsPreorder R] [OrderedRing R] : (0 : R) 1 :=
Preorder.le_of_lt zero_lt_one
theorem not_one_lt_zero [LE R] [LT R] [LawfulOrderLT R] [IsPreorder R] [OrderedRing R] : ¬ ((1 : R) < 0) :=
fun h => Preorder.lt_irrefl (0 : R) (Preorder.lt_trans zero_lt_one h)
section PartialOrder
variable [LE R] [LT R] [IsPartialOrder R] [OrderedRing R]
theorem mul_pos {a b : R} (h₁ : 0 < a) (h₂ : 0 < b) : 0 < a * b := by
simpa [Semiring.zero_mul] using mul_lt_mul_of_pos_right h₁ h₂
variable [LawfulOrderLT R]
theorem zero_le_one : (0 : R) 1 := Preorder.le_of_lt zero_lt_one
theorem not_one_lt_zero : ¬ ((1 : R) < 0) :=
fun h => Preorder.lt_irrefl (0 : R) (Preorder.lt_trans zero_lt_one h)
theorem mul_le_mul_of_nonneg_left {a b c : R} (h : a b) (h' : 0 c) : c * a c * b := by
rw [PartialOrder.le_iff_lt_or_eq] at h'
cases h' with

View File

@@ -82,10 +82,6 @@ class Semiring (α : Type u) extends Add α, Mul α where
ofNat_succ : a : Nat, OfNat.ofNat (α := α) (a + 1) = OfNat.ofNat a + 1 := by intros; rfl
/-- Numerals are consistently defined with respect to the canonical map from natural numbers. -/
ofNat_eq_natCast : n : Nat, OfNat.ofNat (α := α) n = Nat.cast n := by intros; rfl
/--
Multiplying by a numeral is consistently defined with respect to the canonical map from natural
numbers.
-/
nsmul_eq_natCast_mul : n : Nat, a : α, n a = Nat.cast n * a := by intros; rfl
/--
@@ -208,11 +204,6 @@ theorem pow_add (a : α) (k₁ k₂ : Nat) : a ^ (k₁ + k₂) = a^k₁ * a^k₂
theorem pow_add_congr (a r : α) (k k₁ k₂ : Nat) : k = k₁ + k₂ a^k₁ * a^k₂ = r a ^ k = r := by
intros; subst k r; rw [pow_add]
theorem one_pow (n : Nat) : (1 : α) ^ n = 1 := by
induction n
next => simp [pow_zero]
next => simp [pow_succ, *, mul_one]
theorem natCast_pow (x : Nat) (k : Nat) : ((x ^ k : Nat) : α) = (x : α) ^ k := by
induction k
next => simp [pow_zero, Nat.pow_zero, natCast_one]
@@ -385,11 +376,6 @@ variable {α : Type u} [CommSemiring α]
theorem mul_left_comm (a b c : α) : a * (b * c) = b * (a * c) := by
rw [ mul_assoc, mul_assoc, mul_comm a]
theorem mul_pow (a b : α) (n : Nat) : (a*b)^n = a^n * b^n := by
induction n
next => simp [pow_zero, mul_one]
next n ih => simp [pow_succ, ih, mul_comm, mul_assoc, mul_left_comm]
end CommSemiring
open Semiring hiding add_comm add_assoc add_zero

View File

@@ -656,29 +656,6 @@ noncomputable def Expr.toPoly_k (e : Expr) : Poly :=
(k.beq 0))
e
def Mon.degreeOf (m : Mon) (x : Var) : Nat :=
match m with
| .unit => 0
| .mult pw m => bif pw.x == x then pw.k else degreeOf m x
def Mon.cancelVar (m : Mon) (x : Var) : Mon :=
match m with
| .unit => .unit
| .mult pw m => bif pw.x == x then m else .mult pw (cancelVar m x)
def Poly.cancelVar' (c : Int) (x : Var) (p : Poly) (acc : Poly) : Poly :=
match p with
| .num k => acc.addConst k
| .add k m p =>
let n := m.degreeOf x
bif n > 0 && c^n k then
cancelVar' c x p (acc.insert (k / (c^n)) (m.cancelVar x))
else
cancelVar' c x p (acc.insert k m)
def Poly.cancelVar (c : Int) (x : Var) (p : Poly) : Poly :=
cancelVar' c x p (.num 0)
@[simp] theorem Expr.toPoly_k_eq_toPoly (e : Expr) : e.toPoly_k = e.toPoly := by
induction e <;> simp only [toPoly, toPoly_k]
next a ih => rw [Poly.mulConst_k_eq_mulConst]; congr
@@ -1198,72 +1175,6 @@ theorem Expr.eq_of_toPoly_nc_eq {α} [Ring α] (ctx : Context α) (a b : Expr) (
simp [denote_toPoly_nc] at h
assumption
section
attribute [local simp] Semiring.pow_zero Semiring.mul_one Semiring.one_mul cond_eq_ite
theorem Mon.denote_cancelVar [CommSemiring α] (ctx : Context α) (m : Mon) (x : Var)
: m.denote ctx = x.denote ctx ^ (m.degreeOf x) * (m.cancelVar x).denote ctx := by
fun_induction cancelVar <;> simp [degreeOf]
next h =>
simp at h; simp [*, denote, Power.denote_eq]
next h ih =>
simp at h; simp [*, denote, Semiring.mul_assoc, CommSemiring.mul_comm, CommSemiring.mul_left_comm]
theorem Poly.denote_cancelVar' {α} [CommRing α] (ctx : Context α) (p : Poly) (c : Int) (x : Var) (acc : Poly)
: c 0 c * x.denote ctx = 1 (p.cancelVar' c x acc).denote ctx = p.denote ctx + acc.denote ctx := by
intro h₁ h₂
fun_induction cancelVar'
next acc k => simp [denote_addConst, denote, Semiring.add_comm]
next h ih =>
simp [ih, denote_insert, denote]
conv => rhs; rw [Mon.denote_cancelVar (x := x)]
simp [ Semiring.add_assoc]
congr 1
rw [Semiring.add_comm, Ring.zsmul_eq_intCast_mul,]
congr 1
simp +zetaDelta [Int.dvd_def] at h
have d, h := h.2
simp +zetaDelta [h]
rw [Int.mul_ediv_cancel_left _ (Int.pow_ne_zero h₁)]
rw [Ring.intCast_mul]
conv => rhs; lhs; rw [CommSemiring.mul_comm]
rw [Semiring.mul_assoc, Ring.intCast_pow]
congr 1
rw [ Semiring.mul_assoc, CommSemiring.mul_pow, h₂, Semiring.one_pow, Semiring.one_mul]
next ih =>
simp [ih, denote_insert, denote, Ring.zsmul_eq_intCast_mul, Semiring.add_assoc,
Semiring.add_comm, add_left_comm]
theorem Poly.denote_cancelVar {α} [CommRing α] (ctx : Context α) (p : Poly) (c : Int) (x : Var)
: c 0 c * x.denote ctx = 1 (p.cancelVar c x).denote ctx = p.denote ctx := by
intro h₁ h₂
have := denote_cancelVar' ctx p c x (.num 0) h₁ h₂
rw [cancelVar, this, denote, Ring.intCast_zero, Semiring.add_zero]
noncomputable def cancel_var_cert (c : Int) (x : Var) (p₁ p₂ : Poly) : Bool :=
c != 0 && p₂.beq' (p₁.cancelVar c x)
theorem eq_cancel_var {α} [CommRing α] (ctx : Context α) (c : Int) (x : Var) (p₁ p₂ : Poly)
: cancel_var_cert c x p₁ p₂ c * x.denote ctx = 1 p₁.denote ctx = 0 p₂.denote ctx = 0 := by
simp [cancel_var_cert]; intros h₁ _ h₂ _; subst p₂
simp [Poly.denote_cancelVar ctx p₁ c x h₁ h₂]; assumption
theorem diseq_cancel_var {α} [CommRing α] (ctx : Context α) (c : Int) (x : Var) (p₁ p₂ : Poly)
: cancel_var_cert c x p₁ p₂ c * x.denote ctx = 1 p₁.denote ctx 0 p₂.denote ctx 0 := by
simp [cancel_var_cert]; intros h₁ _ h₂ _; subst p₂
simp [Poly.denote_cancelVar ctx p₁ c x h₁ h₂]; assumption
theorem le_cancel_var {α} [CommRing α] [LE α] (ctx : Context α) (c : Int) (x : Var) (p₁ p₂ : Poly)
: cancel_var_cert c x p₁ p₂ c * x.denote ctx = 1 p₁.denote ctx 0 p₂.denote ctx 0 := by
simp [cancel_var_cert]; intros h₁ _ h₂ _; subst p₂
simp [Poly.denote_cancelVar ctx p₁ c x h₁ h₂]; assumption
theorem lt_cancel_var {α} [CommRing α] [LT α] (ctx : Context α) (c : Int) (x : Var) (p₁ p₂ : Poly)
: cancel_var_cert c x p₁ p₂ c * x.denote ctx = 1 p₁.denote ctx < 0 p₂.denote ctx < 0 := by
simp [cancel_var_cert]; intros h₁ _ h₂ _; subst p₂
simp [Poly.denote_cancelVar ctx p₁ c x h₁ h₂]; assumption
end
/-!
Theorems for justifying the procedure for commutative rings with a characteristic in `grind`.
-/
@@ -1482,34 +1393,15 @@ theorem simp {α} [CommRing α] (ctx : Context α) (k₁ : Int) (p₁ : Poly) (k
noncomputable def mul_cert (p₁ : Poly) (k : Int) (p : Poly) : Bool :=
p₁.mulConst_k k |>.beq' p
theorem mul {α} [CommRing α] (ctx : Context α) (p₁ : Poly) (k : Int) (p : Poly)
def mul {α} [CommRing α] (ctx : Context α) (p₁ : Poly) (k : Int) (p : Poly)
: mul_cert p₁ k p p₁.denote ctx = 0 p.denote ctx = 0 := by
simp [mul_cert]; intro _ h; subst p
simp [Poly.denote_mulConst, *, mul_zero]
theorem eq_mul {α} [CommRing α] (ctx : Context α) (p₁ : Poly) (k : Int) (p : Poly)
: mul_cert p₁ k p p₁.denote ctx = 0 p.denote ctx = 0 := by
apply mul
noncomputable def mul_ne_cert (p₁ : Poly) (k : Int) (p : Poly) : Bool :=
k != 0 && (p₁.mulConst_k k |>.beq' p)
theorem diseq_mul {α} [CommRing α] [NoNatZeroDivisors α] (ctx : Context α) (p₁ : Poly) (k : Int) (p : Poly)
: mul_ne_cert p₁ k p p₁.denote ctx 0 p.denote ctx 0 := by
simp [mul_ne_cert]; intro h₁ _ h₂; subst p
simp [Poly.denote_mulConst]; intro h₃
rw [ zsmul_eq_intCast_mul] at h₃
have := no_int_zero_divisors h₁ h₃
contradiction
theorem inv {α} [CommRing α] (ctx : Context α) (p₁ : Poly) (p : Poly)
: mul_cert p₁ (-1) p p₁.denote ctx = 0 p.denote ctx = 0 :=
mul ctx p₁ (-1) p
noncomputable def div_cert (p₁ : Poly) (k : Int) (p : Poly) : Bool :=
!Int.beq' k 0 |>.and' (p.mulConst_k k |>.beq' p₁)
theorem div {α} [CommRing α] (ctx : Context α) [NoNatZeroDivisors α] (p₁ : Poly) (k : Int) (p : Poly)
def div {α} [CommRing α] (ctx : Context α) [NoNatZeroDivisors α] (p₁ : Poly) (k : Int) (p : Poly)
: div_cert p₁ k p p₁.denote ctx = 0 p.denote ctx = 0 := by
simp [div_cert]; intro hnz _ h; subst p₁
simp [Poly.denote_mulConst, zsmul_eq_intCast_mul] at h
@@ -1683,79 +1575,60 @@ theorem Poly.denoteAsIntModule_eq_denote {α} [CommRing α] (ctx : Context α) (
open Stepwise
theorem eq_norm {α} [CommRing α] (ctx : Context α) (lhs rhs : Expr) (p : Poly)
: core_cert lhs rhs p lhs.denote ctx = rhs.denote ctx p.denote ctx = 0 := by
apply core
theorem eq_int_module {α} [CommRing α] (ctx : Context α) (p : Poly)
: p.denote ctx = 0 p.denoteAsIntModule ctx = 0 := by
simp [Poly.denoteAsIntModule_eq_denote]
: core_cert lhs rhs p lhs.denote ctx = rhs.denote ctx p.denoteAsIntModule ctx = 0 := by
rw [Poly.denoteAsIntModule_eq_denote]; apply core
theorem diseq_norm {α} [CommRing α] (ctx : Context α) (lhs rhs : Expr) (p : Poly)
: core_cert lhs rhs p lhs.denote ctx rhs.denote ctx p.denote ctx 0 := by
simp [core_cert]; intro _ h; subst p; simp [Expr.denote_toPoly, Expr.denote]
: core_cert lhs rhs p lhs.denote ctx rhs.denote ctx p.denoteAsIntModule ctx 0 := by
simp [core_cert, Poly.denoteAsIntModule_eq_denote]; intro _ h; subst p; simp [Expr.denote_toPoly, Expr.denote]
intro h; rw [sub_eq_zero_iff] at h; contradiction
theorem diseq_int_module {α} [CommRing α] (ctx : Context α) (p : Poly)
: p.denote ctx 0 p.denoteAsIntModule ctx 0 := by
simp [Poly.denoteAsIntModule_eq_denote]
open OrderedAdd
theorem le_norm {α} [CommRing α] [LE α] [LT α] [IsPreorder α] [OrderedRing α] (ctx : Context α) (lhs rhs : Expr) (p : Poly)
: core_cert lhs rhs p lhs.denote ctx rhs.denote ctx p.denote ctx 0 := by
simp [core_cert]; intro _ h; subst p; simp [Expr.denote_toPoly, Expr.denote]
: core_cert lhs rhs p lhs.denote ctx rhs.denote ctx p.denoteAsIntModule ctx 0 := by
simp [core_cert, Poly.denoteAsIntModule_eq_denote]; intro _ h; subst p; simp [Expr.denote_toPoly, Expr.denote]
replace h := add_le_left h ((-1) * rhs.denote ctx)
rw [neg_mul, sub_eq_add_neg, one_mul, sub_eq_add_neg, sub_self] at h
assumption
theorem le_int_module {α} [CommRing α] [LE α] (ctx : Context α) (p : Poly)
: p.denote ctx 0 p.denoteAsIntModule ctx 0 := by
simp [Poly.denoteAsIntModule_eq_denote]
noncomputable def mul_ineq_cert (p₁ : Poly) (k : Int) (p : Poly) : Bool :=
k > 0 && (p₁.mulConst_k k |>.beq' p)
theorem le_mul {α} [CommRing α] [LE α] [LT α] [IsPreorder α] [OrderedRing α] (ctx : Context α) (p₁ : Poly) (k : Int) (p : Poly)
: mul_ineq_cert p₁ k p p₁.denote ctx 0 p.denote ctx 0 := by
simp [mul_ineq_cert]; intro h₁ _ h₂; subst p; simp [Poly.denote_mulConst]
replace h₂ := zsmul_nonpos (Int.le_of_lt h₁) h₂
simp [Ring.zsmul_eq_intCast_mul] at h₂
assumption
theorem lt_norm {α} [CommRing α] [LE α] [LT α] [LawfulOrderLT α] [IsPreorder α] [OrderedRing α] (ctx : Context α) (lhs rhs : Expr) (p : Poly)
: core_cert lhs rhs p lhs.denote ctx < rhs.denote ctx p.denote ctx < 0 := by
simp [core_cert]; intro _ h; subst p; simp [Expr.denote_toPoly, Expr.denote]
: core_cert lhs rhs p lhs.denote ctx < rhs.denote ctx p.denoteAsIntModule ctx < 0 := by
simp [core_cert, Poly.denoteAsIntModule_eq_denote]; intro _ h; subst p; simp [Expr.denote_toPoly, Expr.denote]
replace h := add_lt_left h ((-1) * rhs.denote ctx)
rw [neg_mul, sub_eq_add_neg, one_mul, sub_eq_add_neg, sub_self] at h
assumption
theorem lt_int_module {α} [CommRing α] [LT α] (ctx : Context α) (p : Poly)
: p.denote ctx < 0 p.denoteAsIntModule ctx < 0 := by
simp [Poly.denoteAsIntModule_eq_denote]
theorem lt_mul {α} [CommRing α] [LE α] [LT α] [LawfulOrderLT α] [IsPreorder α] [OrderedRing α] (ctx : Context α) (p₁ : Poly) (k : Int) (p : Poly)
: mul_ineq_cert p₁ k p p₁.denote ctx < 0 p.denote ctx < 0 := by
simp [mul_ineq_cert]; intro h₁ _ h₂; subst p; simp [Poly.denote_mulConst]
replace h₂ := zsmul_neg_iff k h₂ |>.mpr h₁
simp [Ring.zsmul_eq_intCast_mul] at h₂
assumption
theorem not_le_norm {α} [CommRing α] [LE α] [LT α] [LawfulOrderLT α] [IsLinearOrder α] [OrderedRing α] (ctx : Context α) (lhs rhs : Expr) (p : Poly)
: core_cert rhs lhs p ¬ lhs.denote ctx rhs.denote ctx p.denote ctx < 0 := by
simp [core_cert]; intro _ h₁; subst p; simp [Expr.denote_toPoly, Expr.denote]
: core_cert rhs lhs p ¬ lhs.denote ctx rhs.denote ctx p.denoteAsIntModule ctx < 0 := by
simp [core_cert, Poly.denoteAsIntModule_eq_denote]; intro _ h₁; subst p; simp [Expr.denote_toPoly, Expr.denote]
replace h₁ := LinearOrder.lt_of_not_le h₁
replace h₁ := add_lt_left h₁ (-lhs.denote ctx)
simp [ sub_eq_add_neg, sub_self] at h₁
assumption
theorem not_lt_norm {α} [CommRing α] [LE α] [LT α] [LawfulOrderLT α] [IsLinearOrder α] [OrderedRing α] (ctx : Context α) (lhs rhs : Expr) (p : Poly)
: core_cert rhs lhs p ¬ lhs.denote ctx < rhs.denote ctx p.denote ctx 0 := by
simp [core_cert]; intro _ h₁; subst p; simp [Expr.denote_toPoly, Expr.denote]
: core_cert rhs lhs p ¬ lhs.denote ctx < rhs.denote ctx p.denoteAsIntModule ctx 0 := by
simp [core_cert, Poly.denoteAsIntModule_eq_denote]; intro _ h₁; subst p; simp [Expr.denote_toPoly, Expr.denote]
replace h₁ := LinearOrder.le_of_not_lt h₁
replace h₁ := add_le_left h₁ (-lhs.denote ctx)
simp [ sub_eq_add_neg, sub_self] at h₁
assumption
theorem not_le_norm' {α} [CommRing α] [LE α] [LT α] [IsPreorder α] [OrderedRing α] (ctx : Context α) (lhs rhs : Expr) (p : Poly)
: core_cert lhs rhs p ¬ lhs.denote ctx rhs.denote ctx ¬ p.denoteAsIntModule ctx 0 := by
simp [core_cert, Poly.denoteAsIntModule_eq_denote]; intro _ h₁; subst p; simp [Expr.denote_toPoly, Expr.denote]; intro h
replace h : rhs.denote ctx + (lhs.denote ctx - rhs.denote ctx) _ := add_le_right (rhs.denote ctx) h
rw [sub_eq_add_neg, add_left_comm, sub_eq_add_neg, sub_self] at h; simp [add_zero] at h
contradiction
theorem not_lt_norm' {α} [CommRing α] [LE α] [LT α] [LawfulOrderLT α] [IsPreorder α] [OrderedRing α] (ctx : Context α) (lhs rhs : Expr) (p : Poly)
: core_cert lhs rhs p ¬ lhs.denote ctx < rhs.denote ctx ¬ p.denoteAsIntModule ctx < 0 := by
simp [core_cert, Poly.denoteAsIntModule_eq_denote]; intro _ h₁; subst p; simp [Expr.denote_toPoly, Expr.denote]; intro h
replace h : rhs.denote ctx + (lhs.denote ctx - rhs.denote ctx) < _ := add_lt_right (rhs.denote ctx) h
rw [sub_eq_add_neg, add_left_comm, sub_eq_add_neg, sub_self] at h; simp [add_zero] at h
contradiction
theorem inv_int_eq [Field α] [IsCharP α 0] (b : Int) : b != 0 (denoteInt b : α) * (denoteInt b)⁻¹ = 1 := by
simp; intro h
have : (denoteInt b : α) 0 := by
@@ -1763,13 +1636,6 @@ theorem inv_int_eq [Field α] [IsCharP α 0] (b : Int) : b != 0 → (denoteInt b
have := IsCharP.intCast_eq_zero_iff (α := α) 0 b; simp [*] at this
rw [Field.mul_inv_cancel this]
theorem intCast_eq_denoteInt [Field α] (b : Int) : (IntCast.intCast b : α) = denoteInt b := by
simp [denoteInt_eq]
theorem inv_int_eq' [Field α] [IsCharP α 0] (b : Int) : b != 0 (IntCast.intCast b : α) * (denoteInt b)⁻¹ = 1 := by
rw [intCast_eq_denoteInt]
apply inv_int_eq
theorem inv_int_eqC {α c} [Field α] [IsCharP α c] (b : Int) : b % c != 0 (denoteInt b : α) * (denoteInt b)⁻¹ = 1 := by
simp; intro h
have : (denoteInt b : α) 0 := by

View File

@@ -16,7 +16,6 @@ namespace Lean.Grind
A field is a commutative ring with inverses for all non-zero elements.
-/
class Field (α : Type u) extends CommRing α, Inv α, Div α where
/-- An exponentiation operator. -/
[zpow : HPow α Int α]
/-- Division is multiplication by the inverse. -/
div_eq_mul_inv : a b : α, a / b = a * b⁻¹
@@ -95,7 +94,7 @@ theorem inv_eq_zero_iff {a : α} : a⁻¹ = 0 ↔ a = 0 := by
theorem zero_eq_inv_iff {a : α} : 0 = a⁻¹ 0 = a := by
rw [eq_comm, inv_eq_zero_iff, eq_comm]
theorem of_mul_eq_zero {a b : α} : a * b = 0 a = 0 b = 0 := by
theorem of_mul_eq_zero {a b : α} : a*b = 0 a = 0 b = 0 := by
cases (Classical.em (a = 0)); · simp [*, Semiring.zero_mul]
cases (Classical.em (b = 0)); · simp [*, Semiring.mul_zero]
rename_i h₁ h₂
@@ -170,52 +169,6 @@ theorem zpow_add {a : α} (h : a ≠ 0) (m n : Int) : a ^ (m + n) = a ^ m * a ^
| zero => simp [Int.add_neg_one, zpow_sub_one h, zpow_neg_one]
| succ n ih => rw [Int.natCast_add_one, Int.neg_add, Int.add_neg_one, Int.add_sub_assoc, zpow_sub_one h, zpow_sub_one h, ih, Semiring.mul_assoc]
theorem div_zero {x : α} : x / 0 = 0 := by rw [div_eq_mul_inv, inv_zero, Semiring.mul_zero]
theorem mul_div {x y z : α} : x * (y / z) = (x * y) / z := by
rw [div_eq_mul_inv, div_eq_mul_inv, Semiring.mul_assoc]
theorem div_mul {x y z : α} : x / y * z = x * z / y := by
rw [div_eq_mul_inv, div_eq_mul_inv, Semiring.mul_assoc, CommSemiring.mul_comm y⁻¹,
Semiring.mul_assoc]
theorem div_add {x y z : α} (hy : y 0) : x / y + z = (x + y * z) / y := by
rw [div_eq_mul_inv, div_eq_mul_inv, Semiring.right_distrib, CommSemiring.mul_comm y,
Semiring.mul_assoc, Field.mul_inv_cancel hy, Semiring.mul_one]
theorem add_div {x y z : α} (hz : z 0) : x + y / z = (x * z + y) / z := by
rw [div_eq_mul_inv, div_eq_mul_inv, Semiring.right_distrib, Semiring.mul_assoc,
Field.mul_inv_cancel hz, Semiring.mul_one]
theorem div_div_right {x y z : α} : x / (y / z) = x * z / y := by
rw [div_eq_mul_inv, div_eq_mul_inv, div_eq_mul_inv, inv_mul, inv_inv, CommSemiring.mul_comm y⁻¹,
Semiring.mul_assoc]
theorem div_div_left {x y z : α} : (x / y) / z = x / (y * z) := by
rw [div_eq_mul_inv, div_eq_mul_inv, div_eq_mul_inv, inv_mul, Semiring.mul_assoc]
theorem div_mul_cancel {x y : α} (h : y 0) : x / y * y = x := by
rw [div_eq_mul_inv, Semiring.mul_assoc, Field.inv_mul_cancel h, Semiring.mul_one]
attribute [local instance] Semiring.natCast in
theorem natCast_ne_zero [IsCharP α 0] {n : Nat} (h : n 0) : (n : α) 0 := by
simpa [IsCharP.natCast_eq_zero_iff]
attribute [local instance] Ring.intCast in
theorem intCast_div_of_dvd {x y : Int} (h : y x) (w : (y : α) 0) :
((x / y : Int) : α) = ((x : α) / (y : α)) := by
obtain z, rfl := h
by_cases hy : y = 0
· simp_all [Ring.intCast_zero]
· rw [Int.mul_ediv_cancel_left _ hy]
rw [Ring.intCast_mul, CommSemiring.mul_comm, div_eq_mul_inv, Semiring.mul_assoc,
mul_inv_cancel w, Semiring.mul_one]
attribute [local instance] Semiring.natCast in
theorem natCast_div_of_dvd {x y : Nat} (h : y x) (w : (y : α) 0) :
((x / y : Nat) : α) = ((x : α) / (y : α)) := by
obtain z, rfl := h
by_cases hy : y = 0
· simp_all [Semiring.natCast_zero]
· rw [Nat.mul_div_cancel_left _ (by omega)]
rw [Semiring.natCast_mul, CommSemiring.mul_comm, div_eq_mul_inv, Semiring.mul_assoc,
mul_inv_cancel w, Semiring.mul_one]
-- This is expensive as an instance. Let's see what breaks without it.
def noNatZeroDivisors.ofIsCharPZero [IsCharP α 0] : NoNatZeroDivisors α := NoNatZeroDivisors.mk' <| by
intro a b h w

View File

@@ -299,19 +299,9 @@ syntax (name := grindTrace)
It is a implemented as a thin wrapper around the `grind` tactic, enabling only the `cutsat` solver.
Please use `grind` instead if you need additional capabilities.
**Deprecated**: Use `lia` instead.
-/
syntax (name := cutsat) "cutsat" optConfig : tactic
/--
`lia` solves linear integer arithmetic goals.
It is a implemented as a thin wrapper around the `grind` tactic, enabling only the `cutsat` solver.
Please use `grind` instead if you need additional capabilities.
-/
syntax (name := lia) "lia" optConfig : tactic
/--
`grobner` solves goals that can be phrased as polynomial equations (with further polynomial equations as hypotheses)
over commutative (semi)rings, using the Grobner basis algorithm.

View File

@@ -108,7 +108,7 @@ instance : ToInt (Fin n) (.co 0 n) where
toInt_inj x y w := Fin.eq_of_val_eq (Int.ofNat_inj.mp w)
toInt_mem := by simp
@[simp, grind =] theorem toInt_fin (x : Fin n) : ToInt.toInt x = (x.val : Int) := rfl
@[simp] theorem toInt_fin (x : Fin n) : ToInt.toInt x = (x.val : Int) := rfl
instance [NeZero n] : ToInt.Zero (Fin n) (.co 0 n) where
toInt_zero := rfl
@@ -116,9 +116,6 @@ instance [NeZero n] : ToInt.Zero (Fin n) (.co 0 n) where
instance [NeZero n] : ToInt.OfNat (Fin n) (.co 0 n) where
toInt_ofNat x := by simp; rfl
theorem ofNat_FinZero (n : Nat) [NeZero n] : ToInt.toInt (OfNat.ofNat 0 : Fin n) = 0 := by
rw [ToInt.toInt, instToIntFinCoOfNatIntCast, Fin.instOfNat, Fin.ofNat]; simp
instance : ToInt.Add (Fin n) (.co 0 n) where
toInt_add x y := by rfl

View File

@@ -631,8 +631,6 @@ structure Subtype {α : Sort u} (p : α → Prop) where
-/
property : p val
grind_pattern Subtype.property => self.val
set_option linter.unusedVariables.funArgs false in
/--
Gadget for optional parameter support.
@@ -2264,7 +2262,6 @@ structure Fin (n : Nat) where
isLt : LT.lt val n
attribute [coe] Fin.val
grind_pattern Fin.isLt => self.val
theorem Fin.eq_of_val_eq {n} : {i j : Fin n}, Eq i.val j.val Eq i j
| _, _, _, _, rfl => rfl
@@ -3449,11 +3446,11 @@ structure String where ofByteArray ::
/-- The bytes of the UTF-8 encoding of the string. Since strings have a special representation in
the runtime, this function actually takes linear time and space at runtime. For efficient access
to the string's bytes, use `String.utf8ByteSize` and `String.getUTF8Byte`. -/
toByteArray : ByteArray
bytes : ByteArray
/-- The bytes of the string form valid UTF-8. -/
isValidUTF8 : ByteArray.IsValidUTF8 toByteArray
isValidUTF8 : ByteArray.IsValidUTF8 bytes
attribute [extern "lean_string_to_utf8"] String.toByteArray
attribute [extern "lean_string_to_utf8"] String.bytes
attribute [extern "lean_string_from_utf8_unchecked"] String.ofByteArray
/--
@@ -3468,7 +3465,7 @@ def String.decEq (s₁ s₂ : @& String) : Decidable (Eq s₁ s₂) :=
| s₁, _, s₂, _ =>
dite (Eq s₁ s₂) (fun h => match s₁, s₂, h with | _, _, Eq.refl _ => isTrue rfl)
(fun h => isFalse
(fun h' => h (congrArg (fun s => Array.toList (ByteArray.data (String.toByteArray s))) h')))
(fun h' => h (congrArg (fun s => Array.toList (ByteArray.data (String.bytes s))) h')))
instance : DecidableEq String := String.decEq
@@ -3482,7 +3479,7 @@ be translated internally to byte positions, which takes linear time.
A byte position `p` is *valid* for a string `s` if `0 ≤ p ≤ s.rawEndPos` and `p` lies on a UTF-8
character boundary, see `String.Pos.IsValid`.
There is another type, `String.Pos`, which bundles the validity predicate. Using `String.Pos`
There is another type, `String.ValidPos`, which bundles the validity predicate. Using `String.ValidPos`
instead of `String.Pos.Raw` is recommended because it will lead to less error handling and fewer edge cases.
-/
structure String.Pos.Raw where
@@ -3534,7 +3531,7 @@ At runtime, this function takes constant time because the byte length of strings
-/
@[extern "lean_string_utf8_byte_size"]
def String.utf8ByteSize (s : @& String) : Nat :=
s.toByteArray.size
s.bytes.size
/--
A UTF-8 byte position that points at the end of a string, just after the last character.

View File

@@ -93,7 +93,7 @@ An absolute path starts at the root directory or a drive letter. Accessing files
path does not depend on the current working directory.
-/
def isAbsolute (p : FilePath) : Bool :=
pathSeparators.contains p.toString.front || (isWindows && p.toString.length > 1 && p.toString.startPos.next?.bind (·.get?) == some ':')
pathSeparators.contains p.toString.front || (isWindows && p.toString.length > 1 && p.toString.startValidPos.next?.bind (·.get?) == some ':')
/--
A relative path is one that depends on the current working directory for interpretation. Relative
@@ -122,7 +122,7 @@ instance : HDiv FilePath String FilePath where
hDiv p sub := FilePath.join p sub
private def posOfLastSep (p : FilePath) : Option String.Pos.Raw :=
p.toString.revFind? pathSeparators.contains |>.map String.Pos.offset
p.toString.revFind pathSeparators.contains
/--
Returns the parent directory of a path, if there is one.
@@ -173,7 +173,7 @@ Examples:
-/
def fileStem (p : FilePath) : Option String :=
p.fileName.map fun fname =>
match fname.revFind? '.' |>.map String.Pos.offset with
match fname.revPosOf '.' with
| some 0 => fname
| some pos => String.Pos.Raw.extract fname 0 pos
| none => fname
@@ -192,7 +192,7 @@ Examples:
-/
def extension (p : FilePath) : Option String :=
p.fileName.bind fun fname =>
match fname.revFind? '.' |>.map String.Pos.offset with
match fname.revPosOf '.' with
| some 0 => none
| some pos => some <| String.Pos.Raw.extract fname (pos + '.') fname.rawEndPos
| none => none

View File

@@ -10,7 +10,6 @@ public import Init.System.IOError
public import Init.System.FilePath
public import Init.Data.Ord.UInt
import Init.Data.String.TakeDrop
import Init.Data.String.Search
public section
@@ -565,20 +564,9 @@ Waits until any of the tasks in the list has finished, then return its result.
(h : tasks.length > 0 := by exact Nat.zero_lt_succ _) : BaseIO α :=
return tasks[0].get
/--
Given a non-empty list of tasks, wait for the first to complete.
Return the value and the list of remaining tasks.
-/
def waitAny' (tasks : List (Task α)) (h : 0 < tasks.length := by exact Nat.zero_lt_succ _) :
BaseIO (α × List (Task α)) := do
let (i, a) IO.waitAny
(tasks.mapIdx fun i t => t.map (sync := true) fun a => (i, a))
(by simp_all)
return (a, tasks.eraseIdx i)
/--
Returns the number of _heartbeats_ that have occurred during the current thread's execution. The
heartbeat count is the number of "small" memory allocations performed in a thread.
heartbeat count is the number of small memory allocations performed in a thread.
Heartbeats used to implement timeouts that are more deterministic across different hardware.
-/

View File

@@ -55,9 +55,6 @@ syntax (name := tryTrace) "try?" optConfig : tactic
/-- Helper internal tactic for implementing the tactic `try?`. -/
syntax (name := attemptAll) "attempt_all " withPosition((ppDedent(ppLine) colGe "| " tacticSeq)+) : tactic
/-- Helper internal tactic for implementing the tactic `try?` with parallel execution. -/
syntax (name := attemptAllPar) "attempt_all_par " withPosition((ppDedent(ppLine) colGe "| " tacticSeq)+) : tactic
/-- Helper internal tactic used to implement `evalSuggest` in `try?` -/
syntax (name := tryResult) "try_suggestions " tactic* : tactic

View File

@@ -29,7 +29,7 @@ partial def Loop.forIn {β : Type u} {m : Type u → Type v} [Monad m] (_ : Loop
| ForInStep.yield b => loop b
loop init
instance [Monad m] : ForIn m Loop Unit where
instance : ForIn m Loop Unit where
forIn := Loop.forIn
syntax "repeat " doSeq : doElem

View File

@@ -76,7 +76,7 @@ builtin_initialize externAttr : ParametricAttribute ExternAttrData ←
def getExternAttrData? (env : Environment) (n : Name) : Option ExternAttrData :=
externAttr.getParam? env n
private def parseOptNum : Nat (pattern : String) (it : pattern.Pos) Nat pattern.Pos × Nat
private def parseOptNum : Nat (pattern : String) (it : pattern.ValidPos) Nat pattern.ValidPos × Nat
| 0, _ , it, r => (it, r)
| n+1, pattern, it, r =>
if h : it.IsAtEnd then (it, r)
@@ -86,7 +86,7 @@ private def parseOptNum : Nat → (pattern : String) → (it : pattern.Pos) →
then parseOptNum n pattern (it.next h) (r*10 + (c.toNat - '0'.toNat))
else (it, r)
def expandExternPatternAux (args : List String) : Nat (pattern : String) (it : pattern.Pos) String String
def expandExternPatternAux (args : List String) : Nat (pattern : String) (it : pattern.ValidPos) String String
| 0, _, _, r => r
| i+1, pattern, it, r =>
if h : it.IsAtEnd then r
@@ -99,7 +99,7 @@ def expandExternPatternAux (args : List String) : Nat → (pattern : String) →
expandExternPatternAux args i pattern it (r ++ args.getD j "")
def expandExternPattern (pattern : String) (args : List String) : String :=
expandExternPatternAux args pattern.length pattern pattern.startPos ""
expandExternPatternAux args pattern.length pattern pattern.startValidPos ""
def mkSimpleFnCall (fn : String) (args : List String) : String :=
fn ++ "(" ++ ((args.intersperse ", ").foldl (·++·) "") ++ ")"

View File

@@ -27,7 +27,6 @@ public import Lean.Compiler.IR.Sorry
public import Lean.Compiler.IR.ToIR
public import Lean.Compiler.IR.ToIRType
public import Lean.Compiler.IR.Meta
public import Lean.Compiler.IR.Toposort
-- The following imports are not required by the compiler. They are here to ensure that there
-- are no orphaned modules.
@@ -72,7 +71,6 @@ def compile (decls : Array Decl) : CompilerM (Array Decl) := do
decls updateSorryDep decls
logDecls `result decls
checkDecls decls
decls toposortDecls decls
addDecls decls
inferMeta decls
return decls

View File

@@ -8,7 +8,6 @@ module
prelude
public import Lean.Compiler.IR.Boxing
import Lean.Compiler.IR.RC
public section
@@ -27,12 +26,11 @@ def addExtern (declName : Name) (externAttrData : ExternAttrData) : CoreM Unit :
type := b
nextVarIndex := nextVarIndex + 1
let irType toIRType type
let decls := #[.extern declName params irType externAttrData]
if !isPrivateName declName then
modifyEnv (Compiler.LCNF.setDeclPublic · declName)
let decls := ExplicitBoxing.addBoxedVersions ( Lean.getEnv) decls
let decls explicitRC decls
logDecls `result decls
addDecls decls
let decl := .extern declName params irType externAttrData
addDecl decl
if !isPrivateName decl.name then
modifyEnv (Compiler.LCNF.setDeclPublic · decl.name)
if ExplicitBoxing.requiresBoxedVersion ( Lean.getEnv) decl then
addDecl (ExplicitBoxing.mkBoxedVersion decl)
end Lean.IR

View File

@@ -26,6 +26,12 @@ Assumptions:
`Expr.isShared`, `Expr.isTaggedPtr`, and `FnBody.set`.
-/
def mkBoxedName (n : Name) : Name :=
Name.mkStr n "_boxed"
def isBoxedName (name : Name) : Bool :=
name matches .str _ "_boxed"
abbrev N := StateM Nat
private def N.mkFresh : N VarId :=

View File

@@ -139,20 +139,10 @@ def findEnvDecl (env : Environment) (declName : Name) (includeServer := false):
private def findInterpDecl (env : Environment) (declName : Name) : Option Decl :=
findEnvDecl (includeServer := true) env declName
namespace ExplicitBoxing
def mkBoxedName (n : Name) : Name :=
Name.mkStr n "_boxed"
def isBoxedName (name : Name) : Bool :=
name matches .str _ "_boxed"
end ExplicitBoxing
/-- Like ``findInterpDecl env (declName ++ `_boxed)`` but with optimized negative lookup. -/
@[export lean_ir_find_env_decl_boxed]
private def findInterpDeclBoxed (env : Environment) (declName : Name) : Option Decl :=
let boxed := ExplicitBoxing.mkBoxedName declName
let boxed := declName ++ `_boxed
-- Important: get module index of base name, not boxed version. Usually the interpreter never
-- does negative lookups except in the case of `call_boxed` which must check whether a boxed
-- version exists. If `declName` exists as an imported declaration but `declName'` doesn't, the

View File

@@ -1,70 +0,0 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Henrik Böving
-/
module
prelude
public import Lean.Compiler.IR.CompilerM
/-!
This module "topologically sorts" an SCC of decls (an SCC of decls in the pipeline may in fact
contain more than one SCC at the moment). This is relevant because EmitC relies on the invariant
that the constants (and in particular also the closed terms) occur in a reverse topologically sorted
order for emitting them.
-/
namespace Lean.IR
structure TopoState where
declsMap : Std.HashMap Name Decl
seen : Std.HashSet Name
order : Array Decl
abbrev ToposortM := StateRefT TopoState CompilerM
partial def toposort (decls : Array Decl) : CompilerM (Array Decl) := do
let declsMap := .ofList (decls.toList.map (fun d => (d.name, d)))
let (_, s) go decls |>.run {
declsMap,
seen := .emptyWithCapacity decls.size,
order := .emptyWithCapacity decls.size
}
return s.order
where
go (decls : Array Decl) : ToposortM Unit := do
decls.forM process
process (decl : Decl) : ToposortM Unit := do
if ( get).seen.contains decl.name then
return ()
modify fun s => { s with seen := s.seen.insert decl.name }
let .fdecl (body := body) .. := decl | unreachable!
processBody body
modify fun s => { s with order := s.order.push decl }
processBody (b : FnBody) : ToposortM Unit := do
match b with
| .vdecl _ _ e b =>
match e with
| .fap c .. | .pap c .. =>
if let some decl := ( get).declsMap[c]? then
process decl
| _ => pure ()
processBody b
| .jdecl _ _ v b =>
processBody v
processBody b
| .case _ _ _ cs => cs.forM (fun alt => processBody alt.body)
| .jmp .. | .ret .. | .unreachable => return ()
| _ => processBody b.body
public def toposortDecls (decls : Array Decl) : CompilerM (Array Decl) := do
let (externDecls, otherDecls) := decls.partition (fun decl => decl.isExtern)
let otherDecls toposort otherDecls
return externDecls ++ otherDecls
end Lean.IR

View File

@@ -35,7 +35,7 @@ inductive Value where
A set of values are possible.
-/
| choice (vs : List Value)
deriving Inhabited
deriving Inhabited, Repr
namespace Value
@@ -43,36 +43,17 @@ namespace Value
def maxValueDepth := 8
protected partial def beq : Value Value Bool
| bot, bot => true
| top, top => true
| ctor i1 vs1 , ctor i2 vs2 =>
i1 == i2 && Array.isEqv vs1 vs2 Value.beq
| choice vs1 , choice vs2 =>
let isSubset as bs := as.all (fun a => bs.any fun b => Value.beq a b)
isSubset vs1 vs2 && isSubset vs2 vs1
| _, _ => false
| bot, bot => true
| top, top => true
| ctor i1 vs1 , ctor i2 vs2 =>
i1 == i2 && Array.isEqv vs1 vs2 Value.beq
| choice vs1 , choice vs2 =>
let isSubset as bs := as.all (fun a => bs.any fun b => Value.beq a b)
isSubset vs1 vs2 && isSubset vs2 vs1
| _, _ => false
instance : BEq Value := Value.beq
protected partial def toFormat : Value Format
| bot => ""
| top => ""
| ctor i vs =>
if vs.isEmpty then
format i
else
.paren <| format i ++ .join (vs.toList.map fun v => " " ++ Value.toFormat v)
| choice vs =>
.paren <| .joinSep (vs.map Value.toFormat) " | "
instance : Repr Value where
reprPrec v _ := Value.toFormat v
def inductValOfCtor (ctorName : Name) (env : Environment) : InductiveVal := Id.run do
let some (.ctorInfo info) env.find? ctorName | unreachable!
let some (.inductInfo info) env.find? info.induct | unreachable!
return info
mutual
/--
@@ -81,60 +62,32 @@ is a constructor that is already contained within `vs` try to detect
the difference between these values and merge them accordingly into a
choice node further down the tree.
-/
partial def addChoice (env : Environment) (vs : List Value) (v : Value) : List Value :=
partial def addChoice (vs : List Value) (v : Value) : List Value :=
match vs, v with
| [], v => [v]
| v1@(ctor i1 vs1) :: cs, ctor i2 vs2 =>
| v1@(ctor i1 _ ) :: cs, ctor i2 _ =>
if i1 == i2 then
ctor i1 (Array.zipWith (merge env) vs1 vs2) :: cs
(merge v1 v) :: cs
else
v1 :: addChoice env cs v
| _, _ => panic! s!"invalid addChoice {repr v} into {repr vs}"
v1 :: addChoice cs v
| _, _ => panic! "invalid addChoice"
/--
Merge two values into one. `bot` is the neutral element, `top` the annihilator.
-/
partial def merge (env : Environment) (v1 v2 : Value) : Value :=
let newValue :=
match v1, v2 with
| bot, v | v, bot => v
| top, _ | _, top => top
| ctor i1 vs1, ctor i2 vs2 =>
if i1 == i2 then
ctor i1 (Array.zipWith (merge env) vs1 vs2)
else
choice [v1, v2]
| choice vs1, choice vs2 =>
choice (vs1.foldl (addChoice env) vs2)
| choice vs, v | v, choice vs =>
choice (addChoice env vs v)
match newValue with
| .top | .bot => newValue
| .choice vs => cleanup vs
| .ctor ctorName .. =>
if eligible newValue && inductHasNumCtors ctorName env 1 then
top
partial def merge (v1 v2 : Value) : Value :=
match v1, v2 with
| bot, v | v, bot => v
| top, _ | _, top => top
| ctor i1 vs1, ctor i2 vs2 =>
if i1 == i2 then
ctor i1 (Array.zipWith merge vs1 vs2)
else
newValue
where
cleanup (vs : List Value) : Value := Id.run do
if vs.all eligible then
let .ctor ctorName .. := vs.head! | unreachable!
if inductHasNumCtors ctorName env vs.length then
top
else
choice vs
else
choice vs
inductHasNumCtors (ctorName : Name) (env : Environment) (n : Nat) : Bool := Id.run do
let induct := inductValOfCtor ctorName env
n == induct.numCtors
@[inline]
eligible (value : Value) : Bool := Id.run do
let .ctor _ args := value | return false
args.all (· == .top)
choice [v1, v2]
| choice vs1, choice vs2 =>
choice (vs1.foldl addChoice vs2)
| choice vs, v | v, choice vs =>
choice (addChoice vs v)
end
@@ -153,16 +106,19 @@ where
| remainingDepth + 1 =>
match v with
| ctor i vs =>
let induct := inductValOfCtor i env
if forbiddenTypes.contains induct.name then
let typeName := i.getPrefix
if forbiddenTypes.contains typeName then
top
else
let cont forbiddenTypes' :=
ctor i (vs.map (go · forbiddenTypes' remainingDepth))
if induct.isRec then
cont <| forbiddenTypes.insert induct.name
else
cont forbiddenTypes
match env.find? typeName with
| some (.inductInfo type) =>
if type.isRec then
cont <| forbiddenTypes.insert typeName
else
cont forbiddenTypes
| _ => cont forbiddenTypes
| choice vs =>
let vs := vs.map (go · forbiddenTypes remainingDepth)
if vs.elem top then
@@ -173,7 +129,7 @@ where
/-- Widening operator that guarantees termination in our abstract interpreter. -/
def widening (env : Environment) (v1 v2 : Value) : Value :=
truncate env (merge env v1 v2)
truncate env (merge v1 v2)
/--
Check whether a certain constructor is part of a `Value` by name.
@@ -581,9 +537,7 @@ def inferStep : InterpM Bool := do
withReader (fun ctx => { ctx with currFnIdx := idx }) do
decl.params.forM fun p => updateVarAssignment p.fvarId .top
match decl.value with
| .code code .. =>
withTraceNode `Compiler.elimDeadBranches (fun _ => return m!"Analyzing {decl.name}") do
interpCode code
| .code code .. => interpCode code
| .extern .. => updateCurrFnSummary .top
let newVal getFunVal idx
if currentVal != newVal then
@@ -593,14 +547,13 @@ def inferStep : InterpM Bool := do
/--
Run `inferStep` until it reaches a fix point.
-/
partial def inferMain (n : Nat := 0) : InterpM Unit := do
partial def inferMain : InterpM Unit := do
let ctx read
modify fun s => { s with assignments := ctx.decls.map fun _ => {} }
let modified inferStep
if modified then
inferMain (n + 1)
inferMain
else
trace[Compiler.elimDeadBranches] m!"Termination after {n} steps"
return ()
/--
@@ -650,11 +603,6 @@ end UnreachableBranches
open UnreachableBranches in
def Decl.elimDeadBranches (decls : Array Decl) : CompilerM (Array Decl) := do
/-
We sort declarations by size here to ensure that when we restart in inferStep it will mostly be
small declarations that get re-analyzed.
-/
let decls := decls.qsort (fun l r => (l.size, l.name.toString).lexLt (r.size, r.name.toString))
let mut assignments := decls.map fun _ => {}
let initialVal i :=
/-
@@ -667,9 +615,7 @@ def Decl.elimDeadBranches (decls : Array Decl) : CompilerM (Array Decl) := do
let mut funVals := decls.size.fold (init := .empty) fun i _ p => p.push (initialVal i)
let ctx := { decls }
let mut state := { assignments, funVals }
(_, state)
withTraceNode `Compiler.elimDeadBranches (fun _ => return m!"Analyzing block: {decls.map (·.name)}")
inferMain |>.run ctx |>.run state
(_, state) inferMain |>.run ctx |>.run state
funVals := state.funVals
assignments := state.assignments
modifyEnv fun e =>

View File

@@ -238,13 +238,6 @@ def mkKey (params : Array Param) (decls : Array CodeDecl) (body : LetValue) : Co
let key := ToExpr.run do
ToExpr.withParams params do
ToExpr.mkLambdaM params ( ToExpr.abstractM body)
let pre e := do
-- beta reduce if possible
if e.isHeadBetaTarget then
return .visit e.headBeta
else
return .continue
let key Meta.MetaM.run' <| Meta.transform (usedLetOnly := true) key pre
return normLevelParams key
open Internalize in

View File

@@ -19,10 +19,9 @@ def findStructCtorInfo? (typeName : Name) : CoreM (Option ConstructorVal) := do
let some (.ctorInfo ctorInfo) := ( getEnv).find? ctorName | return none
return ctorInfo
def mkFieldParamsForCtorType (ctorType : Expr) (numParams : Nat) (numFields : Nat) :
CompilerM (Array Param) := do
let mut type Meta.MetaM.run' <| toLCNFType ctorType
type toMonoType type
def mkFieldParamsForCtorType (ctorType : Expr) (numParams : Nat) (numFields : Nat)
: CompilerM (Array Param) := do
let mut type := ctorType
for _ in *...numParams do
match type with
| .forallE _ _ body _ =>
@@ -32,7 +31,7 @@ def mkFieldParamsForCtorType (ctorType : Expr) (numParams : Nat) (numFields : Na
for _ in *...numFields do
match type with
| .forallE name fieldType body _ =>
let param mkParam name fieldType false
let param mkParam name ( toMonoType fieldType) false
fields := fields.push param
type := body
| _ => unreachable!

View File

@@ -29,8 +29,8 @@ where finally
simp [i, UInt32.lt_iff_toNat_lt, this]; omega
def mangleAux (s : String) (pos : s.Pos) (r : String) : String :=
if h : pos = s.endPos then r else
def mangleAux (s : String) (pos : s.ValidPos) (r : String) : String :=
if h : pos = s.endValidPos then r else
let c := pos.get h
let pos := pos.next h
if c.isAlpha || c.isDigit then
@@ -46,16 +46,16 @@ def mangleAux (s : String) (pos : s.Pos) (r : String) : String :=
termination_by pos
public def mangle (s : String) : String :=
mangleAux s s.startPos ""
mangleAux s s.startValidPos ""
end String
namespace Lean
def checkLowerHex : Nat (s : String) s.Pos Bool
def checkLowerHex : Nat (s : String) s.ValidPos Bool
| 0, _, _ => true
| k + 1, s, pos =>
if h : pos = s.endPos then
if h : pos = s.endValidPos then
false
else
let ch := pos.get h
@@ -69,19 +69,19 @@ def fromHex? (c : Char) : Option Nat :=
some (c.val - 87).toNat
else none
def parseLowerHex? (k : Nat) (s : String) (p : s.Pos) (acc : Nat) :
Option (s.Pos × Nat) :=
def parseLowerHex? (k : Nat) (s : String) (p : s.ValidPos) (acc : Nat) :
Option (s.ValidPos × Nat) :=
match k with
| 0 => some (p, acc)
| k + 1 =>
if h : p = s.endPos then
if h : p = s.endValidPos then
none
else
match fromHex? (p.get h) with
| some d => parseLowerHex? k s (p.next h) (acc <<< 4 ||| d)
| none => none
theorem lt_of_parseLowerHex?_eq_some {k : Nat} {s : String} {p q : s.Pos} {acc : Nat}
theorem lt_of_parseLowerHex?_eq_some {k : Nat} {s : String} {p q : s.ValidPos} {acc : Nat}
{r : Nat} (hk : 0 < k) : parseLowerHex? k s p acc = some (q, r) p < q := by
fun_induction parseLowerHex? with
| case1 => simp at hk
@@ -89,10 +89,10 @@ theorem lt_of_parseLowerHex?_eq_some {k : Nat} {s : String} {p q : s.Pos} {acc :
| case3 p acc k h d x ih =>
match k with
| 0 => simpa [parseLowerHex?] using fun h _ => h p.lt_next
| k + 1 => exact fun h => String.Pos.lt_trans p.lt_next (ih (by simp) h)
| k + 1 => exact fun h => String.ValidPos.lt_trans p.lt_next (ih (by simp) h)
| case4 => simp
def checkDisambiguation (s : String) (p : s.Pos) : Bool :=
def checkDisambiguation (s : String) (p : s.ValidPos) : Bool :=
if h : _ then
let b := p.get h
if b = '_' then
@@ -111,8 +111,8 @@ termination_by p
def needDisambiguation (prev : Name) (next : String) : Bool :=
(match prev with
| .str _ s => h, (s.endPos.prev h).get (by simp) = '_'
| _ => false) || checkDisambiguation next next.startPos
| .str _ s => h, (s.endValidPos.prev h).get (by simp) = '_'
| _ => false) || checkDisambiguation next next.startValidPos
def Name.mangleAux : Name String
| Name.anonymous => ""
@@ -120,7 +120,7 @@ def Name.mangleAux : Name → String
let m := String.mangle s
match p with
| Name.anonymous =>
if checkDisambiguation m m.startPos then "00" ++ m else m
if checkDisambiguation m m.startValidPos then "00" ++ m else m
| p =>
let m1 := mangleAux p
m1 ++ (if needDisambiguation p m then "_00" else "_") ++ m
@@ -149,9 +149,9 @@ public def mkPackageSymbolPrefix (pkg? : Option PkgId) : String :=
pkg?.elim "l_" (s!"lp_{·.mangle}_")
-- assumes `s` has been generated `Name.mangle n ""`
def Name.demangleAux (s : String) (p₀ : s.Pos) (res : Name)
def Name.demangleAux (s : String) (p₀ : s.ValidPos) (res : Name)
(acc : String) (ucount : Nat) : Name :=
if hp₀ : p₀ = s.endPos then res.str (acc.pushn '_' (ucount / 2)) else
if hp₀ : p₀ = s.endValidPos then res.str (acc.pushn '_' (ucount / 2)) else
let ch := p₀.get hp₀
let p := p₀.next hp₀
if ch = '_' then demangleAux s p res acc (ucount + 1) else
@@ -159,7 +159,7 @@ def Name.demangleAux (s : String) (p₀ : s.Pos) (res : Name)
demangleAux s p res (acc.pushn '_' (ucount / 2) |>.push ch) 0
else if ch.isDigit then
let res := res.str (acc.pushn '_' (ucount / 2))
if h : ch = '0' h : p s.endPos, p.get h = '0' then
if h : ch = '0' h : p s.endValidPos, p.get h = '0' then
demangleAux s (p.next h.2.1) res "" 0
else
decodeNum s p res (ch.val - 48).toNat
@@ -167,40 +167,40 @@ def Name.demangleAux (s : String) (p₀ : s.Pos) (res : Name)
match ch, h₁ : parseLowerHex? 2 s p 0 with
| 'x', some (q, v) =>
let acc := acc.pushn '_' (ucount / 2)
have : p₀ < q := String.Pos.lt_trans p₀.lt_next (lt_of_parseLowerHex?_eq_some (by decide) h₁)
have : p₀ < q := String.ValidPos.lt_trans p₀.lt_next (lt_of_parseLowerHex?_eq_some (by decide) h₁)
demangleAux s q res (acc.push (Char.ofNat v)) 0
| _, _ =>
match ch, h₂ : parseLowerHex? 4 s p 0 with
| 'u', some (q, v) =>
let acc := acc.pushn '_' (ucount / 2)
have : p₀ < q := String.Pos.lt_trans p₀.lt_next (lt_of_parseLowerHex?_eq_some (by decide) h₂)
have : p₀ < q := String.ValidPos.lt_trans p₀.lt_next (lt_of_parseLowerHex?_eq_some (by decide) h₂)
demangleAux s q res (acc.push (Char.ofNat v)) 0
| _, _ =>
match ch, h₃ : parseLowerHex? 8 s p 0 with
| 'U', some (q, v) =>
let acc := acc.pushn '_' (ucount / 2)
have : p₀ < q := String.Pos.lt_trans p₀.lt_next (lt_of_parseLowerHex?_eq_some (by decide) h₃)
have : p₀ < q := String.ValidPos.lt_trans p₀.lt_next (lt_of_parseLowerHex?_eq_some (by decide) h₃)
demangleAux s q res (acc.push (Char.ofNat v)) 0
| _, _ => demangleAux s p (res.str acc) ("".pushn '_' (ucount / 2) |>.push ch) 0
termination_by p₀
where
decodeNum (s : String) (p : s.Pos) (res : Name) (n : Nat) : Name :=
if h : p = s.endPos then res.num n else
decodeNum (s : String) (p : s.ValidPos) (res : Name) (n : Nat) : Name :=
if h : p = s.endValidPos then res.num n else
let ch := p.get h
let p := p.next h
if ch.isDigit then
decodeNum s p res (n * 10 + (ch.val - 48).toNat)
else -- assume ch = '_'
let res := res.num n
if h : p = s.endPos then res else
if h : p = s.endValidPos then res else
nameStart s (p.next h) res -- assume s.get' p h = '_'
termination_by p
nameStart (s : String) (p : s.Pos) (res : Name) : Name :=
if h : p = s.endPos then res else
nameStart (s : String) (p : s.ValidPos) (res : Name) : Name :=
if h : p = s.endValidPos then res else
let ch := p.get h
let p := p.next h
if ch.isDigit then
if h : ch = '0' h : p s.endPos, p.get h = '0' then
if h : ch = '0' h : p s.endValidPos, p.get h = '0' then
demangleAux s (p.next h.2.1) res "" 0
else
decodeNum s p res (ch.val - 48).toNat
@@ -212,7 +212,7 @@ where
/-- Assuming `s` has been produced by `Name.mangle _ ""`, return the original name. -/
public def Name.demangle (s : String) : Name :=
demangleAux.nameStart s s.startPos .anonymous
demangleAux.nameStart s s.startValidPos .anonymous
/--
Returns the demangled version of `s`, if it's the result of `Name.mangle _ ""`. Otherwise returns

View File

@@ -435,9 +435,6 @@ def mkFreshUserName (n : Name) : CoreM Name :=
| Except.error (Exception.internal id _) => throw <| IO.userError <| "internal exception #" ++ toString id.idx
| Except.ok a => return a
@[inline] def CoreM.toIO' (x : CoreM α) (ctx : Context) (s : State) : IO α :=
(·.1) <$> x.toIO ctx s
-- withIncRecDepth for a monad `m` such that `[MonadControlT CoreM n]`
protected def withIncRecDepth [Monad m] [MonadControlT CoreM m] (x : m α) : m α :=
controlAt CoreM fun runInBase => withIncRecDepth (runInBase x)
@@ -522,7 +519,7 @@ instance : MonadLog CoreM where
if ( read).suppressElabErrors then
-- discard elaboration errors, except for a few important and unlikely misleading ones, on
-- parse error
unless msg.data.hasTag (· matches `Elab.synthPlaceholder | `Tactic.unsolvedGoals | `lean.inductionWithNoAlts._namedError | `trace) do
unless msg.data.hasTag (· matches `Elab.synthPlaceholder | `Tactic.unsolvedGoals | `trace) do
return
let ctx read

View File

@@ -110,7 +110,7 @@ def all (p : α → β → Bool) : AssocList α β → Bool
| ForInStep.yield d => loop d es
loop init as
instance [Monad m] : ForIn m (AssocList α β) (α × β) where
instance : ForIn m (AssocList α β) (α × β) where
forIn := AssocList.forIn
end Lean.AssocList

View File

@@ -32,11 +32,11 @@ public def levenshtein (str1 str2 : String) (cutoff : Nat) : Option Nat := Id.ru
for h : i in [0:v0.size] do
v0 := v0.set i i
let mut iter1 := str1.startPos
let mut iter1 := str1.startValidPos
let mut i := 0
while h1 : ¬iter1.IsAtEnd do
v1 := v1.set 0 (i+1)
let mut iter2 := str2.startPos
let mut iter2 := str2.startValidPos
let mut j : Fin (len2 + 1) := 0
while h2 : ¬iter2.IsAtEnd do
let j' : Fin _ := j + 1

View File

@@ -183,7 +183,7 @@ def updateSyntax (m : KVMap) (k : Name) (f : Syntax → Syntax) : KVMap :=
(kv : KVMap) (init : δ) (f : Name × DataValue δ m (ForInStep δ)) : m δ :=
forIn kv.entries init f
instance [Monad m] : ForIn m KVMap (Name × DataValue) where
instance : ForIn m KVMap (Name × DataValue) where
forIn := KVMap.forIn
def subsetAux : List (Name × DataValue) KVMap Bool

View File

@@ -188,7 +188,7 @@ instance : FromJson RefInfo where
@[expose] def ModuleRefs := Std.TreeMap RefIdent RefInfo
deriving EmptyCollection
instance [Monad m] : ForIn m ModuleRefs (RefIdent × RefInfo) where
instance : ForIn m ModuleRefs (RefIdent × RefInfo) where
forIn map init f :=
let map : Std.TreeMap RefIdent RefInfo := map
forIn map init f

View File

@@ -10,7 +10,6 @@ prelude
public import Lean.Data.Lsp.Basic
meta import Lean.Data.Json
public import Lean.Expr
public import Init.Data.String.Search
public section

View File

@@ -9,7 +9,6 @@ module
prelude
public import Lean.Data.Lsp.BasicAux
public import Lean.DeclarationRange
import Init.Data.String.Search
public section

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