mirror of
https://github.com/leanprover/lean4.git
synced 2026-04-08 21:24:09 +00:00
Compare commits
217 Commits
sofia/asyn
...
weakLeanAr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
032cffff2d | ||
|
|
4f6bcc5ada | ||
|
|
0650cbe0fa | ||
|
|
8bb07f336d | ||
|
|
c16e88644c | ||
|
|
96d502bd11 | ||
|
|
d48863fc2b | ||
|
|
c4a664eb5d | ||
|
|
0cd6dbaad2 | ||
|
|
34d00cb50d | ||
|
|
a73be70607 | ||
|
|
3d49476058 | ||
|
|
adc45d7c7b | ||
|
|
9efba691e7 | ||
|
|
681856324f | ||
|
|
9f49ea63e2 | ||
|
|
3770b3dcb8 | ||
|
|
3c6ea49d0e | ||
|
|
608e0d06a8 | ||
|
|
5fdeaf0d5a | ||
|
|
da91aed2e2 | ||
|
|
e57d84bba0 | ||
|
|
772b5663d2 | ||
|
|
c7983a8c65 | ||
|
|
d3b04871f5 | ||
|
|
acae2b44fd | ||
|
|
fcc070f18f | ||
|
|
9aad86a576 | ||
|
|
2bcbb676f5 | ||
|
|
f7ec39d6a1 | ||
|
|
aaf0f6e7f5 | ||
|
|
5bf590e710 | ||
|
|
159f069863 | ||
|
|
aa1144602b | ||
|
|
ffc2c0ab1a | ||
|
|
8dc4c16fce | ||
|
|
861bc19e0c | ||
|
|
8f1c18d9f4 | ||
|
|
097f3ebdbc | ||
|
|
861f722844 | ||
|
|
eac9315962 | ||
|
|
8b52f4e8f7 | ||
|
|
402a6096b9 | ||
|
|
978bde4a0f | ||
|
|
8aa0c21bf8 | ||
|
|
1aa860af33 | ||
|
|
cdd982a030 | ||
|
|
f11d137a30 | ||
|
|
fc0cf68539 | ||
|
|
46a0a0eb59 | ||
|
|
916004bd3c | ||
|
|
a145b9c11a | ||
|
|
67b6e815b9 | ||
|
|
33c3604b87 | ||
|
|
504e099c5d | ||
|
|
17795b02ee | ||
|
|
48800e438c | ||
|
|
f395593ffc | ||
|
|
a88f81bc28 | ||
|
|
313abdb49f | ||
|
|
f08983bf01 | ||
|
|
22308dbaaa | ||
|
|
51e87865c5 | ||
|
|
75ec8e42c8 | ||
|
|
9fc62b7042 | ||
|
|
583c223b16 | ||
|
|
ccc7157c08 | ||
|
|
05046dc3d7 | ||
|
|
43f18fd502 | ||
|
|
b06eb981a3 | ||
|
|
f72137f53a | ||
|
|
96dbc324f3 | ||
|
|
d6e69649b6 | ||
|
|
337f1c455b | ||
|
|
6871abaa44 | ||
|
|
8c0bb68ee5 | ||
|
|
ae19b3e248 | ||
|
|
d0d135dbe2 | ||
|
|
088b299343 | ||
|
|
82c35eb517 | ||
|
|
abcf400e90 | ||
|
|
42854412c3 | ||
|
|
c84aa086c7 | ||
|
|
7168289c57 | ||
|
|
febd1caf36 | ||
|
|
79ac2d93b0 | ||
|
|
210d4d00fa | ||
|
|
938c19aace | ||
|
|
e06fc0b5e8 | ||
|
|
f2d36227cf | ||
|
|
0b401cd17c | ||
|
|
fda4793215 | ||
|
|
215aa4010b | ||
|
|
142ca24192 | ||
|
|
c71a0ea9a5 | ||
|
|
439c3c5544 | ||
|
|
2bc7a77806 | ||
|
|
e55f69acd0 | ||
|
|
50785098d8 | ||
|
|
fee2d7a6e8 | ||
|
|
bc5210d52a | ||
|
|
12c547122f | ||
|
|
f9c8b5e93d | ||
|
|
f8f12fdbc8 | ||
|
|
7f424b371e | ||
|
|
d56424b587 | ||
|
|
144db355ea | ||
|
|
0975b7136a | ||
|
|
ccef9588ae | ||
|
|
a8bbc95d9f | ||
|
|
a54eafb84f | ||
|
|
6f2745d88b | ||
|
|
25c71d91aa | ||
|
|
db491ddd35 | ||
|
|
e8c3485e08 | ||
|
|
dee571e13b | ||
|
|
51f67be2bd | ||
|
|
c77f2124fb | ||
|
|
d634f80149 | ||
|
|
40cdec76c5 | ||
|
|
4227765e2b | ||
|
|
438d1f1fe1 | ||
|
|
4786e082dc | ||
|
|
bd5fb4e90c | ||
|
|
e60078db3b | ||
|
|
f7102363de | ||
|
|
ce073771b1 | ||
|
|
dec394d3a4 | ||
|
|
6457e3686f | ||
|
|
c14fa66068 | ||
|
|
d0aa7d2faa | ||
|
|
4117ceaf84 | ||
|
|
a824e5b85e | ||
|
|
83c6f6e5ac | ||
|
|
9ffd748104 | ||
|
|
fd8d89853b | ||
|
|
0260c91d03 | ||
|
|
7ef25b8fe3 | ||
|
|
50544489a9 | ||
|
|
e9a8b965aa | ||
|
|
0f277c72bf | ||
|
|
59ce52473a | ||
|
|
2b55144c3f | ||
|
|
c381c62060 | ||
|
|
e6df474dd9 | ||
|
|
e0de32ad48 | ||
|
|
fb1dc9112b | ||
|
|
86175bea00 | ||
|
|
9eb249e38c | ||
|
|
b5036e4d81 | ||
|
|
fb1eb9aaa7 | ||
|
|
33e63bb6c3 | ||
|
|
482d7a11f2 | ||
|
|
aef0cea683 | ||
|
|
720cbd6434 | ||
|
|
26ad4d6972 | ||
|
|
4a17b2f471 | ||
|
|
fcdd9d1ae8 | ||
|
|
47427f8c77 | ||
|
|
08595c5f8f | ||
|
|
019b104a7d | ||
|
|
2e421c9970 | ||
|
|
e381960614 | ||
|
|
346c9cb16a | ||
|
|
189cea9f80 | ||
|
|
b9028fa6e9 | ||
|
|
0c0edcc96c | ||
|
|
9f4db470c4 | ||
|
|
8ae39633d1 | ||
|
|
cffacf1b10 | ||
|
|
b858d0fbf2 | ||
|
|
9a3678935d | ||
|
|
c9708e7bd7 | ||
|
|
8f6411ad57 | ||
|
|
ae0d4e3ac4 | ||
|
|
4bf7fa7447 | ||
|
|
40558129cf | ||
|
|
88b746dd48 | ||
|
|
2a25e4f3ae | ||
|
|
13f8ce8492 | ||
|
|
dbfd0d35f2 | ||
|
|
b4c4f265aa | ||
|
|
e848039ba9 | ||
|
|
9fa1a252f2 | ||
|
|
1cf030c5d4 | ||
|
|
545f27956b | ||
|
|
7897dc91e6 | ||
|
|
98f5266407 | ||
|
|
90b5e3185b | ||
|
|
973062e4e1 | ||
|
|
4a62d4a79b | ||
|
|
e2120d85c0 | ||
|
|
e33d0d33da | ||
|
|
d2ecad2e91 | ||
|
|
7097e37a1c | ||
|
|
1362cc6041 | ||
|
|
4d6accd55d | ||
|
|
6f98a76d01 | ||
|
|
609a05a90a | ||
|
|
511be304d7 | ||
|
|
0b9ad3fb8d | ||
|
|
ae7e551934 | ||
|
|
8e6f2750da | ||
|
|
492fda3bca | ||
|
|
9676f54cc5 | ||
|
|
7e3e7cf5d9 | ||
|
|
c6a89cc716 | ||
|
|
5099f96ae7 | ||
|
|
5e1b6ed663 | ||
|
|
d2907b5c96 | ||
|
|
be424ada14 | ||
|
|
d78525b302 | ||
|
|
518a135777 | ||
|
|
fd5329126b | ||
|
|
2e937ec789 | ||
|
|
90125ed205 | ||
|
|
1b63e7dfc6 |
@@ -7,6 +7,11 @@ To build Lean you should use `make -j$(nproc) -C build/release`.
|
||||
The build uses `ccache`, and in a sandbox `ccache` may complain about read-only file systems.
|
||||
Use `CCACHE_READONLY` and `CCACHE_TEMPDIR` instead of disabling ccache completely.
|
||||
|
||||
To rebuild individual modules without a full build, use Lake directly:
|
||||
```
|
||||
cd src && lake build Init.Prelude
|
||||
```
|
||||
|
||||
## Running Tests
|
||||
|
||||
See `tests/README.md` for full documentation. Quick reference:
|
||||
@@ -16,19 +21,32 @@ See `tests/README.md` for full documentation. Quick reference:
|
||||
CTEST_PARALLEL_LEVEL="$(nproc)" CTEST_OUTPUT_ON_FAILURE=1 \
|
||||
make -C build/release -j "$(nproc)" test
|
||||
|
||||
# Specific test by name (supports regex via ctest -R)
|
||||
# Specific test by name (supports regex via ctest -R; double-quote special chars like |)
|
||||
CTEST_PARALLEL_LEVEL="$(nproc)" CTEST_OUTPUT_ON_FAILURE=1 \
|
||||
make -C build/release -j "$(nproc)" test ARGS='-R grind_ematch'
|
||||
make -C build/release -j "$(nproc)" test ARGS="-R 'grind_ematch'"
|
||||
|
||||
# Multiple tests matching a pattern
|
||||
CTEST_PARALLEL_LEVEL="$(nproc)" CTEST_OUTPUT_ON_FAILURE=1 \
|
||||
make -C build/release -j "$(nproc)" test ARGS="-R 'treemap|phashmap'"
|
||||
|
||||
# Rerun only previously failed tests
|
||||
CTEST_PARALLEL_LEVEL="$(nproc)" CTEST_OUTPUT_ON_FAILURE=1 \
|
||||
make -C build/release -j "$(nproc)" test ARGS='--rerun-failed'
|
||||
|
||||
# Single test from tests/foo/bar/ (quick check during development)
|
||||
CTEST_PARALLEL_LEVEL="$(nproc)" CTEST_OUTPUT_ON_FAILURE=1 \
|
||||
make -C build/release -j "$(nproc)" test ARGS=-R testname'
|
||||
# Run a test manually without ctest (test pile: pass filename relative to the pile dir)
|
||||
tests/with_stage1_test_env.sh tests/elab_bench/run_bench.sh cbv_decide.lean
|
||||
tests/with_stage1_test_env.sh tests/elab/run_test.sh grind_indexmap.lean
|
||||
```
|
||||
|
||||
## Benchmark vs Test Problem Sizes
|
||||
|
||||
Benchmarks are also run as tests. Use the `TEST_BENCH` environment variable (unset in tests, set to `1` in benchmarks) to scale problem sizes:
|
||||
|
||||
- In `compile_bench` `.init.sh` files: check `$TEST_BENCH` and set `TEST_ARGS` accordingly
|
||||
- In `elab_bench` Lean files: use `(← IO.getEnv "TEST_BENCH") == some "1"` to switch between small (test) and large (bench) inputs
|
||||
|
||||
See `tests/README.md` for the full benchmark writing guide.
|
||||
|
||||
## Testing stage 2
|
||||
|
||||
When requested to test stage 2, build it as follows:
|
||||
@@ -43,6 +61,11 @@ make -C build/release/stage2 clean-stdlib
|
||||
```
|
||||
must be run manually before building.
|
||||
|
||||
To rebuild individual stage 2 modules without a full `make stage2`, use Lake directly:
|
||||
```
|
||||
cd build/release/stage2 && lake build Init.Prelude
|
||||
```
|
||||
|
||||
## New features
|
||||
|
||||
When asked to implement new features:
|
||||
|
||||
@@ -157,6 +157,16 @@ Note: `gh pr checks --watch` exits as soon as ALL checks complete (pass or fail)
|
||||
fail while others are still running, `--watch` will continue until everything settles, then exit
|
||||
with a non-zero code. So a background `--watch` finishing = all checks done; check which failed.
|
||||
|
||||
## Mathlib Bump Branches
|
||||
|
||||
Mathlib `bump/v4.X.0` branches live on the **fork** `leanprover-community/mathlib4-nightly-testing`,
|
||||
NOT on `leanprover-community/mathlib4`.
|
||||
|
||||
## Never Force-Update Remote Refs Without Confirmation
|
||||
|
||||
Never force-update an existing remote branch or tag via `git push --force` or the GitHub API
|
||||
without explicit user confirmation.
|
||||
|
||||
## Error Handling
|
||||
|
||||
**CRITICAL**: If something goes wrong or a command fails:
|
||||
|
||||
43
.github/workflows/build-template.yml
vendored
43
.github/workflows/build-template.yml
vendored
@@ -33,7 +33,7 @@ jobs:
|
||||
include: ${{fromJson(inputs.config)}}
|
||||
# complete all jobs
|
||||
fail-fast: false
|
||||
runs-on: ${{ endsWith(matrix.os, '-with-cache') && fromJSON(format('["{0}", "nscloud-git-mirror-1gb"]', matrix.os)) || matrix.os }}
|
||||
runs-on: ${{ endsWith(matrix.os, '-with-cache') && fromJSON(format('["{0}", "nscloud-git-mirror-5gb"]', matrix.os)) || matrix.os }}
|
||||
defaults:
|
||||
run:
|
||||
shell: ${{ matrix.shell || 'nix develop -c bash -euxo pipefail {0}' }}
|
||||
@@ -78,7 +78,7 @@ jobs:
|
||||
# (needs to be after "Install *" to use the right shell)
|
||||
- name: CI Merge Checkout
|
||||
run: |
|
||||
git fetch --depth=1 origin ${{ github.sha }}
|
||||
git fetch --depth=${{ matrix.name == 'Linux Lake (Cached)' && '10' || '1' }} origin ${{ github.sha }}
|
||||
git checkout FETCH_HEAD flake.nix flake.lock script/prepare-* tests/elab/importStructure.lean
|
||||
if: github.event_name == 'pull_request'
|
||||
# (needs to be after "Checkout" so files don't get overridden)
|
||||
@@ -125,7 +125,7 @@ jobs:
|
||||
else
|
||||
echo "TARGET_STAGE=stage1" >> $GITHUB_ENV
|
||||
fi
|
||||
- name: Build
|
||||
- name: Configure Build
|
||||
run: |
|
||||
ulimit -c unlimited # coredumps
|
||||
[ -d build ] || mkdir build
|
||||
@@ -162,7 +162,21 @@ jobs:
|
||||
fi
|
||||
# contortion to support empty OPTIONS with old macOS bash
|
||||
cmake .. --preset ${{ matrix.CMAKE_PRESET || 'release' }} -B . ${{ matrix.CMAKE_OPTIONS }} ${OPTIONS[@]+"${OPTIONS[@]}"} -DLEAN_INSTALL_PREFIX=$PWD/..
|
||||
time make $TARGET_STAGE -j$NPROC
|
||||
- name: Build Stage 0 & Configure Stage 1
|
||||
run: |
|
||||
ulimit -c unlimited # coredumps
|
||||
time make -C build stage1-configure -j$NPROC
|
||||
- name: Download Lake Cache
|
||||
if: matrix.name == 'Linux Lake (Cached)'
|
||||
run: |
|
||||
cd src
|
||||
../build/stage0/bin/lake cache get --repo=${{ github.repository }}
|
||||
timeout-minutes: 20 # prevent excessive hanging from network issues
|
||||
continue-on-error: true
|
||||
- name: Build Target Stage
|
||||
run: |
|
||||
ulimit -c unlimited # coredumps
|
||||
time make -C build $TARGET_STAGE -j$NPROC
|
||||
# Should be done as early as possible and in particular *before* "Check rebootstrap" which
|
||||
# changes the state of stage1/
|
||||
- name: Save Cache
|
||||
@@ -181,6 +195,21 @@ jobs:
|
||||
build/stage1/**/*.c
|
||||
build/stage1/**/*.c.o*' || '' }}
|
||||
key: ${{ steps.restore-cache.outputs.cache-primary-key }}
|
||||
- name: Upload Lake Cache
|
||||
# Caching on cancellation created some mysterious issues perhaps related to improper build
|
||||
# shutdown. Also, since this needs access to secrets, it cannot be run on forks.
|
||||
if: matrix.name == 'Linux Lake' && !cancelled() && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository)
|
||||
run: |
|
||||
curl --version
|
||||
cd src
|
||||
time ../build/stage0/bin/lake build -o ../build/lake-mappings.jsonl
|
||||
time ../build/stage0/bin/lake cache put ../build/lake-mappings.jsonl --repo=${{ github.repository }}
|
||||
env:
|
||||
LAKE_CACHE_KEY: ${{ secrets.LAKE_CACHE_KEY }}
|
||||
LAKE_CACHE_ARTIFACT_ENDPOINT: ${{ vars.LAKE_CACHE_ENDPOINT }}/a1
|
||||
LAKE_CACHE_REVISION_ENDPOINT: ${{ vars.LAKE_CACHE_ENDPOINT }}/r1
|
||||
timeout-minutes: 20 # prevent excessive hanging from network issues
|
||||
continue-on-error: true
|
||||
- name: Install
|
||||
run: |
|
||||
make -C build/$TARGET_STAGE install
|
||||
@@ -247,10 +276,10 @@ jobs:
|
||||
- name: Check rebootstrap
|
||||
run: |
|
||||
set -e
|
||||
# clean rebuild in case of Makefile changes/Lake does not detect uncommited stage 0
|
||||
# changes yet
|
||||
git config user.email "stage0@lean-fro.org"
|
||||
git config user.name "update-stage0"
|
||||
make -C build update-stage0
|
||||
make -C build/stage1 clean-stdlib
|
||||
git commit --allow-empty -m "chore: update-stage0"
|
||||
time make -C build -j$NPROC
|
||||
time ctest --preset ${{ matrix.CMAKE_PRESET || 'release' }} --test-dir build/stage1 -j$NPROC
|
||||
if: matrix.check-rebootstrap
|
||||
|
||||
29
.github/workflows/check-empty-pr.yml
vendored
Normal file
29
.github/workflows/check-empty-pr.yml
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
name: Check for empty PR
|
||||
|
||||
on:
|
||||
merge_group:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
check-empty-pr:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
|
||||
fetch-depth: 0
|
||||
filter: tree:0
|
||||
|
||||
- name: Check for empty diff
|
||||
run: |
|
||||
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
|
||||
base=$(git merge-base "origin/${{ github.base_ref }}" HEAD)
|
||||
else
|
||||
base=$(git rev-parse HEAD^1)
|
||||
fi
|
||||
if git diff --quiet "$base" HEAD --; then
|
||||
echo "This PR introduces no changes compared to its base branch." | tee "$GITHUB_STEP_SUMMARY"
|
||||
echo "It may be a duplicate of an already-merged PR." | tee -a "$GITHUB_STEP_SUMMARY"
|
||||
exit 1
|
||||
fi
|
||||
shell: bash
|
||||
59
.github/workflows/ci.yml
vendored
59
.github/workflows/ci.yml
vendored
@@ -61,20 +61,35 @@ jobs:
|
||||
git remote add nightly https://foo:'${{ secrets.PUSH_NIGHTLY_TOKEN }}'@github.com/${{ github.repository_owner }}/lean4-nightly.git
|
||||
git fetch nightly --tags
|
||||
if [[ '${{ github.event_name }}' == 'workflow_dispatch' ]]; then
|
||||
# Manual re-release: create a revision of the most recent nightly
|
||||
BASE_NIGHTLY=$(git tag -l 'nightly-*' | sort -rV | head -1)
|
||||
# Strip any existing -revK suffix to get the base date tag
|
||||
BASE_NIGHTLY="${BASE_NIGHTLY%%-rev*}"
|
||||
REV=1
|
||||
while git rev-parse "refs/tags/${BASE_NIGHTLY}-rev${REV}" >/dev/null 2>&1; do
|
||||
REV=$((REV + 1))
|
||||
done
|
||||
LEAN_VERSION_STRING="${BASE_NIGHTLY}-rev${REV}"
|
||||
# Manual re-release: retry today's nightly, or create a revision if it already exists
|
||||
TODAY_NIGHTLY="nightly-$(date -u +%F)"
|
||||
if git rev-parse "refs/tags/${TODAY_NIGHTLY}" >/dev/null 2>&1; then
|
||||
# Today's nightly already exists, create a revision
|
||||
REV=1
|
||||
while git rev-parse "refs/tags/${TODAY_NIGHTLY}-rev${REV}" >/dev/null 2>&1; do
|
||||
REV=$((REV + 1))
|
||||
done
|
||||
LEAN_VERSION_STRING="${TODAY_NIGHTLY}-rev${REV}"
|
||||
else
|
||||
# Today's nightly doesn't exist yet (e.g. scheduled run failed), create it
|
||||
LEAN_VERSION_STRING="${TODAY_NIGHTLY}"
|
||||
fi
|
||||
echo "nightly=$LEAN_VERSION_STRING" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
# Scheduled: do nothing if commit already has a different tag
|
||||
# Scheduled: do nothing if commit already has a different tag (e.g. a release tag)
|
||||
LEAN_VERSION_STRING="nightly-$(date -u +%F)"
|
||||
if [[ "$(git name-rev --name-only --tags --no-undefined HEAD 2> /dev/null || echo "$LEAN_VERSION_STRING")" == "$LEAN_VERSION_STRING" ]]; then
|
||||
HEAD_TAG="$(git name-rev --name-only --tags --no-undefined HEAD 2> /dev/null || true)"
|
||||
if [[ -n "$HEAD_TAG" && "$HEAD_TAG" != "$LEAN_VERSION_STRING" ]]; then
|
||||
echo "HEAD already tagged as ${HEAD_TAG}, skipping nightly"
|
||||
elif git rev-parse "refs/tags/${LEAN_VERSION_STRING}" >/dev/null 2>&1; then
|
||||
# Today's nightly already exists (e.g. from a manual release), create a revision
|
||||
REV=1
|
||||
while git rev-parse "refs/tags/${LEAN_VERSION_STRING}-rev${REV}" >/dev/null 2>&1; do
|
||||
REV=$((REV + 1))
|
||||
done
|
||||
LEAN_VERSION_STRING="${LEAN_VERSION_STRING}-rev${REV}"
|
||||
echo "nightly=$LEAN_VERSION_STRING" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "nightly=$LEAN_VERSION_STRING" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
fi
|
||||
@@ -128,7 +143,7 @@ jobs:
|
||||
CMAKE_MAJOR=$(grep -E "^set\(LEAN_VERSION_MAJOR " src/CMakeLists.txt | grep -oE '[0-9]+')
|
||||
CMAKE_MINOR=$(grep -E "^set\(LEAN_VERSION_MINOR " src/CMakeLists.txt | grep -oE '[0-9]+')
|
||||
CMAKE_PATCH=$(grep -E "^set\(LEAN_VERSION_PATCH " src/CMakeLists.txt | grep -oE '[0-9]+')
|
||||
CMAKE_IS_RELEASE=$(grep -m 1 -E "^set\(LEAN_VERSION_IS_RELEASE " src/CMakeLists.txt | sed -nE 's/^set\(LEAN_VERSION_IS_RELEASE ([0-9]+)\).*/\1/p')
|
||||
CMAKE_IS_RELEASE=$(grep -m 1 -E "^set\(LEAN_VERSION_IS_RELEASE " src/CMakeLists.txt | grep -oE '[0-9]+' | head -1)
|
||||
|
||||
# Expected values from tag parsing
|
||||
TAG_MAJOR="${{ steps.set-release.outputs.LEAN_VERSION_MAJOR }}"
|
||||
@@ -240,7 +255,7 @@ jobs:
|
||||
// portable release build: use channel with older glibc (2.26)
|
||||
"name": "Linux release",
|
||||
// usually not a bottleneck so make exclusive to `fast-ci`
|
||||
"os": large && fast ? "nscloud-ubuntu-22.04-amd64-8x16-with-cache" : "ubuntu-latest",
|
||||
"os": large && fast ? "nscloud-ubuntu-24.04-amd64-8x16-with-cache" : "ubuntu-latest",
|
||||
"release": true,
|
||||
// Special handling for release jobs. We want:
|
||||
// 1. To run it in PRs so developers get PR toolchains (so secondary without tests is sufficient)
|
||||
@@ -261,7 +276,7 @@ jobs:
|
||||
},
|
||||
{
|
||||
"name": "Linux Lake",
|
||||
"os": large ? "nscloud-ubuntu-22.04-amd64-8x16-with-cache" : "ubuntu-latest",
|
||||
"os": large ? "nscloud-ubuntu-24.04-amd64-8x16-with-cache" : "ubuntu-latest",
|
||||
"enabled": true,
|
||||
"check-rebootstrap": level >= 1,
|
||||
"check-stage3": level >= 2,
|
||||
@@ -269,7 +284,19 @@ jobs:
|
||||
// NOTE: `test-bench` currently seems to be broken on `ubuntu-latest`
|
||||
"test-bench": large && level >= 2,
|
||||
// We are not warning-free yet on all platforms, start here
|
||||
"CMAKE_OPTIONS": "-DLEAN_EXTRA_CXX_FLAGS=-Werror",
|
||||
"CMAKE_OPTIONS": "-DLEAN_EXTRA_CXX_FLAGS=-Werror -DUSE_LAKE_CACHE=ON",
|
||||
},
|
||||
{
|
||||
"name": "Linux Lake (Cached)",
|
||||
"os": large ? "nscloud-ubuntu-24.04-amd64-8x16-with-cache" : "ubuntu-latest",
|
||||
"enabled": true,
|
||||
"check-rebootstrap": level >= 1,
|
||||
"check-stage3": level >= 2,
|
||||
"test": true,
|
||||
"secondary": true,
|
||||
// NOTE: `test-bench` currently seems to be broken on `ubuntu-latest`
|
||||
"test-bench": large && level >= 2,
|
||||
"CMAKE_OPTIONS": "-DLEAN_EXTRA_CXX_FLAGS=-Werror -DUSE_LAKE_CACHE=ON",
|
||||
},
|
||||
{
|
||||
"name": "Linux Reldebug",
|
||||
@@ -283,7 +310,7 @@ jobs:
|
||||
{
|
||||
"name": "Linux fsanitize",
|
||||
// Always run on large if available, more reliable regarding timeouts
|
||||
"os": large ? "nscloud-ubuntu-22.04-amd64-16x32-with-cache" : "ubuntu-latest",
|
||||
"os": large ? "nscloud-ubuntu-24.04-amd64-16x32-with-cache" : "ubuntu-latest",
|
||||
"enabled": level >= 2,
|
||||
// do not fail nightlies on this for now
|
||||
"secondary": level <= 2,
|
||||
|
||||
@@ -6,6 +6,6 @@ vscode:
|
||||
- leanprover.lean4
|
||||
|
||||
tasks:
|
||||
- name: Release build
|
||||
init: cmake --preset release
|
||||
- name: Build
|
||||
init: cmake --preset dev
|
||||
command: make -C build/release -j$(nproc || sysctl -n hw.logicalcpu)
|
||||
|
||||
9
.vscode/tasks.json
vendored
9
.vscode/tasks.json
vendored
@@ -11,6 +11,15 @@
|
||||
"isDefault": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "build stage2",
|
||||
"type": "shell",
|
||||
"command": "make -C build/release stage2 -j$(nproc 2>/dev/null || sysctl -n hw.logicalcpu 2>/dev/null || echo 4)",
|
||||
"problemMatcher": [],
|
||||
"group": {
|
||||
"kind": "build"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "build-old",
|
||||
"type": "shell",
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.21)
|
||||
include(ExternalProject)
|
||||
include(FetchContent)
|
||||
|
||||
if(NOT CMAKE_GENERATOR MATCHES "Makefiles")
|
||||
message(FATAL_ERROR "Only makefile generators are supported")
|
||||
@@ -34,7 +36,6 @@ foreach(var ${vars})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
include(ExternalProject)
|
||||
project(LEAN CXX C)
|
||||
|
||||
if(NOT (DEFINED STAGE0_CMAKE_EXECUTABLE_SUFFIX))
|
||||
@@ -114,21 +115,22 @@ if(NOT CMAKE_SYSTEM_NAME MATCHES "Emscripten")
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
list(APPEND STAGE0_ARGS -DLEANTAR=${LEANTAR})
|
||||
list(APPEND CL_ARGS -DCADICAL=${CADICAL} -DLEANTAR=${LEANTAR})
|
||||
endif()
|
||||
|
||||
if(USE_MIMALLOC)
|
||||
ExternalProject_Add(
|
||||
FetchContent_Declare(
|
||||
mimalloc
|
||||
PREFIX mimalloc
|
||||
GIT_REPOSITORY https://github.com/microsoft/mimalloc
|
||||
GIT_TAG v2.2.3
|
||||
# just download, we compile it as part of each stage as it is small
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
# Unnecessarily deep directory structure, but it saves us from a complicated
|
||||
# stage0 update for now. If we ever update the other dependencies like
|
||||
# cadical, it might be worth reorganizing the directory structure.
|
||||
SOURCE_DIR
|
||||
"${CMAKE_BINARY_DIR}/mimalloc/src/mimalloc"
|
||||
)
|
||||
list(APPEND EXTRA_DEPENDS mimalloc)
|
||||
FetchContent_MakeAvailable(mimalloc)
|
||||
endif()
|
||||
|
||||
if(NOT STAGE1_PREV_STAGE)
|
||||
|
||||
@@ -8,16 +8,26 @@
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "release",
|
||||
"displayName": "Default development optimized build config",
|
||||
"displayName": "Release build config",
|
||||
"generator": "Unix Makefiles",
|
||||
"binaryDir": "${sourceDir}/build/release"
|
||||
},
|
||||
{
|
||||
"name": "dev",
|
||||
"displayName": "Default development optimized build config",
|
||||
"cacheVariables": {
|
||||
"STRIP_BINARIES": "OFF"
|
||||
},
|
||||
"generator": "Unix Makefiles",
|
||||
"binaryDir": "${sourceDir}/build/dev"
|
||||
},
|
||||
{
|
||||
"name": "debug",
|
||||
"displayName": "Debug build config",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Debug",
|
||||
"LEAN_EXTRA_CXX_FLAGS": "-DLEAN_DEFAULT_THREAD_STACK_SIZE=16*1024*1024",
|
||||
"CMAKE_BUILD_TYPE": "Debug"
|
||||
"STRIP_BINARIES": "OFF"
|
||||
},
|
||||
"generator": "Unix Makefiles",
|
||||
"binaryDir": "${sourceDir}/build/debug"
|
||||
@@ -26,7 +36,8 @@
|
||||
"name": "reldebug",
|
||||
"displayName": "Release with assertions enabled",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "RelWithAssert"
|
||||
"CMAKE_BUILD_TYPE": "RelWithAssert",
|
||||
"STRIP_BINARIES": "OFF"
|
||||
},
|
||||
"generator": "Unix Makefiles",
|
||||
"binaryDir": "${sourceDir}/build/reldebug"
|
||||
@@ -38,6 +49,7 @@
|
||||
"LEAN_EXTRA_CXX_FLAGS": "-fsanitize=address,undefined -DLEAN_DEFAULT_THREAD_STACK_SIZE=16*1024*1024",
|
||||
"LEANC_EXTRA_CC_FLAGS": "-fsanitize=address,undefined",
|
||||
"LEAN_EXTRA_LINKER_FLAGS": "-fsanitize=address,undefined -fsanitize-link-c++-runtime",
|
||||
"STRIP_BINARIES": "OFF",
|
||||
"SMALL_ALLOCATOR": "OFF",
|
||||
"USE_MIMALLOC": "OFF",
|
||||
"BSYMBOLIC": "OFF",
|
||||
@@ -58,6 +70,10 @@
|
||||
"name": "release",
|
||||
"configurePreset": "release"
|
||||
},
|
||||
{
|
||||
"name": "dev",
|
||||
"configurePreset": "dev"
|
||||
},
|
||||
{
|
||||
"name": "debug",
|
||||
"configurePreset": "debug"
|
||||
@@ -81,6 +97,11 @@
|
||||
"configurePreset": "release",
|
||||
"output": {"outputOnFailure": true, "shortProgress": true}
|
||||
},
|
||||
{
|
||||
"name": "dev",
|
||||
"configurePreset": "dev",
|
||||
"output": {"outputOnFailure": true, "shortProgress": true}
|
||||
},
|
||||
{
|
||||
"name": "debug",
|
||||
"configurePreset": "debug",
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
30
|
||||
interp.lean:146:4: warning: declaration uses `sorry`
|
||||
interp.lean:146:0: warning: declaration uses `sorry`
|
||||
3628800
|
||||
|
||||
@@ -30,6 +30,9 @@ cd lean4
|
||||
cmake --preset release
|
||||
make -C build/release -j$(nproc || sysctl -n hw.logicalcpu)
|
||||
```
|
||||
|
||||
For development, `cmake --preset dev` is recommended instead.
|
||||
|
||||
You can replace `$(nproc || sysctl -n hw.logicalcpu)` with the desired parallelism amount.
|
||||
|
||||
The above commands will compile the Lean library and binaries into the
|
||||
|
||||
@@ -236,7 +236,7 @@ def parse_version(version_str):
|
||||
def is_version_gte(version1, version2):
|
||||
"""Check if version1 >= version2, including proper handling of release candidates."""
|
||||
# Check if version1 is a nightly toolchain
|
||||
if version1.startswith("leanprover/lean4:nightly-"):
|
||||
if version1.startswith("leanprover/lean4:nightly-") or version1.startswith("leanprover/lean4-nightly:"):
|
||||
return False
|
||||
return parse_version(version1) >= parse_version(version2)
|
||||
|
||||
@@ -311,16 +311,16 @@ def check_cmake_version(repo_url, branch, version_major, version_minor, github_t
|
||||
print(f" ❌ Could not retrieve {cmake_file_path} from {branch}")
|
||||
return False
|
||||
|
||||
expected_lines = [
|
||||
f"set(LEAN_VERSION_MAJOR {version_major})",
|
||||
f"set(LEAN_VERSION_MINOR {version_minor})",
|
||||
f"set(LEAN_VERSION_PATCH 0)",
|
||||
f"set(LEAN_VERSION_IS_RELEASE 1)"
|
||||
expected_patterns = [
|
||||
(f"LEAN_VERSION_MAJOR", rf"^set\(LEAN_VERSION_MAJOR\s+{version_major}[\s)]", f"set(LEAN_VERSION_MAJOR {version_major} ...)"),
|
||||
(f"LEAN_VERSION_MINOR", rf"^set\(LEAN_VERSION_MINOR\s+{version_minor}[\s)]", f"set(LEAN_VERSION_MINOR {version_minor} ...)"),
|
||||
(f"LEAN_VERSION_PATCH", rf"^set\(LEAN_VERSION_PATCH\s+0[\s)]", f"set(LEAN_VERSION_PATCH 0 ...)"),
|
||||
(f"LEAN_VERSION_IS_RELEASE", rf"^set\(LEAN_VERSION_IS_RELEASE\s+1[\s)]", f"set(LEAN_VERSION_IS_RELEASE 1 ...)"),
|
||||
]
|
||||
|
||||
for line in expected_lines:
|
||||
if not any(l.strip().startswith(line) for l in content.splitlines()):
|
||||
print(f" ❌ Missing or incorrect line in {cmake_file_path}: {line}")
|
||||
for name, pattern, display in expected_patterns:
|
||||
if not any(re.match(pattern, l.strip()) for l in content.splitlines()):
|
||||
print(f" ❌ Missing or incorrect line in {cmake_file_path}: {display}")
|
||||
return False
|
||||
|
||||
print(f" ✅ CMake version settings are correct in {cmake_file_path}")
|
||||
@@ -343,11 +343,11 @@ def check_stage0_version(repo_url, branch, version_major, version_minor, github_
|
||||
for line in content.splitlines():
|
||||
stripped = line.strip()
|
||||
if stripped.startswith("set(LEAN_VERSION_MAJOR "):
|
||||
actual = stripped.split()[-1].rstrip(")")
|
||||
actual = stripped.split()[1].rstrip(")")
|
||||
if actual != str(version_major):
|
||||
errors.append(f"LEAN_VERSION_MAJOR: expected {version_major}, found {actual}")
|
||||
elif stripped.startswith("set(LEAN_VERSION_MINOR "):
|
||||
actual = stripped.split()[-1].rstrip(")")
|
||||
actual = stripped.split()[1].rstrip(")")
|
||||
if actual != str(version_minor):
|
||||
errors.append(f"LEAN_VERSION_MINOR: expected {version_minor}, found {actual}")
|
||||
|
||||
|
||||
@@ -14,13 +14,6 @@ repositories:
|
||||
bump-branch: true
|
||||
dependencies: []
|
||||
|
||||
- name: lean4checker
|
||||
url: https://github.com/leanprover/lean4checker
|
||||
toolchain-tag: true
|
||||
stable-branch: true
|
||||
branch: master
|
||||
dependencies: []
|
||||
|
||||
- name: quote4
|
||||
url: https://github.com/leanprover-community/quote4
|
||||
toolchain-tag: true
|
||||
@@ -35,6 +28,14 @@ repositories:
|
||||
branch: main
|
||||
dependencies: []
|
||||
|
||||
- name: leansqlite
|
||||
url: https://github.com/leanprover/leansqlite
|
||||
toolchain-tag: true
|
||||
stable-branch: false
|
||||
branch: main
|
||||
dependencies:
|
||||
- plausible
|
||||
|
||||
- name: verso
|
||||
url: https://github.com/leanprover/verso
|
||||
toolchain-tag: true
|
||||
@@ -107,7 +108,7 @@ repositories:
|
||||
toolchain-tag: true
|
||||
stable-branch: false
|
||||
branch: main
|
||||
dependencies: [lean4-cli, BibtexQuery, mathlib4]
|
||||
dependencies: [lean4-cli, BibtexQuery, mathlib4, leansqlite]
|
||||
|
||||
- name: cslib
|
||||
url: https://github.com/leanprover/cslib
|
||||
|
||||
@@ -8,7 +8,7 @@ endif()
|
||||
include(ExternalProject)
|
||||
project(LEAN CXX C)
|
||||
set(LEAN_VERSION_MAJOR 4 CACHE STRING "")
|
||||
set(LEAN_VERSION_MINOR 30 CACHE STRING "")
|
||||
set(LEAN_VERSION_MINOR 31 CACHE STRING "")
|
||||
set(LEAN_VERSION_PATCH 0 CACHE STRING "")
|
||||
set(LEAN_VERSION_IS_RELEASE 0 CACHE STRING "") # This number is 1 in the release revision, and 0 otherwise.
|
||||
set(LEAN_SPECIAL_VERSION_DESC "" CACHE STRING "Additional version description like 'nightly-2018-03-11'")
|
||||
@@ -80,6 +80,7 @@ option(CCACHE "use ccache" ON)
|
||||
option(SPLIT_STACK "SPLIT_STACK" OFF)
|
||||
# When OFF we disable LLVM support
|
||||
option(LLVM "LLVM" OFF)
|
||||
option(STRIP_BINARIES "Strip produced binaries" ON)
|
||||
|
||||
# When ON we include githash in the version string
|
||||
option(USE_GITHASH "GIT_HASH" ON)
|
||||
@@ -614,6 +615,38 @@ else()
|
||||
OUTPUT_VARIABLE GIT_SHA1
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
# Fallback for jj workspaces where git cannot find .git directly.
|
||||
# Use `jj git root` to find the backing git repo, then `jj log` to
|
||||
# resolve the current workspace's commit (git HEAD points to the root
|
||||
# workspace, not the current one).
|
||||
if("${GIT_SHA1}" STREQUAL "")
|
||||
find_program(JJ_EXECUTABLE jj)
|
||||
if(JJ_EXECUTABLE)
|
||||
execute_process(
|
||||
COMMAND "${JJ_EXECUTABLE}" git root
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
OUTPUT_VARIABLE _jj_git_dir
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
ERROR_QUIET
|
||||
RESULT_VARIABLE _jj_git_root_result
|
||||
)
|
||||
execute_process(
|
||||
COMMAND "${JJ_EXECUTABLE}" log -r @ --no-graph -T "commit_id"
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
OUTPUT_VARIABLE _jj_commit
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
ERROR_QUIET
|
||||
RESULT_VARIABLE _jj_rev_result
|
||||
)
|
||||
if(_jj_git_root_result EQUAL 0 AND _jj_rev_result EQUAL 0)
|
||||
execute_process(
|
||||
COMMAND git --git-dir "${_jj_git_dir}" ls-tree "${_jj_commit}" stage0 --object-only
|
||||
OUTPUT_VARIABLE GIT_SHA1
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
message(STATUS "stage0 sha1: ${GIT_SHA1}")
|
||||
# Now that we've prepared the information for the next stage, we can forget that we will use
|
||||
# Lake in the future as we won't use it in this stage
|
||||
@@ -762,7 +795,7 @@ if(STAGE GREATER 0 AND CADICAL AND INSTALL_CADICAL)
|
||||
add_dependencies(leancpp copy-cadical)
|
||||
endif()
|
||||
|
||||
if(STAGE GREATER 0 AND LEANTAR AND INSTALL_LEANTAR)
|
||||
if(LEANTAR AND INSTALL_LEANTAR)
|
||||
add_custom_target(
|
||||
copy-leantar
|
||||
COMMAND cmake -E copy_if_different "${LEANTAR}" "${CMAKE_BINARY_DIR}/bin/leantar${CMAKE_EXECUTABLE_SUFFIX}"
|
||||
@@ -797,7 +830,14 @@ if(LLVM AND STAGE GREATER 0)
|
||||
set(EXTRA_LEANMAKE_OPTS "LLVM=1")
|
||||
endif()
|
||||
|
||||
set(STDLIBS Init Std Lean Leanc)
|
||||
set(
|
||||
STDLIBS
|
||||
Init
|
||||
Std
|
||||
Lean
|
||||
Leanc
|
||||
LeanIR
|
||||
)
|
||||
if(NOT CMAKE_SYSTEM_NAME MATCHES "Emscripten")
|
||||
list(APPEND STDLIBS Lake LeanChecker)
|
||||
endif()
|
||||
@@ -904,9 +944,13 @@ if(PREV_STAGE)
|
||||
add_custom_target(update-stage0-commit COMMAND git commit -m "chore: update stage0" DEPENDS update-stage0)
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_SYSTEM_NAME MATCHES "Emscripten")
|
||||
add_custom_target(leanir ALL DEPENDS leanshared COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make leanir VERBATIM)
|
||||
endif()
|
||||
|
||||
# use Bash version for building, use Lean version in bin/ for tests & distribution
|
||||
configure_file("${LEAN_SOURCE_DIR}/bin/leanc.in" "${CMAKE_BINARY_DIR}/leanc.sh" @ONLY)
|
||||
if(STAGE GREATER 0 AND EXISTS "${LEAN_SOURCE_DIR}/Leanc.lean" AND NOT CMAKE_SYSTEM_NAME MATCHES "Emscripten")
|
||||
if(STAGE GREATER 0 AND NOT CMAKE_SYSTEM_NAME MATCHES "Emscripten")
|
||||
configure_file("${LEAN_SOURCE_DIR}/Leanc.lean" "${CMAKE_BINARY_DIR}/leanc/Leanc.lean" @ONLY)
|
||||
add_custom_target(
|
||||
leanc
|
||||
@@ -926,7 +970,7 @@ if(STAGE GREATER 0 AND CADICAL AND INSTALL_CADICAL)
|
||||
install(PROGRAMS "${CADICAL}" DESTINATION bin)
|
||||
endif()
|
||||
|
||||
if(STAGE GREATER 0 AND LEANTAR AND INSTALL_LEANTAR)
|
||||
if(LEANTAR AND INSTALL_LEANTAR)
|
||||
install(PROGRAMS "${LEANTAR}" DESTINATION bin)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ set_option linter.unusedVariables false in -- `s` unused
|
||||
Use a monadic action that may throw an exception by providing explicit success and failure
|
||||
continuations.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
@[always_inline, inline, expose]
|
||||
def runK {ε α : Type u} (x : ExceptCpsT ε m α) (s : ε) (ok : α → m β) (error : ε → m β) : m β :=
|
||||
x _ ok error
|
||||
|
||||
@@ -83,6 +83,8 @@ of `True`.
|
||||
-/
|
||||
instance : MonadAttach (ExceptCpsT ε m) := .trivial
|
||||
|
||||
@[simp] theorem throw_bind [Monad m] (e : ε) (f : α → ExceptCpsT ε m β) : (throw e >>= f : ExceptCpsT ε m β) = throw e := rfl
|
||||
|
||||
@[simp] theorem run_pure [Monad m] : run (pure x : ExceptCpsT ε m α) = pure (Except.ok x) := rfl
|
||||
|
||||
@[simp] theorem run_lift {α ε : Type u} [Monad m] (x : m α) : run (ExceptCpsT.lift x : ExceptCpsT ε m α) = (x >>= fun a => pure (Except.ok a) : m (Except ε α)) := rfl
|
||||
@@ -91,7 +93,20 @@ instance : MonadAttach (ExceptCpsT ε m) := .trivial
|
||||
|
||||
@[simp] theorem run_bind_lift [Monad m] (x : m α) (f : α → ExceptCpsT ε m β) : run (ExceptCpsT.lift x >>= f : ExceptCpsT ε m β) = x >>= fun a => run (f a) := rfl
|
||||
|
||||
@[simp] theorem run_bind_throw [Monad m] (e : ε) (f : α → ExceptCpsT ε m β) : run (throw e >>= f : ExceptCpsT ε m β) = run (throw e) := rfl
|
||||
@[deprecated throw_bind (since := "2026-03-13")]
|
||||
theorem run_bind_throw [Monad m] (e : ε) (f : α → ExceptCpsT ε m β) : run (throw e >>= f : ExceptCpsT ε m β) = run (throw e) := rfl
|
||||
|
||||
@[simp] theorem runK_pure :
|
||||
runK (pure x : ExceptCpsT ε m α) s ok error = ok x := rfl
|
||||
|
||||
@[simp] theorem runK_lift {α ε : Type u} [Monad m] (x : m α) (s : ε) (ok : α → m β) (error : ε → m β) :
|
||||
runK (ExceptCpsT.lift x : ExceptCpsT ε m α) s ok error = x >>= ok := rfl
|
||||
|
||||
@[simp] theorem runK_throw [Monad m] :
|
||||
runK (throw e : ExceptCpsT ε m β) s ok error = error e := rfl
|
||||
|
||||
@[simp] theorem runK_bind_lift [Monad m] (x : m α) (f : α → ExceptCpsT ε m β) :
|
||||
runK (ExceptCpsT.lift x >>= f : ExceptCpsT ε m β) s ok error = x >>= fun a => runK (f a) s ok error := rfl
|
||||
|
||||
@[simp] theorem runCatch_pure [Monad m] : runCatch (pure x : ExceptCpsT α m α) = pure x := rfl
|
||||
|
||||
@@ -102,6 +117,7 @@ instance : MonadAttach (ExceptCpsT ε m) := .trivial
|
||||
|
||||
@[simp] theorem runCatch_bind_lift [Monad m] (x : m α) (f : α → ExceptCpsT β m β) : runCatch (ExceptCpsT.lift x >>= f : ExceptCpsT β m β) = x >>= fun a => runCatch (f a) := rfl
|
||||
|
||||
@[simp] theorem runCatch_bind_throw [Monad m] (e : β) (f : α → ExceptCpsT β m β) : runCatch (throw e >>= f : ExceptCpsT β m β) = pure e := rfl
|
||||
@[deprecated throw_bind (since := "2026-03-13")]
|
||||
theorem runCatch_bind_throw [Monad m] (e : β) (f : α → ExceptCpsT β m β) : runCatch (throw e >>= f : ExceptCpsT β m β) = pure e := rfl
|
||||
|
||||
end ExceptCpsT
|
||||
|
||||
@@ -72,11 +72,11 @@ public instance [Monad m] [LawfulMonad m] [MonadAttach m] [LawfulMonadAttach m]
|
||||
|
||||
public instance [Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawfulMonadAttach m] :
|
||||
WeaklyLawfulMonadAttach (StateRefT' ω σ m) :=
|
||||
inferInstanceAs (WeaklyLawfulMonadAttach (ReaderT _ _))
|
||||
inferInstanceAs (WeaklyLawfulMonadAttach (ReaderT (ST.Ref ω σ) m))
|
||||
|
||||
public instance [Monad m] [MonadAttach m] [LawfulMonad m] [LawfulMonadAttach m] :
|
||||
LawfulMonadAttach (StateRefT' ω σ m) :=
|
||||
inferInstanceAs (LawfulMonadAttach (ReaderT _ _))
|
||||
inferInstanceAs (LawfulMonadAttach (ReaderT (ST.Ref ω σ) m))
|
||||
|
||||
section
|
||||
|
||||
|
||||
@@ -103,11 +103,11 @@ namespace StateRefT'
|
||||
instance {ω σ : Type} {m : Type → Type} [Monad m] : LawfulMonadLift m (StateRefT' ω σ m) where
|
||||
monadLift_pure _ := by
|
||||
simp only [MonadLift.monadLift, pure]
|
||||
unfold StateRefT'.lift ReaderT.pure
|
||||
unfold StateRefT'.lift instMonad._aux_5 ReaderT.pure
|
||||
simp only
|
||||
monadLift_bind _ _ := by
|
||||
simp only [MonadLift.monadLift, bind]
|
||||
unfold StateRefT'.lift ReaderT.bind
|
||||
unfold StateRefT'.lift instMonad._aux_13 ReaderT.bind
|
||||
simp only
|
||||
|
||||
end StateRefT'
|
||||
|
||||
@@ -172,6 +172,8 @@ instance thunkCoe : CoeTail α (Thunk α) where
|
||||
-- Since coercions are expanded eagerly, `a` is evaluated lazily.
|
||||
coe a := ⟨fun _ => a⟩
|
||||
|
||||
instance [Inhabited α] : Inhabited (Thunk α) := ⟨.pure default⟩
|
||||
|
||||
/-- A variation on `Eq.ndrec` with the equality argument first. -/
|
||||
abbrev Eq.ndrecOn.{u1, u2} {α : Sort u2} {a : α} {motive : α → Sort u1} {b : α} (h : a = b) (m : motive a) : motive b :=
|
||||
Eq.ndrec m h
|
||||
|
||||
@@ -113,7 +113,7 @@ public theorem _root_.List.min?_toArray [Min α] {l : List α} :
|
||||
· simp [List.min_toArray, List.min_eq_get_min?, - List.get_min?]
|
||||
· simp_all
|
||||
|
||||
@[simp, grind =]
|
||||
@[simp, grind =, cbv_eval ←]
|
||||
public theorem min?_toList [Min α] {xs : Array α} :
|
||||
xs.toList.min? = xs.min? := by
|
||||
cases xs; simp
|
||||
@@ -153,7 +153,7 @@ public theorem _root_.List.max?_toArray [Max α] {l : List α} :
|
||||
· simp [List.max_toArray, List.max_eq_get_max?, - List.get_max?]
|
||||
· simp_all
|
||||
|
||||
@[simp, grind =]
|
||||
@[simp, grind =, cbv_eval ←]
|
||||
public theorem max?_toList [Max α] {xs : Array α} :
|
||||
xs.toList.max? = xs.max? := by
|
||||
cases xs; simp
|
||||
|
||||
@@ -134,6 +134,7 @@ theorem Array.toList_mergeSort {xs : Array α} {le : α → α → Bool} :
|
||||
(xs.mergeSort le).toList = xs.toList.mergeSort le := by
|
||||
rw [Array.mergeSort, Subarray.toList_mergeSort, Array.toList_mkSlice_rii]
|
||||
|
||||
@[cbv_eval]
|
||||
theorem Array.mergeSort_eq_toArray_mergeSort_toList {xs : Array α} {le : α → α → Bool} :
|
||||
xs.mergeSort le = (xs.toList.mergeSort le).toArray := by
|
||||
simp [← toList_mergeSort]
|
||||
|
||||
@@ -66,3 +66,8 @@ theorem BEq.neq_of_beq_of_neq [BEq α] [PartialEquivBEq α] {a b c : α} :
|
||||
instance (priority := low) [BEq α] [LawfulBEq α] : EquivBEq α where
|
||||
symm h := beq_iff_eq.2 <| Eq.symm <| beq_iff_eq.1 h
|
||||
trans hab hbc := beq_iff_eq.2 <| (beq_iff_eq.1 hab).trans <| beq_iff_eq.1 hbc
|
||||
|
||||
theorem equivBEq_of_iff_apply_eq [BEq α] (f : α → β) (hf : ∀ a b, a == b ↔ f a = f b) : EquivBEq α where
|
||||
rfl := by simp [hf]
|
||||
symm := by simp [hf, eq_comm]
|
||||
trans hab hbc := (hf _ _).2 (Eq.trans ((hf _ _).1 hab) ((hf _ _).1 hbc))
|
||||
|
||||
@@ -20,12 +20,20 @@ universe u
|
||||
|
||||
namespace ByteArray
|
||||
|
||||
deriving instance BEq for ByteArray
|
||||
@[extern "lean_sarray_dec_eq"]
|
||||
def beq (lhs rhs : @& ByteArray) : Bool :=
|
||||
lhs.data == rhs.data
|
||||
|
||||
instance : BEq ByteArray where
|
||||
beq := beq
|
||||
|
||||
attribute [ext] ByteArray
|
||||
|
||||
instance : DecidableEq ByteArray :=
|
||||
fun _ _ => decidable_of_decidable_of_iff ByteArray.ext_iff.symm
|
||||
@[extern "lean_sarray_dec_eq"]
|
||||
def decEq (lhs rhs : @& ByteArray) : Decidable (lhs = rhs) :=
|
||||
decidable_of_decidable_of_iff ByteArray.ext_iff.symm
|
||||
|
||||
instance : DecidableEq ByteArray := decEq
|
||||
|
||||
instance : Inhabited ByteArray where
|
||||
default := empty
|
||||
|
||||
@@ -98,4 +98,8 @@ theorem toNat_inj {c d : Char} : c.toNat = d.toNat ↔ c = d := by
|
||||
theorem isDigit_iff_toNat {c : Char} : c.isDigit ↔ '0'.toNat ≤ c.toNat ∧ c.toNat ≤ '9'.toNat := by
|
||||
simp [isDigit, UInt32.le_iff_toNat_le]
|
||||
|
||||
@[simp]
|
||||
theorem toNat_mk {val : UInt32} {h} : (Char.mk val h).toNat = val.toNat := by
|
||||
simp [← toNat_val]
|
||||
|
||||
end Char
|
||||
|
||||
@@ -527,6 +527,14 @@ theorem castLE_of_eq {m n : Nat} (h : m = n) {h' : m ≤ n} : castLE h' = Fin.ca
|
||||
|
||||
@[simp, grind =] theorem val_castAdd (m : Nat) (i : Fin n) : (castAdd m i : Nat) = i := rfl
|
||||
|
||||
/-
|
||||
**Note**
|
||||
The current pattern inference heuristic includes the implicit term `n + m` as pattern of the pattern,
|
||||
but arithmetic is problematic in patterns because it is an interpreted symbol. For example,
|
||||
we will fail to match `@val n (castNat 0 i)`. Thus, we mark the implicit subterm with `no_index`
|
||||
-/
|
||||
grind_pattern val_castAdd => @val (no_index _) (castAdd m i)
|
||||
|
||||
@[deprecated val_castAdd (since := "2025-11-21")]
|
||||
theorem coe_castAdd (m : Nat) (i : Fin n) : (castAdd m i : Nat) = i := rfl
|
||||
|
||||
@@ -637,7 +645,15 @@ 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
|
||||
@[simp] theorem val_addNat (m : Nat) (i : Fin n) : (addNat i m : Nat) = i + m := rfl
|
||||
|
||||
/-
|
||||
**Note**
|
||||
The current pattern inference heuristic includes the implicit term `n + m` as pattern of the pattern,
|
||||
but arithmetic is problematic in patterns because it is an interpreted symbol. For example,
|
||||
we will fail to match `@val n (addNat i 0)`. Thus, we mark the implicit subterm with `no_index`
|
||||
-/
|
||||
grind_pattern val_addNat => @val (no_index _) (addNat i m)
|
||||
|
||||
@[deprecated val_addNat (since := "2025-11-21")]
|
||||
theorem coe_addNat (m : Nat) (i : Fin n) : (addNat i m : Nat) = i + m := rfl
|
||||
|
||||
@@ -18,3 +18,4 @@ public import Init.Data.Int.Pow
|
||||
public import Init.Data.Int.Cooper
|
||||
public import Init.Data.Int.Linear
|
||||
public import Init.Data.Int.OfNat
|
||||
public import Init.Data.Int.ToString
|
||||
|
||||
24
src/Init/Data/Int/Repr.lean
Normal file
24
src/Init/Data/Int/Repr.lean
Normal file
@@ -0,0 +1,24 @@
|
||||
/-
|
||||
Copyright (c) 2016 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Author: Leonardo de Moura
|
||||
-/
|
||||
module
|
||||
|
||||
prelude
|
||||
public import Init.Data.Repr
|
||||
public import Init.Data.String.Defs
|
||||
|
||||
namespace Int
|
||||
|
||||
/--
|
||||
Returns the decimal string representation of an integer.
|
||||
-/
|
||||
public protected def repr : Int → String
|
||||
| ofNat m => Nat.repr m
|
||||
| negSucc m => "-" ++ Nat.repr (Nat.succ m)
|
||||
|
||||
public instance : Repr Int where
|
||||
reprPrec i prec := if i < 0 then Repr.addAppParen i.repr prec else i.repr
|
||||
|
||||
end Int
|
||||
23
src/Init/Data/Int/ToString.lean
Normal file
23
src/Init/Data/Int/ToString.lean
Normal file
@@ -0,0 +1,23 @@
|
||||
/-
|
||||
Copyright (c) 2026 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Julia Markus Himmel
|
||||
-/
|
||||
module
|
||||
|
||||
prelude
|
||||
public import Init.Data.ToString.Extra
|
||||
import all Init.Data.Int.Repr
|
||||
import Init.Data.Int.Order
|
||||
import Init.Data.Int.LemmasAux
|
||||
|
||||
namespace Int
|
||||
|
||||
public theorem repr_eq_if {a : Int} :
|
||||
a.repr = if 0 ≤ a then a.toNat.repr else "-" ++ (-a).toNat.repr := by
|
||||
cases a <;> simp [Int.repr]
|
||||
|
||||
@[simp]
|
||||
public theorem toString_eq_repr {a : Int} : toString a = a.repr := (rfl)
|
||||
|
||||
end Int
|
||||
@@ -37,7 +37,7 @@ The standard library does not provide a `Productive` instance for this case.
|
||||
|
||||
This combinator incurs an additional O(1) cost with each output of `it₁` and `it₂`.
|
||||
-/
|
||||
@[inline, expose]
|
||||
@[cbv_opaque, inline, expose]
|
||||
def Iter.append {α₁ : Type w} {α₂ : Type w} {β : Type w}
|
||||
[Iterator α₁ Id β] [Iterator α₂ Id β]
|
||||
(it₁ : Iter (α := α₁) β) (it₂ : Iter (α := α₂) β) :
|
||||
|
||||
@@ -13,7 +13,7 @@ public section
|
||||
namespace Std
|
||||
open Std.Iterators
|
||||
|
||||
@[always_inline, inline, expose, inherit_doc IterM.attachWith]
|
||||
@[cbv_opaque, always_inline, inline, expose, inherit_doc IterM.attachWith]
|
||||
def Iter.attachWith {α β : Type w}
|
||||
[Iterator α Id β]
|
||||
(it : Iter (α := α) β) (P : β → Prop) (h : ∀ out, it.IsPlausibleIndirectOutput out → P out) :
|
||||
|
||||
@@ -282,17 +282,17 @@ def Iter.mapM {α β γ : Type w} [Iterator α Id β] {m : Type w → Type w'}
|
||||
[Monad m] [MonadAttach m] (f : β → m γ) (it : Iter (α := α) β) :=
|
||||
(letI : MonadLift Id m := ⟨pure⟩; it.toIterM.mapM f : IterM m γ)
|
||||
|
||||
@[always_inline, inline, inherit_doc IterM.filterMap, expose]
|
||||
@[cbv_opaque, always_inline, inline, inherit_doc IterM.filterMap, expose]
|
||||
def Iter.filterMap {α : Type w} {β : Type w} {γ : Type w} [Iterator α Id β]
|
||||
(f : β → Option γ) (it : Iter (α := α) β) :=
|
||||
((it.toIterM.filterMap f).toIter : Iter γ)
|
||||
|
||||
@[always_inline, inline, inherit_doc IterM.filter, expose]
|
||||
@[cbv_opaque, always_inline, inline, inherit_doc IterM.filter, expose]
|
||||
def Iter.filter {α : Type w} {β : Type w} [Iterator α Id β]
|
||||
(f : β → Bool) (it : Iter (α := α) β) :=
|
||||
((it.toIterM.filter f).toIter : Iter β)
|
||||
|
||||
@[always_inline, inline, inherit_doc IterM.map, expose]
|
||||
@[cbv_opaque, always_inline, inline, inherit_doc IterM.map, expose]
|
||||
def Iter.map {α : Type w} {β : Type w} {γ : Type w} [Iterator α Id β]
|
||||
(f : β → γ) (it : Iter (α := α) β) :=
|
||||
((it.toIterM.map f).toIter : Iter γ)
|
||||
|
||||
@@ -44,7 +44,7 @@ public def Iter.flatMapAfter {α : Type w} {β : Type w} {α₂ : Type w}
|
||||
(f : β → Iter (α := α₂) γ) (it₁ : Iter (α := α) β) (it₂ : Option (Iter (α := α₂) γ)) :=
|
||||
((it₁.toIterM.flatMapAfter (fun b => (f b).toIterM) (Iter.toIterM <$> it₂)).toIter : Iter γ)
|
||||
|
||||
@[always_inline, expose, inherit_doc IterM.flatMap]
|
||||
@[cbv_opaque, always_inline, expose, inherit_doc IterM.flatMap]
|
||||
public def Iter.flatMap {α : Type w} {β : Type w} {α₂ : Type w}
|
||||
{γ : Type w} [Iterator α Id β] [Iterator α₂ Id γ]
|
||||
(f : β → Iter (α := α₂) γ) (it : Iter (α := α) β) :=
|
||||
|
||||
@@ -168,6 +168,13 @@ instance Map.instIterator {α β γ : Type w} {m : Type w → Type w'} {n : Type
|
||||
Iterator (Map α m n lift f) n γ :=
|
||||
inferInstanceAs <| Iterator (FilterMap α m n lift _) n γ
|
||||
|
||||
theorem Map.instIterator_eq_filterMapInstIterator {α β γ : Type w} {m : Type w → Type w'}
|
||||
{n : Type w → Type w''} [Monad n]
|
||||
[Iterator α m β] {lift : ⦃α : Type w⦄ → m α → n α} {f : β → PostconditionT n γ} :
|
||||
Map.instIterator (α := α) (β := β) (γ := γ) (m := m) (n := n) (lift := lift) (f := f) =
|
||||
FilterMap.instIterator :=
|
||||
rfl
|
||||
|
||||
private def FilterMap.instFinitenessRelation {α β γ : Type w} {m : Type w → Type w'}
|
||||
{n : Type w → Type w''} [Monad n] [Iterator α m β] {lift : ⦃α : Type w⦄ → m α → n α}
|
||||
{f : β → PostconditionT n (Option γ)} [Finite α m] :
|
||||
|
||||
@@ -36,7 +36,7 @@ it.take 3 ---a--⊥
|
||||
|
||||
This combinator incurs an additional O(1) cost with each output of `it`.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
@[cbv_opaque, always_inline, inline]
|
||||
def Iter.take {α : Type w} {β : Type w} [Iterator α Id β] (n : Nat) (it : Iter (α := α) β) :
|
||||
Iter (α := Take α Id) β :=
|
||||
it.toIterM.take n |>.toIter
|
||||
|
||||
@@ -44,7 +44,7 @@ it.uLift n ---.up a----.up b---.up c--.up d---⊥
|
||||
* `Finite`: only if the original iterator is finite
|
||||
* `Productive`: only if the original iterator is productive
|
||||
-/
|
||||
@[always_inline, inline, expose]
|
||||
@[cbv_opaque, always_inline, inline, expose]
|
||||
def Iter.uLift (it : Iter (α := α) β) :
|
||||
Iter (α := Types.ULiftIterator.{v} α Id Id β (fun _ => monadLift)) (ULift β) :=
|
||||
(it.toIterM.uLift Id).toIter
|
||||
|
||||
@@ -32,7 +32,7 @@ Traverses the given iterator and stores the emitted values in an array.
|
||||
If the iterator is not finite, this function might run forever. The variant
|
||||
`it.ensureTermination.toArray` always terminates after finitely many steps.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
@[cbv_opaque, always_inline, inline]
|
||||
def Iter.toArray {α : Type w} {β : Type w}
|
||||
[Iterator α Id β] (it : Iter (α := α) β) : Array β :=
|
||||
it.toIterM.toArray.run
|
||||
@@ -66,7 +66,7 @@ lists are prepend-only, this `toListRev` is usually more efficient that `toList`
|
||||
If the iterator is not finite, this function might run forever. The variant
|
||||
`it.ensureTermination.toListRev` always terminates after finitely many steps.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
@[always_inline, inline, cbv_opaque]
|
||||
def Iter.toListRev {α : Type w} {β : Type w}
|
||||
[Iterator α Id β] (it : Iter (α := α) β) : List β :=
|
||||
it.toIterM.toListRev.run
|
||||
@@ -101,7 +101,7 @@ lists are prepend-only, `toListRev` is usually more efficient that `toList`.
|
||||
If the iterator is not finite, this function might run forever. The variant
|
||||
`it.ensureTermination.toList` always terminates after finitely many steps.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
@[cbv_opaque, always_inline, inline]
|
||||
def Iter.toList {α : Type w} {β : Type w}
|
||||
[Iterator α Id β] (it : Iter (α := α) β) : List β :=
|
||||
it.toIterM.toList.run
|
||||
|
||||
@@ -226,7 +226,7 @@ any element emitted by the iterator {name}`it`.
|
||||
{lit}`O(|xs|)`. Short-circuits upon encountering the first match. The elements in {name}`it` are
|
||||
examined in order of iteration.
|
||||
-/
|
||||
@[inline]
|
||||
@[inline, cbv_opaque]
|
||||
def Iter.any {α β : Type w}
|
||||
[Iterator α Id β] [IteratorLoop α Id Id]
|
||||
(p : β → Bool) (it : Iter (α := α) β) : Bool :=
|
||||
@@ -292,7 +292,7 @@ all element emitted by the iterator {name}`it`.
|
||||
{lit}`O(|xs|)`. Short-circuits upon encountering the first match. The elements in {name}`it` are
|
||||
examined in order of iteration.
|
||||
-/
|
||||
@[inline]
|
||||
@[inline, cbv_opaque]
|
||||
def Iter.all {α β : Type w}
|
||||
[Iterator α Id β] [IteratorLoop α Id Id]
|
||||
(p : β → Bool) (it : Iter (α := α) β) : Bool :=
|
||||
@@ -644,7 +644,7 @@ Examples:
|
||||
* `[7, 6].iter.first? = some 7`
|
||||
* `[].iter.first? = none`
|
||||
-/
|
||||
@[inline]
|
||||
@[inline, cbv_opaque]
|
||||
def Iter.first? {α β : Type w} [Iterator α Id β] [IteratorLoop α Id Id]
|
||||
(it : Iter (α := α) β) : Option β :=
|
||||
it.toIterM.first?.run
|
||||
|
||||
@@ -56,7 +56,7 @@ theorem Iter.Intermediate.step_appendSnd {α₁ α₂ β : Type w}
|
||||
simp only [Iter.step, appendSnd, toIterM_toIter, IterM.Intermediate.step_appendSnd, Id.run_bind]
|
||||
cases it₂.toIterM.step.run.inflate using PlausibleIterStep.casesOn <;> simp
|
||||
|
||||
@[simp]
|
||||
@[cbv_eval, simp]
|
||||
theorem Iter.toList_append {α₁ α₂ β : Type w}
|
||||
[Iterator α₁ Id β] [Iterator α₂ Id β] [Finite α₁ Id] [Finite α₂ Id]
|
||||
{it₁ : Iter (α := α₁) β} {it₂ : Iter (α := α₂) β} :
|
||||
@@ -70,7 +70,7 @@ theorem Iter.toListRev_append {α₁ α₂ β : Type w}
|
||||
(it₁.append it₂).toListRev = it₂.toListRev ++ it₁.toListRev := by
|
||||
simp [append_eq_toIter_append_toIterM, toListRev_eq_toListRev_toIterM]
|
||||
|
||||
@[simp]
|
||||
@[cbv_eval, simp]
|
||||
theorem Iter.toArray_append {α₁ α₂ β : Type w}
|
||||
[Iterator α₁ Id β] [Iterator α₂ Id β] [Finite α₁ Id] [Finite α₂ Id]
|
||||
{it₁ : Iter (α := α₁) β} {it₂ : Iter (α := α₂) β} :
|
||||
|
||||
@@ -34,7 +34,7 @@ theorem Iter.unattach_toList_attachWith [Iterator α Id β]
|
||||
← Id.run_map (f := List.unattach), IterM.map_unattach_toList_attachWith,
|
||||
Iter.toList_eq_toList_toIterM]
|
||||
|
||||
@[simp]
|
||||
@[cbv_eval, simp]
|
||||
theorem Iter.toList_attachWith [Iterator α Id β]
|
||||
{it : Iter (α := α) β} {hP}
|
||||
[Finite α Id] :
|
||||
@@ -68,7 +68,7 @@ theorem Iter.unattach_toArray_attachWith [Iterator α Id β]
|
||||
(it.attachWith P hP).toListRev.unattach = it.toListRev := by
|
||||
simp [toListRev_eq]
|
||||
|
||||
@[simp]
|
||||
@[cbv_eval, simp]
|
||||
theorem Iter.toArray_attachWith [Iterator α Id β]
|
||||
{it : Iter (α := α) β} {hP}
|
||||
[Finite α Id] :
|
||||
|
||||
@@ -297,7 +297,7 @@ def Iter.val_step_filter {f : β → Bool} :
|
||||
· simp
|
||||
· simp
|
||||
|
||||
@[simp]
|
||||
@[cbv_eval, simp]
|
||||
theorem Iter.toList_filterMap [Finite α Id]
|
||||
{f : β → Option γ} :
|
||||
(it.filterMap f).toList = it.toList.filterMap f := by
|
||||
@@ -315,12 +315,12 @@ theorem Iter.toList_mapM [Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawful
|
||||
(it.mapM f).toList = it.toList.mapM f := by
|
||||
simp [Iter.mapM_eq_toIter_mapM_toIterM, IterM.toList_mapM, Iter.toList_eq_toList_toIterM]
|
||||
|
||||
@[simp]
|
||||
@[cbv_eval, simp]
|
||||
theorem Iter.toList_map [Finite α Id] {f : β → γ} :
|
||||
(it.map f).toList = it.toList.map f := by
|
||||
simp [map_eq_toIter_map_toIterM, IterM.toList_map, Iter.toList_eq_toList_toIterM]
|
||||
|
||||
@[simp]
|
||||
@[cbv_eval, simp]
|
||||
theorem Iter.toList_filter [Finite α Id] {f : β → Bool} :
|
||||
(it.filter f).toList = it.toList.filter f := by
|
||||
simp [filter_eq_toIter_filter_toIterM, IterM.toList_filter, Iter.toList_eq_toList_toIterM]
|
||||
@@ -369,7 +369,7 @@ theorem Iter.toListRev_filter [Finite α Id]
|
||||
(it.filter f).toListRev = it.toListRev.filter f := by
|
||||
simp [filter_eq_toIter_filter_toIterM, IterM.toListRev_filter, Iter.toListRev_eq_toListRev_toIterM]
|
||||
|
||||
@[simp]
|
||||
@[cbv_eval, simp]
|
||||
theorem Iter.toArray_filterMap [Finite α Id]
|
||||
{f : β → Option γ} :
|
||||
(it.filterMap f).toArray = it.toArray.filterMap f := by
|
||||
@@ -387,13 +387,13 @@ theorem Iter.toArray_mapM [Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawfu
|
||||
(it.mapM f).toArray = it.toArray.mapM f := by
|
||||
simp [Iter.mapM_eq_toIter_mapM_toIterM, IterM.toArray_mapM, Iter.toArray_eq_toArray_toIterM]
|
||||
|
||||
@[simp]
|
||||
@[cbv_eval, simp]
|
||||
theorem Iter.toArray_map [Finite α Id] {f : β → γ} :
|
||||
(it.map f).toArray = it.toArray.map f := by
|
||||
simp [map_eq_toIter_map_toIterM, IterM.toArray_map, Iter.toArray_eq_toArray_toIterM]
|
||||
|
||||
@[simp]
|
||||
theorem Iter.toArray_filter[Finite α Id] {f : β → Bool} :
|
||||
@[cbv_eval, simp]
|
||||
theorem Iter.toArray_filter [Finite α Id] {f : β → Bool} :
|
||||
(it.filter f).toArray = it.toArray.filter f := by
|
||||
simp [filter_eq_toIter_filter_toIterM, IterM.toArray_filter, Iter.toArray_eq_toArray_toIterM]
|
||||
|
||||
|
||||
@@ -254,6 +254,7 @@ public theorem Iter.toArray_flatMapAfter {α α₂ β γ : Type w} [Iterator α
|
||||
unfold Iter.toArray
|
||||
cases it₂ <;> simp [map, IterM.toArray_map_eq_toArray_mapM, - IterM.toArray_map]
|
||||
|
||||
@[cbv_eval]
|
||||
public theorem Iter.toList_flatMap {α α₂ β γ : Type w} [Iterator α Id β] [Iterator α₂ Id γ]
|
||||
[Finite α Id] [Finite α₂ Id]
|
||||
[Iterator α Id β] [Iterator α₂ Id γ] [Finite α Id] [Finite α₂ Id]
|
||||
@@ -261,6 +262,7 @@ public theorem Iter.toList_flatMap {α α₂ β γ : Type w} [Iterator α Id β]
|
||||
(it₁.flatMap f).toList = (it₁.map fun b => (f b).toList).toList.flatten := by
|
||||
simp [flatMap, toList_flatMapAfter]
|
||||
|
||||
@[cbv_eval]
|
||||
public theorem Iter.toArray_flatMap {α α₂ β γ : Type w} [Iterator α Id β] [Iterator α₂ Id γ]
|
||||
[Finite α Id] [Finite α₂ Id]
|
||||
[Iterator α Id β] [Iterator α₂ Id γ] [Finite α Id] [Finite α₂ Id]
|
||||
|
||||
@@ -271,7 +271,7 @@ private def optionPelim' {α : Type u_1} (t : Option α) {β : Sort u_2}
|
||||
|
||||
/--
|
||||
Inserts an `Option` case distinction after the first computation of a call to `MonadAttach.pbind`.
|
||||
This lemma is useful for simplifying the second computation, which often involes `match` expressions
|
||||
This lemma is useful for simplifying the second computation, which often involves `match` expressions
|
||||
that use `pbind`'s proof term.
|
||||
-/
|
||||
private theorem pbind_eq_pbind_if_isSome [Monad m] [MonadAttach m] (x : m (Option α)) (f : (_ : _) → _ → m β) :
|
||||
@@ -699,18 +699,16 @@ theorem IterM.toList_map {α β β' : Type w} {m : Type w → Type w'} [Monad m]
|
||||
(it : IterM (α := α) m β) :
|
||||
(it.map f).toList = (fun x => x.map f) <$> it.toList := by
|
||||
rw [← List.filterMap_eq_map, ← toList_filterMap]
|
||||
let t := type_of% (it.map f)
|
||||
let t' := type_of% (it.filterMap (some ∘ f))
|
||||
simp only [map, mapWithPostcondition, InternalCombinators.map, filterMap,
|
||||
filterMapWithPostcondition, InternalCombinators.filterMap]
|
||||
unfold Map
|
||||
congr
|
||||
· simp [Map]
|
||||
· simp [Map.instIterator, inferInstanceAs]
|
||||
· simp
|
||||
· rw [Map.instIterator_eq_filterMapInstIterator]
|
||||
congr
|
||||
simp
|
||||
· simp only [map, mapWithPostcondition, InternalCombinators.map, Function.comp_apply, filterMap,
|
||||
filterMapWithPostcondition, InternalCombinators.filterMap]
|
||||
congr
|
||||
· simp [Map]
|
||||
· simp
|
||||
· simp
|
||||
· simp
|
||||
|
||||
@[simp]
|
||||
theorem IterM.toList_filter {α : Type w} {m : Type w → Type w'} [Monad m] [LawfulMonad m]
|
||||
@@ -1310,7 +1308,8 @@ theorem IterM.forIn_mapWithPostcondition
|
||||
haveI : MonadLift n o := ⟨monadLift⟩
|
||||
forIn (it.mapWithPostcondition f) init g =
|
||||
forIn it init (fun out acc => do g (← (f out).run) acc) := by
|
||||
unfold mapWithPostcondition InternalCombinators.map Map.instIterator Map.instIteratorLoop Map
|
||||
unfold mapWithPostcondition InternalCombinators.map Map.instIteratorLoop Map
|
||||
rw [Map.instIterator_eq_filterMapInstIterator]
|
||||
rw [← InternalCombinators.filterMap, ← filterMapWithPostcondition, forIn_filterMapWithPostcondition]
|
||||
simp
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ theorem Iter.atIdxSlow?_take {α β}
|
||||
simp only [atIdxSlow?_eq_match (it := it.take k), step_take, h']
|
||||
cases k <;> cases l <;> simp
|
||||
|
||||
@[simp]
|
||||
@[cbv_eval, simp]
|
||||
theorem Iter.toList_take_of_finite {α β} [Iterator α Id β] {n : Nat}
|
||||
[Finite α Id] {it : Iter (α := α) β} :
|
||||
(it.take n).toList = it.toList.take n := by
|
||||
@@ -89,7 +89,7 @@ theorem Iter.toListRev_take_of_finite {α β} [Iterator α Id β] {n : Nat}
|
||||
(it.take n).toListRev = it.toListRev.drop (it.toList.length - n) := by
|
||||
rw [toListRev_eq, toList_take_of_finite, List.reverse_take, toListRev_eq]
|
||||
|
||||
@[simp]
|
||||
@[cbv_eval, simp]
|
||||
theorem Iter.toArray_take_of_finite {α β} [Iterator α Id β] {n : Nat}
|
||||
[Finite α Id] {it : Iter (α := α) β} :
|
||||
(it.take n).toArray = it.toArray.take n := by
|
||||
|
||||
@@ -38,7 +38,7 @@ theorem Iter.step_uLift [Iterator α Id β] {it : Iter (α := α) β} :
|
||||
PlausibleIterStep.done, pure_bind]
|
||||
cases it.toIterM.step.run.inflate using PlausibleIterStep.casesOn <;> simp
|
||||
|
||||
@[simp]
|
||||
@[cbv_eval, simp]
|
||||
theorem Iter.toList_uLift [Iterator α Id β] {it : Iter (α := α) β}
|
||||
[Finite α Id] :
|
||||
it.uLift.toList = it.toList.map ULift.up := by
|
||||
@@ -52,7 +52,7 @@ theorem Iter.toListRev_uLift [Iterator α Id β] {it : Iter (α := α) β}
|
||||
it.uLift.toListRev = it.toListRev.map ULift.up := by
|
||||
rw [toListRev_eq, toListRev_eq, toList_uLift, List.map_reverse]
|
||||
|
||||
@[simp]
|
||||
@[cbv_eval, simp]
|
||||
theorem Iter.toArray_uLift [Iterator α Id β] {it : Iter (α := α) β}
|
||||
[Finite α Id] :
|
||||
it.uLift.toArray = it.toArray.map ULift.up := by
|
||||
|
||||
@@ -88,7 +88,7 @@ theorem Iter.toList_toArray_ensureTermination {α β} [Iterator α Id β] [Finit
|
||||
it.ensureTermination.toArray.toList = it.toList := by
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
@[cbv_eval ←, simp]
|
||||
theorem Iter.toArray_toList {α β} [Iterator α Id β] [Finite α Id]
|
||||
{it : Iter (α := α) β} :
|
||||
it.toList.toArray = it.toArray := by
|
||||
@@ -110,6 +110,7 @@ theorem Iter.reverse_toListRev_ensureTermination [Iterator α Id β] [Finite α
|
||||
it.ensureTermination.toListRev.reverse = it.toList := by
|
||||
simp
|
||||
|
||||
@[cbv_eval]
|
||||
theorem Iter.toListRev_eq {α β} [Iterator α Id β] [Finite α Id]
|
||||
{it : Iter (α := α) β} :
|
||||
it.toListRev = it.toList.reverse := by
|
||||
|
||||
@@ -449,7 +449,7 @@ theorem Iter.toArray_eq_fold {α β : Type w} [Iterator α Id β]
|
||||
rw [← fold_hom (List.toArray)]
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
@[cbv_eval ←, simp]
|
||||
theorem Iter.foldl_toList {α β : Type w} {γ : Type x} [Iterator α Id β] [Finite α Id]
|
||||
[IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
|
||||
{f : γ → β → γ} {init : γ} {it : Iter (α := α) β} :
|
||||
@@ -637,6 +637,7 @@ theorem Iter.any_eq_forIn {α β : Type w} [Iterator α Id β]
|
||||
return .yield false)).run := by
|
||||
simp [any_eq_anyM, anyM_eq_forIn]
|
||||
|
||||
@[cbv_eval ←]
|
||||
theorem Iter.any_toList {α β : Type w} [Iterator α Id β]
|
||||
[Finite α Id] [IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
|
||||
{it : Iter (α := α) β} {p : β → Bool} :
|
||||
@@ -727,6 +728,7 @@ theorem Iter.all_eq_forIn {α β : Type w} [Iterator α Id β]
|
||||
return .done false)).run := by
|
||||
simp [all_eq_allM, allM_eq_forIn]
|
||||
|
||||
@[cbv_eval ←]
|
||||
theorem Iter.all_toList {α β : Type w} [Iterator α Id β]
|
||||
[Finite α Id] [IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
|
||||
{it : Iter (α := α) β} {p : β → Bool} :
|
||||
@@ -954,7 +956,7 @@ theorem Iter.first?_eq_match_step {α β : Type w} [Iterator α Id β] [Iterator
|
||||
generalize it.toIterM.step.run.inflate = s
|
||||
rcases s with ⟨_|_|_, _⟩ <;> simp [Iter.first?_eq_first?_toIterM]
|
||||
|
||||
@[simp, grind =]
|
||||
@[simp, grind =, cbv_eval ←]
|
||||
theorem Iter.head?_toList {α β : Type w} [Iterator α Id β] [IteratorLoop α Id Id]
|
||||
[Finite α Id] [LawfulIteratorLoop α Id Id] {it : Iter (α := α) β} :
|
||||
it.toList.head? = it.first? := by
|
||||
|
||||
@@ -33,12 +33,12 @@ theorem List.step_iter_cons {x : β} {xs : List β} :
|
||||
((x :: xs).iter).step = ⟨.yield xs.iter x, rfl⟩ := by
|
||||
simp [List.iter, List.iterM, IterM.toIter, Iter.step_eq]
|
||||
|
||||
@[simp, grind =]
|
||||
@[cbv_eval, simp, grind =]
|
||||
theorem List.toArray_iter {l : List β} :
|
||||
l.iter.toArray = l.toArray := by
|
||||
simp [List.iter, List.toArray_iterM, Iter.toArray_eq_toArray_toIterM]
|
||||
|
||||
@[simp, grind =]
|
||||
@[cbv_eval, simp, grind =]
|
||||
theorem List.toList_iter {l : List β} :
|
||||
l.iter.toList = l := by
|
||||
simp [List.iter, List.toList_iterM]
|
||||
|
||||
@@ -29,7 +29,7 @@ The monadic version of this iterator is `List.iterM`.
|
||||
* `Finite` instance: always
|
||||
* `Productive` instance: always
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
@[cbv_opaque, always_inline, inline]
|
||||
def List.iter {α : Type w} (l : List α) :
|
||||
Iter (α := ListIterator α) α :=
|
||||
((l.iterM Id).toIter : Iter α)
|
||||
|
||||
@@ -46,7 +46,7 @@ The non-monadic version of this iterator is `List.iter`.
|
||||
* `Finite` instance: always
|
||||
* `Productive` instance: always
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
@[cbv_opaque, always_inline, inline]
|
||||
def _root_.List.iterM {α : Type w} (l : List α) (m : Type w → Type w') [Pure m] :
|
||||
IterM (α := ListIterator α) m α :=
|
||||
⟨{ list := l }⟩
|
||||
|
||||
@@ -1246,6 +1246,24 @@ def IsInfix (l₁ : List α) (l₂ : List α) : Prop := Exists fun s => Exists f
|
||||
/-- not `isInfix` -/
|
||||
recommended_spelling "infix" for "<:+:" in [IsInfix, «term_<:+:_»]
|
||||
|
||||
/--
|
||||
Checks whether the first list is a contiguous sub-list of the second.
|
||||
|
||||
The relation `List.IsInfixOf` expresses this property with respect to logical equality.
|
||||
|
||||
Examples:
|
||||
* `[2, 3].isInfixOf_internal [1, 2, 3, 4] = true`
|
||||
* `[2, 3].isInfixOf_internal [1, 3, 2, 4] = false`
|
||||
* `[2, 3].isInfixOf_internal [2, 3] = true`
|
||||
* `[2, 3].isInfixOf_internal [1] = false`
|
||||
|
||||
Used internally by the `cbv` tactic.
|
||||
-/
|
||||
def isInfixOf_internal [BEq α] (l₁ l₂ : List α) : Bool :=
|
||||
l₁.isPrefixOf l₂ || match l₂ with
|
||||
| [] => false
|
||||
| _ :: l₂ => isInfixOf_internal l₁ l₂
|
||||
|
||||
/-! ### splitAt -/
|
||||
|
||||
/--
|
||||
|
||||
@@ -481,13 +481,13 @@ protected theorem maxIdxOn_nil_eq_iff_false [LE β] [DecidableLE β] {f : α →
|
||||
@[simp]
|
||||
protected theorem maxIdxOn_singleton [LE β] [DecidableLE β] {x : α} {f : α → β} :
|
||||
[x].maxIdxOn f (of_decide_eq_false rfl) = 0 :=
|
||||
letI : LE β := (inferInstanceAs (LE β)).opposite
|
||||
letI : LE β := (inferInstance : LE β).opposite
|
||||
List.minIdxOn_singleton
|
||||
|
||||
@[simp]
|
||||
protected theorem maxIdxOn_lt_length [LE β] [DecidableLE β] {f : α → β} {xs : List α}
|
||||
(h : xs ≠ []) : xs.maxIdxOn f h < xs.length :=
|
||||
letI : LE β := (inferInstanceAs (LE β)).opposite
|
||||
letI : LE β := (inferInstance : LE β).opposite
|
||||
List.minIdxOn_lt_length h
|
||||
|
||||
protected theorem maxIdxOn_le_of_apply_getElem_le_apply_maxOn [LE β] [DecidableLE β] [IsLinearPreorder β]
|
||||
@@ -495,7 +495,7 @@ protected theorem maxIdxOn_le_of_apply_getElem_le_apply_maxOn [LE β] [Decidable
|
||||
{k : Nat} (hi : k < xs.length) (hle : f (xs.maxOn f h) ≤ f xs[k]) :
|
||||
xs.maxIdxOn f h ≤ k := by
|
||||
simp only [List.maxIdxOn_eq_minIdxOn, List.maxOn_eq_minOn] at hle ⊢
|
||||
letI : LE β := (inferInstanceAs (LE β)).opposite
|
||||
letI : LE β := (inferInstance : LE β).opposite
|
||||
exact List.minIdxOn_le_of_apply_getElem_le_apply_minOn h hi (by simpa [LE.le_opposite_iff] using hle)
|
||||
|
||||
protected theorem apply_maxOn_lt_apply_getElem_of_lt_maxIdxOn [LE β] [DecidableLE β] [LT β] [IsLinearPreorder β]
|
||||
@@ -513,7 +513,7 @@ protected theorem getElem_maxIdxOn [LE β] [DecidableLE β] [IsLinearPreorder β
|
||||
{f : α → β} {xs : List α} (h : xs ≠ []) :
|
||||
xs[xs.maxIdxOn f h] = xs.maxOn f h := by
|
||||
simp only [List.maxIdxOn_eq_minIdxOn, List.maxOn_eq_minOn]
|
||||
letI : LE β := (inferInstanceAs (LE β)).opposite
|
||||
letI : LE β := (inferInstance : LE β).opposite
|
||||
exact List.getElem_minIdxOn h
|
||||
|
||||
protected theorem le_maxIdxOn_of_apply_getElem_lt_apply_getElem [LE β] [DecidableLE β] [LT β]
|
||||
@@ -562,14 +562,14 @@ protected theorem maxIdxOn_cons
|
||||
else if f (xs.maxOn f h) ≤ f x then 0
|
||||
else (xs.maxIdxOn f h) + 1 := by
|
||||
simp only [List.maxIdxOn_eq_minIdxOn, List.maxOn_eq_minOn]
|
||||
letI : LE β := (inferInstanceAs (LE β)).opposite
|
||||
letI : LE β := (inferInstance : LE β).opposite
|
||||
simpa [LE.le_opposite_iff] using List.minIdxOn_cons (f := f)
|
||||
|
||||
protected theorem maxIdxOn_eq_zero_iff [LE β] [DecidableLE β] [IsLinearPreorder β]
|
||||
{xs : List α} {f : α → β} (h : xs ≠ []) :
|
||||
xs.maxIdxOn f h = 0 ↔ ∀ x ∈ xs, f x ≤ f (xs.head h) := by
|
||||
simp only [List.maxIdxOn_eq_minIdxOn]
|
||||
letI : LE β := (inferInstanceAs (LE β)).opposite
|
||||
letI : LE β := (inferInstance : LE β).opposite
|
||||
simpa [LE.le_opposite_iff] using List.minIdxOn_eq_zero_iff h (f := f)
|
||||
|
||||
protected theorem maxIdxOn_append [LE β] [DecidableLE β] [IsLinearPreorder β]
|
||||
@@ -580,26 +580,26 @@ protected theorem maxIdxOn_append [LE β] [DecidableLE β] [IsLinearPreorder β]
|
||||
else
|
||||
xs.length + ys.maxIdxOn f hys := by
|
||||
simp only [List.maxIdxOn_eq_minIdxOn, List.maxOn_eq_minOn]
|
||||
letI : LE β := (inferInstanceAs (LE β)).opposite
|
||||
letI : LE β := (inferInstance : LE β).opposite
|
||||
simpa [LE.le_opposite_iff] using List.minIdxOn_append hxs hys (f := f)
|
||||
|
||||
protected theorem left_le_maxIdxOn_append [LE β] [DecidableLE β] [IsLinearPreorder β]
|
||||
{xs ys : List α} {f : α → β} (h : xs ≠ []) :
|
||||
xs.maxIdxOn f h ≤ (xs ++ ys).maxIdxOn f (by simp [h]) :=
|
||||
letI : LE β := (inferInstanceAs (LE β)).opposite
|
||||
letI : LE β := (inferInstance : LE β).opposite
|
||||
List.left_le_minIdxOn_append h
|
||||
|
||||
protected theorem maxIdxOn_take_le [LE β] [DecidableLE β] [IsLinearPreorder β]
|
||||
{xs : List α} {f : α → β} {i : Nat} (h : xs.take i ≠ []) :
|
||||
(xs.take i).maxIdxOn f h ≤ xs.maxIdxOn f (List.ne_nil_of_take_ne_nil h) :=
|
||||
letI : LE β := (inferInstanceAs (LE β)).opposite
|
||||
letI : LE β := (inferInstance : LE β).opposite
|
||||
List.minIdxOn_take_le h
|
||||
|
||||
@[simp]
|
||||
protected theorem maxIdxOn_replicate [LE β] [DecidableLE β] [Refl (α := β) (· ≤ ·)]
|
||||
{n : Nat} {a : α} {f : α → β} (h : replicate n a ≠ []) :
|
||||
(replicate n a).maxIdxOn f h = 0 :=
|
||||
letI : LE β := (inferInstanceAs (LE β)).opposite
|
||||
letI : LE β := (inferInstance : LE β).opposite
|
||||
List.minIdxOn_replicate h
|
||||
|
||||
@[simp]
|
||||
|
||||
@@ -297,13 +297,13 @@ protected theorem maxOn_cons
|
||||
(x :: xs).maxOn f (by exact of_decide_eq_false rfl) =
|
||||
if h : xs = [] then x else maxOn f x (xs.maxOn f h) := by
|
||||
simp only [maxOn_eq_minOn]
|
||||
letI : LE β := (inferInstanceAs (LE β)).opposite
|
||||
letI : LE β := (inferInstance : LE β).opposite
|
||||
exact List.minOn_cons (f := f)
|
||||
|
||||
protected theorem maxOn_cons_cons [LE β] [DecidableLE β] {a b : α} {l : List α} {f : α → β} :
|
||||
(a :: b :: l).maxOn f (by simp) = (maxOn f a b :: l).maxOn f (by simp) := by
|
||||
simp only [List.maxOn_eq_minOn, maxOn_eq_minOn]
|
||||
letI : LE β := (inferInstanceAs (LE β)).opposite
|
||||
letI : LE β := (inferInstance : LE β).opposite
|
||||
exact List.minOn_cons_cons
|
||||
|
||||
@[simp]
|
||||
@@ -334,51 +334,51 @@ protected theorem maxOn_id [Max α] [LE α] [DecidableLE α] [LawfulOrderLeftLea
|
||||
{xs : List α} (h : xs ≠ []) :
|
||||
xs.maxOn id h = xs.max h := by
|
||||
simp only [List.maxOn_eq_minOn]
|
||||
letI : LE α := (inferInstanceAs (LE α)).opposite
|
||||
letI : Min α := (inferInstanceAs (Max α)).oppositeMin
|
||||
letI : LE α := (inferInstance : LE α).opposite
|
||||
letI : Min α := (inferInstance : Max α).oppositeMin
|
||||
simpa only [List.max_eq_min] using List.minOn_id h
|
||||
|
||||
@[simp]
|
||||
protected theorem maxOn_mem [LE β] [DecidableLE β] {xs : List α}
|
||||
{f : α → β} {h : xs ≠ []} : xs.maxOn f h ∈ xs :=
|
||||
letI : LE β := (inferInstanceAs (LE β)).opposite
|
||||
letI : LE β := (inferInstance : LE β).opposite
|
||||
List.minOn_mem (f := f)
|
||||
|
||||
protected theorem le_apply_maxOn_of_mem [LE β] [DecidableLE β] [IsLinearPreorder β]
|
||||
{xs : List α} {f : α → β} {y : α} (hx : y ∈ xs) :
|
||||
f y ≤ f (xs.maxOn f (List.ne_nil_of_mem hx)) := by
|
||||
rw [List.maxOn_eq_minOn]
|
||||
letI : LE β := (inferInstanceAs (LE β)).opposite
|
||||
letI : LE β := (inferInstance : LE β).opposite
|
||||
simpa [LE.le_opposite_iff] using List.apply_minOn_le_of_mem (f := f) hx
|
||||
|
||||
protected theorem apply_maxOn_le_iff [LE β] [DecidableLE β] [IsLinearPreorder β] {xs : List α}
|
||||
{f : α → β} (h : xs ≠ []) {b : β} :
|
||||
f (xs.maxOn f h) ≤ b ↔ ∀ x ∈ xs, f x ≤ b := by
|
||||
rw [List.maxOn_eq_minOn]
|
||||
letI : LE β := (inferInstanceAs (LE β)).opposite
|
||||
letI : LE β := (inferInstance : LE β).opposite
|
||||
simpa [LE.le_opposite_iff] using List.le_apply_minOn_iff (f := f) h
|
||||
|
||||
protected theorem le_apply_maxOn_iff [LE β] [DecidableLE β] [IsLinearPreorder β] {xs : List α}
|
||||
{f : α → β} (h : xs ≠ []) {b : β} :
|
||||
b ≤ f (xs.maxOn f h) ↔ ∃ x ∈ xs, b ≤ f x := by
|
||||
rw [List.maxOn_eq_minOn]
|
||||
letI : LE β := (inferInstanceAs (LE β)).opposite
|
||||
letI : LE β := (inferInstance : LE β).opposite
|
||||
simpa [LE.le_opposite_iff] using List.apply_minOn_le_iff (f := f) h
|
||||
|
||||
protected theorem apply_maxOn_lt_iff
|
||||
[LE β] [DecidableLE β] [LT β] [IsLinearPreorder β] [LawfulOrderLT β]
|
||||
{xs : List α} {f : α → β} (h : xs ≠ []) {b : β} :
|
||||
f (xs.maxOn f h) < b ↔ ∀ x ∈ xs, f x < b := by
|
||||
letI : LE β := (inferInstanceAs (LE β)).opposite
|
||||
letI : LT β := (inferInstanceAs (LT β)).opposite
|
||||
letI : LE β := (inferInstance : LE β).opposite
|
||||
letI : LT β := (inferInstance : LT β).opposite
|
||||
simpa [LT.lt_opposite_iff] using List.lt_apply_minOn_iff (f := f) h
|
||||
|
||||
protected theorem lt_apply_maxOn_iff
|
||||
[LE β] [DecidableLE β] [LT β] [IsLinearPreorder β] [LawfulOrderLT β]
|
||||
{xs : List α} {f : α → β} (h : xs ≠ []) {b : β} :
|
||||
b < f (xs.maxOn f h) ↔ ∃ x ∈ xs, b < f x := by
|
||||
letI : LE β := (inferInstanceAs (LE β)).opposite
|
||||
letI : LT β := (inferInstanceAs (LT β)).opposite
|
||||
letI : LE β := (inferInstance : LE β).opposite
|
||||
letI : LT β := (inferInstance : LT β).opposite
|
||||
simpa [LT.lt_opposite_iff] using List.apply_minOn_lt_iff (f := f) h
|
||||
|
||||
protected theorem apply_maxOn_le_apply_maxOn_of_subset [LE β] [DecidableLE β]
|
||||
@@ -386,14 +386,14 @@ protected theorem apply_maxOn_le_apply_maxOn_of_subset [LE β] [DecidableLE β]
|
||||
haveI : xs ≠ [] := by intro h; rw [h] at hxs; simp_all [subset_nil]
|
||||
f (ys.maxOn f hys) ≤ f (xs.maxOn f this) := by
|
||||
rw [List.maxOn_eq_minOn]
|
||||
letI : LE β := (inferInstanceAs (LE β)).opposite
|
||||
letI : LE β := (inferInstance : LE β).opposite
|
||||
simpa [LE.le_opposite_iff] using List.apply_minOn_le_apply_minOn_of_subset (f := f) hxs hys
|
||||
|
||||
protected theorem apply_maxOn_take_le [LE β] [DecidableLE β] [IsLinearPreorder β]
|
||||
{xs : List α} {f : α → β} {i : Nat} (h : xs.take i ≠ []) :
|
||||
f ((xs.take i).maxOn f h) ≤ f (xs.maxOn f (List.ne_nil_of_take_ne_nil h)) := by
|
||||
rw [List.maxOn_eq_minOn]
|
||||
letI : LE β := (inferInstanceAs (LE β)).opposite
|
||||
letI : LE β := (inferInstance : LE β).opposite
|
||||
simpa [LE.le_opposite_iff] using List.le_apply_minOn_take (f := f) h
|
||||
|
||||
protected theorem le_apply_maxOn_append_left [LE β] [DecidableLE β] [IsLinearPreorder β]
|
||||
@@ -401,7 +401,7 @@ protected theorem le_apply_maxOn_append_left [LE β] [DecidableLE β] [IsLinearP
|
||||
f (xs.maxOn f h) ≤
|
||||
f ((xs ++ ys).maxOn f (append_ne_nil_of_left_ne_nil h ys)) := by
|
||||
rw [List.maxOn_eq_minOn]
|
||||
letI : LE β := (inferInstanceAs (LE β)).opposite
|
||||
letI : LE β := (inferInstance : LE β).opposite
|
||||
simpa [LE.le_opposite_iff] using List.apply_minOn_append_le_left (f := f) h
|
||||
|
||||
protected theorem le_apply_maxOn_append_right [LE β] [DecidableLE β] [IsLinearPreorder β]
|
||||
@@ -409,7 +409,7 @@ protected theorem le_apply_maxOn_append_right [LE β] [DecidableLE β] [IsLinear
|
||||
f (ys.maxOn f h) ≤
|
||||
f ((xs ++ ys).maxOn f (append_ne_nil_of_right_ne_nil xs h)) := by
|
||||
rw [List.maxOn_eq_minOn]
|
||||
letI : LE β := (inferInstanceAs (LE β)).opposite
|
||||
letI : LE β := (inferInstance : LE β).opposite
|
||||
simpa [LE.le_opposite_iff] using List.apply_minOn_append_le_right (f := f) h
|
||||
|
||||
@[simp]
|
||||
@@ -417,21 +417,21 @@ protected theorem maxOn_append [LE β] [DecidableLE β] [IsLinearPreorder β] {x
|
||||
{f : α → β} (hxs : xs ≠ []) (hys : ys ≠ []) :
|
||||
(xs ++ ys).maxOn f (by simp [hxs]) = maxOn f (xs.maxOn f hxs) (ys.maxOn f hys) := by
|
||||
simp only [List.maxOn_eq_minOn, maxOn_eq_minOn]
|
||||
letI : LE β := (inferInstanceAs (LE β)).opposite
|
||||
letI : LE β := (inferInstance : LE β).opposite
|
||||
simpa [LE.le_opposite_iff] using List.minOn_append (f := f) hxs hys
|
||||
|
||||
protected theorem maxOn_eq_head [LE β] [DecidableLE β] [IsLinearPreorder β] {xs : List α}
|
||||
{f : α → β} (h : xs ≠ []) (h' : ∀ x ∈ xs, f x ≤ f (xs.head h)) :
|
||||
xs.maxOn f h = xs.head h := by
|
||||
rw [List.maxOn_eq_minOn]
|
||||
letI : LE β := (inferInstanceAs (LE β)).opposite
|
||||
letI : LE β := (inferInstance : LE β).opposite
|
||||
simpa [LE.le_opposite_iff] using List.minOn_eq_head (f := f) h (by simpa [LE.le_opposite_iff] using h')
|
||||
|
||||
protected theorem max_map
|
||||
[LE β] [DecidableLE β] [Max β] [IsLinearPreorder β] [LawfulOrderLeftLeaningMax β] {xs : List α}
|
||||
{f : α → β} (h : xs ≠ []) : (xs.map f).max (by simpa) = f (xs.maxOn f h) := by
|
||||
letI : LE β := (inferInstanceAs (LE β)).opposite
|
||||
letI : Min β := (inferInstanceAs (Max β)).oppositeMin
|
||||
letI : LE β := (inferInstance : LE β).opposite
|
||||
letI : Min β := (inferInstance : Max β).oppositeMin
|
||||
simpa [List.max_eq_min] using List.min_map (f := f) h
|
||||
|
||||
protected theorem maxOn_eq_max [Max α] [LE α] [DecidableLE α] [LawfulOrderLeftLeaningMax α]
|
||||
@@ -458,7 +458,7 @@ protected theorem max_map_eq_max [Max α] [LE α] [DecidableLE α] [LawfulOrderL
|
||||
protected theorem maxOn_replicate [LE β] [DecidableLE β] [IsLinearPreorder β]
|
||||
{n : Nat} {a : α} {f : α → β} (h : replicate n a ≠ []) :
|
||||
(replicate n a).maxOn f h = a :=
|
||||
letI : LE β := (inferInstanceAs (LE β)).opposite
|
||||
letI : LE β := (inferInstance : LE β).opposite
|
||||
List.minOn_replicate (f := f) h
|
||||
|
||||
/-! # minOn? -/
|
||||
@@ -579,7 +579,7 @@ protected theorem maxOn?_nil [LE β] [DecidableLE β] {f : α → β} :
|
||||
protected theorem maxOn?_cons_eq_some_maxOn
|
||||
[LE β] [DecidableLE β] {f : α → β} {x : α} {xs : List α} :
|
||||
(x :: xs).maxOn? f = some ((x :: xs).maxOn f (fun h => nomatch h)) :=
|
||||
letI : LE β := (inferInstanceAs (LE β)).opposite
|
||||
letI : LE β := (inferInstance : LE β).opposite
|
||||
List.minOn?_cons_eq_some_minOn
|
||||
|
||||
protected theorem maxOn?_cons
|
||||
@@ -588,7 +588,7 @@ protected theorem maxOn?_cons
|
||||
have : maxOn f x = (letI : LE β := LE.opposite inferInstance; minOn f x) := by
|
||||
ext; simp only [maxOn_eq_minOn]
|
||||
simp only [List.maxOn?_eq_minOn?, this]
|
||||
letI : LE β := (inferInstanceAs (LE β)).opposite
|
||||
letI : LE β := (inferInstance : LE β).opposite
|
||||
exact List.minOn?_cons
|
||||
|
||||
@[simp]
|
||||
@@ -599,8 +599,8 @@ protected theorem maxOn?_singleton [LE β] [DecidableLE β] {x : α} {f : α →
|
||||
@[simp]
|
||||
protected theorem maxOn?_id [Max α] [LE α] [DecidableLE α] [LawfulOrderLeftLeaningMax α]
|
||||
{xs : List α} : xs.maxOn? id = xs.max? := by
|
||||
letI : LE α := (inferInstanceAs (LE α)).opposite
|
||||
letI : Min α := (inferInstanceAs (Max α)).oppositeMin
|
||||
letI : LE α := (inferInstance : LE α).opposite
|
||||
letI : Min α := (inferInstance : Max α).oppositeMin
|
||||
simpa only [List.maxOn?_eq_minOn?, List.max?_eq_min?] using List.minOn?_id (α := α)
|
||||
|
||||
protected theorem maxOn?_eq_if
|
||||
@@ -610,7 +610,7 @@ protected theorem maxOn?_eq_if
|
||||
some (xs.maxOn f h)
|
||||
else
|
||||
none :=
|
||||
letI : LE β := (inferInstanceAs (LE β)).opposite
|
||||
letI : LE β := (inferInstance : LE β).opposite
|
||||
List.minOn?_eq_if
|
||||
|
||||
@[simp]
|
||||
@@ -620,55 +620,55 @@ protected theorem isSome_maxOn?_iff [LE β] [DecidableLE β] {f : α → β} {xs
|
||||
|
||||
protected theorem maxOn_eq_get_maxOn? [LE β] [DecidableLE β] {f : α → β} {xs : List α}
|
||||
(h : xs ≠ []) : xs.maxOn f h = (xs.maxOn? f).get (List.isSome_maxOn?_iff.mpr h) :=
|
||||
letI : LE β := (inferInstanceAs (LE β)).opposite
|
||||
letI : LE β := (inferInstance : LE β).opposite
|
||||
List.minOn_eq_get_minOn? (f := f) h
|
||||
|
||||
protected theorem maxOn?_eq_some_maxOn [LE β] [DecidableLE β] {f : α → β} {xs : List α}
|
||||
(h : xs ≠ []) : xs.maxOn? f = some (xs.maxOn f h) :=
|
||||
letI : LE β := (inferInstanceAs (LE β)).opposite
|
||||
letI : LE β := (inferInstance : LE β).opposite
|
||||
List.minOn?_eq_some_minOn (f := f) h
|
||||
|
||||
@[simp]
|
||||
protected theorem get_maxOn? [LE β] [DecidableLE β] {f : α → β} {xs : List α}
|
||||
(h : xs ≠ []) : (xs.maxOn? f).get (List.isSome_maxOn?_iff.mpr h) = xs.maxOn f h :=
|
||||
letI : LE β := (inferInstanceAs (LE β)).opposite
|
||||
letI : LE β := (inferInstance : LE β).opposite
|
||||
List.get_minOn? (f := f) h
|
||||
|
||||
protected theorem maxOn_eq_of_maxOn?_eq_some
|
||||
[LE β] [DecidableLE β] {f : α → β} {xs : List α} {x : α} (h : xs.maxOn? f = some x) :
|
||||
xs.maxOn f (List.isSome_maxOn?_iff.mp (Option.isSome_of_eq_some h)) = x :=
|
||||
letI : LE β := (inferInstanceAs (LE β)).opposite
|
||||
letI : LE β := (inferInstance : LE β).opposite
|
||||
List.minOn_eq_of_minOn?_eq_some (f := f) h
|
||||
|
||||
protected theorem isSome_maxOn?_of_mem
|
||||
[LE β] [DecidableLE β] {f : α → β} {xs : List α} {x : α} (h : x ∈ xs) :
|
||||
(xs.maxOn? f).isSome :=
|
||||
letI : LE β := (inferInstanceAs (LE β)).opposite
|
||||
letI : LE β := (inferInstance : LE β).opposite
|
||||
List.isSome_minOn?_of_mem (f := f) h
|
||||
|
||||
protected theorem le_apply_get_maxOn?_of_mem
|
||||
[LE β] [DecidableLE β] [IsLinearPreorder β] {f : α → β} {xs : List α} {x : α} (h : x ∈ xs) :
|
||||
f x ≤ f ((xs.maxOn? f).get (List.isSome_maxOn?_of_mem h)) := by
|
||||
simp only [List.maxOn?_eq_minOn?]
|
||||
letI : LE β := (inferInstanceAs (LE β)).opposite
|
||||
letI : LE β := (inferInstance : LE β).opposite
|
||||
simpa [LE.le_opposite_iff] using List.apply_get_minOn?_le_of_mem (f := f) h
|
||||
|
||||
protected theorem maxOn?_mem [LE β] [DecidableLE β] {xs : List α}
|
||||
{f : α → β} (h : xs.maxOn? f = some a) : a ∈ xs :=
|
||||
letI : LE β := (inferInstanceAs (LE β)).opposite
|
||||
letI : LE β := (inferInstance : LE β).opposite
|
||||
List.minOn?_mem (f := f) h
|
||||
|
||||
protected theorem maxOn?_replicate [LE β] [DecidableLE β] [IsLinearPreorder β]
|
||||
{n : Nat} {a : α} {f : α → β} :
|
||||
(replicate n a).maxOn? f = if n = 0 then none else some a :=
|
||||
letI : LE β := (inferInstanceAs (LE β)).opposite
|
||||
letI : LE β := (inferInstance : LE β).opposite
|
||||
List.minOn?_replicate
|
||||
|
||||
@[simp]
|
||||
protected theorem maxOn?_replicate_of_pos [LE β] [DecidableLE β] [IsLinearPreorder β]
|
||||
{n : Nat} {a : α} {f : α → β} (h : 0 < n) :
|
||||
(replicate n a).maxOn? f = some a :=
|
||||
letI : LE β := (inferInstanceAs (LE β)).opposite
|
||||
letI : LE β := (inferInstance : LE β).opposite
|
||||
List.minOn?_replicate_of_pos (f := f) h
|
||||
|
||||
@[simp]
|
||||
@@ -678,7 +678,7 @@ protected theorem maxOn?_append [LE β] [DecidableLE β] [IsLinearPreorder β]
|
||||
have : maxOn f = (letI : LE β := LE.opposite inferInstance; minOn f) := by
|
||||
ext; simp only [maxOn_eq_minOn]
|
||||
simp only [List.maxOn?_eq_minOn?, this]
|
||||
letI : LE β := (inferInstanceAs (LE β)).opposite
|
||||
letI : LE β := (inferInstance : LE β).opposite
|
||||
exact List.minOn?_append xs ys f
|
||||
|
||||
end List
|
||||
|
||||
@@ -1297,6 +1297,31 @@ instance [DecidableEq α] (l₁ l₂ : List α) : Decidable (l₁ <+: l₂) :=
|
||||
instance [DecidableEq α] (l₁ l₂ : List α) : Decidable (l₁ <:+ l₂) :=
|
||||
decidable_of_iff (l₁.isSuffixOf l₂) isSuffixOf_iff_suffix
|
||||
|
||||
/-
|
||||
Used internally by the `cbv` tactic.
|
||||
-/
|
||||
theorem isInfixOf_internal_iff_isInfix [BEq α] [LawfulBEq α] {l₁ l₂ : List α} :
|
||||
l₁.isInfixOf_internal l₂ ↔ l₁ <:+: l₂ := by
|
||||
induction l₂ with
|
||||
| nil => simp [isInfixOf_internal, IsInfix]
|
||||
| cons a l₂ ih =>
|
||||
simp only [isInfixOf_internal, Bool.or_eq_true]
|
||||
constructor
|
||||
· rintro (h | h)
|
||||
· exact (isPrefixOf_iff_prefix.mp h).isInfix
|
||||
· exact infix_cons <| ih.mp h
|
||||
· intro ⟨s, t, h⟩
|
||||
match s with
|
||||
| [] => left; exact isPrefixOf_iff_prefix.mpr ⟨t, h⟩
|
||||
| a' :: s' =>
|
||||
right; exact ih.mpr ⟨s', t, List.cons.inj h |>.2⟩
|
||||
|
||||
/-
|
||||
Used internally by the `cbv` tactic.
|
||||
-/
|
||||
instance [DecidableEq α] (l₁ l₂ : List α) : Decidable (l₁ <:+: l₂) :=
|
||||
decidable_of_iff (l₁.isInfixOf_internal l₂) isInfixOf_internal_iff_isInfix
|
||||
|
||||
theorem prefix_iff_eq_append : l₁ <+: l₂ ↔ l₁ ++ drop (length l₁) l₂ = l₂ :=
|
||||
⟨by rintro ⟨r, rfl⟩; rw [drop_left], fun e => ⟨_, e⟩⟩
|
||||
|
||||
|
||||
@@ -298,7 +298,7 @@ theorem ofDigitChars_cons {c : Char} {cs : List Char} {init : Nat} :
|
||||
simp [ofDigitChars]
|
||||
|
||||
theorem ofDigitChars_cons_digitChar_of_lt_ten {n : Nat} (hn : n < 10) {cs : List Char} {init : Nat} :
|
||||
ofDigitChars 10 (n.digitChar :: cs) init = ofDigitChars 10 cs (10 * init + n) := by
|
||||
ofDigitChars b (n.digitChar :: cs) init = ofDigitChars b cs (b * init + n) := by
|
||||
simp [ofDigitChars_cons, Nat.toNat_digitChar_sub_48_of_lt_ten hn]
|
||||
|
||||
theorem ofDigitChars_eq_ofDigitChars_zero {l : List Char} {init : Nat} :
|
||||
@@ -320,15 +320,17 @@ theorem ofDigitChars_replicate_zero {n : Nat} : ofDigitChars b (List.replicate n
|
||||
| zero => simp
|
||||
| succ n ih => simp [List.replicate_succ, ofDigitChars_cons, ih, Nat.pow_succ, Nat.mul_assoc]
|
||||
|
||||
@[simp]
|
||||
theorem ofDigitChars_toDigits {n : Nat} : ofDigitChars 10 (toDigits 10 n) 0 = n := by
|
||||
have : 1 < 10 := by decide
|
||||
induction n using base_induction 10 this with
|
||||
theorem ofDigitChars_toDigits {b n : Nat} (hb' : 1 < b) (hb : b ≤ 10) : ofDigitChars b (toDigits b n) 0 = n := by
|
||||
induction n using base_induction b hb' with
|
||||
| single m hm =>
|
||||
simp [Nat.toDigits_of_lt_base hm, ofDigitChars_cons_digitChar_of_lt_ten hm]
|
||||
simp [Nat.toDigits_of_lt_base hm, ofDigitChars_cons_digitChar_of_lt_ten (by omega : m < 10)]
|
||||
| digit m k hk hm ih =>
|
||||
rw [← Nat.toDigits_append_toDigits this hm hk,
|
||||
rw [← Nat.toDigits_append_toDigits hb' hm hk,
|
||||
ofDigitChars_append, ih, Nat.toDigits_of_lt_base hk,
|
||||
ofDigitChars_cons_digitChar_of_lt_ten hk, ofDigitChars_nil]
|
||||
ofDigitChars_cons_digitChar_of_lt_ten (Nat.lt_of_lt_of_le hk hb), ofDigitChars_nil]
|
||||
|
||||
@[simp]
|
||||
theorem ofDigitChars_ten_toDigits {n : Nat} : ofDigitChars 10 (toDigits 10 n) 0 = n :=
|
||||
ofDigitChars_toDigits (by decide) (by decide)
|
||||
|
||||
end Nat
|
||||
|
||||
@@ -9,7 +9,7 @@ prelude
|
||||
public import Init.Data.Order.Ord
|
||||
public import Init.Data.String.Basic
|
||||
import Init.Data.Char.Lemmas
|
||||
import Init.Data.String.Lemmas
|
||||
import Init.Data.String.Lemmas.StringOrder
|
||||
|
||||
public section
|
||||
|
||||
|
||||
@@ -243,6 +243,10 @@ public theorem lt_iff_le_and_ne [LE α] [LT α] [LawfulOrderLT α] [IsPartialOrd
|
||||
a < b ↔ a ≤ b ∧ a ≠ b := by
|
||||
simpa [le_iff_lt_or_eq, or_and_right] using Std.ne_of_lt
|
||||
|
||||
public theorem lt_trichotomy [LT α] [Std.Trichotomous (α := α) (· < ·)] (a b : α) :
|
||||
a < b ∨ a = b ∨ b < a :=
|
||||
Trichotomous.rel_or_eq_or_rel_swap
|
||||
|
||||
end LT
|
||||
end Std
|
||||
|
||||
|
||||
@@ -39,8 +39,8 @@ public theorem minOn_id [Min α] [LE α] [DecidableLE α] [LawfulOrderLeftLeanin
|
||||
|
||||
public theorem maxOn_id [Max α] [LE α] [DecidableLE α] [LawfulOrderLeftLeaningMax α] {x y : α} :
|
||||
maxOn id x y = max x y := by
|
||||
letI : LE α := (inferInstanceAs (LE α)).opposite
|
||||
letI : Min α := (inferInstanceAs (Max α)).oppositeMin
|
||||
letI : LE α := (inferInstance : LE α).opposite
|
||||
letI : Min α := (inferInstance : Max α).oppositeMin
|
||||
simp [maxOn, minOn_id, Max.min_oppositeMin, this]
|
||||
|
||||
public theorem minOn_eq_or [LE β] [DecidableLE β] {f : α → β} {x y : α} :
|
||||
@@ -168,32 +168,32 @@ public theorem maxOn_eq_right_of_lt
|
||||
[LE β] [DecidableLE β] [LT β] [Total (α := β) (· ≤ ·)] [LawfulOrderLT β]
|
||||
{f : α → β} {x y : α} (h : f x < f y) :
|
||||
maxOn f x y = y :=
|
||||
letI : LE β := (inferInstanceAs (LE β)).opposite
|
||||
letI : LT β := (inferInstanceAs (LT β)).opposite
|
||||
letI : LE β := (inferInstance : LE β).opposite
|
||||
letI : LT β := (inferInstance : LT β).opposite
|
||||
minOn_eq_right_of_lt (h := by simpa [LT.lt_opposite_iff] using h) ..
|
||||
|
||||
public theorem left_le_apply_maxOn [le : LE β] [DecidableLE β] [IsLinearPreorder β] {f : α → β}
|
||||
{x y : α} : f x ≤ f (maxOn f x y) := by
|
||||
rw [maxOn_eq_minOn]
|
||||
letI : LE β := (inferInstanceAs (LE β)).opposite
|
||||
letI : LE β := (inferInstance : LE β).opposite
|
||||
simpa only [LE.le_opposite_iff] using apply_minOn_le_left (f := f) ..
|
||||
|
||||
public theorem right_le_apply_maxOn [LE β] [DecidableLE β] [IsLinearPreorder β] {f : α → β}
|
||||
{x y : α} : f y ≤ f (maxOn f x y) := by
|
||||
rw [maxOn_eq_minOn]
|
||||
letI : LE β := (inferInstanceAs (LE β)).opposite
|
||||
letI : LE β := (inferInstance : LE β).opposite
|
||||
simpa only [LE.le_opposite_iff] using apply_minOn_le_right (f := f)
|
||||
|
||||
public theorem apply_maxOn_le_iff [LE β] [DecidableLE β] [IsLinearPreorder β] {f : α → β}
|
||||
{x y : α} {b : β} :
|
||||
f (maxOn f x y) ≤ b ↔ f x ≤ b ∧ f y ≤ b := by
|
||||
rw [maxOn_eq_minOn]
|
||||
letI : LE β := (inferInstanceAs (LE β)).opposite
|
||||
letI : LE β := (inferInstance : LE β).opposite
|
||||
simpa only [LE.le_opposite_iff] using le_apply_minOn_iff (f := f)
|
||||
|
||||
public theorem maxOn_assoc [LE β] [DecidableLE β] [IsLinearPreorder β] {f : α → β}
|
||||
{x y z : α} : maxOn f (maxOn f x y) z = maxOn f x (maxOn f y z) :=
|
||||
letI : LE β := (inferInstanceAs (LE β)).opposite
|
||||
letI : LE β := (inferInstance : LE β).opposite
|
||||
minOn_assoc (f := f)
|
||||
|
||||
public instance [LE β] [DecidableLE β] [IsLinearPreorder β] {f : α → β} :
|
||||
@@ -203,8 +203,8 @@ public instance [LE β] [DecidableLE β] [IsLinearPreorder β] {f : α → β} :
|
||||
|
||||
public theorem max_apply [LE β] [DecidableLE β] [Max β] [LawfulOrderLeftLeaningMax β]
|
||||
{f : α → β} {x y : α} : max (f x) (f y) = f (maxOn f x y) := by
|
||||
letI : LE β := (inferInstanceAs (LE β)).opposite
|
||||
letI : Min β := (inferInstanceAs (Max β)).oppositeMin
|
||||
letI : LE β := (inferInstance : LE β).opposite
|
||||
letI : Min β := (inferInstance : Max β).oppositeMin
|
||||
simpa [Max.min_oppositeMin] using min_apply (f := f)
|
||||
|
||||
public theorem apply_maxOn [LE β] [DecidableLE β] [Max β] [LawfulOrderLeftLeaningMax β]
|
||||
|
||||
@@ -44,7 +44,7 @@ def min' [LE α] [DecidableLE α] (a b : α) : α :=
|
||||
|
||||
open scoped Std.OppositeOrderInstances in
|
||||
def max' [LE α] [DecidableLE α] (a b : α) : α :=
|
||||
letI : LE α := (inferInstanceAs (LE α)).opposite
|
||||
letI : LE α := (inferInstance : LE α).opposite
|
||||
-- `DecidableLE` for the opposite order is derived automatically via `OppositeOrderInstances`
|
||||
min' a b
|
||||
```
|
||||
|
||||
@@ -411,6 +411,7 @@ private theorem Rii.Internal.toArray_eq_toArray_iter [Least? α]
|
||||
r.toArray = (Internal.iter r).toArray := by
|
||||
rfl
|
||||
|
||||
@[cbv_eval]
|
||||
public theorem Rxc.Iterator.toList_eq_match [LE α] [DecidableLE α]
|
||||
[UpwardEnumerable α] [Rxc.IsAlwaysFinite α] [LawfulUpwardEnumerable α]
|
||||
[LawfulUpwardEnumerableLE α]
|
||||
@@ -428,6 +429,7 @@ public theorem Rxc.Iterator.toList_eq_match [LE α] [DecidableLE α]
|
||||
· simp [*]
|
||||
· split <;> rename_i heq' <;> simp [*]
|
||||
|
||||
@[cbv_eval]
|
||||
public theorem Rxc.Iterator.toArray_eq_match [LE α] [DecidableLE α]
|
||||
[UpwardEnumerable α] [Rxc.IsAlwaysFinite α] [LawfulUpwardEnumerable α]
|
||||
[LawfulUpwardEnumerableLE α]
|
||||
@@ -443,6 +445,7 @@ public theorem Rxc.Iterator.toArray_eq_match [LE α] [DecidableLE α]
|
||||
· rfl
|
||||
· split <;> simp
|
||||
|
||||
@[cbv_eval]
|
||||
public theorem Rxo.Iterator.toList_eq_match [LT α] [DecidableLT α]
|
||||
[UpwardEnumerable α] [Rxo.IsAlwaysFinite α] [LawfulUpwardEnumerable α]
|
||||
[LawfulUpwardEnumerableLT α]
|
||||
@@ -459,6 +462,7 @@ public theorem Rxo.Iterator.toList_eq_match [LT α] [DecidableLT α]
|
||||
· simp [*]
|
||||
· split <;> rename_i heq' <;> simp [*]
|
||||
|
||||
@[cbv_eval]
|
||||
public theorem Rxo.Iterator.toArray_eq_match [LT α] [DecidableLT α]
|
||||
[UpwardEnumerable α] [Rxo.IsAlwaysFinite α] [LawfulUpwardEnumerable α]
|
||||
[LawfulUpwardEnumerableLT α]
|
||||
@@ -491,6 +495,7 @@ public theorem Rxc.Iterator.toList_eq_toList_rxoIterator [LE α] [DecidableLE α
|
||||
· simpa [UpwardEnumerable.lt_iff, UpwardEnumerable.le_iff, UpwardEnumerable.lt_succ_iff] using h
|
||||
· simpa [UpwardEnumerable.lt_iff, UpwardEnumerable.le_iff, UpwardEnumerable.lt_succ_iff] using h
|
||||
|
||||
@[cbv_eval]
|
||||
public theorem Rxi.Iterator.toList_eq_match
|
||||
[UpwardEnumerable α] [Rxi.IsAlwaysFinite α] [LawfulUpwardEnumerable α]
|
||||
{it : Iter (α := Rxi.Iterator α) α} :
|
||||
@@ -502,6 +507,7 @@ public theorem Rxi.Iterator.toList_eq_match
|
||||
simp only [Iter.toList_eq_match_step (it := it), Rxi.Iterator.step_eq_step, Rxi.Iterator.step]
|
||||
split <;> rename_i heq <;> simp [*]
|
||||
|
||||
@[cbv_eval]
|
||||
public theorem Rxi.Iterator.toArray_eq_match
|
||||
[UpwardEnumerable α] [Rxi.IsAlwaysFinite α] [LawfulUpwardEnumerable α]
|
||||
{it : Iter (α := Rxi.Iterator α) α} :
|
||||
@@ -608,6 +614,7 @@ namespace Rcc
|
||||
|
||||
variable {r : Rcc α}
|
||||
|
||||
@[cbv_eval]
|
||||
public theorem toList_eq_if_roc [LE α] [DecidableLE α] [UpwardEnumerable α]
|
||||
[LawfulUpwardEnumerable α] [LawfulUpwardEnumerableLE α] [Rxc.IsAlwaysFinite α] :
|
||||
r.toList = if r.lower ≤ r.upper then
|
||||
@@ -755,6 +762,7 @@ public theorem ClosedOpen.toList_succ_succ_eq_map [LE α] [DecidableLE α] [Upwa
|
||||
(lo...=hi).toList.map succ :=
|
||||
Rcc.toList_succ_succ_eq_map
|
||||
|
||||
@[cbv_eval]
|
||||
public theorem forIn'_eq_forIn'_toList [LE α] [DecidableLE α] [UpwardEnumerable α]
|
||||
[LawfulUpwardEnumerableLE α] [Rxc.IsAlwaysFinite α] [LawfulUpwardEnumerable α]
|
||||
{γ : Type u} {init : γ} {m : Type u → Type w} [Monad m] [LawfulMonad m]
|
||||
@@ -844,6 +852,7 @@ namespace Rco
|
||||
|
||||
variable {r : Rco α}
|
||||
|
||||
@[cbv_eval]
|
||||
public theorem toList_eq_if_roo [UpwardEnumerable α] [LT α] [DecidableLT α]
|
||||
[LawfulUpwardEnumerable α] [Rxo.IsAlwaysFinite α] [LawfulUpwardEnumerableLT α] :
|
||||
r.toList = if r.lower < r.upper then
|
||||
@@ -1011,6 +1020,7 @@ public theorem toArray_succ_succ_eq_map [LE α] [DecidableLE α] [LT α] [Decida
|
||||
(lo...hi).toArray.map succ := by
|
||||
simp [← toArray_toList, toList_succ_succ_eq_map]
|
||||
|
||||
@[cbv_eval]
|
||||
public theorem forIn'_eq_forIn'_toList [LE α] [LT α] [DecidableLT α]
|
||||
[UpwardEnumerable α] [LawfulUpwardEnumerableLE α] [LawfulUpwardEnumerableLT α]
|
||||
[Rxo.IsAlwaysFinite α] [LawfulUpwardEnumerable α]
|
||||
@@ -1224,6 +1234,7 @@ public theorem toArray_succ_succ_eq_map [LE α] [DecidableLE α]
|
||||
((succ lo)...*).toArray = (lo...*).toArray.map succ := by
|
||||
simp [← toArray_toList, toList_succ_succ_eq_map]
|
||||
|
||||
@[cbv_eval]
|
||||
public theorem forIn'_eq_forIn'_toList [LE α]
|
||||
[UpwardEnumerable α] [LawfulUpwardEnumerableLE α]
|
||||
[Rxi.IsAlwaysFinite α] [LawfulUpwardEnumerable α]
|
||||
@@ -1330,6 +1341,7 @@ public theorem toArray_eq_match [LE α] [DecidableLE α] [UpwardEnumerable α]
|
||||
rw [Internal.toArray_eq_toArray_iter, Rxc.Iterator.toArray_eq_match (it := Internal.iter r)]
|
||||
simp [Internal.iter, Internal.toArray_eq_toArray_iter]
|
||||
|
||||
@[cbv_eval]
|
||||
public theorem toList_eq_match_rcc [LE α] [DecidableLE α] [UpwardEnumerable α]
|
||||
[LawfulUpwardEnumerable α] [LawfulUpwardEnumerableLE α] [Rxc.IsAlwaysFinite α] :
|
||||
r.toList = match UpwardEnumerable.succ? r.lower with
|
||||
@@ -1473,6 +1485,7 @@ public theorem toArray_succ_succ_eq_map [LE α] [DecidableLE α] [LT α] [Decida
|
||||
(lo<...=hi).toArray.map succ := by
|
||||
simp [← toArray_toList, toList_succ_succ_eq_map]
|
||||
|
||||
@[cbv_eval]
|
||||
public theorem forIn'_eq_forIn'_toList [LE α] [DecidableLE α] [LT α]
|
||||
[UpwardEnumerable α] [LawfulUpwardEnumerableLE α] [LawfulUpwardEnumerableLT α]
|
||||
[Rxc.IsAlwaysFinite α] [LawfulUpwardEnumerable α] {γ : Type u} {init : γ} {m : Type u → Type w}
|
||||
@@ -1572,6 +1585,7 @@ public theorem toArray_eq_match [LE α] [LT α] [DecidableLT α] [UpwardEnumerab
|
||||
#[] := by
|
||||
rw [Internal.toArray_eq_toArray_iter, Rxo.Iterator.toArray_eq_match]; rfl
|
||||
|
||||
@[cbv_eval]
|
||||
public theorem toList_eq_match_rco [UpwardEnumerable α] [LT α] [DecidableLT α]
|
||||
[LawfulUpwardEnumerable α] [LawfulUpwardEnumerableLT α] [Rxo.IsAlwaysFinite α] :
|
||||
r.toList = match UpwardEnumerable.succ? r.lower with
|
||||
@@ -1705,6 +1719,7 @@ public theorem toArray_succ_succ_eq_map [LT α] [DecidableLT α]
|
||||
(lo<...hi).toArray.map succ := by
|
||||
simp [← toArray_toList, toList_succ_succ_eq_map]
|
||||
|
||||
@[cbv_eval]
|
||||
public theorem forIn'_eq_forIn'_toList [LT α] [DecidableLT α]
|
||||
[UpwardEnumerable α] [LawfulUpwardEnumerableLT α]
|
||||
[Rxo.IsAlwaysFinite α] [LawfulUpwardEnumerable α]
|
||||
@@ -1939,6 +1954,7 @@ public theorem toArray_succ_succ_eq_map [LT α] [DecidableLT α]
|
||||
((succ lo)<...*).toArray = (lo<...*).toArray.map succ := by
|
||||
simp [← toArray_toList, toList_succ_succ_eq_map]
|
||||
|
||||
@[cbv_eval]
|
||||
public theorem forIn'_eq_forIn'_toList [LT α]
|
||||
[UpwardEnumerable α] [LawfulUpwardEnumerableLT α]
|
||||
[Rxi.IsAlwaysFinite α] [LawfulUpwardEnumerable α]
|
||||
@@ -2039,6 +2055,7 @@ public theorem toList_toArray [Least? α] [LE α] [DecidableLE α] [UpwardEnumer
|
||||
r.toArray.toList = r.toList := by
|
||||
simp [Internal.toList_eq_toList_iter, Internal.toArray_eq_toArray_iter]
|
||||
|
||||
@[cbv_eval]
|
||||
public theorem toList_eq_match_rcc [LE α] [DecidableLE α] [Least? α] [UpwardEnumerable α]
|
||||
[LawfulUpwardEnumerable α] [LawfulUpwardEnumerableLE α]
|
||||
[Rxc.IsAlwaysFinite α] :
|
||||
@@ -2231,6 +2248,7 @@ public theorem toArray_succ_eq_map [LE α] [DecidableLE α] [Least? α]
|
||||
#[UpwardEnumerable.least (hn := ⟨r.upper⟩)] ++ (*...=hi).toArray.map succ := by
|
||||
simp [← toArray_toList, toList_succ_eq_map]
|
||||
|
||||
@[cbv_eval]
|
||||
public theorem forIn'_eq_forIn'_toList [LE α] [DecidableLE α] [Least? α]
|
||||
[UpwardEnumerable α] [LawfulUpwardEnumerableLE α] [LawfulUpwardEnumerableLeast? α]
|
||||
[Rxc.IsAlwaysFinite α] [LawfulUpwardEnumerable α] {γ : Type u} {init : γ} {m : Type u → Type w}
|
||||
@@ -2340,6 +2358,7 @@ public theorem toList_toArray [Least? α] [LT α] [DecidableLT α] [UpwardEnumer
|
||||
r.toArray.toList = r.toList := by
|
||||
simp [Internal.toList_eq_toList_iter, Internal.toArray_eq_toArray_iter]
|
||||
|
||||
@[cbv_eval]
|
||||
public theorem toList_eq_match_rco [LT α] [DecidableLT α] [Least? α] [UpwardEnumerable α]
|
||||
[LawfulUpwardEnumerable α] [LawfulUpwardEnumerableLT α]
|
||||
[Rxo.IsAlwaysFinite α] :
|
||||
@@ -2550,6 +2569,7 @@ public theorem toArray_succ_eq_map [LT α] [DecidableLT α] [Least? α]
|
||||
#[UpwardEnumerable.least (hn := ⟨r.upper⟩)] ++ (*...hi).toArray.map succ := by
|
||||
simp [← toArray_toList, toList_succ_eq_map]
|
||||
|
||||
@[cbv_eval]
|
||||
public theorem forIn'_eq_forIn'_toList [LT α] [DecidableLT α] [Least? α]
|
||||
[UpwardEnumerable α] [LawfulUpwardEnumerableLT α] [LawfulUpwardEnumerableLeast? α]
|
||||
[Rxo.IsAlwaysFinite α] [LawfulUpwardEnumerable α] {γ : Type u} {init : γ} {m : Type u → Type w}
|
||||
@@ -2788,6 +2808,7 @@ public theorem pairwise_toList_le [LE α] [Least? α]
|
||||
|> List.Pairwise.imp UpwardEnumerable.le_of_lt
|
||||
|> List.Pairwise.imp (fun hle => (UpwardEnumerable.le_iff ..).mpr hle)
|
||||
|
||||
@[cbv_eval]
|
||||
public theorem forIn'_eq_forIn'_toList [Least? α]
|
||||
[UpwardEnumerable α] [LawfulUpwardEnumerableLeast? α]
|
||||
[Rxi.IsAlwaysFinite α] [LawfulUpwardEnumerable α] {γ : Type u} {init : γ} {m : Type u → Type w}
|
||||
|
||||
@@ -354,16 +354,6 @@ end Nat
|
||||
instance : Repr Nat where
|
||||
reprPrec n _ := Nat.repr n
|
||||
|
||||
/--
|
||||
Returns the decimal string representation of an integer.
|
||||
-/
|
||||
protected def Int.repr : Int → String
|
||||
| ofNat m => Nat.repr m
|
||||
| negSucc m => String.Internal.append "-" (Nat.repr (succ m))
|
||||
|
||||
instance : Repr Int where
|
||||
reprPrec i prec := if i < 0 then Repr.addAppParen i.repr prec else i.repr
|
||||
|
||||
def hexDigitRepr (n : Nat) : String :=
|
||||
String.singleton <| Nat.digitChar n
|
||||
|
||||
|
||||
@@ -126,7 +126,7 @@ public theorem forIn_toList {α : Type u} {s : Subarray α}
|
||||
ForIn.forIn s.toList init f = ForIn.forIn s init f :=
|
||||
Slice.forIn_toList
|
||||
|
||||
@[grind =]
|
||||
@[cbv_eval, grind =]
|
||||
public theorem forIn_eq_forIn_toList {α : Type u} {s : Subarray α}
|
||||
{m : Type v → Type w} [Monad m] [LawfulMonad m] {γ : Type v} {init : γ}
|
||||
{f : α → γ → m (ForInStep γ)} :
|
||||
@@ -193,6 +193,7 @@ public theorem Array.toSubarray_eq_toSubarray_of_min_eq_min {xs : Array α}
|
||||
simp [*]; omega
|
||||
· simp
|
||||
|
||||
@[cbv_eval]
|
||||
public theorem Array.toSubarray_eq_min {xs : Array α} {lo hi : Nat} :
|
||||
xs.toSubarray lo hi = ⟨⟨xs, min lo (min hi xs.size), min hi xs.size, Nat.min_le_right _ _,
|
||||
Nat.min_le_right _ _⟩⟩ := by
|
||||
@@ -243,6 +244,7 @@ private theorem Std.Internal.List.extract_eq_drop_take' {l : List α} {start sto
|
||||
List.length_take, ge_iff_le, h₁]
|
||||
omega
|
||||
|
||||
@[cbv_eval]
|
||||
public theorem Subarray.toList_eq_drop_take {xs : Subarray α} :
|
||||
xs.toList = (xs.array.toList.take xs.stop).drop xs.start := by
|
||||
rw [Subarray.toList_eq, Array.toList_extract, Std.Internal.List.extract_eq_drop_take']
|
||||
|
||||
@@ -852,6 +852,10 @@ theorem Slice.rawEndPos_copy {s : Slice} : s.copy.rawEndPos = s.rawEndPos := by
|
||||
theorem copy_toSlice {s : String} : s.toSlice.copy = s := by
|
||||
simp [← toByteArray_inj, Slice.toByteArray_copy, ← size_toByteArray]
|
||||
|
||||
@[simp]
|
||||
theorem copy_comp_toSlice : String.Slice.copy ∘ String.toSlice = id := by
|
||||
ext; simp
|
||||
|
||||
theorem Slice.getUTF8Byte_eq_getUTF8Byte_copy {s : Slice} {p : Pos.Raw} {h : p < s.rawEndPos} :
|
||||
s.getUTF8Byte p h = s.copy.getUTF8Byte p (by simpa) := by
|
||||
simp [getUTF8Byte, String.getUTF8Byte, toByteArray_copy, ByteArray.getElem_extract]
|
||||
@@ -1382,6 +1386,11 @@ theorem Slice.copy_eq_copy_sliceTo {s : Slice} {pos : s.Pos} :
|
||||
rw [Nat.max_eq_right]
|
||||
exact pos.offset_str_le_offset_endExclusive
|
||||
|
||||
@[simp]
|
||||
theorem Slice.sliceTo_append_sliceFrom {s : Slice} {pos : s.Pos} :
|
||||
(s.sliceTo pos).copy ++ (s.sliceFrom pos).copy = s.copy :=
|
||||
copy_eq_copy_sliceTo.symm
|
||||
|
||||
/-- Given a slice `s` and a position on `s.copy`, obtain the corresponding position on `s`. -/
|
||||
@[inline]
|
||||
def Pos.ofCopy {s : Slice} (pos : s.copy.Pos) : s.Pos where
|
||||
@@ -1741,6 +1750,31 @@ theorem Slice.Pos.offset_cast {s t : Slice} {pos : s.Pos} {h : s.copy = t.copy}
|
||||
theorem Slice.Pos.cast_rfl {s : Slice} {pos : s.Pos} : pos.cast rfl = pos :=
|
||||
Slice.Pos.ext (by simp)
|
||||
|
||||
@[simp]
|
||||
theorem Slice.Pos.cast_cast {s t u : Slice} {hst : s.copy = t.copy} {htu : t.copy = u.copy}
|
||||
{pos : s.Pos} : (pos.cast hst).cast htu = pos.cast (hst.trans htu) :=
|
||||
Slice.Pos.ext (by simp)
|
||||
|
||||
@[simp]
|
||||
theorem Slice.Pos.cast_inj {s t : Slice} {hst : s.copy = t.copy} {p q : s.Pos} : p.cast hst = q.cast hst ↔ p = q := by
|
||||
simp [Slice.Pos.ext_iff]
|
||||
|
||||
@[simp]
|
||||
theorem Slice.Pos.cast_startPos {s t : Slice} {hst : s.copy = t.copy} : s.startPos.cast hst = t.startPos :=
|
||||
Slice.Pos.ext (by simp)
|
||||
|
||||
@[simp]
|
||||
theorem Slice.Pos.cast_eq_startPos {s t : Slice} {p : s.Pos} {hst : s.copy = t.copy} : p.cast hst = t.startPos ↔ p = s.startPos := by
|
||||
rw [← cast_startPos (hst := hst), Pos.cast_inj]
|
||||
|
||||
@[simp]
|
||||
theorem Slice.Pos.cast_endPos {s t : Slice} {hst : s.copy = t.copy} : s.endPos.cast hst = t.endPos :=
|
||||
Slice.Pos.ext (by simp [← rawEndPos_copy, hst])
|
||||
|
||||
@[simp]
|
||||
theorem Slice.Pos.cast_eq_endPos {s t : Slice} {p : s.Pos} {hst : s.copy = t.copy} : p.cast hst = t.endPos ↔ p = s.endPos := by
|
||||
rw [← cast_endPos (hst := hst), Pos.cast_inj]
|
||||
|
||||
@[simp]
|
||||
theorem Slice.Pos.cast_le_cast_iff {s t : Slice} {pos pos' : s.Pos} {h : s.copy = t.copy} :
|
||||
pos.cast h ≤ pos'.cast h ↔ pos ≤ pos' := by
|
||||
@@ -1751,6 +1785,22 @@ theorem Slice.Pos.cast_lt_cast_iff {s t : Slice} {pos pos' : s.Pos} {h : s.copy
|
||||
pos.cast h < pos'.cast h ↔ pos < pos' := by
|
||||
simp [Slice.Pos.lt_iff]
|
||||
|
||||
theorem Slice.Pos.cast_le_iff {s t : Slice} {pos : s.Pos} {pos' : t.Pos} {h : s.copy = t.copy} :
|
||||
pos.cast h ≤ pos' ↔ pos ≤ pos'.cast h.symm := by
|
||||
simp [Slice.Pos.le_iff]
|
||||
|
||||
theorem Slice.Pos.le_cast_iff {s t : Slice} {pos : t.Pos} {pos' : s.Pos} {h : s.copy = t.copy} :
|
||||
pos ≤ pos'.cast h ↔ pos.cast h.symm ≤ pos' := by
|
||||
simp [Slice.Pos.le_iff]
|
||||
|
||||
theorem Slice.Pos.cast_lt_iff {s t : Slice} {pos : s.Pos} {pos' : t.Pos} {h : s.copy = t.copy} :
|
||||
pos.cast h < pos' ↔ pos < pos'.cast h.symm := by
|
||||
simp [Slice.Pos.lt_iff]
|
||||
|
||||
theorem Slice.Pos.lt_cast_iff {s t : Slice} {pos : t.Pos} {pos' : s.Pos} {h : s.copy = t.copy} :
|
||||
pos < pos'.cast h ↔ pos.cast h.symm < pos' := by
|
||||
simp [Slice.Pos.lt_iff]
|
||||
|
||||
/-- Constructs a valid position on `t` from a valid position on `s` and a proof that `s = t`. -/
|
||||
@[inline]
|
||||
def Pos.cast {s t : String} (pos : s.Pos) (h : s = t) : t.Pos where
|
||||
@@ -1765,6 +1815,31 @@ theorem Pos.offset_cast {s t : String} {pos : s.Pos} {h : s = t} :
|
||||
theorem Pos.cast_rfl {s : String} {pos : s.Pos} : pos.cast rfl = pos :=
|
||||
Pos.ext (by simp)
|
||||
|
||||
@[simp]
|
||||
theorem Pos.cast_cast {s t u : String} {hst : s = t} {htu : t = u}
|
||||
{pos : s.Pos} : (pos.cast hst).cast htu = pos.cast (hst.trans htu) :=
|
||||
Pos.ext (by simp)
|
||||
|
||||
@[simp]
|
||||
theorem Pos.cast_inj {s t : String} {hst : s = t} {p q : s.Pos} : p.cast hst = q.cast hst ↔ p = q := by
|
||||
simp [Pos.ext_iff]
|
||||
|
||||
@[simp]
|
||||
theorem Pos.cast_startPos {s t : String} {hst : s = t} : s.startPos.cast hst = t.startPos := by
|
||||
subst hst; simp
|
||||
|
||||
@[simp]
|
||||
theorem Pos.cast_eq_startPos {s t : String} {hst : s = t} {p : s.Pos} : p.cast hst = t.startPos ↔ p = s.startPos := by
|
||||
rw [← Pos.cast_startPos (hst := hst), Pos.cast_inj]
|
||||
|
||||
@[simp]
|
||||
theorem Pos.cast_endPos {s t : String} {hst : s = t} : s.endPos.cast hst = t.endPos := by
|
||||
subst hst; simp
|
||||
|
||||
@[simp]
|
||||
theorem Pos.cast_eq_endPos {s t : String} {hst : s = t} {p : s.Pos} : p.cast hst = t.endPos ↔ p = s.endPos := by
|
||||
rw [← Pos.cast_endPos (hst := hst), Pos.cast_inj]
|
||||
|
||||
@[simp]
|
||||
theorem Pos.cast_le_cast_iff {s t : String} {pos pos' : s.Pos} {h : s = t} :
|
||||
pos.cast h ≤ pos'.cast h ↔ pos ≤ pos' := by
|
||||
@@ -1775,6 +1850,22 @@ theorem Pos.cast_lt_cast_iff {s t : String} {pos pos' : s.Pos} {h : s = t} :
|
||||
pos.cast h < pos'.cast h ↔ pos < pos' := by
|
||||
cases h; simp
|
||||
|
||||
theorem Pos.cast_le_iff {s t : String} {pos : s.Pos} {pos' : t.Pos} {h : s = t} :
|
||||
pos.cast h ≤ pos' ↔ pos ≤ pos'.cast h.symm := by
|
||||
simp [Pos.le_iff]
|
||||
|
||||
theorem Pos.le_cast_iff {s t : String} {pos : t.Pos} {pos' : s.Pos} {h : s = t} :
|
||||
pos ≤ pos'.cast h ↔ pos.cast h.symm ≤ pos' := by
|
||||
simp [Pos.le_iff]
|
||||
|
||||
theorem Pos.cast_lt_iff {s t : String} {pos : s.Pos} {pos' : t.Pos} {h : s = t} :
|
||||
pos.cast h < pos' ↔ pos < pos'.cast h.symm := by
|
||||
simp [Pos.lt_iff]
|
||||
|
||||
theorem Pos.lt_cast_iff {s t : String} {pos : t.Pos} {pos' : s.Pos} {h : s = t} :
|
||||
pos < pos'.cast h ↔ pos.cast h.symm < pos' := by
|
||||
simp [Pos.lt_iff]
|
||||
|
||||
theorem Pos.copy_toSlice_eq_cast {s : String} (p : s.Pos) :
|
||||
p.toSlice.copy = p.cast copy_toSlice.symm :=
|
||||
Pos.ext (by simp)
|
||||
@@ -2050,6 +2141,10 @@ theorem Pos.le_ofToSlice_iff {s : String} {p : s.Pos} {q : s.toSlice.Pos} :
|
||||
theorem Pos.toSlice_lt_toSlice_iff {s : String} {p q : s.Pos} :
|
||||
p.toSlice < q.toSlice ↔ p < q := Iff.rfl
|
||||
|
||||
@[simp]
|
||||
theorem Pos.toSlice_le_toSlice_iff {s : String} {p q : s.Pos} :
|
||||
p.toSlice ≤ q.toSlice ↔ p ≤ q := Iff.rfl
|
||||
|
||||
theorem Pos.next_le_of_lt {s : String} {p q : s.Pos} {h} : p < q → p.next h ≤ q := by
|
||||
rw [next, Pos.ofToSlice_le_iff, ← Pos.toSlice_lt_toSlice_iff]
|
||||
exact Slice.Pos.next_le_of_lt
|
||||
|
||||
@@ -363,7 +363,7 @@ theorem toBitVec_eq_of_parseFirstByte_eq_threeMore {b : UInt8} (h : parseFirstBy
|
||||
public def isInvalidContinuationByte (b : UInt8) : Bool :=
|
||||
b &&& 0xc0 != 0x80
|
||||
|
||||
theorem isInvalidContinutationByte_eq_false_iff {b : UInt8} :
|
||||
theorem isInvalidContinuationByte_eq_false_iff {b : UInt8} :
|
||||
isInvalidContinuationByte b = false ↔ b &&& 0xc0 = 0x80 := by
|
||||
simp [isInvalidContinuationByte]
|
||||
|
||||
|
||||
@@ -187,6 +187,9 @@ theorem append_right_inj (s : String) {t₁ t₂ : String} :
|
||||
theorem append_assoc {s₁ s₂ s₃ : String} : s₁ ++ s₂ ++ s₃ = s₁ ++ (s₂ ++ s₃) := by
|
||||
simp [← toByteArray_inj, ByteArray.append_assoc]
|
||||
|
||||
instance : Std.Associative (α := String) (· ++ ·) where
|
||||
assoc _ _ _ := append_assoc
|
||||
|
||||
@[simp]
|
||||
theorem utf8ByteSize_eq_zero_iff {s : String} : s.utf8ByteSize = 0 ↔ s = "" := by
|
||||
refine ⟨fun h => ?_, fun h => h ▸ utf8ByteSize_empty⟩
|
||||
|
||||
@@ -6,29 +6,5 @@ Authors: Markus Himmel
|
||||
module
|
||||
|
||||
prelude
|
||||
public import Init.Data.Iterators.Combinators.FilterMap
|
||||
public import Init.Data.Iterators.Consumers.Collect
|
||||
|
||||
set_option doc.verso true
|
||||
|
||||
namespace Std
|
||||
|
||||
/--
|
||||
Convenience function for turning an iterator into a list of strings, provided the output of the
|
||||
iterator implements {name}`ToString`.
|
||||
-/
|
||||
@[inline]
|
||||
public abbrev Iter.toStringList {α β : Type} [Iterator α Id β] [ToString β]
|
||||
(it : Iter (α := α) β) : List String :=
|
||||
it.map toString |>.toList
|
||||
|
||||
/--
|
||||
Convenience function for turning an iterator into an array of strings, provided the output of the
|
||||
iterator implements {name}`ToString`.
|
||||
-/
|
||||
@[inline]
|
||||
public abbrev Iter.toStringArray {α β : Type} [Iterator α Id β] [ToString β]
|
||||
(it : Iter (α := α) β) : Array String :=
|
||||
it.map toString |>.toArray
|
||||
|
||||
end Std
|
||||
public import Init.Data.String.Iter.Basic
|
||||
public import Init.Data.String.Iter.Intercalate
|
||||
|
||||
34
src/Init/Data/String/Iter/Basic.lean
Normal file
34
src/Init/Data/String/Iter/Basic.lean
Normal file
@@ -0,0 +1,34 @@
|
||||
/-
|
||||
Copyright (c) 2026 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.Iterators.Combinators.FilterMap
|
||||
public import Init.Data.Iterators.Consumers.Collect
|
||||
|
||||
set_option doc.verso true
|
||||
|
||||
namespace Std
|
||||
|
||||
/--
|
||||
Convenience function for turning an iterator into a list of strings, provided the output of the
|
||||
iterator implements {name}`ToString`.
|
||||
-/
|
||||
@[inline]
|
||||
public abbrev Iter.toStringList {α β : Type} [Iterator α Id β] [ToString β]
|
||||
(it : Iter (α := α) β) : List String :=
|
||||
it.map toString |>.toList
|
||||
|
||||
/--
|
||||
Convenience function for turning an iterator into an array of strings, provided the output of the
|
||||
iterator implements {name}`ToString`.
|
||||
-/
|
||||
@[inline]
|
||||
public abbrev Iter.toStringArray {α β : Type} [Iterator α Id β] [ToString β]
|
||||
(it : Iter (α := α) β) : Array String :=
|
||||
it.map toString |>.toArray
|
||||
|
||||
end Std
|
||||
37
src/Init/Data/String/Iter/Intercalate.lean
Normal file
37
src/Init/Data/String/Iter/Intercalate.lean
Normal file
@@ -0,0 +1,37 @@
|
||||
/-
|
||||
Copyright (c) 2026 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Julia Markus Himmel
|
||||
-/
|
||||
module
|
||||
|
||||
prelude
|
||||
public import Init.Data.Iterators.Combinators.Monadic.FilterMap
|
||||
public import Init.Data.String.Basic
|
||||
import Init.Data.String.Slice
|
||||
|
||||
set_option doc.verso true
|
||||
|
||||
namespace Std
|
||||
|
||||
/--
|
||||
Appends all the elements in the iterator, in order.
|
||||
-/
|
||||
@[inline]
|
||||
public def Iter.joinString {α β : Type} [Iterator α Id β] [ToString β]
|
||||
(it : Std.Iter (α := α) β) : String :=
|
||||
(it.map toString).fold (init := "") (· ++ ·)
|
||||
|
||||
/--
|
||||
Appends the elements of the iterator into a string, placing the separator {name}`s` between them.
|
||||
-/
|
||||
@[inline]
|
||||
public def Iter.intercalateString {α β : Type} [Iterator α Id β] [ToString β]
|
||||
(s : String.Slice) (it : Std.Iter (α := α) β) : String :=
|
||||
it.map toString
|
||||
|>.fold (init := none) (fun
|
||||
| none, sl => some sl
|
||||
| some str, sl => some (str ++ s ++ sl))
|
||||
|>.getD ""
|
||||
|
||||
end Std
|
||||
@@ -27,6 +27,7 @@ deriving Inhabited
|
||||
/--
|
||||
Creates an iterator over the valid positions within {name}`s`, starting at {name}`p`.
|
||||
-/
|
||||
@[cbv_opaque]
|
||||
def positionsFrom {s : Slice} (p : s.Pos) :
|
||||
Std.Iter (α := PosIterator s) { p : s.Pos // p ≠ s.endPos } :=
|
||||
{ internalState := { currPos := p } }
|
||||
@@ -99,7 +100,7 @@ Examples:
|
||||
* {lean}`"abc".toSlice.chars.toList = ['a', 'b', 'c']`
|
||||
* {lean}`"ab∀c".toSlice.chars.toList = ['a', 'b', '∀', 'c']`
|
||||
-/
|
||||
@[expose, inline]
|
||||
@[cbv_opaque, expose, inline]
|
||||
def chars (s : Slice) :=
|
||||
Std.Iter.map (fun ⟨pos, h⟩ => pos.get h) (positions s)
|
||||
|
||||
@@ -188,7 +189,7 @@ Example:
|
||||
* {lean}`"abc".toSlice.revChars.toList = ['c', 'b', 'a']`
|
||||
* {lean}`"ab∀c".toSlice.revChars.toList = ['c', '∀', 'b', 'a']`
|
||||
-/
|
||||
@[expose, inline]
|
||||
@[cbv_opaque, expose, inline]
|
||||
def revChars (s : Slice) :=
|
||||
Std.Iter.map (fun ⟨pos, h⟩ => pos.get h) (revPositions s)
|
||||
|
||||
@@ -347,7 +348,7 @@ Examples:
|
||||
* {lean}`"coffee tea and water".toSlice.foldl (fun n c => if c.isWhitespace then n + 1 else n) 0 = 3`
|
||||
* {lean}`"coffee tea water".toSlice.foldl (·.push ·) "" = "coffee tea water"`
|
||||
-/
|
||||
@[inline]
|
||||
@[cbv_opaque, inline]
|
||||
def foldl {α : Type u} (f : α → Char → α) (init : α) (s : Slice) : α :=
|
||||
Std.Iter.fold f init (chars s)
|
||||
|
||||
@@ -398,7 +399,7 @@ Examples:
|
||||
* {lean}`"abc".chars.toList = ['a', 'b', 'c']`
|
||||
* {lean}`"ab∀c".chars.toList = ['a', 'b', '∀', 'c']`
|
||||
-/
|
||||
@[inline]
|
||||
@[cbv_opaque, inline]
|
||||
def chars (s : String) :=
|
||||
(s.toSlice.chars : Std.Iter Char)
|
||||
|
||||
@@ -432,7 +433,7 @@ Example:
|
||||
* {lean}`"abc".revChars.toList = ['c', 'b', 'a']`
|
||||
* {lean}`"ab∀c".revChars.toList = ['c', '∀', 'b', 'a']`
|
||||
-/
|
||||
@[inline]
|
||||
@[cbv_opaque, inline]
|
||||
def revChars (s : String) :=
|
||||
(s.toSlice.revChars : Std.Iter Char)
|
||||
|
||||
|
||||
@@ -17,49 +17,7 @@ public import Init.Data.String.Lemmas.Pattern
|
||||
public import Init.Data.String.Lemmas.Slice
|
||||
public import Init.Data.String.Lemmas.Iterate
|
||||
public import Init.Data.String.Lemmas.Intercalate
|
||||
import Init.Data.Order.Lemmas
|
||||
public import Init.Data.String.Basic
|
||||
import Init.Data.Char.Lemmas
|
||||
import Init.Data.Char.Order
|
||||
import Init.Data.List.Lex
|
||||
|
||||
public section
|
||||
|
||||
open Std
|
||||
|
||||
namespace String
|
||||
|
||||
@[deprecated toList_inj (since := "2025-10-30")]
|
||||
protected theorem data_eq_of_eq {a b : String} (h : a = b) : a.toList = b.toList :=
|
||||
h ▸ rfl
|
||||
@[deprecated toList_inj (since := "2025-10-30")]
|
||||
protected theorem ne_of_data_ne {a b : String} (h : a.toList ≠ b.toList) : a ≠ b := by
|
||||
simpa [← toList_inj]
|
||||
|
||||
@[simp] protected theorem not_le {a b : String} : ¬ a ≤ b ↔ b < a := Decidable.not_not
|
||||
@[simp] protected theorem not_lt {a b : String} : ¬ a < b ↔ b ≤ a := Iff.rfl
|
||||
@[simp] protected theorem le_refl (a : String) : a ≤ a := List.le_refl _
|
||||
@[simp] protected theorem lt_irrefl (a : String) : ¬ a < a := List.lt_irrefl _
|
||||
|
||||
attribute [local instance] Char.notLTTrans Char.ltTrichotomous Char.ltAsymm
|
||||
|
||||
protected theorem le_trans {a b c : String} : a ≤ b → b ≤ c → a ≤ c := List.le_trans
|
||||
protected theorem lt_trans {a b c : String} : a < b → b < c → a < c := List.lt_trans
|
||||
protected theorem le_total (a b : String) : a ≤ b ∨ b ≤ a := List.le_total _ _
|
||||
protected theorem le_antisymm {a b : String} : a ≤ b → b ≤ a → a = b := fun h₁ h₂ => String.ext (List.le_antisymm (as := a.toList) (bs := b.toList) h₁ h₂)
|
||||
protected theorem lt_asymm {a b : String} (h : a < b) : ¬ b < a := List.lt_asymm h
|
||||
protected theorem ne_of_lt {a b : String} (h : a < b) : a ≠ b := by
|
||||
have := String.lt_irrefl a
|
||||
intro h; subst h; contradiction
|
||||
|
||||
instance instIsLinearOrder : IsLinearOrder String := by
|
||||
apply IsLinearOrder.of_le
|
||||
case le_antisymm => constructor; apply String.le_antisymm
|
||||
case le_trans => constructor; apply String.le_trans
|
||||
case le_total => constructor; apply String.le_total
|
||||
|
||||
instance : LawfulOrderLT String where
|
||||
lt_iff a b := by
|
||||
simp [← String.not_le, Decidable.imp_iff_not_or, Std.Total.total]
|
||||
|
||||
end String
|
||||
public import Init.Data.String.Lemmas.Iter
|
||||
public import Init.Data.String.Lemmas.Hashable
|
||||
public import Init.Data.String.Lemmas.TakeDrop
|
||||
public import Init.Data.String.Lemmas.StringOrder
|
||||
|
||||
@@ -7,6 +7,7 @@ module
|
||||
|
||||
prelude
|
||||
public import Init.Data.String.Basic
|
||||
import all Init.Data.String.Basic
|
||||
import Init.Data.ByteArray.Lemmas
|
||||
import Init.Data.Nat.MinMax
|
||||
|
||||
@@ -21,6 +22,10 @@ public section
|
||||
|
||||
namespace String
|
||||
|
||||
@[simp]
|
||||
theorem singleton_inj {c d : Char} : singleton c = singleton d ↔ c = d := by
|
||||
simp [← toList_inj]
|
||||
|
||||
@[simp]
|
||||
theorem singleton_append_inj : singleton c ++ s = singleton d ++ t ↔ c = d ∧ s = t := by
|
||||
simp [← toList_inj]
|
||||
@@ -56,6 +61,11 @@ theorem singleton_ne_empty {c : Char} : singleton c ≠ "" := by
|
||||
theorem empty_ne_singleton {c : Char} : "" ≠ singleton c := by
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem ofList_cons {c : Char} {l : List Char} :
|
||||
String.ofList (c :: l) = String.singleton c ++ String.ofList l := by
|
||||
simp [← toList_inj]
|
||||
|
||||
@[simp]
|
||||
theorem Slice.Pos.copy_inj {s : Slice} {p₁ p₂ : s.Pos} : p₁.copy = p₂.copy ↔ p₁ = p₂ := by
|
||||
simp [String.Pos.ext_iff, Pos.ext_iff]
|
||||
@@ -185,18 +195,74 @@ theorem sliceTo_slice {s : String} {p₁ p₂ h p} :
|
||||
theorem Slice.sliceFrom_startPos {s : Slice} : s.sliceFrom s.startPos = s := by
|
||||
ext <;> simp
|
||||
|
||||
@[simp]
|
||||
theorem Slice.sliceFrom_eq_self_iff {s : Slice} {p : s.Pos} : s.sliceFrom p = s ↔ p = s.startPos := by
|
||||
refine ⟨?_, by rintro rfl; simp⟩
|
||||
rcases s with ⟨str, startInclusive, endExclusive, h⟩
|
||||
simp [sliceFrom, Slice.startPos, String.Pos.ext_iff, Pos.Raw.ext_iff, Slice.Pos.ext_iff]
|
||||
|
||||
@[simp]
|
||||
theorem Slice.sliceTo_endPos {s : Slice} : s.sliceTo s.endPos = s := by
|
||||
ext <;> simp
|
||||
|
||||
@[simp]
|
||||
theorem Slice.sliceTo_eq_self_iff {s : Slice} {p : s.Pos} : s.sliceTo p = s ↔ p = s.endPos := by
|
||||
refine ⟨?_, by rintro rfl; simp⟩
|
||||
rcases s with ⟨str, startInclusive, endExclusive, h⟩
|
||||
simp [sliceTo, Slice.endPos, String.Pos.ext_iff, Pos.Raw.ext_iff, Slice.Pos.ext_iff,
|
||||
utf8ByteSize_eq]
|
||||
omega
|
||||
|
||||
@[simp]
|
||||
theorem Slice.slice_startPos {s : Slice} {p : s.Pos} :
|
||||
s.slice s.startPos p (Pos.startPos_le _) = s.sliceTo p := by
|
||||
ext <;> simp
|
||||
|
||||
@[simp]
|
||||
theorem Slice.slice_eq_self_iff {s : Slice} {p₁ p₂ : s.Pos} {h} :
|
||||
s.slice p₁ p₂ h = s ↔ p₁ = s.startPos ∧ p₂ = s.endPos := by
|
||||
refine ⟨?_, by rintro ⟨rfl, rfl⟩; simp⟩
|
||||
rcases s with ⟨str, startInclusive, endExclusive, h⟩
|
||||
simp [slice, Slice.endPos, String.Pos.ext_iff, Pos.Raw.ext_iff, Slice.Pos.ext_iff,
|
||||
utf8ByteSize_eq]
|
||||
omega
|
||||
|
||||
@[simp]
|
||||
theorem Slice.slice_endPos {s : Slice} {p : s.Pos} :
|
||||
s.slice p s.endPos (Pos.le_endPos _) = s.sliceFrom p := by
|
||||
ext <;> simp
|
||||
|
||||
@[simp]
|
||||
theorem sliceFrom_startPos {s : String} : s.sliceFrom s.startPos = s := by
|
||||
ext <;> simp
|
||||
|
||||
@[simp]
|
||||
theorem sliceFrom_eq_toSlice_iff {s : String} {p : s.Pos} : s.sliceFrom p = s.toSlice ↔ p = s.startPos := by
|
||||
simp [← sliceFrom_toSlice]
|
||||
|
||||
@[simp]
|
||||
theorem sliceTo_endPos {s : String} : s.sliceTo s.endPos = s := by
|
||||
ext <;> simp
|
||||
|
||||
@[simp]
|
||||
theorem sliceTo_eq_toSlice_iff {s : String} {p : s.Pos} : s.sliceTo p = s.toSlice ↔ p = s.endPos := by
|
||||
simp [← sliceTo_toSlice]
|
||||
|
||||
@[simp]
|
||||
theorem slice_startPos {s : String} {p : s.Pos} :
|
||||
s.slice s.startPos p (Pos.startPos_le _) = s.sliceTo p := by
|
||||
ext <;> simp
|
||||
|
||||
@[simp]
|
||||
theorem slice_endPos {s : String} {p : s.Pos} :
|
||||
s.slice p s.endPos (Pos.le_endPos _) = s.sliceFrom p := by
|
||||
ext <;> simp
|
||||
|
||||
@[simp]
|
||||
theorem slice_eq_toSlice_iff {s : String} {p₁ p₂ : s.Pos} {h} :
|
||||
s.slice p₁ p₂ h = s.toSlice ↔ p₁ = s.startPos ∧ p₂ = s.endPos := by
|
||||
simp [← slice_toSlice]
|
||||
|
||||
end Iterate
|
||||
|
||||
theorem Slice.copy_eq_copy_slice {s : Slice} {pos₁ pos₂ : s.Pos} {h} :
|
||||
@@ -244,4 +310,81 @@ theorem Pos.get_ofToSlice {s : String} {p : (s.toSlice).Pos} {h} :
|
||||
@[simp]
|
||||
theorem push_empty {c : Char} : "".push c = singleton c := rfl
|
||||
|
||||
namespace Slice.Pos
|
||||
|
||||
@[simp]
|
||||
theorem nextn_zero {s : Slice} {p : s.Pos} : p.nextn 0 = p := by
|
||||
simp [nextn]
|
||||
|
||||
theorem nextn_add_one {s : Slice} {p : s.Pos} :
|
||||
p.nextn (n + 1) = if h : p = s.endPos then p else (p.next h).nextn n := by
|
||||
simp [nextn]
|
||||
|
||||
@[simp]
|
||||
theorem nextn_endPos {s : Slice} : s.endPos.nextn n = s.endPos := by
|
||||
cases n <;> simp [nextn_add_one]
|
||||
|
||||
end Slice.Pos
|
||||
|
||||
namespace Pos
|
||||
|
||||
theorem nextn_eq_nextn_toSlice {s : String} {p : s.Pos} : p.nextn n = Pos.ofToSlice (p.toSlice.nextn n) :=
|
||||
(rfl)
|
||||
|
||||
@[simp]
|
||||
theorem nextn_zero {s : String} {p : s.Pos} : p.nextn 0 = p := by
|
||||
simp [nextn_eq_nextn_toSlice]
|
||||
|
||||
theorem nextn_add_one {s : String} {p : s.Pos} :
|
||||
p.nextn (n + 1) = if h : p = s.endPos then p else (p.next h).nextn n := by
|
||||
simp only [nextn_eq_nextn_toSlice, Slice.Pos.nextn_add_one, endPos_toSlice, toSlice_inj]
|
||||
split <;> simp [Pos.next_toSlice]
|
||||
|
||||
theorem nextn_toSlice {s : String} {p : s.Pos} : p.toSlice.nextn n = (p.nextn n).toSlice := by
|
||||
induction n generalizing p with simp_all [nextn_add_one, Slice.Pos.nextn_add_one, apply_dite Pos.toSlice, next_toSlice]
|
||||
|
||||
theorem toSlice_nextn {s : String} {p : s.Pos} : (p.nextn n).toSlice = p.toSlice.nextn n :=
|
||||
nextn_toSlice.symm
|
||||
|
||||
@[simp]
|
||||
theorem nextn_endPos {s : String} : s.endPos.nextn n = s.endPos := by
|
||||
cases n <;> simp [nextn_add_one]
|
||||
|
||||
end Pos
|
||||
|
||||
@[simp]
|
||||
theorem Slice.Pos.cast_toSlice_copy {s : Slice} {pos : s.Pos} :
|
||||
pos.copy.toSlice.cast (by simp) = pos := by
|
||||
ext; simp
|
||||
|
||||
@[simp]
|
||||
theorem Slice.Pos.sliceFrom_eq_startPos {s : Slice} {p : s.Pos} :
|
||||
(Pos.sliceFrom p p (Pos.le_refl _)) = Slice.startPos _ := by
|
||||
simp [← Pos.ofSliceFrom_inj]
|
||||
|
||||
@[simp]
|
||||
theorem Slice.Pos.sliceFrom_endPos {s : Slice} {p : s.Pos} :
|
||||
(Pos.sliceFrom p s.endPos (Pos.le_endPos _)) = Slice.endPos _ := by
|
||||
simp [← Pos.ofSliceFrom_inj]
|
||||
|
||||
@[simp]
|
||||
theorem Slice.Pos.sliceTo_startPos {s : Slice} {p : s.Pos} :
|
||||
(Pos.sliceTo p s.startPos (Pos.startPos_le _)) = Slice.startPos _ := by
|
||||
simp [← Pos.ofSliceTo_inj]
|
||||
|
||||
@[simp]
|
||||
theorem Slice.Pos.sliceTo_eq_endPos {s : Slice} {p : s.Pos} :
|
||||
(Pos.sliceTo p p (Pos.le_refl _)) = Slice.endPos _ := by
|
||||
simp [← Pos.ofSliceTo_inj]
|
||||
|
||||
@[simp]
|
||||
theorem Slice.Pos.slice_eq_startPos {s : Slice} {p₀ p₁ : s.Pos} {h} :
|
||||
(Pos.slice p₀ p₀ p₁ (Pos.le_refl _) h) = Slice.startPos _ := by
|
||||
simp [← Pos.ofSlice_inj]
|
||||
|
||||
@[simp]
|
||||
theorem Slice.Pos.slice_eq_endPos {s : Slice} {p₀ p₁ : s.Pos} {h} :
|
||||
(Pos.slice p₁ p₀ p₁ h (Pos.le_refl _)) = Slice.endPos _ := by
|
||||
simp [← Pos.ofSlice_inj]
|
||||
|
||||
end String
|
||||
|
||||
@@ -11,6 +11,8 @@ import all Init.Data.String.FindPos
|
||||
import Init.Data.String.OrderInstances
|
||||
import Init.Data.String.Lemmas.Order
|
||||
import Init.Data.Order.Lemmas
|
||||
import Init.Data.Option.Lemmas
|
||||
import Init.ByCases
|
||||
|
||||
public section
|
||||
|
||||
@@ -199,6 +201,10 @@ theorem Pos.prev_eq_iff {s : Slice} {p q : s.Pos} {h} :
|
||||
theorem Pos.prev_lt {s : Slice} {p : s.Pos} {h} : p.prev h < p := by
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem Pos.prev_le {s : Slice} {p : s.Pos} {h} : p.prev h ≤ p :=
|
||||
Std.le_of_lt (by simp)
|
||||
|
||||
@[simp]
|
||||
theorem Pos.prev_ne_endPos {s : Slice} {p : s.Pos} {h} : p.prev h ≠ s.endPos :=
|
||||
ne_endPos_of_lt prev_lt
|
||||
@@ -209,6 +215,29 @@ theorem Pos.prevn_le {s : Slice} {p : s.Pos} {n : Nat} : p.prevn n ≤ p := by
|
||||
| case2 p n h ih => exact Std.le_of_lt (by simpa using ih)
|
||||
| case3 => simp
|
||||
|
||||
theorem Pos.ofSliceTo_prev {s : Slice} {p₀ : s.Pos} {p : (s.sliceTo p₀).Pos} {h} :
|
||||
Pos.ofSliceTo (p.prev h) = (Pos.ofSliceTo p).prev (by simpa [← Pos.ofSliceTo_inj] using h) := by
|
||||
rw [eq_comm, Pos.prev_eq_iff]
|
||||
simp only [Pos.ofSliceTo_lt_ofSliceTo_iff, Pos.le_ofSliceTo_iff]
|
||||
simp [Pos.lt_ofSliceTo_iff]
|
||||
|
||||
theorem Pos.prev_ofSliceTo {s : Slice} {p₀ : s.Pos} {p : (s.sliceTo p₀).Pos} {h} :
|
||||
(Pos.ofSliceTo p).prev h = Pos.ofSliceTo (p.prev (by simpa [← Pos.ofSliceTo_inj])) := by
|
||||
simp [ofSliceTo_prev]
|
||||
|
||||
theorem Pos.ofSliceFrom_prev {s : Slice} {p₀ : s.Pos} {p : (s.sliceFrom p₀).Pos} {h} :
|
||||
Pos.ofSliceFrom (p.prev h) = (Pos.ofSliceFrom p).prev (by exact ofSliceFrom_ne_startPos h) := by
|
||||
rw [eq_comm, Pos.prev_eq_iff]
|
||||
simp only [Pos.ofSliceFrom_lt_ofSliceFrom_iff, Pos.le_ofSliceFrom_iff]
|
||||
simp [Pos.lt_ofSliceFrom_iff]
|
||||
|
||||
theorem Pos.ofSlice_prev {s : Slice} {p₀ p₁ : s.Pos} {h}
|
||||
{p : (s.slice p₀ p₁ h).Pos} {h'} :
|
||||
Pos.ofSlice (p.prev h') = (Pos.ofSlice p).prev (by exact ofSlice_ne_startPos h') := by
|
||||
rw [eq_comm, Pos.prev_eq_iff]
|
||||
simp only [ofSlice_lt_ofSlice_iff, le_ofSlice_iff]
|
||||
simpa +contextual [← ofSlice_lt_ofSlice_iff] using fun q hq => Std.le_of_lt (Std.lt_of_lt_of_le hq ofSlice_le)
|
||||
|
||||
@[simp]
|
||||
theorem Pos.prev_next {s : Slice} {p : s.Pos} {h} : (p.next h).prev (by simp) = p :=
|
||||
prev_eq_iff.2 (by simp)
|
||||
@@ -217,6 +246,23 @@ theorem Pos.prev_next {s : Slice} {p : s.Pos} {h} : (p.next h).prev (by simp) =
|
||||
theorem Pos.next_prev {s : Slice} {p : s.Pos} {h} : (p.prev h).next (by simp) = p :=
|
||||
next_eq_iff.2 (by simp)
|
||||
|
||||
theorem Pos.prev?_eq_dif {s : Slice} {p : s.Pos} : p.prev? = if h : p = s.startPos then none else some (p.prev h) :=
|
||||
(rfl)
|
||||
|
||||
theorem Pos.prev?_eq_some_prev {s : Slice} {p : s.Pos} (h : p ≠ s.startPos) : p.prev? = some (p.prev h) := by
|
||||
simp [Pos.prev?, h]
|
||||
|
||||
@[simp]
|
||||
theorem Pos.prev?_eq_none_iff {s : Slice} {p : s.Pos} : p.prev? = none ↔ p = s.startPos := by
|
||||
simp [Pos.prev?]
|
||||
|
||||
theorem Pos.prev?_eq_none {s : Slice} {p : s.Pos} (h : p = s.startPos) : p.prev? = none :=
|
||||
prev?_eq_none_iff.2 h
|
||||
|
||||
@[simp]
|
||||
theorem Pos.prev?_startPos {s : Slice} : s.startPos.prev? = none := by
|
||||
simp
|
||||
|
||||
end Slice
|
||||
|
||||
@[simp]
|
||||
@@ -420,6 +466,10 @@ theorem Pos.prev_eq_iff {s : String} {p q : s.Pos} {h} :
|
||||
theorem Pos.prev_lt {s : String} {p : s.Pos} {h} : p.prev h < p := by
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem Pos.prev_le {s : String} {p : s.Pos} {h} : p.prev h ≤ p :=
|
||||
Std.le_of_lt (by simp)
|
||||
|
||||
@[simp]
|
||||
theorem Pos.prev_ne_endPos {s : String} {p : s.Pos} {h} : p.prev h ≠ s.endPos :=
|
||||
ne_endPos_of_lt prev_lt
|
||||
@@ -428,14 +478,45 @@ theorem Pos.toSlice_prev {s : String} {p : s.Pos} {h} :
|
||||
(p.prev h).toSlice = p.toSlice.prev (by simpa [toSlice_inj]) := by
|
||||
simp [prev]
|
||||
|
||||
theorem Pos.ofToSlice_prev {s : String} {p : s.toSlice.Pos} {h} :
|
||||
Pos.ofToSlice (p.prev h) = (Pos.ofToSlice p).prev (by simpa [← toSlice_inj]) := by
|
||||
simp [prev]
|
||||
|
||||
theorem Pos.prev_toSlice {s : String} {p : s.Pos} {h} :
|
||||
p.toSlice.prev h = (p.prev (by simpa [← toSlice_inj])).toSlice := by
|
||||
simp [prev]
|
||||
|
||||
theorem Pos.prev_ofToSlice {s : String} {p : s.toSlice.Pos} {h} :
|
||||
(Pos.ofToSlice p).prev h = Pos.ofToSlice (p.prev (by simpa [← ofToSlice_inj])) := by
|
||||
simp [prev]
|
||||
|
||||
theorem Pos.prevn_le {s : String} {p : s.Pos} {n : Nat} :
|
||||
p.prevn n ≤ p := by
|
||||
simpa [Pos.le_iff, ← offset_toSlice] using Slice.Pos.prevn_le
|
||||
|
||||
theorem Pos.ofSliceTo_prev {s : String} {p₀ : s.Pos} {p : (s.sliceTo p₀).Pos} {h} :
|
||||
Pos.ofSliceTo (p.prev h) = (Pos.ofSliceTo p).prev (by simpa [← Pos.ofSliceTo_inj] using h) := by
|
||||
rw [eq_comm, Pos.prev_eq_iff]
|
||||
simp only [Pos.ofSliceTo_lt_ofSliceTo_iff, Pos.le_ofSliceTo_iff]
|
||||
simp [Pos.lt_ofSliceTo_iff]
|
||||
|
||||
theorem Pos.prev_ofSliceTo {s : String} {p₀ : s.Pos} {p : (s.sliceTo p₀).Pos} {h} :
|
||||
(Pos.ofSliceTo p).prev h = Pos.ofSliceTo (p.prev (by simpa [← Pos.ofSliceTo_inj])) := by
|
||||
simp [ofSliceTo_prev]
|
||||
|
||||
theorem Pos.ofSliceFrom_prev {s : String} {p₀ : s.Pos} {p : (s.sliceFrom p₀).Pos} {h} :
|
||||
Pos.ofSliceFrom (p.prev h) = (Pos.ofSliceFrom p).prev (by exact ofSliceFrom_ne_startPos h) := by
|
||||
rw [eq_comm, Pos.prev_eq_iff]
|
||||
simp only [Pos.ofSliceFrom_lt_ofSliceFrom_iff, Pos.le_ofSliceFrom_iff]
|
||||
simp [Pos.lt_ofSliceFrom_iff]
|
||||
|
||||
theorem Pos.ofSlice_prev {s : String} {p₀ p₁ : s.Pos} {h}
|
||||
{p : (s.slice p₀ p₁ h).Pos} {h'} :
|
||||
Pos.ofSlice (p.prev h') = (Pos.ofSlice p).prev (by exact ofSlice_ne_startPos h') := by
|
||||
rw [eq_comm, Pos.prev_eq_iff]
|
||||
simp only [ofSlice_lt_ofSlice_iff, le_ofSlice_iff]
|
||||
simpa +contextual [← ofSlice_lt_ofSlice_iff] using fun q hq => Std.le_of_lt (Std.lt_of_lt_of_le hq ofSlice_le)
|
||||
|
||||
@[simp]
|
||||
theorem Pos.prev_next {s : String} {p : s.Pos} {h} : (p.next h).prev (by simp) = p :=
|
||||
prev_eq_iff.2 (by simp)
|
||||
@@ -444,4 +525,71 @@ theorem Pos.prev_next {s : String} {p : s.Pos} {h} : (p.next h).prev (by simp) =
|
||||
theorem Pos.next_prev {s : String} {p : s.Pos} {h} : (p.prev h).next (by simp) = p :=
|
||||
next_eq_iff.2 (by simp)
|
||||
|
||||
theorem Pos.prev?_eq_prev?_toSlice {s : String} {p : s.Pos} : p.prev? = p.toSlice.prev?.map Pos.ofToSlice :=
|
||||
(rfl)
|
||||
|
||||
theorem Pos.prev?_toSlice {s : String} {p : s.Pos} : p.toSlice.prev? = p.prev?.map Pos.toSlice := by
|
||||
simp [prev?_eq_prev?_toSlice]
|
||||
|
||||
theorem Pos.prev?_eq_dif {s : String} {p : s.Pos} : p.prev? = if h : p = s.startPos then none else some (p.prev h) := by
|
||||
simp [prev?_eq_prev?_toSlice, Slice.Pos.prev?_eq_dif, apply_dite (Option.map Pos.ofToSlice),
|
||||
ofToSlice_prev]
|
||||
|
||||
theorem Pos.prev?_eq_some_prev {s : String} {p : s.Pos} (h : p ≠ s.startPos) : p.prev? = some (p.prev h) := by
|
||||
simp [prev?_eq_prev?_toSlice, Slice.Pos.prev?_eq_some_prev (by simpa : p.toSlice ≠ s.toSlice.startPos),
|
||||
ofToSlice_prev]
|
||||
|
||||
@[simp]
|
||||
theorem Pos.prev?_eq_none_iff {s : String} {p : s.Pos} : p.prev? = none ↔ p = s.startPos := by
|
||||
simp [prev?_eq_prev?_toSlice]
|
||||
|
||||
theorem Pos.prev?_eq_none {s : String} {p : s.Pos} (h : p = s.startPos) : p.prev? = none :=
|
||||
prev?_eq_none_iff.2 h
|
||||
|
||||
@[simp]
|
||||
theorem Pos.prev?_startPos {s : String} : s.startPos.prev? = none := by
|
||||
simp
|
||||
|
||||
namespace Slice.Pos
|
||||
|
||||
@[simp]
|
||||
theorem prevn_zero {s : Slice} {p : s.Pos} : p.prevn 0 = p := by
|
||||
simp [prevn]
|
||||
|
||||
theorem prevn_add_one {s : Slice} {p : s.Pos} :
|
||||
p.prevn (n + 1) = if h : p = s.startPos then p else (p.prev h).prevn n := by
|
||||
simp [prevn]
|
||||
|
||||
@[simp]
|
||||
theorem prevn_startPos {s : Slice} : s.startPos.prevn n = s.startPos := by
|
||||
cases n <;> simp [prevn_add_one]
|
||||
|
||||
end Slice.Pos
|
||||
|
||||
namespace Pos
|
||||
|
||||
theorem prevn_eq_prevn_toSlice {s : String} {p : s.Pos} : p.prevn n = Pos.ofToSlice (p.toSlice.prevn n) :=
|
||||
(rfl)
|
||||
|
||||
@[simp]
|
||||
theorem prevn_zero {s : String} {p : s.Pos} : p.prevn 0 = p := by
|
||||
simp [prevn_eq_prevn_toSlice]
|
||||
|
||||
theorem prevn_add_one {s : String} {p : s.Pos} :
|
||||
p.prevn (n + 1) = if h : p = s.startPos then p else (p.prev h).prevn n := by
|
||||
simp only [prevn_eq_prevn_toSlice, Slice.Pos.prevn_add_one, startPos_toSlice, toSlice_inj]
|
||||
split <;> simp [Pos.prev_toSlice]
|
||||
|
||||
theorem prevn_toSlice {s : String} {p : s.Pos} : p.toSlice.prevn n = (p.prevn n).toSlice := by
|
||||
induction n generalizing p with simp_all [prevn_add_one, Slice.Pos.prevn_add_one, apply_dite Pos.toSlice, prev_toSlice]
|
||||
|
||||
theorem toSlice_prevn {s : String} {p : s.Pos} : (p.prevn n).toSlice = p.toSlice.prevn n :=
|
||||
prevn_toSlice.symm
|
||||
|
||||
@[simp]
|
||||
theorem prevn_startPos {s : String} : s.startPos.prevn n = s.startPos := by
|
||||
cases n <;> simp [prevn_add_one]
|
||||
|
||||
end Pos
|
||||
|
||||
end String
|
||||
|
||||
25
src/Init/Data/String/Lemmas/Hashable.lean
Normal file
25
src/Init/Data/String/Lemmas/Hashable.lean
Normal file
@@ -0,0 +1,25 @@
|
||||
/-
|
||||
Copyright (c) 2026 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Julia Markus Himmel
|
||||
-/
|
||||
module
|
||||
|
||||
prelude
|
||||
public import Init.Data.String.Slice
|
||||
public import Init.Data.LawfulHashable
|
||||
import all Init.Data.String.Slice
|
||||
import Init.Data.String.Lemmas.Slice
|
||||
|
||||
namespace String
|
||||
|
||||
public theorem hash_eq {s : String} : hash s = String.hash s := rfl
|
||||
|
||||
namespace Slice
|
||||
|
||||
public theorem hash_eq {s : String.Slice} : hash s = String.hash s.copy := (rfl)
|
||||
|
||||
public instance : LawfulHashable String.Slice where
|
||||
hash_eq a b hab := by simp [hash_eq, beq_eq_true_iff.1 hab]
|
||||
|
||||
end String.Slice
|
||||
@@ -10,6 +10,7 @@ public import Init.Data.String.Defs
|
||||
import all Init.Data.String.Defs
|
||||
public import Init.Data.String.Slice
|
||||
import all Init.Data.String.Slice
|
||||
import Init.ByCases
|
||||
|
||||
public section
|
||||
|
||||
@@ -42,6 +43,16 @@ theorem intercalate_cons_of_ne_nil {s t : String} {l : List String} (h : l ≠ [
|
||||
match l, h with
|
||||
| u::l, _ => by simp
|
||||
|
||||
theorem intercalate_append_of_ne_nil {l m : List String} {s : String} (hl : l ≠ []) (hm : m ≠ []) :
|
||||
s.intercalate (l ++ m) = s.intercalate l ++ s ++ s.intercalate m := by
|
||||
induction l with
|
||||
| nil => simp_all
|
||||
| cons hd tl ih =>
|
||||
rw [List.cons_append, intercalate_cons_of_ne_nil (by simp_all)]
|
||||
by_cases ht : tl = []
|
||||
· simp_all
|
||||
· simp [ih ht, intercalate_cons_of_ne_nil ht, String.append_assoc]
|
||||
|
||||
@[simp]
|
||||
theorem toList_intercalate {s : String} {l : List String} :
|
||||
(s.intercalate l).toList = s.toList.intercalate (l.map String.toList) := by
|
||||
@@ -49,6 +60,32 @@ theorem toList_intercalate {s : String} {l : List String} :
|
||||
| nil => simp
|
||||
| cons hd tl ih => cases tl <;> simp_all
|
||||
|
||||
theorem join_eq_foldl : join l = l.foldl (fun r s => r ++ s) "" :=
|
||||
(rfl)
|
||||
|
||||
@[simp]
|
||||
theorem join_nil : join [] = "" := by
|
||||
simp [join]
|
||||
|
||||
@[simp]
|
||||
theorem join_cons : join (s :: l) = s ++ join l := by
|
||||
simp only [join, List.foldl_cons, empty_append]
|
||||
conv => lhs; rw [← String.append_empty (s := s)]
|
||||
rw [List.foldl_assoc]
|
||||
|
||||
@[simp]
|
||||
theorem toList_join {l : List String} : (String.join l).toList = l.flatMap String.toList := by
|
||||
induction l <;> simp_all
|
||||
|
||||
@[simp]
|
||||
theorem join_append {l m : List String} : String.join (l ++ m) = String.join l ++ String.join m := by
|
||||
simp [← toList_inj]
|
||||
|
||||
@[simp]
|
||||
theorem length_join {l : List String} : (String.join l).length = (l.map String.length).sum := by
|
||||
simp only [← length_toList, toList_join, List.length_flatMap]
|
||||
simp
|
||||
|
||||
namespace Slice
|
||||
|
||||
@[simp]
|
||||
@@ -65,6 +102,10 @@ theorem intercalate_eq {s : Slice} {l : List Slice} :
|
||||
| nil => simp [intercalate]
|
||||
| cons hd tl ih => cases tl <;> simp_all [intercalate, intercalate.go, intercalateGo_append]
|
||||
|
||||
@[simp]
|
||||
theorem join_eq {l : List Slice} : join l = String.join (l.map copy) := by
|
||||
simp [join, String.join, List.foldl_map]
|
||||
|
||||
end Slice
|
||||
|
||||
end String
|
||||
|
||||
@@ -204,7 +204,7 @@ theorem Slice.copy_sliceTo_startPos {s : Slice} : (s.sliceTo s.startPos).copy =
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem Slice.copy_sliceFrom_startPos {s : Slice} : (s.sliceFrom s.endPos).copy = "" := by
|
||||
theorem Slice.copy_sliceFrom_endPos {s : Slice} : (s.sliceFrom s.endPos).copy = "" := by
|
||||
simp
|
||||
|
||||
end CopyEqEmpty
|
||||
|
||||
50
src/Init/Data/String/Lemmas/Iter.lean
Normal file
50
src/Init/Data/String/Lemmas/Iter.lean
Normal file
@@ -0,0 +1,50 @@
|
||||
/-
|
||||
Copyright (c) 2026 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Julia Markus Himmel
|
||||
-/
|
||||
module
|
||||
|
||||
prelude
|
||||
public import Init.Data.String.Iter.Intercalate
|
||||
public import Init.Data.String.Slice
|
||||
import all Init.Data.String.Iter.Intercalate
|
||||
import all Init.Data.String.Defs
|
||||
import Init.Data.String.Lemmas.Intercalate
|
||||
import Init.Data.Iterators.Lemmas.Consumers.Loop
|
||||
import Init.Data.Iterators.Lemmas.Combinators.FilterMap
|
||||
|
||||
namespace Std.Iter
|
||||
|
||||
@[simp]
|
||||
public theorem joinString_eq {α β : Type} [Std.Iterator α Id β] [Std.Iterators.Finite α Id]
|
||||
[ToString β] {it : Std.Iter (α := α) β} :
|
||||
it.joinString = String.join (it.toList.map toString) := by
|
||||
rw [joinString, String.join, ← foldl_toList, toList_map]
|
||||
|
||||
@[simp]
|
||||
public theorem intercalateString_eq {α β : Type} [Std.Iterator α Id β] [Std.Iterators.Finite α Id]
|
||||
[ToString β] {s : String.Slice} {it : Std.Iter (α := α) β} :
|
||||
it.intercalateString s = s.copy.intercalate (it.toList.map toString) := by
|
||||
simp only [intercalateString, String.appendSlice_eq, ← foldl_toList, toList_map]
|
||||
generalize s.copy = s
|
||||
suffices ∀ (l m : List String),
|
||||
(l.foldl (init := if m = [] then none else some (s.intercalate m))
|
||||
(fun | none, sl => some sl | some str, sl => some (str ++ s ++ sl))).getD ""
|
||||
= s.intercalate (m ++ l) by
|
||||
simpa [-foldl_toList] using this (it.toList.map toString) []
|
||||
intro l m
|
||||
induction l generalizing m with
|
||||
| nil => cases m <;> simp
|
||||
| cons hd tl ih =>
|
||||
rw [List.append_cons, ← ih, List.foldl_cons]
|
||||
congr
|
||||
simp only [List.append_eq_nil_iff, List.cons_ne_self, and_false, ↓reduceIte]
|
||||
match m with
|
||||
| [] => simp
|
||||
| x::xs =>
|
||||
simp only [reduceCtorEq, ↓reduceIte, List.cons_append, Option.some.injEq]
|
||||
rw [← List.cons_append, String.intercalate_append_of_ne_nil (by simp) (by simp),
|
||||
String.intercalate_singleton]
|
||||
|
||||
end Std.Iter
|
||||
@@ -31,7 +31,7 @@ namespace Slice
|
||||
/--
|
||||
A list of all positions starting at {name}`p`.
|
||||
|
||||
This function is not meant to be used in actual progams. Actual programs should use
|
||||
This function is not meant to be used in actual programs. Actual programs should use
|
||||
{name}`Slice.positionsFrom` or {name}`Slice.positions`.
|
||||
-/
|
||||
protected def Model.positionsFrom {s : Slice} (p : s.Pos) : List { p : s.Pos // p ≠ s.endPos } :=
|
||||
@@ -76,7 +76,7 @@ theorem Model.map_get_positionsFrom_startPos {s : Slice} :
|
||||
(Model.positionsFrom s.startPos).map (fun p => p.1.get p.2) = s.copy.toList :=
|
||||
Model.map_get_positionsFrom_of_splits (splits_startPos s)
|
||||
|
||||
@[simp]
|
||||
@[cbv_eval, simp]
|
||||
theorem toList_positionsFrom {s : Slice} {p : s.Pos} :
|
||||
(s.positionsFrom p).toList = Model.positionsFrom p := by
|
||||
rw [positionsFrom]
|
||||
@@ -91,7 +91,7 @@ theorem toList_positionsFrom {s : Slice} {p : s.Pos} :
|
||||
theorem toList_positions {s : Slice} : s.positions.toList = Model.positionsFrom s.startPos := by
|
||||
simp [positions]
|
||||
|
||||
@[simp]
|
||||
@[cbv_eval, simp]
|
||||
theorem toList_chars {s : Slice} : s.chars.toList = s.copy.toList := by
|
||||
simp [chars, Model.map_get_positionsFrom_startPos]
|
||||
|
||||
@@ -177,20 +177,20 @@ theorem toList_revPositionsFrom {s : Slice} {p : s.Pos} :
|
||||
theorem toList_revPositions {s : Slice} : s.revPositions.toList = Model.revPositionsFrom s.endPos := by
|
||||
simp [revPositions]
|
||||
|
||||
@[simp]
|
||||
@[cbv_eval, simp]
|
||||
theorem toList_revChars {s : Slice} : s.revChars.toList = s.copy.toList.reverse := by
|
||||
simp [revChars, Model.map_get_revPositionsFrom_endPos]
|
||||
|
||||
theorem forIn_eq_forIn_chars {m : Type u → Type v} [Monad m] {s : Slice} {b} {f : Char → β → m (ForInStep β)} :
|
||||
ForIn.forIn s b f = ForIn.forIn s.chars b f := rfl
|
||||
|
||||
@[simp]
|
||||
@[cbv_eval, simp]
|
||||
theorem forIn_eq_forIn_toList {m : Type u → Type v} [Monad m] [LawfulMonad m] {s : Slice} {b}
|
||||
{f : Char → β → m (ForInStep β)} :
|
||||
ForIn.forIn s b f = ForIn.forIn s.copy.toList b f := by
|
||||
rw [forIn_eq_forIn_chars, ← Std.Iter.forIn_toList, toList_chars]
|
||||
|
||||
@[simp]
|
||||
@[cbv_eval, simp]
|
||||
theorem foldl_eq_foldl_toList {α : Type u} {f : α → Char → α} {init : α} {s : Slice} :
|
||||
s.foldl f init = s.copy.toList.foldl f init := by
|
||||
rw [foldl, ← Std.Iter.foldl_toList, toList_chars]
|
||||
@@ -206,7 +206,7 @@ end Slice
|
||||
/--
|
||||
A list of all positions starting at {name}`p`.
|
||||
|
||||
This function is not meant to be used in actual progams. Actual programs should use
|
||||
This function is not meant to be used in actual programs. Actual programs should use
|
||||
{name}`Slice.positionsFrom` or {name}`Slice.positions`.
|
||||
-/
|
||||
protected def Model.positionsFrom {s : String} (p : s.Pos) : List { p : s.Pos // p ≠ s.endPos } :=
|
||||
@@ -262,10 +262,11 @@ theorem toList_positionsFrom {s : String} {p : s.Pos} :
|
||||
(s.positionsFrom p).toList = Model.positionsFrom p := by
|
||||
simp [positionsFrom, Internal.ofToSliceWithProof, Model.positionsFrom_eq_map]
|
||||
|
||||
@[cbv_eval]
|
||||
theorem toList_positions {s : String} : s.positions.toList = Model.positionsFrom s.startPos := by
|
||||
simp [positions]
|
||||
|
||||
@[simp]
|
||||
@[cbv_eval, simp]
|
||||
theorem toList_chars {s : String} : s.chars.toList = s.toList := by
|
||||
simp [chars]
|
||||
|
||||
@@ -353,7 +354,7 @@ theorem toList_revPositions {s : String} :
|
||||
s.revPositions.toList = Model.revPositionsFrom s.endPos := by
|
||||
simp [revPositions]
|
||||
|
||||
@[simp]
|
||||
@[cbv_eval, simp]
|
||||
theorem toList_revChars {s : String} : s.revChars.toList = s.toList.reverse := by
|
||||
simp [revChars]
|
||||
|
||||
|
||||
@@ -49,6 +49,14 @@ theorem toList_mapAux {f : Char → Char} {s : String} {p : s.Pos}
|
||||
theorem toList_map {f : Char → Char} {s : String} : (s.map f).toList = s.toList.map f := by
|
||||
simp [map, toList_mapAux s.splits_startPos]
|
||||
|
||||
/-
|
||||
Used internally by the `cbv` tactic.
|
||||
-/
|
||||
@[cbv_eval]
|
||||
theorem map_eq_internal {f : Char → Char} {s : String} : s.map f = .ofList (s.toList.map f) := by
|
||||
apply String.toList_injective
|
||||
simp only [toList_map, toList_ofList]
|
||||
|
||||
@[simp]
|
||||
theorem length_map {f : Char → Char} {s : String} : (s.map f).length = s.length := by
|
||||
simp [← length_toList]
|
||||
|
||||
@@ -11,6 +11,7 @@ import Init.Data.String.OrderInstances
|
||||
import Init.Data.String.Lemmas.Basic
|
||||
import Init.Data.Order.Lemmas
|
||||
import Init.Omega
|
||||
import Init.ByCases
|
||||
|
||||
public section
|
||||
|
||||
@@ -70,7 +71,7 @@ theorem Pos.le_startPos {s : String} (p : s.Pos) : p ≤ s.startPos ↔ p = s.st
|
||||
⟨fun h => Std.le_antisymm h (startPos_le _), by simp +contextual⟩
|
||||
|
||||
@[simp]
|
||||
theorem Pos.startPos_lt_iff {s : String} {p : s.Pos} : s.startPos < p ↔ p ≠ s.startPos := by
|
||||
theorem Pos.startPos_lt_iff {s : String} (p : s.Pos) : s.startPos < p ↔ p ≠ s.startPos := by
|
||||
simp [← le_startPos, Std.not_le]
|
||||
|
||||
@[simp]
|
||||
@@ -235,6 +236,10 @@ theorem Slice.Pos.ofSliceFrom_next {s : Slice} {p₀ : s.Pos} {p : (s.sliceFrom
|
||||
Pos.next_le_iff_lt, true_and]
|
||||
simp [Pos.ofSliceFrom_lt_iff]
|
||||
|
||||
theorem Slice.Pos.next_ofSliceFrom {s : Slice} {p₀ : s.Pos} {p : (s.sliceFrom p₀).Pos} {h} :
|
||||
(Pos.ofSliceFrom p).next h = Pos.ofSliceFrom (p.next (by simpa [← Pos.ofSliceFrom_inj])) := by
|
||||
simp [ofSliceFrom_next]
|
||||
|
||||
theorem Pos.ofSliceFrom_next {s : String} {p₀ : s.Pos} {p : (s.sliceFrom p₀).Pos} {h} :
|
||||
Pos.ofSliceFrom (p.next h) = (Pos.ofSliceFrom p).next (by simpa [← Pos.ofSliceFrom_inj] using h) := by
|
||||
rw [eq_comm, Pos.next_eq_iff]
|
||||
@@ -242,6 +247,10 @@ theorem Pos.ofSliceFrom_next {s : String} {p₀ : s.Pos} {p : (s.sliceFrom p₀)
|
||||
Slice.Pos.next_le_iff_lt, true_and]
|
||||
simp [Pos.ofSliceFrom_lt_iff]
|
||||
|
||||
theorem Pos.next_ofSliceFrom {s : String} {p₀ : s.Pos} {p : (s.sliceFrom p₀).Pos} {h} :
|
||||
(Pos.ofSliceFrom p).next h = Pos.ofSliceFrom (p.next (by simpa [← Pos.ofSliceFrom_inj])) := by
|
||||
simp [Pos.ofSliceFrom_next]
|
||||
|
||||
theorem Slice.Pos.le_ofSliceTo_iff {s : Slice} {p₀ : s.Pos} {p : (s.sliceTo p₀).Pos} {q : s.Pos} :
|
||||
q ≤ Pos.ofSliceTo p ↔ ∃ h, Slice.Pos.sliceTo p₀ q h ≤ p := by
|
||||
refine ⟨fun h => ⟨Slice.Pos.le_trans h Pos.ofSliceTo_le, ?_⟩, fun ⟨h, h'⟩ => ?_⟩
|
||||
@@ -359,11 +368,41 @@ theorem Slice.Pos.ofSliceTo_ne_endPos {s : Slice} {p₀ : s.Pos} {p : (s.sliceTo
|
||||
refine (lt_endPos_iff _).1 (Std.lt_of_lt_of_le ?_ (le_endPos p₀))
|
||||
simpa [← lt_endPos_iff, ← ofSliceTo_lt_ofSliceTo_iff] using h
|
||||
|
||||
theorem Slice.Pos.ne_endPos_of_sliceTo_ne_endPos {s : Slice} {p p₀ : s.Pos} {h₀}
|
||||
(h : Pos.sliceTo p₀ p h₀ ≠ Slice.endPos _) : p ≠ s.endPos := by
|
||||
rw [← Pos.ofSliceTo_sliceTo (h := h₀)]
|
||||
apply Pos.ofSliceTo_ne_endPos h
|
||||
|
||||
theorem Slice.Pos.ofSliceFrom_ne_startPos {s : Slice} {p₀ : s.Pos} {p : (s.sliceFrom p₀).Pos}
|
||||
(h : p ≠ (s.sliceFrom p₀).startPos) : Pos.ofSliceFrom p ≠ s.startPos := by
|
||||
refine (startPos_lt_iff _).1 (Std.lt_of_le_of_lt (startPos_le p₀) ?_)
|
||||
simpa [← startPos_lt_iff, ← ofSliceFrom_lt_ofSliceFrom_iff] using h
|
||||
|
||||
theorem Slice.Pos.ne_startPos_of_sliceFrom_ne_startPos {s : Slice} {p p₀ : s.Pos} {h₀}
|
||||
(h : Pos.sliceFrom p₀ p h₀ ≠ Slice.startPos _) : p ≠ s.startPos := by
|
||||
rw [← Pos.ofSliceFrom_sliceFrom (h := h₀)]
|
||||
apply Pos.ofSliceFrom_ne_startPos h
|
||||
|
||||
theorem Pos.ofSliceTo_ne_endPos {s : String} {p₀ : s.Pos} {p : (s.sliceTo p₀).Pos}
|
||||
(h : p ≠ (s.sliceTo p₀).endPos) : Pos.ofSliceTo p ≠ s.endPos := by
|
||||
refine (lt_endPos_iff _).1 (Std.lt_of_lt_of_le ?_ (le_endPos p₀))
|
||||
simpa [← Slice.Pos.lt_endPos_iff, ← ofSliceTo_lt_ofSliceTo_iff] using h
|
||||
|
||||
theorem Pos.ne_endPos_of_sliceTo_ne_endPos {s : String} {p p₀ : s.Pos} {h₀}
|
||||
(h : Pos.sliceTo p₀ p h₀ ≠ Slice.endPos _) : p ≠ s.endPos := by
|
||||
rw [← Pos.ofSliceTo_sliceTo (h := h₀)]
|
||||
apply Pos.ofSliceTo_ne_endPos h
|
||||
|
||||
theorem Pos.ofSliceFrom_ne_startPos {s : String} {p₀ : s.Pos} {p : (s.sliceFrom p₀).Pos}
|
||||
(h : p ≠ (s.sliceFrom p₀).startPos) : Pos.ofSliceFrom p ≠ s.startPos := by
|
||||
refine (startPos_lt_iff _).1 (Std.lt_of_le_of_lt (startPos_le p₀) ?_)
|
||||
simpa [← Slice.Pos.startPos_lt_iff, ← ofSliceFrom_lt_ofSliceFrom_iff] using h
|
||||
|
||||
theorem Pos.ne_startPos_of_sliceFrom_ne_startPos {s : String} {p p₀ : s.Pos} {h₀}
|
||||
(h : Pos.sliceFrom p₀ p h₀ ≠ Slice.startPos _) : p ≠ s.startPos := by
|
||||
rw [← Pos.ofSliceFrom_sliceFrom (h := h₀)]
|
||||
apply Pos.ofSliceFrom_ne_startPos h
|
||||
|
||||
theorem Slice.Pos.ofSliceTo_next {s : Slice} {p₀ : s.Pos} {p : (s.sliceTo p₀).Pos} {h} :
|
||||
Pos.ofSliceTo (p.next h) = (Pos.ofSliceTo p).next (ofSliceTo_ne_endPos h) := by
|
||||
rw [eq_comm, Pos.next_eq_iff]
|
||||
@@ -406,16 +445,130 @@ theorem Pos.slice_le_slice_iff {s : String} {p₀ p₁ : s.Pos} {q r : s.Pos}
|
||||
simp [Slice.Pos.le_iff, Pos.le_iff, Pos.Raw.le_iff] at h₁ h₁' ⊢
|
||||
omega
|
||||
|
||||
theorem Slice.Pos.le_ofSlice_iff {s : Slice} {p₀ p₁ : s.Pos} {h} {p : (s.slice p₀ p₁ h).Pos} {q : s.Pos} :
|
||||
q ≤ Pos.ofSlice p ↔ ∃ h₁, ∀ h₀, Slice.Pos.slice q p₀ p₁ h₀ h₁ ≤ p := by
|
||||
refine ⟨fun h => ⟨Std.le_trans h ofSlice_le, fun h' => ?_⟩, fun ⟨h₁, h⟩ => ?_⟩
|
||||
· simp only [← Slice.Pos.slice_ofSlice (pos := p), slice_le_slice_iff]
|
||||
simpa
|
||||
· by_cases h₀ : p₀ ≤ q
|
||||
· simpa only [← Slice.Pos.ofSlice_slice (h₁ := h₀) (h₂ := h₁), ofSlice_le_ofSlice_iff] using h h₀
|
||||
· exact Std.le_of_lt (Std.lt_of_lt_of_le (Std.not_le.1 h₀) le_ofSlice)
|
||||
|
||||
theorem Slice.Pos.ofSlice_lt_iff {s : Slice} {p₀ p₁ : s.Pos} {h} {p : (s.slice p₀ p₁ h).Pos} {q : s.Pos} :
|
||||
Pos.ofSlice p < q ↔ ∀ h₁, ∃ h₀, p < Slice.Pos.slice q p₀ p₁ h₀ h₁ := by
|
||||
simp [← Std.not_le, le_ofSlice_iff]
|
||||
|
||||
theorem Slice.Pos.lt_ofSlice_iff {s : Slice} {p₀ p₁ : s.Pos} {h} {p : (s.slice p₀ p₁ h).Pos} {q : s.Pos} :
|
||||
q < Pos.ofSlice p ↔ ∃ h₁, ∀ h₀, Slice.Pos.slice q p₀ p₁ h₀ h₁ < p := by
|
||||
refine ⟨fun h => ⟨Std.le_of_lt (Std.lt_of_lt_of_le h ofSlice_le), fun h' => ?_⟩, fun ⟨h₁, h⟩ => ?_⟩
|
||||
· simp only [← Slice.Pos.slice_ofSlice (pos := p), slice_lt_slice_iff]
|
||||
simpa
|
||||
· by_cases h₀ : p₀ ≤ q
|
||||
· simpa only [← Slice.Pos.ofSlice_slice (h₁ := h₀) (h₂ := h₁), ofSlice_lt_ofSlice_iff] using h h₀
|
||||
· exact Std.lt_of_lt_of_le (Std.not_le.1 h₀) le_ofSlice
|
||||
|
||||
theorem Slice.Pos.ofSlice_le_iff {s : Slice} {p₀ p₁ : s.Pos} {h} {p : (s.slice p₀ p₁ h).Pos} {q : s.Pos} :
|
||||
Pos.ofSlice p ≤ q ↔ ∀ h₁, ∃ h₀, p ≤ Slice.Pos.slice q p₀ p₁ h₀ h₁ := by
|
||||
simp [← Std.not_lt, lt_ofSlice_iff]
|
||||
|
||||
theorem Pos.le_ofSlice_iff {s : String} {p₀ p₁ : s.Pos} {h} {p : (s.slice p₀ p₁ h).Pos} {q : s.Pos} :
|
||||
q ≤ Pos.ofSlice p ↔ ∃ h₁, ∀ h₀, Pos.slice q p₀ p₁ h₀ h₁ ≤ p := by
|
||||
refine ⟨fun h => ⟨Std.le_trans h ofSlice_le, fun h' => ?_⟩, fun ⟨h₁, h⟩ => ?_⟩
|
||||
· simp only [← Pos.slice_ofSlice (pos := p), slice_le_slice_iff]
|
||||
simpa
|
||||
· by_cases h₀ : p₀ ≤ q
|
||||
· simpa only [← Pos.ofSlice_slice (h₁ := h₀) (h₂ := h₁), ofSlice_le_ofSlice_iff] using h h₀
|
||||
· exact Std.le_of_lt (Std.lt_of_lt_of_le (Std.not_le.1 h₀) le_ofSlice)
|
||||
|
||||
theorem Pos.ofSlice_lt_iff {s : String} {p₀ p₁ : s.Pos} {h} {p : (s.slice p₀ p₁ h).Pos} {q : s.Pos} :
|
||||
Pos.ofSlice p < q ↔ ∀ h₁, ∃ h₀, p < Pos.slice q p₀ p₁ h₀ h₁ := by
|
||||
simp [← Std.not_le, le_ofSlice_iff]
|
||||
|
||||
theorem Pos.lt_ofSlice_iff {s : String} {p₀ p₁ : s.Pos} {h} {p : (s.slice p₀ p₁ h).Pos} {q : s.Pos} :
|
||||
q < Pos.ofSlice p ↔ ∃ h₁, ∀ h₀, Pos.slice q p₀ p₁ h₀ h₁ < p := by
|
||||
refine ⟨fun h => ⟨Std.le_of_lt (Std.lt_of_lt_of_le h ofSlice_le), fun h' => ?_⟩, fun ⟨h₁, h⟩ => ?_⟩
|
||||
· simp only [← Pos.slice_ofSlice (pos := p), slice_lt_slice_iff]
|
||||
simpa
|
||||
· by_cases h₀ : p₀ ≤ q
|
||||
· simpa only [← Pos.ofSlice_slice (h₁ := h₀) (h₂ := h₁), ofSlice_lt_ofSlice_iff] using h h₀
|
||||
· exact Std.lt_of_lt_of_le (Std.not_le.1 h₀) le_ofSlice
|
||||
|
||||
theorem Pos.ofSlice_le_iff {s : String} {p₀ p₁ : s.Pos} {h} {p : (s.slice p₀ p₁ h).Pos} {q : s.Pos} :
|
||||
Pos.ofSlice p ≤ q ↔ ∀ h₁, ∃ h₀, p ≤ Pos.slice q p₀ p₁ h₀ h₁ := by
|
||||
simp [← Std.not_lt, lt_ofSlice_iff]
|
||||
|
||||
theorem Slice.Pos.slice_le_iff {s : Slice} {p₀ p₁ : s.Pos} {h} {p : (s.slice p₀ p₁ h).Pos} {q : s.Pos} {h₀ h₁} :
|
||||
Slice.Pos.slice q p₀ p₁ h₀ h₁ ≤ p ↔ q ≤ Pos.ofSlice p := by
|
||||
simp [le_ofSlice_iff, h₀, h₁]
|
||||
|
||||
theorem Slice.Pos.lt_slice_iff {s : Slice} {p₀ p₁ : s.Pos} {h} {p : (s.slice p₀ p₁ h).Pos} {q : s.Pos} {h₀ h₁} :
|
||||
p < Slice.Pos.slice q p₀ p₁ h₀ h₁ ↔ Pos.ofSlice p < q := by
|
||||
simp [ofSlice_lt_iff, h₀, h₁]
|
||||
|
||||
theorem Slice.Pos.slice_lt_iff {s : Slice} {p₀ p₁ : s.Pos} {h} {p : (s.slice p₀ p₁ h).Pos} {q : s.Pos} {h₀ h₁} :
|
||||
Slice.Pos.slice q p₀ p₁ h₀ h₁ < p ↔ q < Pos.ofSlice p := by
|
||||
simp [lt_ofSlice_iff, h₀, h₁]
|
||||
|
||||
theorem Slice.Pos.le_slice_iff {s : Slice} {p₀ p₁ : s.Pos} {h} {p : (s.slice p₀ p₁ h).Pos} {q : s.Pos} {h₀ h₁} :
|
||||
p ≤ Slice.Pos.slice q p₀ p₁ h₀ h₁ ↔ Pos.ofSlice p ≤ q := by
|
||||
simp [ofSlice_le_iff, h₀, h₁]
|
||||
|
||||
theorem Pos.slice_le_iff {s : String} {p₀ p₁ : s.Pos} {h} {p : (s.slice p₀ p₁ h).Pos} {q : s.Pos} {h₀ h₁} :
|
||||
Pos.slice q p₀ p₁ h₀ h₁ ≤ p ↔ q ≤ Pos.ofSlice p := by
|
||||
simp [le_ofSlice_iff, h₀, h₁]
|
||||
|
||||
theorem Pos.lt_slice_iff {s : String} {p₀ p₁ : s.Pos} {h} {p : (s.slice p₀ p₁ h).Pos} {q : s.Pos} {h₀ h₁} :
|
||||
p < Pos.slice q p₀ p₁ h₀ h₁ ↔ Pos.ofSlice p < q := by
|
||||
simp [ofSlice_lt_iff, h₀, h₁]
|
||||
|
||||
theorem Pos.slice_lt_iff {s : String} {p₀ p₁ : s.Pos} {h} {p : (s.slice p₀ p₁ h).Pos} {q : s.Pos} {h₀ h₁} :
|
||||
Pos.slice q p₀ p₁ h₀ h₁ < p ↔ q < Pos.ofSlice p := by
|
||||
simp [lt_ofSlice_iff, h₀, h₁]
|
||||
|
||||
theorem Pos.le_slice_iff {s : String} {p₀ p₁ : s.Pos} {h} {p : (s.slice p₀ p₁ h).Pos} {q : s.Pos} {h₀ h₁} :
|
||||
p ≤ Pos.slice q p₀ p₁ h₀ h₁ ↔ Pos.ofSlice p ≤ q := by
|
||||
simp [ofSlice_le_iff, h₀, h₁]
|
||||
|
||||
theorem Slice.Pos.ofSlice_ne_endPos {s : Slice} {p₀ p₁ : s.Pos} {h} {p : (s.slice p₀ p₁ h).Pos}
|
||||
(h : p ≠ (s.slice p₀ p₁ h).endPos) : Pos.ofSlice p ≠ s.endPos := by
|
||||
refine (lt_endPos_iff _).1 (Std.lt_of_lt_of_le ?_ (le_endPos p₁))
|
||||
simpa [← lt_endPos_iff, ← ofSlice_lt_ofSlice_iff] using h
|
||||
|
||||
theorem Slice.Pos.ne_endPos_of_slice_ne_endPos {s : Slice} {p p₀ p₁ : s.Pos} {h₁ h₂}
|
||||
(h : Pos.slice p p₀ p₁ h₁ h₂ ≠ Slice.endPos _) : p ≠ s.endPos := by
|
||||
rw [← Pos.ofSlice_slice (h₁ := h₁) (h₂ := h₂)]
|
||||
apply Pos.ofSlice_ne_endPos h
|
||||
|
||||
theorem Slice.Pos.ofSlice_ne_startPos {s : Slice} {p₀ p₁ : s.Pos} {h} {p : (s.slice p₀ p₁ h).Pos}
|
||||
(h : p ≠ (s.slice p₀ p₁ h).startPos) : Pos.ofSlice p ≠ s.startPos := by
|
||||
refine (startPos_lt_iff _).1 (Std.lt_of_le_of_lt (startPos_le p₀) ?_)
|
||||
simpa [← startPos_lt_iff, ← ofSlice_lt_ofSlice_iff] using h
|
||||
|
||||
theorem Slice.Pos.ne_startPos_of_slice_ne_startPos {s : Slice} {p p₀ p₁ : s.Pos} {h₁ h₂}
|
||||
(h : Pos.slice p p₀ p₁ h₁ h₂ ≠ Slice.startPos _) : p ≠ s.startPos := by
|
||||
rw [← Pos.ofSlice_slice (h₁ := h₁) (h₂ := h₂)]
|
||||
apply Pos.ofSlice_ne_startPos h
|
||||
|
||||
theorem Pos.ofSlice_ne_endPos {s : String} {p₀ p₁ : s.Pos} {h} {p : (s.slice p₀ p₁ h).Pos}
|
||||
(h : p ≠ (s.slice p₀ p₁ h).endPos) : Pos.ofSlice p ≠ s.endPos := by
|
||||
refine (lt_endPos_iff _).1 (Std.lt_of_lt_of_le ?_ (le_endPos p₁))
|
||||
simpa [← Slice.Pos.lt_endPos_iff, ← ofSlice_lt_ofSlice_iff] using h
|
||||
|
||||
theorem Pos.ne_endPos_of_slice_ne_endPos {s : String} {p p₀ p₁ : s.Pos} {h₁ h₂}
|
||||
(h : Pos.slice p p₀ p₁ h₁ h₂ ≠ Slice.endPos _) : p ≠ s.endPos := by
|
||||
rw [← Pos.ofSlice_slice (h₁ := h₁) (h₂ := h₂)]
|
||||
apply Pos.ofSlice_ne_endPos h
|
||||
|
||||
theorem Pos.ofSlice_ne_startPos {s : String} {p₀ p₁ : s.Pos} {h} {p : (s.slice p₀ p₁ h).Pos}
|
||||
(h : p ≠ (s.slice p₀ p₁ h).startPos) : Pos.ofSlice p ≠ s.startPos := by
|
||||
refine (startPos_lt_iff _).1 (Std.lt_of_le_of_lt (startPos_le p₀) ?_)
|
||||
simpa [← Slice.Pos.startPos_lt_iff, ← ofSlice_lt_ofSlice_iff] using h
|
||||
|
||||
theorem Pos.ne_startPos_of_slice_ne_startPos {s : String} {p p₀ p₁ : s.Pos} {h₁ h₂}
|
||||
(h : Pos.slice p p₀ p₁ h₁ h₂ ≠ Slice.startPos _) : p ≠ s.startPos := by
|
||||
rw [← Pos.ofSlice_slice (h₁ := h₁) (h₂ := h₂)]
|
||||
apply Pos.ofSlice_ne_startPos h
|
||||
|
||||
@[simp]
|
||||
theorem Slice.Pos.offset_le_rawEndPos {s : Slice} {p : s.Pos} :
|
||||
p.offset ≤ s.rawEndPos :=
|
||||
@@ -468,21 +621,37 @@ theorem Slice.Pos.get_eq_get_ofSliceTo {s : Slice} {p₀ : s.Pos} {pos : (s.slic
|
||||
pos.get h = (ofSliceTo pos).get (ofSliceTo_ne_endPos h) := by
|
||||
simp [Slice.Pos.get]
|
||||
|
||||
theorem Slice.Pos.get_sliceTo {s : Slice} {p₀ p : s.Pos} {h h'} :
|
||||
(Pos.sliceTo p₀ p h).get h' = p.get (ne_endPos_of_sliceTo_ne_endPos h') := by
|
||||
simp [get_eq_get_ofSliceTo]
|
||||
|
||||
theorem Pos.get_eq_get_ofSliceTo {s : String} {p₀ : s.Pos}
|
||||
{pos : (s.sliceTo p₀).Pos} {h} :
|
||||
pos.get h = (ofSliceTo pos).get (ofSliceTo_ne_endPos h) := by
|
||||
simp [Pos.get, Slice.Pos.get]
|
||||
|
||||
theorem Pos.get_sliceTo {s : String} {p₀ p : s.Pos} {h h'} :
|
||||
(Pos.sliceTo p₀ p h).get h' = p.get (ne_endPos_of_sliceTo_ne_endPos h') := by
|
||||
simp [get_eq_get_ofSliceTo]
|
||||
|
||||
theorem Slice.Pos.get_eq_get_ofSlice {s : Slice} {p₀ p₁ : s.Pos} {h}
|
||||
{pos : (s.slice p₀ p₁ h).Pos} {h'} :
|
||||
pos.get h' = (ofSlice pos).get (ofSlice_ne_endPos h') := by
|
||||
simp [Slice.Pos.get, Nat.add_assoc]
|
||||
|
||||
theorem Slice.Pos.get_slice {s : Slice} {p p₀ p₁ : s.Pos} {h₁ h₂ h} :
|
||||
(Pos.slice p p₀ p₁ h₁ h₂).get h = p.get (ne_endPos_of_slice_ne_endPos h) := by
|
||||
simp [get_eq_get_ofSlice]
|
||||
|
||||
theorem Pos.get_eq_get_ofSlice {s : String} {p₀ p₁ : s.Pos} {h}
|
||||
{pos : (s.slice p₀ p₁ h).Pos} {h'} :
|
||||
pos.get h' = (ofSlice pos).get (ofSlice_ne_endPos h') := by
|
||||
simp [Pos.get, Slice.Pos.get]
|
||||
|
||||
theorem Pos.get_slice {s : String} {p p₀ p₁ : s.Pos} {h₁ h₂ h} :
|
||||
(Pos.slice p p₀ p₁ h₁ h₂).get h = p.get (ne_endPos_of_slice_ne_endPos h) := by
|
||||
simp [get_eq_get_ofSlice]
|
||||
|
||||
theorem Slice.Pos.ofSlice_next {s : Slice} {p₀ p₁ : s.Pos} {h}
|
||||
{p : (s.slice p₀ p₁ h).Pos} {h'} :
|
||||
Pos.ofSlice (p.next h') = (Pos.ofSlice p).next (ofSlice_ne_endPos h') := by
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -20,28 +20,44 @@ import Init.Data.String.Lemmas.Order
|
||||
import Init.Data.Order.Lemmas
|
||||
import Init.Data.String.OrderInstances
|
||||
import Init.Omega
|
||||
import Init.Data.String.Lemmas.FindPos
|
||||
|
||||
public section
|
||||
|
||||
namespace String.Slice.Pattern.Model.Char
|
||||
|
||||
instance {c : Char} : ForwardPatternModel c where
|
||||
instance {c : Char} : PatternModel c where
|
||||
Matches s := s = String.singleton c
|
||||
not_matches_empty := by simp
|
||||
|
||||
instance {c : Char} : NoPrefixForwardPatternModel c :=
|
||||
.of_length_eq (by simp +contextual [ForwardPatternModel.Matches])
|
||||
instance {c : Char} : StrictPatternModel c where
|
||||
not_matches_empty := by simp [PatternModel.Matches]
|
||||
|
||||
instance {c : Char} : NoPrefixPatternModel c :=
|
||||
.of_length_eq (by simp +contextual [PatternModel.Matches])
|
||||
|
||||
instance {c : Char} : NoSuffixPatternModel c :=
|
||||
.of_length_eq (by simp +contextual [PatternModel.Matches])
|
||||
|
||||
theorem isMatch_iff {c : Char} {s : Slice} {pos : s.Pos} :
|
||||
IsMatch c pos ↔
|
||||
∃ (h : s.startPos ≠ s.endPos), pos = s.startPos.next h ∧ s.startPos.get h = c := by
|
||||
simp only [Model.isMatch_iff, ForwardPatternModel.Matches, sliceTo_copy_eq_iff_exists_splits]
|
||||
simp only [Model.isMatch_iff, PatternModel.Matches, copy_sliceTo_eq_iff_exists_splits]
|
||||
refine ⟨?_, ?_⟩
|
||||
· simp only [splits_singleton_iff]
|
||||
exact fun ⟨t₂, h, h₁, h₂, h₃⟩ => ⟨h, h₁, h₂⟩
|
||||
· rintro ⟨h, rfl, rfl⟩
|
||||
exact ⟨_, Slice.splits_next_startPos⟩
|
||||
|
||||
theorem isRevMatch_iff {c : Char} {s : Slice} {pos : s.Pos} :
|
||||
IsRevMatch c pos ↔
|
||||
∃ (h : s.endPos ≠ s.startPos), pos = s.endPos.prev h ∧ (s.endPos.prev h).get (by simp) = c := by
|
||||
simp only [Model.isRevMatch_iff, PatternModel.Matches, copy_sliceFrom_eq_iff_exists_splits]
|
||||
refine ⟨?_, ?_⟩
|
||||
· simp only [splits_singleton_right_iff]
|
||||
exact fun ⟨t₂, h, h₁, h₂, h₃⟩ => ⟨h, h₁, h₂⟩
|
||||
· rintro ⟨h, rfl, rfl⟩
|
||||
exact ⟨_, Slice.splits_prev_endPos⟩
|
||||
|
||||
theorem isLongestMatch_iff {c : Char} {s : Slice} {pos : s.Pos} :
|
||||
IsLongestMatch c pos ↔
|
||||
∃ (h : s.startPos ≠ s.endPos), pos = s.startPos.next h ∧ s.startPos.get h = c := by
|
||||
@@ -52,21 +68,46 @@ theorem isLongestMatchAt_iff {c : Char} {s : Slice} {pos pos' : s.Pos} :
|
||||
simp +contextual [Model.isLongestMatchAt_iff, isLongestMatch_iff, ← Pos.ofSliceFrom_inj,
|
||||
Pos.get_eq_get_ofSliceFrom, Pos.ofSliceFrom_next]
|
||||
|
||||
theorem isLongestRevMatch_iff {c : Char} {s : Slice} {pos : s.Pos} :
|
||||
IsLongestRevMatch c pos ↔
|
||||
∃ (h : s.endPos ≠ s.startPos), pos = s.endPos.prev h ∧ (s.endPos.prev h).get (by simp) = c := by
|
||||
rw [isLongestRevMatch_iff_isRevMatch, isRevMatch_iff]
|
||||
|
||||
theorem isLongestRevMatchAt_iff {c : Char} {s : Slice} {pos pos' : s.Pos} :
|
||||
IsLongestRevMatchAt c pos pos' ↔ ∃ h, pos = pos'.prev h ∧ (pos'.prev h).get (by simp) = c := by
|
||||
simp +contextual [Model.isLongestRevMatchAt_iff, isLongestRevMatch_iff, ← Pos.ofSliceTo_inj,
|
||||
Pos.get_eq_get_ofSliceTo, Pos.ofSliceTo_prev]
|
||||
|
||||
theorem isLongestMatchAt_of_get_eq {c : Char} {s : Slice} {pos : s.Pos} {h : pos ≠ s.endPos}
|
||||
(hc : pos.get h = c) : IsLongestMatchAt c pos (pos.next h) :=
|
||||
isLongestMatchAt_iff.2 ⟨h, by simp [hc]⟩
|
||||
|
||||
theorem isLongestRevMatchAt_of_get_eq {c : Char} {s : Slice} {pos : s.Pos} {h : pos ≠ s.startPos}
|
||||
(hc : (pos.prev h).get (by simp) = c) : IsLongestRevMatchAt c (pos.prev h) pos :=
|
||||
isLongestRevMatchAt_iff.2 ⟨h, by simp [hc]⟩
|
||||
|
||||
instance {c : Char} : LawfulForwardPatternModel c where
|
||||
skipPrefix?_eq_some_iff {s} pos := by
|
||||
simp [isLongestMatch_iff, ForwardPattern.skipPrefix?, and_comm, eq_comm (b := pos)]
|
||||
|
||||
instance {c : Char} : LawfulBackwardPatternModel c where
|
||||
skipSuffix?_eq_some_iff {s} pos := by
|
||||
simp [isLongestRevMatch_iff, BackwardPattern.skipSuffix?, and_comm, eq_comm (b := pos)]
|
||||
|
||||
theorem toSearcher_eq {c : Char} {s : Slice} :
|
||||
ToForwardSearcher.toSearcher c s = ToForwardSearcher.toSearcher (· == c) s := (rfl)
|
||||
|
||||
theorem toBackwardSearcher_eq {c : Char} {s : Slice} :
|
||||
ToBackwardSearcher.toSearcher c s = ToBackwardSearcher.toSearcher (· == c) s := (rfl)
|
||||
|
||||
theorem matchesAt_iff {c : Char} {s : Slice} {pos : s.Pos} :
|
||||
MatchesAt c pos ↔ ∃ (h : pos ≠ s.endPos), pos.get h = c := by
|
||||
simp [matchesAt_iff_exists_isLongestMatchAt, isLongestMatchAt_iff, exists_comm]
|
||||
|
||||
theorem revMatchesAt_iff {c : Char} {s : Slice} {pos : s.Pos} :
|
||||
RevMatchesAt c pos ↔ ∃ (h : pos ≠ s.startPos), (pos.prev h).get (by simp) = c := by
|
||||
simp [revMatchesAt_iff_exists_isLongestRevMatchAt, isLongestRevMatchAt_iff, exists_comm]
|
||||
|
||||
theorem matchesAt_iff_splits {c : Char} {s : Slice} {pos : s.Pos} :
|
||||
MatchesAt c pos ↔ ∃ t₁ t₂, pos.Splits t₁ (singleton c ++ t₂) := by
|
||||
rw [matchesAt_iff]
|
||||
@@ -77,37 +118,131 @@ theorem matchesAt_iff_splits {c : Char} {s : Slice} {pos : s.Pos} :
|
||||
have hne := hs.ne_endPos_of_singleton
|
||||
exact ⟨hne, (singleton_append_inj.mp (hs.eq_right (pos.splits_next_right hne))).1.symm⟩
|
||||
|
||||
theorem revMatchesAt_iff_splits {c : Char} {s : Slice} {pos : s.Pos} :
|
||||
RevMatchesAt c pos ↔ ∃ t₁ t₂, pos.Splits (t₁ ++ singleton c) t₂ := by
|
||||
rw [revMatchesAt_iff]
|
||||
refine ⟨?_, ?_⟩
|
||||
· rintro ⟨h, rfl⟩
|
||||
exact ⟨_, _, pos.splits_prev_right h⟩
|
||||
· rintro ⟨t₁, t₂, hs⟩
|
||||
have hne := hs.ne_startPos_of_singleton
|
||||
refine ⟨hne, ?_⟩
|
||||
have := hs.eq_left (pos.splits_prev_right hne)
|
||||
simp only [append_singleton, push_inj] at this
|
||||
exact this.2.symm
|
||||
|
||||
theorem not_matchesAt_of_get_ne {c : Char} {s : Slice} {pos : s.Pos} {h : pos ≠ s.endPos}
|
||||
(hc : pos.get h ≠ c) : ¬ MatchesAt c pos := by
|
||||
simp [matchesAt_iff, hc]
|
||||
|
||||
theorem not_revMatchesAt_of_get_ne {c : Char} {s : Slice} {pos : s.Pos} {h : pos ≠ s.startPos}
|
||||
(hc : (pos.prev h).get (by simp) ≠ c) : ¬ RevMatchesAt c pos := by
|
||||
simp [revMatchesAt_iff, hc]
|
||||
|
||||
theorem matchAt?_eq {s : Slice} {pos : s.Pos} {c : Char} :
|
||||
matchAt? c pos =
|
||||
if h₀ : ∃ (h : pos ≠ s.endPos), pos.get h = c then some (pos.next h₀.1) else none := by
|
||||
split <;> simp_all [isLongestMatchAt_iff, matchesAt_iff]
|
||||
|
||||
theorem revMatchAt?_eq {s : Slice} {pos : s.Pos} {c : Char} :
|
||||
revMatchAt? c pos =
|
||||
if h₀ : ∃ (h : pos ≠ s.startPos), (pos.prev h).get (by simp) = c then some (pos.prev h₀.1) else none := by
|
||||
split <;> simp_all [isLongestRevMatchAt_iff, revMatchesAt_iff]
|
||||
|
||||
theorem isMatch_iff_isMatch_beq {c : Char} {s : Slice} {pos : s.Pos} :
|
||||
IsMatch c pos ↔ IsMatch (· == c) pos := by
|
||||
simp [isMatch_iff, CharPred.isMatch_iff, beq_iff_eq]
|
||||
|
||||
theorem isRevMatch_iff_isRevMatch_beq {c : Char} {s : Slice} {pos : s.Pos} :
|
||||
IsRevMatch c pos ↔ IsRevMatch (· == c) pos := by
|
||||
simp [isRevMatch_iff, CharPred.isRevMatch_iff, beq_iff_eq]
|
||||
|
||||
theorem isLongestMatch_iff_isLongestMatch_beq {c : Char} {s : Slice} {pos : s.Pos} :
|
||||
IsLongestMatch c pos ↔ IsLongestMatch (· == c) pos := by
|
||||
simp [isLongestMatch_iff_isMatch, isMatch_iff_isMatch_beq]
|
||||
|
||||
theorem isLongestRevMatch_iff_isLongestRevMatch_beq {c : Char} {s : Slice} {pos : s.Pos} :
|
||||
IsLongestRevMatch c pos ↔ IsLongestRevMatch (· == c) pos := by
|
||||
simp [isLongestRevMatch_iff_isRevMatch, isRevMatch_iff_isRevMatch_beq]
|
||||
|
||||
theorem isLongestMatchAt_iff_isLongestMatchAt_beq {c : Char} {s : Slice}
|
||||
{pos pos' : s.Pos} :
|
||||
IsLongestMatchAt c pos pos' ↔ IsLongestMatchAt (· == c) pos pos' := by
|
||||
simp [Model.isLongestMatchAt_iff, isLongestMatch_iff_isLongestMatch_beq]
|
||||
|
||||
theorem isLongestMatchAtChain_iff_isLongestMatchAtChain_beq {c : Char} {s : Slice} {pos pos' : s.Pos} :
|
||||
IsLongestMatchAtChain c pos pos' ↔ IsLongestMatchAtChain (· == c) pos pos' := by
|
||||
refine ⟨fun h => ?_, fun h => ?_⟩
|
||||
· induction h with
|
||||
| nil => simp
|
||||
| cons p₁ p₂ p₃ h₁ h₂ ih => exact .cons _ _ _ (isLongestMatchAt_iff_isLongestMatchAt_beq.1 h₁) ih
|
||||
· induction h with
|
||||
| nil => simp
|
||||
| cons p₁ p₂ p₃ h₁ h₂ ih => exact .cons _ _ _ (isLongestMatchAt_iff_isLongestMatchAt_beq.2 h₁) ih
|
||||
|
||||
theorem isLongestMatchAtChain_iff {c : Char} {s : Slice} {pos pos' : s.Pos} :
|
||||
IsLongestMatchAtChain c pos pos' ↔ pos ≤ pos' ∧ ∀ pos'', pos ≤ pos'' → (h : pos'' < pos') → pos''.get (Pos.ne_endPos_of_lt h) = c := by
|
||||
simp [isLongestMatchAtChain_iff_isLongestMatchAtChain_beq, CharPred.isLongestMatchAtChain_iff]
|
||||
|
||||
theorem isLongestMatchAtChain_iff_toList {c : Char} {s : Slice} {pos pos' : s.Pos} :
|
||||
IsLongestMatchAtChain c pos pos' ↔
|
||||
∃ (h : pos ≤ pos'), (s.slice pos pos' h).copy.toList = List.replicate (s.slice pos pos' h).copy.length c := by
|
||||
simp [isLongestMatchAtChain_iff_isLongestMatchAtChain_beq, CharPred.isLongestMatchAtChain_iff_toList,
|
||||
List.eq_replicate_iff]
|
||||
|
||||
theorem isLongestMatchAtChain_startPos_endPos_iff_toList {c : Char} {s : Slice} :
|
||||
IsLongestMatchAtChain c s.startPos s.endPos ↔ s.copy.toList = List.replicate s.copy.length c := by
|
||||
simp [isLongestMatchAtChain_iff_isLongestMatchAtChain_beq,
|
||||
CharPred.isLongestMatchAtChain_startPos_endPos_iff_toList, List.eq_replicate_iff]
|
||||
|
||||
theorem isLongestRevMatchAt_iff_isLongestRevMatchAt_beq {c : Char} {s : Slice}
|
||||
{pos pos' : s.Pos} :
|
||||
IsLongestRevMatchAt c pos pos' ↔ IsLongestRevMatchAt (· == c) pos pos' := by
|
||||
simp [Model.isLongestRevMatchAt_iff, isLongestRevMatch_iff_isLongestRevMatch_beq]
|
||||
|
||||
theorem isLongestRevMatchAtChain_iff_isLongestRevMatchAtChain_beq {c : Char} {s : Slice} {pos pos' : s.Pos} :
|
||||
IsLongestRevMatchAtChain c pos pos' ↔ IsLongestRevMatchAtChain (· == c) pos pos' := by
|
||||
refine ⟨fun h => ?_, fun h => ?_⟩
|
||||
· induction h with
|
||||
| nil => simp
|
||||
| cons p₂ p₃ _ hmatch ih => exact .cons _ _ _ ih (isLongestRevMatchAt_iff_isLongestRevMatchAt_beq.1 hmatch)
|
||||
· induction h with
|
||||
| nil => simp
|
||||
| cons p₂ p₃ _ hmatch ih => exact .cons _ _ _ ih (isLongestRevMatchAt_iff_isLongestRevMatchAt_beq.2 hmatch)
|
||||
|
||||
theorem isLongestRevMatchAtChain_iff {c : Char} {s : Slice} {pos pos' : s.Pos} :
|
||||
IsLongestRevMatchAtChain c pos pos' ↔ pos ≤ pos' ∧ ∀ pos'', pos ≤ pos'' → (h : pos'' < pos') → pos''.get (Pos.ne_endPos_of_lt h) = c := by
|
||||
simp [isLongestRevMatchAtChain_iff_isLongestRevMatchAtChain_beq, CharPred.isLongestRevMatchAtChain_iff]
|
||||
|
||||
theorem isLongestRevMatchAtChain_iff_toList {c : Char} {s : Slice} {pos pos' : s.Pos} :
|
||||
IsLongestRevMatchAtChain c pos pos' ↔
|
||||
∃ (h : pos ≤ pos'), (s.slice pos pos' h).copy.toList = List.replicate (s.slice pos pos' h).copy.length c := by
|
||||
simp [isLongestRevMatchAtChain_iff_isLongestRevMatchAtChain_beq, CharPred.isLongestRevMatchAtChain_iff_toList,
|
||||
List.eq_replicate_iff]
|
||||
|
||||
theorem isLongestRevMatchAtChain_startPos_endPos_iff_toList {c : Char} {s : Slice} :
|
||||
IsLongestRevMatchAtChain c s.startPos s.endPos ↔ s.copy.toList = List.replicate s.copy.length c := by
|
||||
simp [isLongestRevMatchAtChain_iff_isLongestRevMatchAtChain_beq,
|
||||
CharPred.isLongestRevMatchAtChain_startPos_endPos_iff_toList, List.eq_replicate_iff]
|
||||
|
||||
theorem matchesAt_iff_matchesAt_beq {c : Char} {s : Slice} {pos : s.Pos} :
|
||||
MatchesAt c pos ↔ MatchesAt (· == c) pos := by
|
||||
simp [matchesAt_iff_exists_isLongestMatchAt, isLongestMatchAt_iff_isLongestMatchAt_beq]
|
||||
|
||||
theorem revMatchesAt_iff_revMatchesAt_beq {c : Char} {s : Slice} {pos : s.Pos} :
|
||||
RevMatchesAt c pos ↔ RevMatchesAt (· == c) pos := by
|
||||
simp [revMatchesAt_iff_exists_isLongestRevMatchAt, isLongestRevMatchAt_iff_isLongestRevMatchAt_beq]
|
||||
|
||||
theorem matchAt?_eq_matchAt?_beq {c : Char} {s : Slice} {pos : s.Pos} :
|
||||
matchAt? c pos = matchAt? (· == c) pos := by
|
||||
refine Option.ext (fun pos' => ?_)
|
||||
simp [matchAt?_eq_some_iff, isLongestMatchAt_iff_isLongestMatchAt_beq]
|
||||
|
||||
theorem revMatchAt?_eq_revMatchAt?_beq {c : Char} {s : Slice} {pos : s.Pos} :
|
||||
revMatchAt? c pos = revMatchAt? (· == c) pos := by
|
||||
refine Option.ext (fun pos' => ?_)
|
||||
simp [revMatchAt?_eq_some_iff, isLongestRevMatchAt_iff_isLongestRevMatchAt_beq]
|
||||
|
||||
theorem isValidSearchFrom_iff_isValidSearchFrom_beq {c : Char} {s : Slice} {p : s.Pos}
|
||||
{l : List (SearchStep s)} : IsValidSearchFrom c p l ↔ IsValidSearchFrom (· == c) p l := by
|
||||
refine ⟨fun h => ?_, fun h => ?_⟩
|
||||
@@ -120,11 +255,28 @@ theorem isValidSearchFrom_iff_isValidSearchFrom_beq {c : Char} {s : Slice} {p :
|
||||
| matched => simp_all [IsValidSearchFrom.matched, isLongestMatchAt_iff_isLongestMatchAt_beq]
|
||||
| mismatched => simp_all [IsValidSearchFrom.mismatched, matchesAt_iff_matchesAt_beq]
|
||||
|
||||
theorem isValidRevSearchFrom_iff_isValidRevSearchFrom_beq {c : Char} {s : Slice} {p : s.Pos}
|
||||
{l : List (SearchStep s)} : IsValidRevSearchFrom c p l ↔ IsValidRevSearchFrom (· == c) p l := by
|
||||
refine ⟨fun h => ?_, fun h => ?_⟩
|
||||
· induction h with
|
||||
| startPos => simpa using IsValidRevSearchFrom.startPos
|
||||
| matched => simp_all [IsValidRevSearchFrom.matched, isLongestRevMatchAt_iff_isLongestRevMatchAt_beq]
|
||||
| mismatched => simp_all [IsValidRevSearchFrom.mismatched, revMatchesAt_iff_revMatchesAt_beq]
|
||||
· induction h with
|
||||
| startPos => simpa using IsValidRevSearchFrom.startPos
|
||||
| matched => simp_all [IsValidRevSearchFrom.matched, isLongestRevMatchAt_iff_isLongestRevMatchAt_beq]
|
||||
| mismatched => simp_all [IsValidRevSearchFrom.mismatched, revMatchesAt_iff_revMatchesAt_beq]
|
||||
|
||||
instance {c : Char} : LawfulToForwardSearcherModel c where
|
||||
isValidSearchFrom_toList s := by
|
||||
simpa [toSearcher_eq, isValidSearchFrom_iff_isValidSearchFrom_beq] using
|
||||
LawfulToForwardSearcherModel.isValidSearchFrom_toList (pat := (· == c)) (s := s)
|
||||
|
||||
instance {c : Char} : LawfulToBackwardSearcherModel c where
|
||||
isValidRevSearchFrom_toList s := by
|
||||
simpa [toBackwardSearcher_eq, isValidRevSearchFrom_iff_isValidRevSearchFrom_beq] using
|
||||
LawfulToBackwardSearcherModel.isValidRevSearchFrom_toList (pat := (· == c)) (s := s)
|
||||
|
||||
end Pattern.Model.Char
|
||||
|
||||
theorem startsWith_char_eq_startsWith_beq {c : Char} {s : Slice} :
|
||||
@@ -142,18 +294,21 @@ theorem skipPrefix?_char_eq_skipPrefix?_beq {c : Char} {s : Slice} :
|
||||
theorem Pattern.ForwardPattern.skipPrefix?_char_eq_skipPrefix?_beq {c : Char} {s : Slice} :
|
||||
skipPrefix? c s = skipPrefix? (· == c) s := (rfl)
|
||||
|
||||
theorem Pos.skip?_char_eq_skip?_beq {c : Char} {s : Slice} {pos : s.Pos} :
|
||||
pos.skip? c = pos.skip? (· == c) := (rfl)
|
||||
|
||||
theorem Pos.skipWhile_char_eq_skipWhile_beq {c : Char} {s : Slice} (curr : s.Pos) :
|
||||
Pos.skipWhile curr c = Pos.skipWhile curr (· == c) := by
|
||||
fun_induction Pos.skipWhile curr c with
|
||||
| case1 pos nextCurr h₁ h₂ ih =>
|
||||
conv => rhs; rw [Pos.skipWhile]
|
||||
simp [← Pattern.ForwardPattern.skipPrefix?_char_eq_skipPrefix?_beq, h₁, h₂, ih]
|
||||
simp [← Pos.skip?_char_eq_skip?_beq, h₁, h₂, ih]
|
||||
| case2 pos nextCurr h ih =>
|
||||
conv => rhs; rw [Pos.skipWhile]
|
||||
simp [← Pattern.ForwardPattern.skipPrefix?_char_eq_skipPrefix?_beq, h, ih]
|
||||
simp [← Pos.skip?_char_eq_skip?_beq, h, ih]
|
||||
| case3 pos h =>
|
||||
conv => rhs; rw [Pos.skipWhile]
|
||||
simp [← Pattern.ForwardPattern.skipPrefix?_char_eq_skipPrefix?_beq]
|
||||
simp [← Pos.skip?_char_eq_skip?_beq, h]
|
||||
|
||||
theorem skipPrefixWhile_char_eq_skipPrefixWhile_beq {c : Char} {s : Slice} :
|
||||
s.skipPrefixWhile c = s.skipPrefixWhile (· == c) :=
|
||||
@@ -169,7 +324,7 @@ theorem takeWhile_char_eq_takeWhile_beq {c : Char} {s : Slice} :
|
||||
|
||||
theorem all_char_eq_all_beq {c : Char} {s : Slice} :
|
||||
s.all c = s.all (· == c) := by
|
||||
simp only [all, dropWhile_char_eq_dropWhile_beq]
|
||||
simp only [all, skipPrefixWhile_char_eq_skipPrefixWhile_beq]
|
||||
|
||||
theorem find?_char_eq_find?_beq {c : Char} {s : Slice} :
|
||||
s.find? c = s.find? (· == c) :=
|
||||
@@ -198,18 +353,21 @@ theorem dropSuffix_char_eq_dropSuffix_beq {c : Char} {s : Slice} :
|
||||
theorem Pattern.BackwardPattern.skipSuffix?_char_eq_skipSuffix?_beq {c : Char} {s : Slice} :
|
||||
skipSuffix? c s = skipSuffix? (· == c) s := (rfl)
|
||||
|
||||
theorem Pos.revSkip?_char_eq_revSkip?_beq {c : Char} {s : Slice} {pos : s.Pos} :
|
||||
pos.revSkip? c = pos.revSkip? (· == c) := (rfl)
|
||||
|
||||
theorem Pos.revSkipWhile_char_eq_revSkipWhile_beq {c : Char} {s : Slice} (curr : s.Pos) :
|
||||
Pos.revSkipWhile curr c = Pos.revSkipWhile curr (· == c) := by
|
||||
fun_induction Pos.revSkipWhile curr c with
|
||||
| case1 pos nextCurr h₁ h₂ ih =>
|
||||
conv => rhs; rw [Pos.revSkipWhile]
|
||||
simp [← Pattern.BackwardPattern.skipSuffix?_char_eq_skipSuffix?_beq, h₁, h₂, ih]
|
||||
simp [← Pos.revSkip?_char_eq_revSkip?_beq, h₁, h₂, ih]
|
||||
| case2 pos nextCurr h ih =>
|
||||
conv => rhs; rw [Pos.revSkipWhile]
|
||||
simp [← Pattern.BackwardPattern.skipSuffix?_char_eq_skipSuffix?_beq, h, ih]
|
||||
simp [← Pos.revSkip?_char_eq_revSkip?_beq, h, ih]
|
||||
| case3 pos h =>
|
||||
conv => rhs; rw [Pos.revSkipWhile]
|
||||
simp [← Pattern.BackwardPattern.skipSuffix?_char_eq_skipSuffix?_beq]
|
||||
simp [← Pos.revSkip?_char_eq_revSkip?_beq, h]
|
||||
|
||||
theorem skipSuffixWhile_char_eq_skipSuffixWhile_beq {c : Char} {s : Slice} :
|
||||
s.skipSuffixWhile c = s.skipSuffixWhile (· == c) :=
|
||||
@@ -223,4 +381,16 @@ theorem takeEndWhile_char_eq_takeEndWhile_beq {c : Char} {s : Slice} :
|
||||
s.takeEndWhile c = s.takeEndWhile (· == c) := by
|
||||
simp only [takeEndWhile]; exact congrArg _ skipSuffixWhile_char_eq_skipSuffixWhile_beq
|
||||
|
||||
theorem revFind?_char_eq_revFind?_beq {c : Char} {s : Slice} :
|
||||
s.revFind? c = s.revFind? (· == c) :=
|
||||
(rfl)
|
||||
|
||||
theorem Pos.revFind?_char_eq_revFind?_beq {c : Char} {s : Slice} {p : s.Pos} :
|
||||
p.revFind? c = p.revFind? (· == c) :=
|
||||
(rfl)
|
||||
|
||||
theorem revAll_char_eq_revAll_beq {c : Char} {s : Slice} :
|
||||
s.revAll c = s.revAll (· == c) := by
|
||||
simp [revAll, skipSuffixWhile_char_eq_skipSuffixWhile_beq]
|
||||
|
||||
end String.Slice
|
||||
|
||||
@@ -23,8 +23,8 @@ open Std String.Slice Pattern Pattern.Model
|
||||
|
||||
namespace String.Slice
|
||||
|
||||
theorem Pattern.Model.find?_eq_some_iff {ρ : Type} (pat : ρ) [ForwardPatternModel pat] {σ : Slice → Type}
|
||||
[∀ s, Iterator (σ s) Id (SearchStep s)] [∀ s, Iterators.Finite (σ s) Id]
|
||||
theorem Pattern.Model.find?_eq_some_iff {ρ : Type} (pat : ρ) [PatternModel pat] [StrictPatternModel pat]
|
||||
{σ : Slice → Type} [∀ s, Iterator (σ s) Id (SearchStep s)] [∀ s, Iterators.Finite (σ s) Id]
|
||||
[∀ s, IteratorLoop (σ s) Id Id] [∀ s, LawfulIteratorLoop (σ s) Id Id]
|
||||
[ToForwardSearcher pat σ] [LawfulToForwardSearcherModel pat] {s : Slice} {pos : s.Pos} :
|
||||
s.find? pat = some pos ↔ MatchesAt pat pos ∧ (∀ pos', pos' < pos → ¬ MatchesAt pat pos') := by
|
||||
@@ -40,8 +40,8 @@ theorem Pattern.Model.find?_eq_some_iff {ρ : Type} (pat : ρ) [ForwardPatternMo
|
||||
| matched h₁ _ _ => have := h₁.matchesAt; grind
|
||||
| mismatched => grind
|
||||
|
||||
theorem Pattern.Model.find?_eq_none_iff {ρ : Type} (pat : ρ) [ForwardPatternModel pat] {σ : Slice → Type}
|
||||
[∀ s, Iterator (σ s) Id (SearchStep s)] [∀ s, Iterators.Finite (σ s) Id]
|
||||
theorem Pattern.Model.find?_eq_none_iff {ρ : Type} (pat : ρ) [PatternModel pat] [StrictPatternModel pat]
|
||||
{σ : Slice → Type} [∀ s, Iterator (σ s) Id (SearchStep s)] [∀ s, Iterators.Finite (σ s) Id]
|
||||
[∀ s, IteratorLoop (σ s) Id Id] [∀ s, LawfulIteratorLoop (σ s) Id Id]
|
||||
[ToForwardSearcher pat σ] [LawfulToForwardSearcherModel pat] {s : Slice} :
|
||||
s.find? pat = none ↔ ∀ (pos : s.Pos), ¬ MatchesAt pat pos := by
|
||||
@@ -65,15 +65,15 @@ theorem find?_eq_none_iff {ρ : Type} (pat : ρ) {σ : Slice → Type}
|
||||
[ToForwardSearcher pat σ] {s : Slice} : s.find? pat = none ↔ s.contains pat = false := by
|
||||
rw [← Option.isNone_iff_eq_none, ← Option.isSome_eq_false_iff, isSome_find?]
|
||||
|
||||
theorem Pattern.Model.contains_eq_false_iff {ρ : Type} (pat : ρ) [ForwardPatternModel pat] {σ : Slice → Type}
|
||||
[∀ s, Iterator (σ s) Id (SearchStep s)] [∀ s, Iterators.Finite (σ s) Id]
|
||||
theorem Pattern.Model.contains_eq_false_iff {ρ : Type} (pat : ρ) [PatternModel pat] [StrictPatternModel pat]
|
||||
{σ : Slice → Type} [∀ s, Iterator (σ s) Id (SearchStep s)] [∀ s, Iterators.Finite (σ s) Id]
|
||||
[∀ s, IteratorLoop (σ s) Id Id] [∀ s, LawfulIteratorLoop (σ s) Id Id]
|
||||
[ToForwardSearcher pat σ] [LawfulToForwardSearcherModel pat] {s : Slice} :
|
||||
s.contains pat = false ↔ ∀ (pos : s.Pos), ¬ MatchesAt pat pos := by
|
||||
rw [← find?_eq_none_iff, Slice.find?_eq_none_iff]
|
||||
|
||||
theorem Pattern.Model.contains_eq_true_iff {ρ : Type} (pat : ρ) [ForwardPatternModel pat] {σ : Slice → Type}
|
||||
[∀ s, Iterator (σ s) Id (SearchStep s)] [∀ s, Iterators.Finite (σ s) Id]
|
||||
theorem Pattern.Model.contains_eq_true_iff {ρ : Type} (pat : ρ) [PatternModel pat] [StrictPatternModel pat]
|
||||
{σ : Slice → Type} [∀ s, Iterator (σ s) Id (SearchStep s)] [∀ s, Iterators.Finite (σ s) Id]
|
||||
[∀ s, IteratorLoop (σ s) Id Id] [∀ s, LawfulIteratorLoop (σ s) Id Id]
|
||||
[ToForwardSearcher pat σ] [LawfulToForwardSearcherModel pat] {s : Slice} :
|
||||
s.contains pat ↔ ∃ (pos : s.Pos), MatchesAt pat pos := by
|
||||
@@ -85,7 +85,7 @@ theorem Pos.find?_eq_find?_sliceFrom {ρ : Type} {pat : ρ} {σ : Slice → Type
|
||||
p.find? pat = ((s.sliceFrom p).find? pat).map Pos.ofSliceFrom :=
|
||||
(rfl)
|
||||
|
||||
theorem Pattern.Model.posFind?_eq_some_iff {ρ : Type} {pat : ρ} [ForwardPatternModel pat] {σ : Slice → Type}
|
||||
theorem Pattern.Model.posFind?_eq_some_iff {ρ : Type} {pat : ρ} [PatternModel pat] [StrictPatternModel pat] {σ : Slice → Type}
|
||||
[∀ s, Iterator (σ s) Id (SearchStep s)] [∀ s, Iterators.Finite (σ s) Id]
|
||||
[∀ s, IteratorLoop (σ s) Id Id] [∀ s, LawfulIteratorLoop (σ s) Id Id]
|
||||
[ToForwardSearcher pat σ] [LawfulToForwardSearcherModel pat] {s : Slice} {pos pos' : s.Pos} :
|
||||
@@ -100,8 +100,8 @@ theorem Pattern.Model.posFind?_eq_some_iff {ρ : Type} {pat : ρ} [ForwardPatter
|
||||
refine ⟨Pos.sliceFrom _ _ h₁, ⟨by simpa using h₂, fun p hp₁ hp₂ => ?_⟩, by simp⟩
|
||||
exact h₃ (Pos.ofSliceFrom p) Slice.Pos.le_ofSliceFrom (Pos.lt_sliceFrom_iff.1 hp₁) hp₂
|
||||
|
||||
theorem Pattern.Model.posFind?_eq_none_iff {ρ : Type} {pat : ρ} [ForwardPatternModel pat] {σ : Slice → Type}
|
||||
[∀ s, Iterator (σ s) Id (SearchStep s)] [∀ s, Iterators.Finite (σ s) Id]
|
||||
theorem Pattern.Model.posFind?_eq_none_iff {ρ : Type} {pat : ρ} [PatternModel pat] [StrictPatternModel pat]
|
||||
{σ : Slice → Type} [∀ s, Iterator (σ s) Id (SearchStep s)] [∀ s, Iterators.Finite (σ s) Id]
|
||||
[∀ s, IteratorLoop (σ s) Id Id] [∀ s, LawfulIteratorLoop (σ s) Id Id]
|
||||
[ToForwardSearcher pat σ] [LawfulToForwardSearcherModel pat] {s : Slice} {pos : s.Pos} :
|
||||
pos.find? pat = none ↔ ∀ pos', pos ≤ pos' → ¬ MatchesAt pat pos' := by
|
||||
|
||||
@@ -183,7 +183,7 @@ theorem find?_char_eq_some_iff_splits {c : Char} {s : String} {pos : s.Pos} :
|
||||
· rintro ⟨t, u, hsplit, hnotin⟩
|
||||
exact ⟨pos.toSlice, ⟨t, u, Pos.splits_toSlice_iff.mpr hsplit, hnotin⟩, by simp⟩
|
||||
|
||||
@[simp]
|
||||
@[cbv_eval, simp]
|
||||
theorem contains_char_eq {c : Char} {s : String} : s.contains c = decide (c ∈ s.toList) := by
|
||||
simp [contains_eq_contains_toSlice, Slice.contains_char_eq, copy_toSlice]
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ theorem find?_prop_eq_some_iff_splits {p : Char → Prop} [DecidablePred p] {s :
|
||||
simp only [find?_prop_eq_find?_decide, find?_bool_eq_some_iff_splits, decide_eq_true_eq,
|
||||
decide_eq_false_iff_not]
|
||||
|
||||
@[simp]
|
||||
@[cbv_eval, simp]
|
||||
theorem contains_bool_eq {p : Char → Bool} {s : Slice} : s.contains p = s.copy.toList.any p := by
|
||||
rw [Bool.eq_iff_iff, Pattern.Model.contains_eq_true_iff]
|
||||
simp only [Pattern.Model.CharPred.matchesAt_iff, ne_eq, List.any_eq_true,
|
||||
|
||||
@@ -49,9 +49,10 @@ theorem contains_slice_iff {t s : Slice} :
|
||||
by_cases ht : t.isEmpty
|
||||
· simp [contains_eq_true_of_isEmpty ht s, copy_eq_empty_iff.mpr ht, String.toList_empty]
|
||||
· simp only [Bool.not_eq_true] at ht
|
||||
have := Pattern.Model.ForwardSliceSearcher.strictPatternModel ht
|
||||
have := Pattern.Model.ForwardSliceSearcher.lawfulToForwardSearcherModel ht
|
||||
simp only [Pattern.Model.contains_eq_true_iff,
|
||||
Pattern.Model.ForwardSliceSearcher.exists_matchesAt_iff_eq_append ht, isInfix_toList_iff]
|
||||
Pattern.Model.ForwardSliceSearcher.exists_matchesAt_iff_eq_append, isInfix_toList_iff]
|
||||
|
||||
@[simp]
|
||||
theorem contains_string_iff {t : String} {s : Slice} :
|
||||
@@ -90,4 +91,12 @@ theorem contains_string_eq_false_iff {t s : String} :
|
||||
s.contains t = false ↔ ¬(t.toList <:+: s.toList) :=
|
||||
Bool.eq_false_iff.trans (not_congr contains_string_iff)
|
||||
|
||||
/-
|
||||
Used internally by the `cbv` tactic.
|
||||
-/
|
||||
@[cbv_eval]
|
||||
theorem contains_string_eq_internal {t s : String} :
|
||||
s.contains t = t.toList.isInfixOf_internal s.toList := by
|
||||
rw [Bool.eq_iff_iff, contains_string_iff, List.isInfixOf_internal_iff_isInfix]
|
||||
|
||||
end String
|
||||
|
||||
@@ -18,125 +18,321 @@ import Init.Data.String.Lemmas.Basic
|
||||
import Init.Data.String.Lemmas.Order
|
||||
import Init.Data.Order.Lemmas
|
||||
import Init.Data.String.OrderInstances
|
||||
import Init.Data.String.Lemmas.Iterate
|
||||
import Init.Omega
|
||||
import Init.Data.String.Lemmas.FindPos
|
||||
|
||||
public section
|
||||
|
||||
namespace String.Slice.Pattern.Model.CharPred
|
||||
|
||||
instance {p : Char → Bool} : ForwardPatternModel p where
|
||||
instance {p : Char → Bool} : PatternModel p where
|
||||
Matches s := ∃ c, s = singleton c ∧ p c
|
||||
not_matches_empty := by
|
||||
simp
|
||||
|
||||
instance {p : Char → Bool} : NoPrefixForwardPatternModel p :=
|
||||
.of_length_eq (by simp +contextual [ForwardPatternModel.Matches])
|
||||
instance {p : Char → Bool} : StrictPatternModel p where
|
||||
not_matches_empty := by simp [PatternModel.Matches]
|
||||
|
||||
instance {p : Char → Bool} : NoPrefixPatternModel p :=
|
||||
.of_length_eq (by simp +contextual [PatternModel.Matches])
|
||||
|
||||
instance {p : Char → Bool} : NoSuffixPatternModel p :=
|
||||
.of_length_eq (by simp +contextual [PatternModel.Matches])
|
||||
|
||||
theorem isMatch_iff {p : Char → Bool} {s : Slice} {pos : s.Pos} :
|
||||
IsMatch p pos ↔
|
||||
∃ (h : s.startPos ≠ s.endPos), pos = s.startPos.next h ∧ p (s.startPos.get h) := by
|
||||
simp only [Model.isMatch_iff, ForwardPatternModel.Matches, sliceTo_copy_eq_iff_exists_splits]
|
||||
simp only [Model.isMatch_iff, PatternModel.Matches, copy_sliceTo_eq_iff_exists_splits]
|
||||
refine ⟨?_, ?_⟩
|
||||
· simp only [splits_singleton_iff]
|
||||
refine fun ⟨c, ⟨t₂, h, h₁, h₂, h₃⟩, hc⟩ => ⟨h, h₁, h₂ ▸ hc⟩
|
||||
· rintro ⟨h, rfl, h'⟩
|
||||
exact ⟨s.startPos.get h, ⟨_, Slice.splits_next_startPos⟩, h'⟩
|
||||
|
||||
theorem isRevMatch_iff {p : Char → Bool} {s : Slice} {pos : s.Pos} :
|
||||
IsRevMatch p pos ↔
|
||||
∃ (h : s.endPos ≠ s.startPos), pos = s.endPos.prev h ∧ p ((s.endPos.prev h).get (by simp)) := by
|
||||
simp only [Model.isRevMatch_iff, PatternModel.Matches, copy_sliceFrom_eq_iff_exists_splits]
|
||||
refine ⟨?_, ?_⟩
|
||||
· simp only [splits_singleton_right_iff]
|
||||
refine fun ⟨c, ⟨t₂, h, h₁, h₂, h₃⟩, hc⟩ => ⟨h, h₁, h₂ ▸ hc⟩
|
||||
· rintro ⟨h, rfl, h'⟩
|
||||
exact ⟨(s.endPos.prev h).get (by simp), ⟨_, Slice.splits_prev_endPos⟩, h'⟩
|
||||
|
||||
theorem isLongestMatch_iff {p : Char → Bool} {s : Slice} {pos : s.Pos} :
|
||||
IsLongestMatch p pos ↔
|
||||
∃ (h : s.startPos ≠ s.endPos), pos = s.startPos.next h ∧ p (s.startPos.get h) := by
|
||||
rw [isLongestMatch_iff_isMatch, isMatch_iff]
|
||||
|
||||
theorem isLongestRevMatch_iff {p : Char → Bool} {s : Slice} {pos : s.Pos} :
|
||||
IsLongestRevMatch p pos ↔
|
||||
∃ (h : s.endPos ≠ s.startPos), pos = s.endPos.prev h ∧ p ((s.endPos.prev h).get (by simp)) := by
|
||||
rw [isLongestRevMatch_iff_isRevMatch, isRevMatch_iff]
|
||||
|
||||
theorem isLongestMatchAt_iff {p : Char → Bool} {s : Slice} {pos pos' : s.Pos} :
|
||||
IsLongestMatchAt p pos pos' ↔ ∃ h, pos' = pos.next h ∧ p (pos.get h) := by
|
||||
simp +contextual [Model.isLongestMatchAt_iff, isLongestMatch_iff, ← Pos.ofSliceFrom_inj,
|
||||
Pos.get_eq_get_ofSliceFrom, Pos.ofSliceFrom_next]
|
||||
|
||||
theorem isLongestMatchAtChain_iff {p : Char → Bool} {s : Slice} {pos pos' : s.Pos} :
|
||||
IsLongestMatchAtChain p pos pos' ↔ pos ≤ pos' ∧ ∀ pos'', pos ≤ pos'' → (h : pos'' < pos') → p (pos''.get (Pos.ne_endPos_of_lt h)) := by
|
||||
induction pos using WellFounded.induction Pos.wellFounded_gt with | h pos ih
|
||||
obtain (h|rfl|h) := Std.lt_trichotomy pos pos'
|
||||
· refine ⟨fun h => ?_, fun ⟨h₁, h₂⟩ => ?_⟩
|
||||
· cases h with
|
||||
| nil => exact (Std.lt_irrefl h).elim
|
||||
| cons _ mid _ h₁ h₂ =>
|
||||
obtain ⟨h₀, rfl, h₁'⟩ := isLongestMatchAt_iff.1 h₁
|
||||
refine ⟨Std.le_of_lt h, fun pos'' hp₁ hp₂ => ?_⟩
|
||||
obtain (hh|rfl) := Std.le_iff_lt_or_eq.1 hp₁
|
||||
· exact ((ih (pos.next (Pos.ne_endPos_of_lt h)) Pos.lt_next).1 h₂).2 _ (by simpa) hp₂
|
||||
· exact h₁'
|
||||
· refine .cons _ (pos.next (Pos.ne_endPos_of_lt h)) _ ?_ ((ih _ Pos.lt_next).2 ?_)
|
||||
· exact isLongestMatchAt_iff.2 ⟨Pos.ne_endPos_of_lt h, rfl, h₂ _ (by simp) h⟩
|
||||
· exact ⟨by simpa, fun pos'' hp₁ hp₂ => h₂ _ (Std.le_trans Pos.le_next hp₁) hp₂⟩
|
||||
· simpa using fun _ h₁ h₂ => (Std.lt_irrefl (Std.lt_of_le_of_lt h₁ h₂)).elim
|
||||
· simpa [Std.not_le.2 h] using fun h' => (Std.not_le.2 h h'.le).elim
|
||||
|
||||
theorem isLongestMatchAtChain_iff_toList {p : Char → Bool} {s : Slice} {pos pos' : s.Pos} :
|
||||
IsLongestMatchAtChain p pos pos' ↔ ∃ (h : pos ≤ pos'), ∀ c, c ∈ (s.slice pos pos' h).copy.toList → p c := by
|
||||
simp only [isLongestMatchAtChain_iff, mem_toList_copy_iff_exists_get, Pos.get_eq_get_ofSlice,
|
||||
forall_exists_index]
|
||||
refine ⟨fun ⟨h₁, h₂⟩ => ⟨h₁, fun c p' hp => ?_⟩, fun ⟨h₁, h₂⟩ => ⟨h₁, fun p' hp₁ hp₂ => ?_⟩⟩
|
||||
· rintro rfl
|
||||
exact h₂ _ Pos.le_ofSlice (by simp [Pos.ofSlice_lt_iff, h₁, hp])
|
||||
· refine h₂ _ (Pos.slice p' _ _ hp₁ (Std.le_of_lt hp₂)) ?_ (by simp)
|
||||
rwa [← Pos.lt_endPos_iff, ← Pos.slice_eq_endPos (h := h₁), Pos.slice_lt_slice_iff]
|
||||
|
||||
theorem isLongestMatchAtChain_startPos_endPos_iff_toList {p : Char → Bool} {s : Slice} :
|
||||
IsLongestMatchAtChain p s.startPos s.endPos ↔ ∀ c, c ∈ s.copy.toList → p c := by
|
||||
simp [isLongestMatchAtChain_iff_toList]
|
||||
|
||||
theorem isLongestRevMatchAt_iff {p : Char → Bool} {s : Slice} {pos pos' : s.Pos} :
|
||||
IsLongestRevMatchAt p pos pos' ↔ ∃ h, pos = pos'.prev h ∧ p ((pos'.prev h).get (by simp)) := by
|
||||
simp +contextual [Model.isLongestRevMatchAt_iff, isLongestRevMatch_iff, ← Pos.ofSliceTo_inj,
|
||||
Pos.get_eq_get_ofSliceTo, Pos.ofSliceTo_prev]
|
||||
|
||||
theorem isLongestMatchAt_of_get {p : Char → Bool} {s : Slice} {pos : s.Pos} {h : pos ≠ s.endPos}
|
||||
(hc : p (pos.get h)) : IsLongestMatchAt p pos (pos.next h) :=
|
||||
isLongestMatchAt_iff.2 ⟨h, by simp [hc]⟩
|
||||
|
||||
theorem isLongestRevMatchAt_of_get {p : Char → Bool} {s : Slice} {pos : s.Pos} {h : pos ≠ s.startPos}
|
||||
(hc : p ((pos.prev h).get (by simp))) : IsLongestRevMatchAt p (pos.prev h) pos :=
|
||||
isLongestRevMatchAt_iff.2 ⟨h, by simp [hc]⟩
|
||||
|
||||
theorem isLongestRevMatchAtChain_iff {p : Char → Bool} {s : Slice} {pos pos' : s.Pos} :
|
||||
IsLongestRevMatchAtChain p pos pos' ↔ pos ≤ pos' ∧ ∀ pos'', pos ≤ pos'' → (h : pos'' < pos') → p (pos''.get (Pos.ne_endPos_of_lt h)) := by
|
||||
induction pos' using WellFounded.induction Pos.wellFounded_lt with | h pos' ih
|
||||
obtain (h|rfl|h) := Std.lt_trichotomy pos pos'
|
||||
· refine ⟨fun h => ?_, fun ⟨h₁, h₂⟩ => ?_⟩
|
||||
· cases h with
|
||||
| nil => exact (Std.lt_irrefl h).elim
|
||||
| cons _ _ hchain hmatch =>
|
||||
obtain ⟨hne, hmid, hp⟩ := isLongestRevMatchAt_iff.1 hmatch
|
||||
refine ⟨Std.le_of_lt h, fun pos'' hp₁ hp₂ => ?_⟩
|
||||
rcases Std.le_iff_lt_or_eq.1 (Pos.le_prev_iff_lt.2 hp₂) with hh | heq
|
||||
· exact ((ih _ Pos.prev_lt).1 (hmid ▸ hchain)).2 _ hp₁ hh
|
||||
· exact heq ▸ hp
|
||||
· have hne : pos' ≠ s.startPos := Slice.Pos.ne_startPos_of_lt h
|
||||
refine .cons _ (pos'.prev hne) _ ((ih _ Pos.prev_lt).2 ?_)
|
||||
(isLongestRevMatchAt_of_get (h₂ _ (Pos.le_prev_iff_lt.2 h) Pos.prev_lt))
|
||||
exact ⟨Pos.le_prev_iff_lt.2 h, fun pos'' hp₁ hp₂ =>
|
||||
h₂ _ hp₁ (Std.lt_trans hp₂ Pos.prev_lt)⟩
|
||||
· simpa using fun _ h₁ h₂ => (Std.lt_irrefl (Std.lt_of_le_of_lt h₁ h₂)).elim
|
||||
· simpa [Std.not_le.2 h] using fun h' => (Std.not_le.2 h h'.le).elim
|
||||
|
||||
theorem isLongestRevMatchAtChain_iff_toList {p : Char → Bool} {s : Slice} {pos pos' : s.Pos} :
|
||||
IsLongestRevMatchAtChain p pos pos' ↔ ∃ (h : pos ≤ pos'), ∀ c, c ∈ (s.slice pos pos' h).copy.toList → p c :=
|
||||
isLongestRevMatchAtChain_iff.trans (isLongestMatchAtChain_iff.symm.trans isLongestMatchAtChain_iff_toList)
|
||||
|
||||
theorem isLongestRevMatchAtChain_startPos_endPos_iff_toList {p : Char → Bool} {s : Slice} :
|
||||
IsLongestRevMatchAtChain p s.startPos s.endPos ↔ ∀ c, c ∈ s.copy.toList → p c := by
|
||||
simp [isLongestRevMatchAtChain_iff_toList]
|
||||
|
||||
instance {p : Char → Bool} : LawfulForwardPatternModel p where
|
||||
skipPrefix?_eq_some_iff {s} pos := by
|
||||
simp [isLongestMatch_iff, ForwardPattern.skipPrefix?, and_comm, eq_comm (b := pos)]
|
||||
|
||||
instance {p : Char → Bool} : LawfulBackwardPatternModel p where
|
||||
skipSuffix?_eq_some_iff {s} pos := by
|
||||
simp [isLongestRevMatch_iff, BackwardPattern.skipSuffix?, and_comm, eq_comm (b := pos)]
|
||||
|
||||
instance {p : Char → Bool} : LawfulToForwardSearcherModel p :=
|
||||
.defaultImplementation
|
||||
|
||||
instance {p : Char → Bool} : LawfulToBackwardSearcherModel p :=
|
||||
.defaultImplementation
|
||||
|
||||
theorem matchesAt_iff {p : Char → Bool} {s : Slice} {pos : s.Pos} :
|
||||
MatchesAt p pos ↔ ∃ (h : pos ≠ s.endPos), p (pos.get h) := by
|
||||
simp [matchesAt_iff_exists_isLongestMatchAt, isLongestMatchAt_iff, exists_comm]
|
||||
|
||||
theorem revMatchesAt_iff {p : Char → Bool} {s : Slice} {pos : s.Pos} :
|
||||
RevMatchesAt p pos ↔ ∃ (h : pos ≠ s.startPos), p ((pos.prev h).get (by simp)) := by
|
||||
simp [revMatchesAt_iff_exists_isLongestRevMatchAt, isLongestRevMatchAt_iff, exists_comm]
|
||||
|
||||
theorem not_matchesAt_of_get {p : Char → Bool} {s : Slice} {pos : s.Pos} {h : pos ≠ s.endPos}
|
||||
(hc : p (pos.get h) = false) : ¬ MatchesAt p pos := by
|
||||
simp [matchesAt_iff, hc]
|
||||
|
||||
theorem not_revMatchesAt_of_get {p : Char → Bool} {s : Slice} {pos : s.Pos} {h : pos ≠ s.startPos}
|
||||
(hc : p ((pos.prev h).get (by simp)) = false) : ¬ RevMatchesAt p pos := by
|
||||
simp [revMatchesAt_iff, hc]
|
||||
|
||||
theorem matchAt?_eq {s : Slice} {pos : s.Pos} {p : Char → Bool} :
|
||||
matchAt? p pos =
|
||||
if h₀ : ∃ (h : pos ≠ s.endPos), p (pos.get h) then some (pos.next h₀.1) else none := by
|
||||
split <;> simp_all [isLongestMatchAt_iff, matchesAt_iff]
|
||||
|
||||
theorem revMatchAt?_eq {s : Slice} {pos : s.Pos} {p : Char → Bool} :
|
||||
revMatchAt? p pos =
|
||||
if h₀ : ∃ (h : pos ≠ s.startPos), p ((pos.prev h).get (by simp)) then some (pos.prev h₀.1) else none := by
|
||||
split <;> simp_all [isLongestRevMatchAt_iff, revMatchesAt_iff]
|
||||
|
||||
namespace Decidable
|
||||
|
||||
instance {p : Char → Prop} [DecidablePred p] : ForwardPatternModel p where
|
||||
Matches := ForwardPatternModel.Matches (decide <| p ·)
|
||||
not_matches_empty := ForwardPatternModel.not_matches_empty (pat := (decide <| p ·))
|
||||
instance {p : Char → Prop} [DecidablePred p] : PatternModel p where
|
||||
Matches := PatternModel.Matches (decide <| p ·)
|
||||
|
||||
instance {p : Char → Prop} [DecidablePred p] : NoPrefixForwardPatternModel p where
|
||||
eq_empty := NoPrefixForwardPatternModel.eq_empty (pat := (decide <| p ·))
|
||||
instance {p : Char → Prop} [DecidablePred p] : StrictPatternModel p where
|
||||
not_matches_empty := StrictPatternModel.not_matches_empty (pat := (decide <| p ·))
|
||||
|
||||
instance {p : Char → Prop} [DecidablePred p] : NoPrefixPatternModel p where
|
||||
eq_empty := NoPrefixPatternModel.eq_empty (pat := (decide <| p ·))
|
||||
|
||||
instance {p : Char → Prop} [DecidablePred p] : NoSuffixPatternModel p where
|
||||
eq_empty := NoSuffixPatternModel.eq_empty (pat := (decide <| p ·))
|
||||
|
||||
theorem isMatch_iff_isMatch_decide {p : Char → Prop} [DecidablePred p] {s : Slice} {pos : s.Pos} :
|
||||
IsMatch p pos ↔ IsMatch (decide <| p ·) pos :=
|
||||
⟨fun ⟨h⟩ => ⟨h⟩, fun ⟨h⟩ => ⟨h⟩⟩
|
||||
|
||||
theorem isRevMatch_iff_isRevMatch_decide {p : Char → Prop} [DecidablePred p] {s : Slice} {pos : s.Pos} :
|
||||
IsRevMatch p pos ↔ IsRevMatch (decide <| p ·) pos :=
|
||||
⟨fun ⟨h⟩ => ⟨h⟩, fun ⟨h⟩ => ⟨h⟩⟩
|
||||
|
||||
theorem isMatch_iff {p : Char → Prop} [DecidablePred p] {s : Slice} {pos : s.Pos} :
|
||||
IsMatch p pos ↔
|
||||
∃ (h : s.startPos ≠ s.endPos), pos = s.startPos.next h ∧ p (s.startPos.get h) := by
|
||||
simp [isMatch_iff_isMatch_decide, CharPred.isMatch_iff]
|
||||
|
||||
theorem isRevMatch_iff {p : Char → Prop} [DecidablePred p] {s : Slice} {pos : s.Pos} :
|
||||
IsRevMatch p pos ↔
|
||||
∃ (h : s.endPos ≠ s.startPos), pos = s.endPos.prev h ∧ p ((s.endPos.prev h).get (by simp)) := by
|
||||
simp [isRevMatch_iff_isRevMatch_decide, CharPred.isRevMatch_iff]
|
||||
|
||||
theorem isLongestMatch_iff {p : Char → Prop} [DecidablePred p] {s : Slice} {pos : s.Pos} :
|
||||
IsLongestMatch p pos ↔
|
||||
∃ (h : s.startPos ≠ s.endPos), pos = s.startPos.next h ∧ p (s.startPos.get h) := by
|
||||
rw [isLongestMatch_iff_isMatch, isMatch_iff]
|
||||
|
||||
theorem isLongestRevMatch_iff {p : Char → Prop} [DecidablePred p] {s : Slice} {pos : s.Pos} :
|
||||
IsLongestRevMatch p pos ↔
|
||||
∃ (h : s.endPos ≠ s.startPos), pos = s.endPos.prev h ∧ p ((s.endPos.prev h).get (by simp)) := by
|
||||
simp [isLongestRevMatch_iff_isRevMatch, isRevMatch_iff]
|
||||
|
||||
theorem isLongestMatch_iff_isLongestMatch_decide {p : Char → Prop} [DecidablePred p] {s : Slice}
|
||||
{pos : s.Pos} : IsLongestMatch p pos ↔ IsLongestMatch (decide <| p ·) pos := by
|
||||
simp [isLongestMatch_iff_isMatch, isMatch_iff_isMatch_decide]
|
||||
|
||||
theorem isLongestRevMatch_iff_isLongestRevMatch_decide {p : Char → Prop} [DecidablePred p] {s : Slice}
|
||||
{pos : s.Pos} : IsLongestRevMatch p pos ↔ IsLongestRevMatch (decide <| p ·) pos := by
|
||||
simp [isLongestRevMatch_iff_isRevMatch, isRevMatch_iff_isRevMatch_decide]
|
||||
|
||||
theorem isLongestMatchAt_iff_isLongestMatchAt_decide {p : Char → Prop} [DecidablePred p]
|
||||
{s : Slice} {pos pos' : s.Pos} :
|
||||
IsLongestMatchAt p pos pos' ↔ IsLongestMatchAt (decide <| p ·) pos pos' := by
|
||||
simp [Model.isLongestMatchAt_iff, isLongestMatch_iff_isLongestMatch_decide]
|
||||
|
||||
theorem isLongestRevMatchAt_iff_isLongestRevMatchAt_decide {p : Char → Prop} [DecidablePred p]
|
||||
{s : Slice} {pos pos' : s.Pos} :
|
||||
IsLongestRevMatchAt p pos pos' ↔ IsLongestRevMatchAt (decide <| p ·) pos pos' := by
|
||||
simp [Model.isLongestRevMatchAt_iff, isLongestRevMatch_iff_isLongestRevMatch_decide]
|
||||
|
||||
theorem isLongestMatchAtChain_iff_isLongestMatchAtChain_decide {p : Char → Prop} [DecidablePred p]
|
||||
{s : Slice} {pos pos' : s.Pos} :
|
||||
IsLongestMatchAtChain p pos pos' ↔ IsLongestMatchAtChain (decide <| p ·) pos pos' := by
|
||||
constructor
|
||||
· intro h; induction h with
|
||||
| nil => exact .nil _
|
||||
| cons _ mid _ hmatch hchain ih =>
|
||||
exact .cons _ mid _ (isLongestMatchAt_iff_isLongestMatchAt_decide.1 hmatch) ih
|
||||
· intro h; induction h with
|
||||
| nil => exact .nil _
|
||||
| cons _ mid _ hmatch hchain ih =>
|
||||
exact .cons _ mid _ (isLongestMatchAt_iff_isLongestMatchAt_decide.2 hmatch) ih
|
||||
|
||||
theorem isLongestRevMatchAtChain_iff_isLongestRevMatchAtChain_decide {p : Char → Prop} [DecidablePred p]
|
||||
{s : Slice} {pos pos' : s.Pos} :
|
||||
IsLongestRevMatchAtChain p pos pos' ↔ IsLongestRevMatchAtChain (decide <| p ·) pos pos' := by
|
||||
constructor
|
||||
· intro h; induction h with
|
||||
| nil => exact .nil _
|
||||
| cons _ _ hchain hmatch ih =>
|
||||
exact .cons _ _ _ ih (isLongestRevMatchAt_iff_isLongestRevMatchAt_decide.1 hmatch)
|
||||
· intro h; induction h with
|
||||
| nil => exact .nil _
|
||||
| cons _ _ hchain hmatch ih =>
|
||||
exact .cons _ _ _ ih (isLongestRevMatchAt_iff_isLongestRevMatchAt_decide.2 hmatch)
|
||||
|
||||
theorem isLongestMatchAt_iff {p : Char → Prop} [DecidablePred p] {s : Slice}
|
||||
{pos pos' : s.Pos} :
|
||||
IsLongestMatchAt p pos pos' ↔ ∃ h, pos' = pos.next h ∧ p (pos.get h) := by
|
||||
simp [isLongestMatchAt_iff_isLongestMatchAt_decide, CharPred.isLongestMatchAt_iff]
|
||||
|
||||
theorem isLongestRevMatchAt_iff {p : Char → Prop} [DecidablePred p] {s : Slice}
|
||||
{pos pos' : s.Pos} :
|
||||
IsLongestRevMatchAt p pos pos' ↔ ∃ h, pos = pos'.prev h ∧ p ((pos'.prev h).get (by simp)) := by
|
||||
simp [isLongestRevMatchAt_iff_isLongestRevMatchAt_decide, CharPred.isLongestRevMatchAt_iff]
|
||||
|
||||
theorem isLongestMatchAt_of_get {p : Char → Prop} [DecidablePred p] {s : Slice} {pos : s.Pos}
|
||||
{h : pos ≠ s.endPos} (hc : p (pos.get h)) : IsLongestMatchAt p pos (pos.next h) :=
|
||||
isLongestMatchAt_iff.2 ⟨h, by simp [hc]⟩
|
||||
|
||||
theorem isLongestRevMatchAt_of_get {p : Char → Prop} [DecidablePred p] {s : Slice} {pos : s.Pos}
|
||||
{h : pos ≠ s.startPos} (hc : p ((pos.prev h).get (by simp))) :
|
||||
IsLongestRevMatchAt p (pos.prev h) pos :=
|
||||
isLongestRevMatchAt_iff.2 ⟨h, by simp [hc]⟩
|
||||
|
||||
theorem matchesAt_iff_matchesAt_decide {p : Char → Prop} [DecidablePred p] {s : Slice}
|
||||
{pos : s.Pos} : MatchesAt p pos ↔ MatchesAt (decide <| p ·) pos := by
|
||||
simp [matchesAt_iff_exists_isLongestMatchAt, isLongestMatchAt_iff_isLongestMatchAt_decide]
|
||||
|
||||
theorem revMatchesAt_iff_revMatchesAt_decide {p : Char → Prop} [DecidablePred p] {s : Slice}
|
||||
{pos : s.Pos} : RevMatchesAt p pos ↔ RevMatchesAt (decide <| p ·) pos := by
|
||||
simp [revMatchesAt_iff_exists_isLongestRevMatchAt, isLongestRevMatchAt_iff_isLongestRevMatchAt_decide]
|
||||
|
||||
theorem matchAt?_eq_matchAt?_decide {p : Char → Prop} [DecidablePred p] {s : Slice}
|
||||
{pos : s.Pos} : matchAt? p pos = matchAt? (decide <| p ·) pos := by
|
||||
ext endPos
|
||||
simp [isLongestMatchAt_iff_isLongestMatchAt_decide]
|
||||
|
||||
theorem revMatchAt?_eq_revMatchAt?_decide {p : Char → Prop} [DecidablePred p] {s : Slice}
|
||||
{pos : s.Pos} : revMatchAt? p pos = revMatchAt? (decide <| p ·) pos := by
|
||||
ext startPos
|
||||
simp [isLongestRevMatchAt_iff_isLongestRevMatchAt_decide]
|
||||
|
||||
theorem skipPrefix?_eq_skipPrefix?_decide {p : Char → Prop} [DecidablePred p] :
|
||||
ForwardPattern.skipPrefix? p = ForwardPattern.skipPrefix? (decide <| p ·) := rfl
|
||||
|
||||
theorem skipSuffix?_eq_skipSuffix?_decide {p : Char → Prop} [DecidablePred p] :
|
||||
BackwardPattern.skipSuffix? p = BackwardPattern.skipSuffix? (decide <| p ·) := rfl
|
||||
|
||||
instance {p : Char → Prop} [DecidablePred p] : LawfulForwardPatternModel p where
|
||||
skipPrefix?_eq_some_iff {s} pos := by
|
||||
rw [skipPrefix?_eq_skipPrefix?_decide, isLongestMatch_iff_isLongestMatch_decide]
|
||||
exact LawfulForwardPatternModel.skipPrefix?_eq_some_iff ..
|
||||
|
||||
instance {p : Char → Prop} [DecidablePred p] : LawfulBackwardPatternModel p where
|
||||
skipSuffix?_eq_some_iff {s} pos := by
|
||||
rw [skipSuffix?_eq_skipSuffix?_decide, isLongestRevMatch_iff_isLongestRevMatch_decide]
|
||||
exact LawfulBackwardPatternModel.skipSuffix?_eq_some_iff ..
|
||||
|
||||
theorem toSearcher_eq {p : Char → Prop} [DecidablePred p] {s : Slice} :
|
||||
ToForwardSearcher.toSearcher p s = ToForwardSearcher.toSearcher (decide <| p ·) s := (rfl)
|
||||
|
||||
theorem toBackwardSearcher_eq {p : Char → Prop} [DecidablePred p] {s : Slice} :
|
||||
ToBackwardSearcher.toSearcher p s = ToBackwardSearcher.toSearcher (decide <| p ·) s := (rfl)
|
||||
|
||||
theorem isValidSearchFrom_iff_isValidSearchFrom_decide {p : Char → Prop} [DecidablePred p]
|
||||
{s : Slice} {pos : s.Pos} {l : List (SearchStep s)} :
|
||||
IsValidSearchFrom p pos l ↔ IsValidSearchFrom (decide <| p ·) pos l := by
|
||||
@@ -150,24 +346,55 @@ theorem isValidSearchFrom_iff_isValidSearchFrom_decide {p : Char → Prop} [Deci
|
||||
| matched => simp_all [IsValidSearchFrom.matched, isLongestMatchAt_iff_isLongestMatchAt_decide]
|
||||
| mismatched => simp_all [IsValidSearchFrom.mismatched, matchesAt_iff_matchesAt_decide]
|
||||
|
||||
theorem isValidRevSearchFrom_iff_isValidRevSearchFrom_decide {p : Char → Prop} [DecidablePred p]
|
||||
{s : Slice} {pos : s.Pos} {l : List (SearchStep s)} :
|
||||
IsValidRevSearchFrom p pos l ↔ IsValidRevSearchFrom (decide <| p ·) pos l := by
|
||||
refine ⟨fun h => ?_, fun h => ?_⟩
|
||||
· induction h with
|
||||
| startPos => simpa using IsValidRevSearchFrom.startPos
|
||||
| matched => simp_all [IsValidRevSearchFrom.matched, isLongestRevMatchAt_iff_isLongestRevMatchAt_decide]
|
||||
| mismatched => simp_all [IsValidRevSearchFrom.mismatched, revMatchesAt_iff_revMatchesAt_decide]
|
||||
· induction h with
|
||||
| startPos => simpa using IsValidRevSearchFrom.startPos
|
||||
| matched => simp_all [IsValidRevSearchFrom.matched, isLongestRevMatchAt_iff_isLongestRevMatchAt_decide]
|
||||
| mismatched => simp_all [IsValidRevSearchFrom.mismatched, revMatchesAt_iff_revMatchesAt_decide]
|
||||
|
||||
instance {p : Char → Prop} [DecidablePred p] : LawfulToForwardSearcherModel p where
|
||||
isValidSearchFrom_toList s := by
|
||||
simpa [toSearcher_eq, isValidSearchFrom_iff_isValidSearchFrom_decide] using
|
||||
LawfulToForwardSearcherModel.isValidSearchFrom_toList (pat := (decide <| p ·)) (s := s)
|
||||
|
||||
instance {p : Char → Prop} [DecidablePred p] : LawfulToBackwardSearcherModel p where
|
||||
isValidRevSearchFrom_toList s := by
|
||||
simpa [toBackwardSearcher_eq, isValidRevSearchFrom_iff_isValidRevSearchFrom_decide] using
|
||||
LawfulToBackwardSearcherModel.isValidRevSearchFrom_toList (pat := (decide <| p ·)) (s := s)
|
||||
|
||||
theorem matchesAt_iff {p : Char → Prop} [DecidablePred p] {s : Slice} {pos : s.Pos} :
|
||||
MatchesAt p pos ↔ ∃ (h : pos ≠ s.endPos), p (pos.get h) := by
|
||||
simp [matchesAt_iff_exists_isLongestMatchAt, isLongestMatchAt_iff, exists_comm]
|
||||
|
||||
theorem revMatchesAt_iff {p : Char → Prop} [DecidablePred p] {s : Slice} {pos : s.Pos} :
|
||||
RevMatchesAt p pos ↔ ∃ (h : pos ≠ s.startPos), p ((pos.prev h).get (by simp)) := by
|
||||
simp [revMatchesAt_iff_exists_isLongestRevMatchAt, isLongestRevMatchAt_iff, exists_comm]
|
||||
|
||||
theorem not_matchesAt_of_get {p : Char → Prop} [DecidablePred p] {s : Slice} {pos : s.Pos}
|
||||
{h : pos ≠ s.endPos} (hc : ¬ p (pos.get h)) : ¬ MatchesAt p pos := by
|
||||
simp [matchesAt_iff, hc]
|
||||
|
||||
theorem not_revMatchesAt_of_get {p : Char → Prop} [DecidablePred p] {s : Slice} {pos : s.Pos}
|
||||
{h : pos ≠ s.startPos} (hc : ¬ p ((pos.prev h).get (by simp))) : ¬ RevMatchesAt p pos := by
|
||||
simp [revMatchesAt_iff, hc]
|
||||
|
||||
theorem matchAt?_eq {s : Slice} {pos : s.Pos} {p : Char → Prop} [DecidablePred p] :
|
||||
matchAt? p pos =
|
||||
if h₀ : ∃ (h : pos ≠ s.endPos), p (pos.get h) then some (pos.next h₀.1) else none := by
|
||||
split <;> simp_all [isLongestMatchAt_iff, matchesAt_iff]
|
||||
|
||||
theorem revMatchAt?_eq {s : Slice} {pos : s.Pos} {p : Char → Prop} [DecidablePred p] :
|
||||
revMatchAt? p pos =
|
||||
if h₀ : ∃ (h : pos ≠ s.startPos), p ((pos.prev h).get (by simp)) then some (pos.prev h₀.1) else none := by
|
||||
split <;> simp_all [isLongestRevMatchAt_iff, revMatchesAt_iff]
|
||||
|
||||
end Decidable
|
||||
|
||||
end Pattern.Model.CharPred
|
||||
@@ -184,6 +411,9 @@ theorem dropPrefix_prop_eq_dropPrefix_decide {p : Char → Prop} [DecidablePred
|
||||
theorem skipPrefix?_prop_eq_skipPrefix?_decide {p : Char → Prop} [DecidablePred p] {s : Slice} :
|
||||
s.skipPrefix? p = s.skipPrefix? (decide <| p ·) := (rfl)
|
||||
|
||||
theorem Pos.skip?_prop_eq_skip?_decide {p : Char → Prop} [DecidablePred p] {s : Slice} {pos : s.Pos} :
|
||||
pos.skip? p = pos.skip? (decide <| p ·) := (rfl)
|
||||
|
||||
theorem Pattern.ForwardPattern.skipPrefix?_prop_eq_skipPrefix?_decide
|
||||
{p : Char → Prop} [DecidablePred p] {s : Slice} :
|
||||
skipPrefix? p s = skipPrefix? (decide <| p ·) s := (rfl)
|
||||
@@ -194,13 +424,13 @@ theorem Pos.skipWhile_prop_eq_skipWhile_decide {p : Char → Prop} [DecidablePre
|
||||
fun_induction Pos.skipWhile curr p with
|
||||
| case1 pos nextCurr h₁ h₂ ih =>
|
||||
conv => rhs; rw [Pos.skipWhile]
|
||||
simp [← Pattern.ForwardPattern.skipPrefix?_prop_eq_skipPrefix?_decide, h₁, h₂, ih]
|
||||
simp [← Pos.skip?_prop_eq_skip?_decide, h₁, h₂, ih]
|
||||
| case2 pos nextCurr h ih =>
|
||||
conv => rhs; rw [Pos.skipWhile]
|
||||
simp [← Pattern.ForwardPattern.skipPrefix?_prop_eq_skipPrefix?_decide, h, ih]
|
||||
simp [← Pos.skip?_prop_eq_skip?_decide, h, ih]
|
||||
| case3 pos h =>
|
||||
conv => rhs; rw [Pos.skipWhile]
|
||||
simp [← Pattern.ForwardPattern.skipPrefix?_prop_eq_skipPrefix?_decide]
|
||||
simp [← Pos.skip?_prop_eq_skip?_decide, h]
|
||||
|
||||
theorem skipPrefixWhile_prop_eq_skipPrefixWhile_decide {p : Char → Prop} [DecidablePred p]
|
||||
{s : Slice} :
|
||||
@@ -217,7 +447,7 @@ theorem takeWhile_prop_eq_takeWhile_decide {p : Char → Prop} [DecidablePred p]
|
||||
|
||||
theorem all_prop_eq_all_decide {p : Char → Prop} [DecidablePred p] {s : Slice} :
|
||||
s.all p = s.all (decide <| p ·) := by
|
||||
simp only [all, dropWhile_prop_eq_dropWhile_decide]
|
||||
simp only [all, skipPrefixWhile_prop_eq_skipPrefixWhile_decide]
|
||||
|
||||
theorem find?_prop_eq_find?_decide {p : Char → Prop} [DecidablePred p] {s : Slice} :
|
||||
s.find? p = s.find? (decide <| p ·) :=
|
||||
@@ -248,19 +478,22 @@ theorem Pattern.BackwardPattern.skipSuffix?_prop_eq_skipSuffix?_decide
|
||||
{p : Char → Prop} [DecidablePred p] {s : Slice} :
|
||||
skipSuffix? p s = skipSuffix? (decide <| p ·) s := (rfl)
|
||||
|
||||
theorem Pos.revSkip?_prop_eq_revSkip?_decide {p : Char → Prop} [DecidablePred p] {s : Slice} {pos : s.Pos} :
|
||||
pos.revSkip? p = pos.revSkip? (decide <| p ·) := (rfl)
|
||||
|
||||
theorem Pos.revSkipWhile_prop_eq_revSkipWhile_decide {p : Char → Prop} [DecidablePred p]
|
||||
{s : Slice} (curr : s.Pos) :
|
||||
Pos.revSkipWhile curr p = Pos.revSkipWhile curr (decide <| p ·) := by
|
||||
fun_induction Pos.revSkipWhile curr p with
|
||||
| case1 pos nextCurr h₁ h₂ ih =>
|
||||
conv => rhs; rw [Pos.revSkipWhile]
|
||||
simp [← Pattern.BackwardPattern.skipSuffix?_prop_eq_skipSuffix?_decide, h₁, h₂, ih]
|
||||
simp [← Pos.revSkip?_prop_eq_revSkip?_decide, h₁, h₂, ih]
|
||||
| case2 pos nextCurr h ih =>
|
||||
conv => rhs; rw [Pos.revSkipWhile]
|
||||
simp [← Pattern.BackwardPattern.skipSuffix?_prop_eq_skipSuffix?_decide, h, ih]
|
||||
simp [← Pos.revSkip?_prop_eq_revSkip?_decide, h, ih]
|
||||
| case3 pos h =>
|
||||
conv => rhs; rw [Pos.revSkipWhile]
|
||||
simp [← Pattern.BackwardPattern.skipSuffix?_prop_eq_skipSuffix?_decide]
|
||||
simp [← Pos.revSkip?_prop_eq_revSkip?_decide, h]
|
||||
|
||||
theorem skipSuffixWhile_prop_eq_skipSuffixWhile_decide {p : Char → Prop} [DecidablePred p]
|
||||
{s : Slice} :
|
||||
@@ -277,4 +510,8 @@ theorem takeEndWhile_prop_eq_takeEndWhile_decide {p : Char → Prop} [DecidableP
|
||||
s.takeEndWhile p = s.takeEndWhile (decide <| p ·) := by
|
||||
simp only [takeEndWhile]; exact congrArg _ skipSuffixWhile_prop_eq_skipSuffixWhile_decide
|
||||
|
||||
theorem revAll_prop_eq_revAll_decide {p : Char → Prop} [DecidablePred p] {s : Slice} :
|
||||
s.revAll p = s.revAll (decide <| p ·) := by
|
||||
simp only [revAll, skipSuffixWhile_prop_eq_skipSuffixWhile_decide]
|
||||
|
||||
end String.Slice
|
||||
|
||||
@@ -28,14 +28,15 @@ set_option doc.verso true
|
||||
# Verification of {name}`String.Slice.splitToSubslice`
|
||||
|
||||
This PR verifies the {name}`String.Slice.splitToSubslice` function by relating it to a model
|
||||
implementation based on the {name}`String.Slice.Pattern.Model.ForwardPatternModel` class.
|
||||
implementation based on the {name}`String.Slice.Pattern.Model.PatternModel` class.
|
||||
|
||||
This gives a low-level correctness proof from which higher-level API lemmas can be derived.
|
||||
-/
|
||||
|
||||
namespace String.Slice.Pattern.Model
|
||||
|
||||
public protected noncomputable def split {ρ : Type} (pat : ρ) [ForwardPatternModel pat] {s : Slice}
|
||||
@[cbv_opaque]
|
||||
public protected noncomputable def split {ρ : Type} (pat : ρ) [PatternModel pat] [StrictPatternModel pat] {s : Slice}
|
||||
(firstRejected curr : s.Pos) (hle : firstRejected ≤ curr) : List s.Subslice :=
|
||||
if h : curr = s.endPos then
|
||||
[s.subslice _ _ hle]
|
||||
@@ -48,12 +49,12 @@ public protected noncomputable def split {ρ : Type} (pat : ρ) [ForwardPatternM
|
||||
termination_by curr
|
||||
|
||||
@[simp]
|
||||
public theorem split_endPos {ρ : Type} {pat : ρ} [ForwardPatternModel pat] {s : Slice}
|
||||
public theorem split_endPos {ρ : Type} {pat : ρ} [PatternModel pat] [StrictPatternModel pat] {s : Slice}
|
||||
{firstRejected : s.Pos} :
|
||||
Model.split (s := s) pat firstRejected s.endPos (by simp) = [s.subslice firstRejected s.endPos (by simp)] := by
|
||||
simp [Model.split]
|
||||
|
||||
public theorem split_eq_of_isLongestMatchAt {ρ : Type} {pat : ρ} [ForwardPatternModel pat]
|
||||
public theorem split_eq_of_isLongestMatchAt {ρ : Type} {pat : ρ} [PatternModel pat] [StrictPatternModel pat]
|
||||
{s : Slice} {firstRejected start stop : s.Pos} {hle} (h : IsLongestMatchAt pat start stop) :
|
||||
Model.split pat firstRejected start hle =
|
||||
s.subslice _ _ hle :: Model.split pat stop stop (by exact Std.le_refl _) := by
|
||||
@@ -62,7 +63,7 @@ public theorem split_eq_of_isLongestMatchAt {ρ : Type} {pat : ρ} [ForwardPatte
|
||||
· congr <;> exact (matchAt?_eq_some_iff.1 ‹_›).eq h
|
||||
· simp [matchAt?_eq_some_iff.2 ‹_›] at *
|
||||
|
||||
public theorem split_eq_of_not_matchesAt {ρ : Type} {pat : ρ} [ForwardPatternModel pat]
|
||||
public theorem split_eq_of_not_matchesAt {ρ : Type} {pat : ρ} [PatternModel pat] [StrictPatternModel pat]
|
||||
{s : Slice} {firstRejected start} (stop : s.Pos) (h₀ : start ≤ stop) {hle}
|
||||
(h : ∀ p, start ≤ p → p < stop → ¬ MatchesAt pat p) :
|
||||
Model.split pat firstRejected start hle =
|
||||
@@ -79,7 +80,7 @@ public theorem split_eq_of_not_matchesAt {ρ : Type} {pat : ρ} [ForwardPatternM
|
||||
· obtain rfl : start = stop := Std.le_antisymm h₀ (Std.not_lt.1 h')
|
||||
simp
|
||||
|
||||
public theorem split_eq_next_of_not_matchesAt {ρ : Type} {pat : ρ} [ForwardPatternModel pat]
|
||||
public theorem split_eq_next_of_not_matchesAt {ρ : Type} {pat : ρ} [PatternModel pat] [StrictPatternModel pat]
|
||||
{s : Slice} {firstRejected start} {hle} (hs : start ≠ s.endPos) (h : ¬ MatchesAt pat start) :
|
||||
Model.split pat firstRejected start hle =
|
||||
Model.split pat firstRejected (start.next hs) (by exact Std.le_trans hle (by simp)) := by
|
||||
@@ -102,7 +103,7 @@ def splitFromSteps {s : Slice} (currPos : s.Pos) (l : List (SearchStep s)) : Lis
|
||||
| .matched p q :: l => s.subslice! currPos p :: splitFromSteps q l
|
||||
|
||||
theorem IsValidSearchFrom.splitFromSteps_eq_extend_split {ρ : Type} (pat : ρ)
|
||||
[ForwardPatternModel pat] (l : List (SearchStep s)) (pos pos' : s.Pos) (h₀ : pos ≤ pos')
|
||||
[PatternModel pat] [StrictPatternModel pat] (l : List (SearchStep s)) (pos pos' : s.Pos) (h₀ : pos ≤ pos')
|
||||
(h' : ∀ p, pos ≤ p → p < pos' → ¬ MatchesAt pat p)
|
||||
(h : IsValidSearchFrom pat pos' l) :
|
||||
splitFromSteps pos l = Model.split pat pos pos' h₀ := by
|
||||
@@ -153,7 +154,8 @@ end Model
|
||||
|
||||
open Model
|
||||
|
||||
public theorem toList_splitToSubslice_eq_modelSplit {ρ : Type} (pat : ρ) [ForwardPatternModel pat]
|
||||
@[cbv_eval]
|
||||
public theorem toList_splitToSubslice_eq_modelSplit {ρ : Type} (pat : ρ) [PatternModel pat] [StrictPatternModel pat]
|
||||
{σ : Slice → Type} [ToForwardSearcher pat σ] [∀ s, Std.Iterator (σ s) Id (SearchStep s)]
|
||||
[∀ s, Std.Iterators.Finite (σ s) Id] [LawfulToForwardSearcherModel pat] (s : Slice) :
|
||||
(s.splitToSubslice pat).toList = Model.split pat s.startPos s.startPos (by exact Std.le_refl _) := by
|
||||
@@ -166,7 +168,7 @@ end Pattern
|
||||
open Pattern
|
||||
|
||||
public theorem toList_splitToSubslice_of_isEmpty {ρ : Type} (pat : ρ)
|
||||
[Model.ForwardPatternModel pat] {σ : Slice → Type}
|
||||
[Model.PatternModel pat] [Model.StrictPatternModel pat] {σ : Slice → Type}
|
||||
[ToForwardSearcher pat σ] [∀ s, Std.Iterator (σ s) Id (SearchStep s)]
|
||||
[∀ s, Std.Iterators.Finite (σ s) Id] [Model.LawfulToForwardSearcherModel pat] {s : Slice}
|
||||
(h : s.isEmpty = true) :
|
||||
@@ -180,7 +182,7 @@ public theorem toList_split_eq_splitToSubslice {ρ : Type} (pat : ρ) {σ : Slic
|
||||
simp [split, Std.Iter.toList_map]
|
||||
|
||||
public theorem toList_split_of_isEmpty {ρ : Type} (pat : ρ)
|
||||
[Model.ForwardPatternModel pat] {σ : Slice → Type}
|
||||
[Model.PatternModel pat] [Model.StrictPatternModel pat] {σ : Slice → Type}
|
||||
[ToForwardSearcher pat σ] [∀ s, Std.Iterator (σ s) Id (SearchStep s)]
|
||||
[∀ s, Std.Iterators.Finite (σ s) Id] [Model.LawfulToForwardSearcherModel pat] {s : Slice}
|
||||
(h : s.isEmpty = true) :
|
||||
@@ -198,7 +200,7 @@ public theorem split_eq_split_toSlice {ρ : Type} {pat : ρ} {σ : Slice → Typ
|
||||
|
||||
@[simp]
|
||||
public theorem toList_split_empty {ρ : Type} (pat : ρ)
|
||||
[Model.ForwardPatternModel pat] {σ : Slice → Type}
|
||||
[Model.PatternModel pat] [Model.StrictPatternModel pat] {σ : Slice → Type}
|
||||
[ToForwardSearcher pat σ] [∀ s, Std.Iterator (σ s) Id (SearchStep s)]
|
||||
[∀ s, Std.Iterators.Finite (σ s) Id] [Model.LawfulToForwardSearcherModel pat] :
|
||||
("".split pat).toList.map Slice.copy = [""] := by
|
||||
|
||||
@@ -23,6 +23,7 @@ import Init.Data.String.OrderInstances
|
||||
import Init.Data.String.Lemmas.Order
|
||||
import Init.Data.String.Lemmas.Intercalate
|
||||
import Init.Data.List.SplitOn.Lemmas
|
||||
import Init.Data.String.Lemmas.Slice
|
||||
|
||||
public section
|
||||
|
||||
@@ -30,6 +31,7 @@ namespace String.Slice
|
||||
|
||||
open Pattern.Model Pattern.Model.Char
|
||||
|
||||
@[cbv_eval]
|
||||
theorem Pattern.Model.split_char_eq_split_beq {c : Char} {s : Slice}
|
||||
(f curr : s.Pos) (hle : f ≤ curr) :
|
||||
Model.split c f curr hle = Model.split (· == c) f curr hle := by
|
||||
@@ -69,6 +71,11 @@ theorem Slice.toList_split_intercalate {c : Char} {l : List Slice} (hl : ∀ s
|
||||
· simp_all
|
||||
· rw [List.splitOn_intercalate] <;> simp_all
|
||||
|
||||
theorem Slice.toList_split_intercalate_beq {c : Char} {l : List Slice} (hl : ∀ s ∈ l, c ∉ s.copy.toList) :
|
||||
((Slice.intercalate (String.singleton c) l).split c).toList ==
|
||||
if l = [] then ["".toSlice] else l := by
|
||||
split <;> simp_all [toList_split_intercalate hl, beq_list_iff]
|
||||
|
||||
theorem toList_split_intercalate {c : Char} {l : List String} (hl : ∀ s ∈ l, c ∉ s.toList) :
|
||||
((String.intercalate (String.singleton c) l).split c).toList.map (·.copy) =
|
||||
if l = [] then [""] else l := by
|
||||
@@ -77,4 +84,9 @@ theorem toList_split_intercalate {c : Char} {l : List String} (hl : ∀ s ∈ l,
|
||||
· simp_all
|
||||
· rw [List.splitOn_intercalate] <;> simp_all
|
||||
|
||||
theorem toList_split_intercalate_beq {c : Char} {l : List String} (hl : ∀ s ∈ l, c ∉ s.toList) :
|
||||
((String.intercalate (String.singleton c) l).split c).toList ==
|
||||
if l = [] then ["".toSlice] else l.map String.toSlice := by
|
||||
split <;> simp_all [toList_split_intercalate hl, Slice.beq_list_iff]
|
||||
|
||||
end String
|
||||
|
||||
@@ -58,12 +58,33 @@ theorem toList_split_bool {s : Slice} {p : Char → Bool} :
|
||||
(s.split p).toList.map Slice.copy = (s.copy.toList.splitOnP p).map String.ofList := by
|
||||
simp [toList_split_eq_splitToSubslice, ← toList_splitToSubslice_bool]
|
||||
|
||||
/-
|
||||
Used internally by the `cbv` tactic.
|
||||
-/
|
||||
@[cbv_eval]
|
||||
theorem Pattern.Model.split_bool_eq_internal {p : Char → Bool} {s : Slice} (f curr : s.Pos) (hle : f ≤ curr) :
|
||||
Model.split p f curr hle =
|
||||
if h : curr = s.endPos then [s.subslice _ _ hle]
|
||||
else if p (curr.get h) then
|
||||
s.subslice _ _ hle :: Model.split p (curr.next h) (curr.next h) (by simp [Std.le_refl])
|
||||
else Model.split p f (curr.next h) (by simp [Std.le_trans hle _]) := by
|
||||
by_cases h : curr = s.endPos
|
||||
· simp only [h, split_endPos, subslice_endPos, ↓reduceDIte]
|
||||
· simp only [h, ↓reduceDIte]
|
||||
by_cases hp : p (curr.get h)
|
||||
· simp only [hp, ↓reduceIte]
|
||||
exact split_eq_of_isLongestMatchAt (isLongestMatchAt_of_get hp)
|
||||
· rw [Bool.not_eq_true] at hp
|
||||
simp only [hp, Bool.false_eq_true, ↓reduceIte]
|
||||
exact split_eq_next_of_not_matchesAt h (not_matchesAt_of_get hp)
|
||||
|
||||
end
|
||||
|
||||
section
|
||||
|
||||
open Pattern.Model Pattern.Model.CharPred.Decidable
|
||||
|
||||
@[cbv_eval]
|
||||
theorem Pattern.Model.split_eq_split_decide {p : Char → Prop} [DecidablePred p] {s : Slice}
|
||||
(f curr : s.Pos) (hle : f ≤ curr) :
|
||||
Model.split p f curr hle = Model.split (decide <| p ·) f curr hle := by
|
||||
|
||||
@@ -10,6 +10,9 @@ public import Init.Data.String.Pattern.String
|
||||
public import Init.Data.String.Lemmas.Pattern.Basic
|
||||
import Init.Data.String.Lemmas.IsEmpty
|
||||
import Init.Data.String.Lemmas.Basic
|
||||
import Init.Data.String.Lemmas.Intercalate
|
||||
import Init.Data.String.OrderInstances
|
||||
import Init.Data.String.Lemmas.Splits
|
||||
import Init.Data.ByteArray.Lemmas
|
||||
import Init.Omega
|
||||
|
||||
@@ -19,52 +22,135 @@ namespace String.Slice.Pattern.Model
|
||||
|
||||
namespace ForwardSliceSearcher
|
||||
|
||||
instance {pat : Slice} : ForwardPatternModel pat where
|
||||
/-
|
||||
See the docstring of `ForwardPatternModel` for an explanation about why we disallow matching the
|
||||
empty string.
|
||||
instance {pat : Slice} : PatternModel pat where
|
||||
Matches s := s = pat.copy
|
||||
|
||||
Requiring `s ≠ ""` is a trick that allows us to give a `ForwardPatternModel` instance
|
||||
unconditionally, without forcing `pat.copy` to be non-empty (which would make it very awkward
|
||||
to state theorems about the instance). It does not change anything about the fact that all lemmas
|
||||
about this instance require `pat.isEmpty = false`.
|
||||
-/
|
||||
Matches s := s ≠ "" ∧ s = pat.copy
|
||||
not_matches_empty := by simp
|
||||
theorem strictPatternModel {pat : Slice} (hpat : pat.isEmpty = false) : StrictPatternModel pat where
|
||||
not_matches_empty := by simpa [PatternModel.Matches]
|
||||
|
||||
instance {pat : Slice} : NoPrefixForwardPatternModel pat :=
|
||||
.of_length_eq (by simp +contextual [ForwardPatternModel.Matches])
|
||||
instance {pat : Slice} : NoPrefixPatternModel pat :=
|
||||
.of_length_eq (by simp +contextual [PatternModel.Matches])
|
||||
|
||||
theorem isMatch_iff {pat s : Slice} {pos : s.Pos} (h : pat.isEmpty = false) :
|
||||
instance {pat : Slice} : NoSuffixPatternModel pat :=
|
||||
.of_length_eq (by simp +contextual [PatternModel.Matches])
|
||||
|
||||
theorem isMatch_iff {pat s : Slice} {pos : s.Pos} :
|
||||
IsMatch pat pos ↔ (s.sliceTo pos).copy = pat.copy := by
|
||||
simp only [Model.isMatch_iff, ForwardPatternModel.Matches, ne_eq, copy_eq_empty_iff,
|
||||
Bool.not_eq_true, and_iff_right_iff_imp]
|
||||
intro h'
|
||||
rw [← isEmpty_copy (s := s.sliceTo pos), h', isEmpty_copy, h]
|
||||
simp [Model.isMatch_iff, PatternModel.Matches]
|
||||
|
||||
theorem isLongestMatch_iff {pat s : Slice} {pos : s.Pos} (h : pat.isEmpty = false) :
|
||||
theorem isRevMatch_iff {pat s : Slice} {pos : s.Pos} :
|
||||
IsRevMatch pat pos ↔ (s.sliceFrom pos).copy = pat.copy := by
|
||||
simp [Model.isRevMatch_iff, PatternModel.Matches]
|
||||
|
||||
theorem isLongestMatch_iff {pat s : Slice} {pos : s.Pos} :
|
||||
IsLongestMatch pat pos ↔ (s.sliceTo pos).copy = pat.copy := by
|
||||
rw [isLongestMatch_iff_isMatch, isMatch_iff h]
|
||||
rw [isLongestMatch_iff_isMatch, isMatch_iff]
|
||||
|
||||
theorem isLongestMatchAt_iff {pat s : Slice} {pos₁ pos₂ : s.Pos} (h : pat.isEmpty = false) :
|
||||
theorem isLongestRevMatch_iff {pat s : Slice} {pos : s.Pos} :
|
||||
IsLongestRevMatch pat pos ↔ (s.sliceFrom pos).copy = pat.copy := by
|
||||
rw [isLongestRevMatch_iff_isRevMatch, isRevMatch_iff]
|
||||
|
||||
theorem isLongestMatchAt_iff {pat s : Slice} {pos₁ pos₂ : s.Pos} :
|
||||
IsLongestMatchAt pat pos₁ pos₂ ↔ ∃ h, (s.slice pos₁ pos₂ h).copy = pat.copy := by
|
||||
simp [Model.isLongestMatchAt_iff, isLongestMatch_iff h]
|
||||
simp [Model.isLongestMatchAt_iff, isLongestMatch_iff]
|
||||
|
||||
theorem isLongestMatchAt_iff_splits {pat s : Slice} {pos₁ pos₂ : s.Pos} (h : pat.isEmpty = false) :
|
||||
theorem isLongestMatchAtChain_iff {pat s : Slice} {pos₁ pos₂ : s.Pos} :
|
||||
IsLongestMatchAtChain pat pos₁ pos₂ ↔
|
||||
∃ h n, (s.slice pos₁ pos₂ h).copy = String.join (List.replicate n pat.copy) := by
|
||||
refine ⟨fun h => ⟨h.le, ?_⟩, fun ⟨h, n, h'⟩ => ?_⟩
|
||||
· induction h with
|
||||
| nil => simpa using ⟨0, by simp⟩
|
||||
| cons p₁ p₂ p₃ h₁ h₂ ih =>
|
||||
rw [isLongestMatchAt_iff] at h₁
|
||||
obtain ⟨n, ih⟩ := ih
|
||||
obtain ⟨h₀, h₁⟩ := h₁
|
||||
have : (s.slice p₁ p₃ (Std.le_trans h₀ h₂.le)).copy = (s.slice p₁ p₂ h₀).copy ++ (s.slice p₂ p₃ h₂.le).copy := by
|
||||
simp [(Slice.Pos.slice p₂ _ _ h₀ h₂.le).splits.eq_append]
|
||||
refine ⟨n + 1, ?_⟩
|
||||
rw [this, h₁, ih]
|
||||
simp [← String.join_cons, ← List.replicate_succ]
|
||||
· induction n generalizing pos₁ pos₂ with
|
||||
| zero => simp_all
|
||||
| succ n ih =>
|
||||
rw [List.replicate_succ, String.join_cons] at h'
|
||||
refine .cons _ (Pos.ofSlice (Pos.ofEqAppend h')) _ ?_ (ih ?_ Pos.ofSlice_le ?_)
|
||||
· simpa [isLongestMatchAt_iff] using (Pos.splits_ofEqAppend h').copy_sliceTo_eq
|
||||
· simpa [sliceFrom_slice ▸ (Pos.splits_ofEqAppend h').copy_sliceFrom_eq] using ⟨n, rfl⟩
|
||||
· simpa using (Pos.splits_ofEqAppend h').copy_sliceFrom_eq
|
||||
|
||||
theorem isLongestMatchAtChain_startPos_endPos_iff {pat s : Slice} :
|
||||
IsLongestMatchAtChain pat s.startPos s.endPos ↔
|
||||
∃ n, s.copy = String.join (List.replicate n pat.copy) := by
|
||||
simp [isLongestMatchAtChain_iff]
|
||||
|
||||
theorem isLongestRevMatchAt_iff {pat s : Slice} {pos₁ pos₂ : s.Pos} :
|
||||
IsLongestRevMatchAt pat pos₁ pos₂ ↔ ∃ h, (s.slice pos₁ pos₂ h).copy = pat.copy := by
|
||||
simp [Model.isLongestRevMatchAt_iff, isLongestRevMatch_iff]
|
||||
|
||||
theorem isLongestRevMatchAtChain_iff {pat s : Slice} {pos₁ pos₂ : s.Pos} :
|
||||
IsLongestRevMatchAtChain pat pos₁ pos₂ ↔
|
||||
∃ h n, (s.slice pos₁ pos₂ h).copy = String.join (List.replicate n pat.copy) := by
|
||||
refine ⟨fun h => ⟨h.le, ?_⟩, fun ⟨h, n, h'⟩ => ?_⟩
|
||||
· induction h with
|
||||
| nil => simpa using ⟨0, by simp⟩
|
||||
| cons p₂ p₃ h₁ h₂ ih =>
|
||||
rw [isLongestRevMatchAt_iff] at h₂
|
||||
obtain ⟨n, ih⟩ := ih
|
||||
obtain ⟨h₀, h₂⟩ := h₂
|
||||
have : (s.slice pos₁ p₃ (Std.le_trans h₁.le h₀)).copy = (s.slice pos₁ p₂ h₁.le).copy ++ (s.slice p₂ p₃ h₀).copy := by
|
||||
simp [(Slice.Pos.slice p₂ _ _ (IsLongestRevMatchAtChain.le ‹_›) h₀).splits.eq_append]
|
||||
refine ⟨n + 1, ?_⟩
|
||||
rw [this, h₂, ih]
|
||||
simp [← List.replicate_append_replicate]
|
||||
· induction n generalizing pos₁ pos₂ with
|
||||
| zero => simp_all
|
||||
| succ n ih =>
|
||||
have h'' : (s.slice pos₁ pos₂ h).copy = String.join (List.replicate n pat.copy) ++ pat.copy := by
|
||||
rw [h', List.replicate_succ', String.join_append]; simp
|
||||
refine .cons _ (Pos.ofSlice (Pos.ofEqAppend h'')) _ (ih ?_ Pos.le_ofSlice ?_) ?_
|
||||
· simpa [sliceTo_slice ▸ (Pos.splits_ofEqAppend h'').copy_sliceTo_eq] using ⟨n, rfl⟩
|
||||
· simpa using (Pos.splits_ofEqAppend h'').copy_sliceTo_eq
|
||||
· simpa [isLongestRevMatchAt_iff] using (Pos.splits_ofEqAppend h'').copy_sliceFrom_eq
|
||||
|
||||
theorem isLongestRevMatchAtChain_startPos_endPos_iff {pat s : Slice} :
|
||||
IsLongestRevMatchAtChain pat s.startPos s.endPos ↔
|
||||
∃ n, s.copy = String.join (List.replicate n pat.copy) := by
|
||||
simp [isLongestRevMatchAtChain_iff]
|
||||
|
||||
theorem isLongestMatchAt_iff_splits {pat s : Slice} {pos₁ pos₂ : s.Pos} :
|
||||
IsLongestMatchAt pat pos₁ pos₂ ↔ ∃ t₁ t₂, pos₁.Splits t₁ (pat.copy ++ t₂) ∧
|
||||
pos₂.Splits (t₁ ++ pat.copy) t₂ := by
|
||||
simp only [isLongestMatchAt_iff h, copy_slice_eq_iff_splits]
|
||||
simp only [isLongestMatchAt_iff, copy_slice_eq_iff_splits]
|
||||
|
||||
theorem isLongestMatch_iff_splits {pat s : Slice} {pos : s.Pos} (h : pat.isEmpty = false) :
|
||||
theorem isLongestRevMatchAt_iff_splits {pat s : Slice} {pos₁ pos₂ : s.Pos} :
|
||||
IsLongestRevMatchAt pat pos₁ pos₂ ↔ ∃ t₁ t₂, pos₁.Splits t₁ (pat.copy ++ t₂) ∧
|
||||
pos₂.Splits (t₁ ++ pat.copy) t₂ := by
|
||||
simp only [isLongestRevMatchAt_iff, copy_slice_eq_iff_splits]
|
||||
|
||||
theorem isLongestMatch_iff_splits {pat s : Slice} {pos : s.Pos} :
|
||||
IsLongestMatch pat pos ↔ ∃ t, pos.Splits pat.copy t := by
|
||||
simp only [← isLongestMatchAt_startPos_iff, isLongestMatchAt_iff_splits h, splits_startPos_iff,
|
||||
and_assoc, exists_and_left, exists_eq_left, empty_append]
|
||||
exact ⟨fun ⟨h, _, h'⟩ => ⟨h, h'⟩, fun ⟨h, h'⟩ => ⟨h, h'.eq_append.symm, h'⟩⟩
|
||||
rw [isLongestMatch_iff, copy_sliceTo_eq_iff_exists_splits]
|
||||
|
||||
theorem isLongestRevMatch_iff_splits {pat s : Slice} {pos : s.Pos} :
|
||||
IsLongestRevMatch pat pos ↔ ∃ t, pos.Splits t pat.copy := by
|
||||
rw [isLongestRevMatch_iff, copy_sliceFrom_eq_iff_exists_splits]
|
||||
|
||||
theorem isLongestMatchAt_iff_extract {pat s : Slice} {pos₁ pos₂ : s.Pos} (h : pat.isEmpty = false) :
|
||||
IsLongestMatchAt pat pos₁ pos₂ ↔
|
||||
s.copy.toByteArray.extract pos₁.offset.byteIdx pos₂.offset.byteIdx = pat.copy.toByteArray := by
|
||||
rw [isLongestMatchAt_iff h]
|
||||
rw [isLongestMatchAt_iff]
|
||||
refine ⟨fun ⟨h, h'⟩ => ?_, fun h' => ?_⟩
|
||||
· simp [← h', toByteArray_copy_slice]
|
||||
· rw [← Slice.toByteArray_copy_ne_empty_iff, ← h', ne_eq, ByteArray.extract_eq_empty_iff] at h
|
||||
exact ⟨by simp [Pos.le_iff, Pos.Raw.le_iff]; omega,
|
||||
by simp [← h', ← toByteArray_inj, toByteArray_copy_slice]⟩
|
||||
|
||||
theorem isLongestRevMatchAt_iff_extract {pat s : Slice} {pos₁ pos₂ : s.Pos}
|
||||
(h : pat.isEmpty = false) :
|
||||
IsLongestRevMatchAt pat pos₁ pos₂ ↔
|
||||
s.copy.toByteArray.extract pos₁.offset.byteIdx pos₂.offset.byteIdx =
|
||||
pat.copy.toByteArray := by
|
||||
rw [isLongestRevMatchAt_iff]
|
||||
refine ⟨fun ⟨h, h'⟩ => ?_, fun h' => ?_⟩
|
||||
· simp [← h', toByteArray_copy_slice]
|
||||
· rw [← Slice.toByteArray_copy_ne_empty_iff, ← h', ne_eq, ByteArray.extract_eq_empty_iff] at h
|
||||
@@ -81,15 +167,32 @@ theorem offset_of_isLongestMatchAt {pat s : Slice} {pos₁ pos₂ : s.Pos} (h :
|
||||
suffices pos₂.offset.byteIdx ≤ s.utf8ByteSize by omega
|
||||
simpa [Pos.le_iff, Pos.Raw.le_iff] using pos₂.le_endPos
|
||||
|
||||
theorem matchesAt_iff_splits {pat s : Slice} {pos : s.Pos} (h : pat.isEmpty = false) :
|
||||
theorem offset_of_isLongestRevMatchAt {pat s : Slice} {pos₁ pos₂ : s.Pos}
|
||||
(h : pat.isEmpty = false) (h' : IsLongestRevMatchAt pat pos₁ pos₂) :
|
||||
pos₂.offset = pos₁.offset.increaseBy pat.utf8ByteSize := by
|
||||
simp only [Pos.Raw.ext_iff, Pos.Raw.byteIdx_increaseBy]
|
||||
rw [isLongestRevMatchAt_iff_extract h] at h'
|
||||
rw [← Slice.toByteArray_copy_ne_empty_iff, ← h', ne_eq, ByteArray.extract_eq_empty_iff] at h
|
||||
replace h' := congrArg ByteArray.size h'
|
||||
simp only [ByteArray.size_extract, size_toByteArray, utf8ByteSize_copy] at h'
|
||||
suffices pos₂.offset.byteIdx ≤ s.utf8ByteSize by omega
|
||||
simpa [Pos.le_iff, Pos.Raw.le_iff] using pos₂.le_endPos
|
||||
|
||||
theorem matchesAt_iff_splits {pat s : Slice} {pos : s.Pos} :
|
||||
MatchesAt pat pos ↔ ∃ t₁ t₂, pos.Splits t₁ (pat.copy ++ t₂) := by
|
||||
simp only [matchesAt_iff_exists_isLongestMatchAt, isLongestMatchAt_iff_splits h]
|
||||
simp only [matchesAt_iff_exists_isLongestMatchAt, isLongestMatchAt_iff_splits]
|
||||
exact ⟨fun ⟨e, t₁, t₂, ht₁, ht₂⟩ => ⟨t₁, t₂, ht₁⟩,
|
||||
fun ⟨t₁, t₂, ht⟩ => ⟨ht.rotateRight, t₁, t₂, ht, ht.splits_rotateRight⟩⟩
|
||||
|
||||
theorem exists_matchesAt_iff_eq_append {pat s : Slice} (h : pat.isEmpty = false) :
|
||||
theorem revMatchesAt_iff_splits {pat s : Slice} {pos : s.Pos} :
|
||||
RevMatchesAt pat pos ↔ ∃ t₁ t₂, pos.Splits (t₁ ++ pat.copy) t₂ := by
|
||||
simp only [revMatchesAt_iff_exists_isLongestRevMatchAt, isLongestRevMatchAt_iff_splits]
|
||||
exact ⟨fun ⟨e, t₁, t₂, ht₁, ht₂⟩ => ⟨t₁, t₂, ht₂⟩,
|
||||
fun ⟨t₁, t₂, ht⟩ => ⟨ht.rotateLeft, t₁, t₂, ht.splits_rotateLeft, ht⟩⟩
|
||||
|
||||
theorem exists_matchesAt_iff_eq_append {pat s : Slice} :
|
||||
(∃ (pos : s.Pos), MatchesAt pat pos) ↔ ∃ t₁ t₂, s.copy = t₁ ++ pat.copy ++ t₂ := by
|
||||
simp only [matchesAt_iff_splits h]
|
||||
simp only [matchesAt_iff_splits]
|
||||
constructor
|
||||
· rintro ⟨pos, t₁, t₂, hsplit⟩
|
||||
exact ⟨t₁, t₂, by rw [hsplit.eq_append, append_assoc]⟩
|
||||
@@ -99,6 +202,18 @@ theorem exists_matchesAt_iff_eq_append {pat s : Slice} (h : pat.isEmpty = false)
|
||||
⟨t₁, pat.copy ++ t₂, by rw [← append_assoc]; exact heq, rfl⟩
|
||||
exact ⟨s.pos _ hvalid, t₁, t₂, ⟨by rw [← append_assoc]; exact heq, by simp⟩⟩
|
||||
|
||||
theorem exists_revMatchesAt_iff_eq_append {pat s : Slice} :
|
||||
(∃ (pos : s.Pos), RevMatchesAt pat pos) ↔ ∃ t₁ t₂, s.copy = t₁ ++ pat.copy ++ t₂ := by
|
||||
simp only [revMatchesAt_iff_splits]
|
||||
constructor
|
||||
· rintro ⟨pos, t₁, t₂, hsplit⟩
|
||||
exact ⟨t₁, t₂, by rw [hsplit.eq_append, append_assoc]⟩
|
||||
· rintro ⟨t₁, t₂, heq⟩
|
||||
have hvalid : (t₁ ++ pat.copy).rawEndPos.IsValidForSlice s :=
|
||||
Pos.Raw.isValidForSlice_iff_exists_append.mpr
|
||||
⟨t₁ ++ pat.copy, t₂, heq, rfl⟩
|
||||
exact ⟨s.pos _ hvalid, t₁, t₂, ⟨heq, by simp⟩⟩
|
||||
|
||||
theorem matchesAt_iff_isLongestMatchAt {pat s : Slice} {pos : s.Pos} (h : pat.isEmpty = false) :
|
||||
MatchesAt pat pos ↔ ∃ (h : (pos.offset.increaseBy pat.utf8ByteSize).IsValidForSlice s),
|
||||
IsLongestMatchAt pat pos (s.pos _ h) := by
|
||||
@@ -108,6 +223,25 @@ theorem matchesAt_iff_isLongestMatchAt {pat s : Slice} {pos : s.Pos} (h : pat.is
|
||||
obtain rfl : p = s.pos _ this := by simpa [Pos.ext_iff] using offset_of_isLongestMatchAt h h'
|
||||
exact h'
|
||||
|
||||
theorem revMatchesAt_iff_isLongestRevMatchAt {pat s : Slice} {pos : s.Pos}
|
||||
(h : pat.isEmpty = false) :
|
||||
RevMatchesAt pat pos ↔
|
||||
∃ (h : (pos.offset.decreaseBy pat.utf8ByteSize).IsValidForSlice s),
|
||||
IsLongestRevMatchAt pat (s.pos _ h) pos := by
|
||||
refine ⟨fun ⟨⟨p, h'⟩⟩ => ?_, fun ⟨_, h⟩ => ⟨⟨_, h⟩⟩⟩
|
||||
have hoff := offset_of_isLongestRevMatchAt h h'
|
||||
have hvalid : (pos.offset.decreaseBy pat.utf8ByteSize).IsValidForSlice s := by
|
||||
rw [show pos.offset.decreaseBy pat.utf8ByteSize = p.offset from by
|
||||
simp [Pos.Raw.ext_iff, Pos.Raw.byteIdx_decreaseBy, Pos.Raw.byteIdx_increaseBy] at hoff ⊢
|
||||
omega]
|
||||
exact p.isValidForSlice
|
||||
refine ⟨hvalid, ?_⟩
|
||||
obtain rfl : p = s.pos _ hvalid := by
|
||||
simp only [Pos.ext_iff, offset_pos]
|
||||
simp [Pos.Raw.ext_iff, Pos.Raw.byteIdx_decreaseBy, Pos.Raw.byteIdx_increaseBy] at hoff ⊢
|
||||
omega
|
||||
exact h'
|
||||
|
||||
theorem matchesAt_iff_getElem {pat s : Slice} {pos : s.Pos} (h : pat.isEmpty = false) :
|
||||
MatchesAt pat pos ↔
|
||||
∃ (h : pos.offset.byteIdx + pat.copy.toByteArray.size ≤ s.copy.toByteArray.size),
|
||||
@@ -146,81 +280,194 @@ end ForwardSliceSearcher
|
||||
|
||||
namespace ForwardStringSearcher
|
||||
|
||||
instance {pat : String} : ForwardPatternModel pat where
|
||||
Matches s := s ≠ "" ∧ s = pat
|
||||
not_matches_empty := by simp
|
||||
instance {pat : String} : PatternModel pat where
|
||||
Matches s := s = pat
|
||||
|
||||
instance {pat : String} : NoPrefixForwardPatternModel pat :=
|
||||
.of_length_eq (by simp +contextual [ForwardPatternModel.Matches])
|
||||
theorem strictPatternModel {pat : String} (h : pat ≠ "") : StrictPatternModel pat where
|
||||
not_matches_empty := by simpa [PatternModel.Matches]
|
||||
|
||||
instance {pat : String} : NoPrefixPatternModel pat :=
|
||||
.of_length_eq (by simp +contextual [PatternModel.Matches])
|
||||
|
||||
instance {pat : String} : NoSuffixPatternModel pat :=
|
||||
.of_length_eq (by simp +contextual [PatternModel.Matches])
|
||||
|
||||
theorem isMatch_iff_slice {pat : String} {s : Slice} {pos : s.Pos} :
|
||||
IsMatch (ρ := String) pat pos ↔ IsMatch (ρ := Slice) pat.toSlice pos := by
|
||||
simp only [Model.isMatch_iff, ForwardPatternModel.Matches, copy_toSlice]
|
||||
simp only [Model.isMatch_iff, PatternModel.Matches, copy_toSlice]
|
||||
|
||||
theorem isRevMatch_iff_slice {pat : String} {s : Slice} {pos : s.Pos} :
|
||||
IsRevMatch (ρ := String) pat pos ↔ IsRevMatch (ρ := Slice) pat.toSlice pos := by
|
||||
simp only [Model.isRevMatch_iff, PatternModel.Matches, copy_toSlice]
|
||||
|
||||
theorem isLongestMatch_iff_isLongestMatch_toSlice {pat : String} {s : Slice} {pos : s.Pos} :
|
||||
IsLongestMatch (ρ := String) pat pos ↔ IsLongestMatch (ρ := Slice) pat.toSlice pos where
|
||||
mp h := ⟨isMatch_iff_slice.1 h.isMatch, fun p hp hm => h.not_isMatch p hp (isMatch_iff_slice.2 hm)⟩
|
||||
mpr h := ⟨isMatch_iff_slice.2 h.isMatch, fun p hp hm => h.not_isMatch p hp (isMatch_iff_slice.1 hm)⟩
|
||||
|
||||
theorem isLongestRevMatch_iff_isLongestRevMatch_toSlice {pat : String} {s : Slice} {pos : s.Pos} :
|
||||
IsLongestRevMatch (ρ := String) pat pos ↔ IsLongestRevMatch (ρ := Slice) pat.toSlice pos where
|
||||
mp h := ⟨isRevMatch_iff_slice.1 h.isRevMatch,
|
||||
fun p hp hm => h.not_isRevMatch p hp (isRevMatch_iff_slice.2 hm)⟩
|
||||
mpr h := ⟨isRevMatch_iff_slice.2 h.isRevMatch,
|
||||
fun p hp hm => h.not_isRevMatch p hp (isRevMatch_iff_slice.1 hm)⟩
|
||||
|
||||
theorem isLongestMatchAt_iff_isLongestMatchAt_toSlice {pat : String} {s : Slice} {pos₁ pos₂ : s.Pos} :
|
||||
IsLongestMatchAt (ρ := String) pat pos₁ pos₂ ↔
|
||||
IsLongestMatchAt (ρ := Slice) pat.toSlice pos₁ pos₂ := by
|
||||
simp [Model.isLongestMatchAt_iff, isLongestMatch_iff_isLongestMatch_toSlice]
|
||||
|
||||
theorem isLongestMatchAtChain_iff_isLongestMatchAtChain_toSlice {pat : String} {s : Slice} {pos₁ pos₂ : s.Pos} :
|
||||
IsLongestMatchAtChain pat pos₁ pos₂ ↔
|
||||
IsLongestMatchAtChain pat.toSlice pos₁ pos₂ := by
|
||||
refine ⟨fun h => ?_, fun h => ?_⟩
|
||||
· induction h with
|
||||
| nil => simp
|
||||
| cons p₁ p₂ p₃ h₁ h₂ ih =>
|
||||
exact .cons _ _ _ (isLongestMatchAt_iff_isLongestMatchAt_toSlice.1 h₁) ih
|
||||
· induction h with
|
||||
| nil => simp
|
||||
| cons p₁ p₂ p₃ h₁ h₂ ih =>
|
||||
exact .cons _ _ _ (isLongestMatchAt_iff_isLongestMatchAt_toSlice.2 h₁) ih
|
||||
|
||||
theorem isLongestMatchAtChain_iff {pat : String} {s : Slice} {pos₁ pos₂ : s.Pos} :
|
||||
IsLongestMatchAtChain pat pos₁ pos₂ ↔
|
||||
∃ h n, (s.slice pos₁ pos₂ h).copy = String.join (List.replicate n pat) := by
|
||||
simp [isLongestMatchAtChain_iff_isLongestMatchAtChain_toSlice,
|
||||
ForwardSliceSearcher.isLongestMatchAtChain_iff]
|
||||
|
||||
theorem isLongestMatchAtChain_startPos_endPos_iff {pat : String} {s : Slice} :
|
||||
IsLongestMatchAtChain pat s.startPos s.endPos ↔
|
||||
∃ n, s.copy = String.join (List.replicate n pat) := by
|
||||
simp [isLongestMatchAtChain_iff]
|
||||
|
||||
theorem isLongestRevMatchAt_iff_isLongestRevMatchAt_toSlice {pat : String} {s : Slice}
|
||||
{pos₁ pos₂ : s.Pos} :
|
||||
IsLongestRevMatchAt (ρ := String) pat pos₁ pos₂ ↔
|
||||
IsLongestRevMatchAt (ρ := Slice) pat.toSlice pos₁ pos₂ := by
|
||||
simp [Model.isLongestRevMatchAt_iff, isLongestRevMatch_iff_isLongestRevMatch_toSlice]
|
||||
|
||||
theorem isLongestRevMatchAtChain_iff_isLongestRevMatchAtChain_toSlice {pat : String} {s : Slice} {pos₁ pos₂ : s.Pos} :
|
||||
IsLongestRevMatchAtChain pat pos₁ pos₂ ↔
|
||||
IsLongestRevMatchAtChain pat.toSlice pos₁ pos₂ := by
|
||||
refine ⟨fun h => ?_, fun h => ?_⟩
|
||||
· induction h with
|
||||
| nil => simp
|
||||
| cons p₂ p₃ _ hmatch ih =>
|
||||
exact .cons _ _ _ ih (isLongestRevMatchAt_iff_isLongestRevMatchAt_toSlice.1 hmatch)
|
||||
· induction h with
|
||||
| nil => simp
|
||||
| cons p₂ p₃ _ hmatch ih =>
|
||||
exact .cons _ _ _ ih (isLongestRevMatchAt_iff_isLongestRevMatchAt_toSlice.2 hmatch)
|
||||
|
||||
theorem isLongestRevMatchAtChain_iff {pat : String} {s : Slice} {pos₁ pos₂ : s.Pos} :
|
||||
IsLongestRevMatchAtChain pat pos₁ pos₂ ↔
|
||||
∃ h n, (s.slice pos₁ pos₂ h).copy = String.join (List.replicate n pat) := by
|
||||
simp [isLongestRevMatchAtChain_iff_isLongestRevMatchAtChain_toSlice,
|
||||
ForwardSliceSearcher.isLongestRevMatchAtChain_iff]
|
||||
|
||||
theorem isLongestRevMatchAtChain_startPos_endPos_iff {pat : String} {s : Slice} :
|
||||
IsLongestRevMatchAtChain pat s.startPos s.endPos ↔
|
||||
∃ n, s.copy = String.join (List.replicate n pat) := by
|
||||
simp [isLongestRevMatchAtChain_iff]
|
||||
|
||||
theorem matchesAt_iff_toSlice {pat : String} {s : Slice} {pos : s.Pos} :
|
||||
MatchesAt (ρ := String) pat pos ↔ MatchesAt (ρ := Slice) pat.toSlice pos := by
|
||||
simp [matchesAt_iff_exists_isLongestMatchAt, isLongestMatchAt_iff_isLongestMatchAt_toSlice]
|
||||
|
||||
private theorem toSlice_isEmpty (h : pat ≠ "") : pat.toSlice.isEmpty = false := by
|
||||
rwa [isEmpty_toSlice, isEmpty_eq_false_iff]
|
||||
theorem revMatchesAt_iff_toSlice {pat : String} {s : Slice} {pos : s.Pos} :
|
||||
RevMatchesAt (ρ := String) pat pos ↔ RevMatchesAt (ρ := Slice) pat.toSlice pos := by
|
||||
simp [revMatchesAt_iff_exists_isLongestRevMatchAt,
|
||||
isLongestRevMatchAt_iff_isLongestRevMatchAt_toSlice]
|
||||
|
||||
theorem isMatch_iff {pat : String} {s : Slice} {pos : s.Pos} (h : pat ≠ "") :
|
||||
theorem isMatch_iff {pat : String} {s : Slice} {pos : s.Pos} :
|
||||
IsMatch pat pos ↔ (s.sliceTo pos).copy = pat := by
|
||||
rw [isMatch_iff_slice, ForwardSliceSearcher.isMatch_iff (toSlice_isEmpty h)]
|
||||
rw [isMatch_iff_slice, ForwardSliceSearcher.isMatch_iff]
|
||||
simp
|
||||
|
||||
theorem isLongestMatch_iff {pat : String} {s : Slice} {pos : s.Pos} (h : pat ≠ "") :
|
||||
IsLongestMatch pat pos ↔ (s.sliceTo pos).copy = pat := by
|
||||
rw [isLongestMatch_iff_isMatch, isMatch_iff h]
|
||||
theorem isRevMatch_iff {pat : String} {s : Slice} {pos : s.Pos} :
|
||||
IsRevMatch pat pos ↔ (s.sliceFrom pos).copy = pat := by
|
||||
rw [isRevMatch_iff_slice, ForwardSliceSearcher.isRevMatch_iff]
|
||||
simp
|
||||
|
||||
theorem isLongestMatchAt_iff {pat : String} {s : Slice} {pos₁ pos₂ : s.Pos} (h : pat ≠ "") :
|
||||
theorem isLongestMatch_iff {pat : String} {s : Slice} {pos : s.Pos} :
|
||||
IsLongestMatch pat pos ↔ (s.sliceTo pos).copy = pat := by
|
||||
rw [isLongestMatch_iff_isMatch, isMatch_iff]
|
||||
|
||||
theorem isLongestRevMatch_iff {pat : String} {s : Slice} {pos : s.Pos} :
|
||||
IsLongestRevMatch pat pos ↔ (s.sliceFrom pos).copy = pat := by
|
||||
rw [isLongestRevMatch_iff_isRevMatch, isRevMatch_iff]
|
||||
|
||||
theorem isLongestMatchAt_iff {pat : String} {s : Slice} {pos₁ pos₂ : s.Pos} :
|
||||
IsLongestMatchAt pat pos₁ pos₂ ↔ ∃ h, (s.slice pos₁ pos₂ h).copy = pat := by
|
||||
rw [isLongestMatchAt_iff_isLongestMatchAt_toSlice,
|
||||
ForwardSliceSearcher.isLongestMatchAt_iff (toSlice_isEmpty h)]
|
||||
ForwardSliceSearcher.isLongestMatchAt_iff]
|
||||
simp
|
||||
|
||||
theorem isLongestMatchAt_iff_splits {pat : String} {s : Slice} {pos₁ pos₂ : s.Pos}
|
||||
(h : pat ≠ "") :
|
||||
theorem isLongestRevMatchAt_iff {pat : String} {s : Slice} {pos₁ pos₂ : s.Pos} :
|
||||
IsLongestRevMatchAt pat pos₁ pos₂ ↔ ∃ h, (s.slice pos₁ pos₂ h).copy = pat := by
|
||||
rw [isLongestRevMatchAt_iff_isLongestRevMatchAt_toSlice,
|
||||
ForwardSliceSearcher.isLongestRevMatchAt_iff]
|
||||
simp
|
||||
|
||||
theorem isLongestMatchAt_iff_splits {pat : String} {s : Slice} {pos₁ pos₂ : s.Pos} :
|
||||
IsLongestMatchAt pat pos₁ pos₂ ↔
|
||||
∃ t₁ t₂, pos₁.Splits t₁ (pat ++ t₂) ∧ pos₂.Splits (t₁ ++ pat) t₂ := by
|
||||
rw [isLongestMatchAt_iff_isLongestMatchAt_toSlice,
|
||||
ForwardSliceSearcher.isLongestMatchAt_iff_splits (toSlice_isEmpty h)]
|
||||
ForwardSliceSearcher.isLongestMatchAt_iff_splits]
|
||||
simp
|
||||
|
||||
theorem isLongestMatchAt_iff_extract {pat : String} {s : Slice} {pos₁ pos₂ : s.Pos}
|
||||
(h : pat ≠ "") :
|
||||
theorem isLongestRevMatchAt_iff_splits {pat : String} {s : Slice} {pos₁ pos₂ : s.Pos} :
|
||||
IsLongestRevMatchAt pat pos₁ pos₂ ↔
|
||||
∃ t₁ t₂, pos₁.Splits t₁ (pat ++ t₂) ∧ pos₂.Splits (t₁ ++ pat) t₂ := by
|
||||
rw [isLongestRevMatchAt_iff_isLongestRevMatchAt_toSlice,
|
||||
ForwardSliceSearcher.isLongestRevMatchAt_iff_splits]
|
||||
simp
|
||||
|
||||
theorem isLongestMatchAt_iff_extract {pat : String} {s : Slice} {pos₁ pos₂ : s.Pos} (h : pat ≠ "") :
|
||||
IsLongestMatchAt pat pos₁ pos₂ ↔
|
||||
s.copy.toByteArray.extract pos₁.offset.byteIdx pos₂.offset.byteIdx = pat.toByteArray := by
|
||||
rw [isLongestMatchAt_iff_isLongestMatchAt_toSlice,
|
||||
ForwardSliceSearcher.isLongestMatchAt_iff_extract (toSlice_isEmpty h)]
|
||||
ForwardSliceSearcher.isLongestMatchAt_iff_extract (by simpa)]
|
||||
simp
|
||||
|
||||
theorem isLongestRevMatchAt_iff_extract {pat : String} {s : Slice} {pos₁ pos₂ : s.Pos}
|
||||
(h : pat ≠ "") :
|
||||
IsLongestRevMatchAt pat pos₁ pos₂ ↔
|
||||
s.copy.toByteArray.extract pos₁.offset.byteIdx pos₂.offset.byteIdx = pat.toByteArray := by
|
||||
rw [isLongestRevMatchAt_iff_isLongestRevMatchAt_toSlice,
|
||||
ForwardSliceSearcher.isLongestRevMatchAt_iff_extract (by simpa)]
|
||||
simp
|
||||
|
||||
theorem offset_of_isLongestMatchAt {pat : String} {s : Slice} {pos₁ pos₂ : s.Pos}
|
||||
(h : pat ≠ "") (h' : IsLongestMatchAt pat pos₁ pos₂) :
|
||||
pos₂.offset = pos₁.offset.increaseBy pat.utf8ByteSize := by
|
||||
rw [show pat.utf8ByteSize = pat.toSlice.utf8ByteSize from utf8ByteSize_toSlice.symm]
|
||||
exact ForwardSliceSearcher.offset_of_isLongestMatchAt (toSlice_isEmpty h)
|
||||
exact ForwardSliceSearcher.offset_of_isLongestMatchAt (by simpa)
|
||||
(isLongestMatchAt_iff_isLongestMatchAt_toSlice.1 h')
|
||||
|
||||
theorem matchesAt_iff_splits {pat : String} {s : Slice} {pos : s.Pos} (h : pat ≠ "") :
|
||||
theorem offset_of_isLongestRevMatchAt {pat : String} {s : Slice} {pos₁ pos₂ : s.Pos}
|
||||
(h : pat ≠ "") (h' : IsLongestRevMatchAt pat pos₁ pos₂) :
|
||||
pos₂.offset = pos₁.offset.increaseBy pat.utf8ByteSize := by
|
||||
rw [show pat.utf8ByteSize = pat.toSlice.utf8ByteSize from utf8ByteSize_toSlice.symm]
|
||||
exact ForwardSliceSearcher.offset_of_isLongestRevMatchAt (by simpa)
|
||||
(isLongestRevMatchAt_iff_isLongestRevMatchAt_toSlice.1 h')
|
||||
|
||||
theorem matchesAt_iff_splits {pat : String} {s : Slice} {pos : s.Pos} :
|
||||
MatchesAt pat pos ↔ ∃ t₁ t₂, pos.Splits t₁ (pat ++ t₂) := by
|
||||
rw [matchesAt_iff_toSlice,
|
||||
ForwardSliceSearcher.matchesAt_iff_splits (toSlice_isEmpty h)]
|
||||
ForwardSliceSearcher.matchesAt_iff_splits]
|
||||
simp
|
||||
|
||||
theorem exists_matchesAt_iff_eq_append {pat : String} {s : Slice} (h : pat ≠ "") :
|
||||
theorem revMatchesAt_iff_splits {pat : String} {s : Slice} {pos : s.Pos} :
|
||||
RevMatchesAt pat pos ↔ ∃ t₁ t₂, pos.Splits (t₁ ++ pat) t₂ := by
|
||||
rw [revMatchesAt_iff_toSlice,
|
||||
ForwardSliceSearcher.revMatchesAt_iff_splits]
|
||||
simp
|
||||
|
||||
theorem exists_matchesAt_iff_eq_append {pat : String} {s : Slice} :
|
||||
(∃ (pos : s.Pos), MatchesAt pat pos) ↔ ∃ t₁ t₂, s.copy = t₁ ++ pat ++ t₂ := by
|
||||
simp only [matchesAt_iff_splits h]
|
||||
simp only [matchesAt_iff_splits]
|
||||
constructor
|
||||
· rintro ⟨pos, t₁, t₂, hsplit⟩
|
||||
exact ⟨t₁, t₂, by rw [hsplit.eq_append, append_assoc]⟩
|
||||
@@ -230,35 +477,58 @@ theorem exists_matchesAt_iff_eq_append {pat : String} {s : Slice} (h : pat ≠ "
|
||||
⟨t₁, pat ++ t₂, by rw [← append_assoc]; exact heq, rfl⟩
|
||||
exact ⟨s.pos _ hvalid, t₁, t₂, ⟨by rw [← append_assoc]; exact heq, by simp⟩⟩
|
||||
|
||||
theorem exists_revMatchesAt_iff_eq_append {pat : String} {s : Slice} :
|
||||
(∃ (pos : s.Pos), RevMatchesAt pat pos) ↔ ∃ t₁ t₂, s.copy = t₁ ++ pat ++ t₂ := by
|
||||
rw [show (∃ (pos : s.Pos), RevMatchesAt (ρ := String) pat pos) ↔
|
||||
(∃ (pos : s.Pos), RevMatchesAt (ρ := Slice) pat.toSlice pos) from by
|
||||
simp [revMatchesAt_iff_toSlice],
|
||||
ForwardSliceSearcher.exists_revMatchesAt_iff_eq_append]
|
||||
simp
|
||||
|
||||
theorem matchesAt_iff_isLongestMatchAt {pat : String} {s : Slice} {pos : s.Pos}
|
||||
(h : pat ≠ "") :
|
||||
MatchesAt pat pos ↔ ∃ (h : (pos.offset.increaseBy pat.utf8ByteSize).IsValidForSlice s),
|
||||
IsLongestMatchAt pat pos (s.pos _ h) := by
|
||||
have key := ForwardSliceSearcher.matchesAt_iff_isLongestMatchAt (pat := pat.toSlice)
|
||||
(toSlice_isEmpty h) (pos := pos)
|
||||
(by simpa) (pos := pos)
|
||||
simp only [utf8ByteSize_toSlice, ← isLongestMatchAt_iff_isLongestMatchAt_toSlice] at key
|
||||
rwa [matchesAt_iff_toSlice]
|
||||
|
||||
theorem revMatchesAt_iff_isLongestRevMatchAt {pat : String} {s : Slice} {pos : s.Pos}
|
||||
(h : pat ≠ "") :
|
||||
RevMatchesAt pat pos ↔
|
||||
∃ (h : (pos.offset.decreaseBy pat.utf8ByteSize).IsValidForSlice s),
|
||||
IsLongestRevMatchAt pat (s.pos _ h) pos := by
|
||||
have key := ForwardSliceSearcher.revMatchesAt_iff_isLongestRevMatchAt (pat := pat.toSlice)
|
||||
(by simpa) (pos := pos)
|
||||
simp only [utf8ByteSize_toSlice, ← isLongestRevMatchAt_iff_isLongestRevMatchAt_toSlice] at key
|
||||
rwa [revMatchesAt_iff_toSlice]
|
||||
|
||||
theorem matchesAt_iff_getElem {pat : String} {s : Slice} {pos : s.Pos} (h : pat ≠ "") :
|
||||
MatchesAt pat pos ↔
|
||||
∃ (h : pos.offset.byteIdx + pat.toByteArray.size ≤ s.copy.toByteArray.size),
|
||||
∀ (j), (hj : j < pat.toByteArray.size) →
|
||||
pat.toByteArray[j] = s.copy.toByteArray[pos.offset.byteIdx + j] := by
|
||||
have key := ForwardSliceSearcher.matchesAt_iff_getElem (pat := pat.toSlice)
|
||||
(toSlice_isEmpty h) (pos := pos)
|
||||
(by simpa) (pos := pos)
|
||||
simp only [copy_toSlice] at key
|
||||
rwa [matchesAt_iff_toSlice]
|
||||
|
||||
theorem le_of_matchesAt {pat : String} {s : Slice} {pos : s.Pos} (h : pat ≠ "")
|
||||
(h' : MatchesAt pat pos) : pos.offset.increaseBy pat.utf8ByteSize ≤ s.rawEndPos := by
|
||||
rw [show pat.utf8ByteSize = pat.toSlice.utf8ByteSize from utf8ByteSize_toSlice.symm]
|
||||
exact ForwardSliceSearcher.le_of_matchesAt (toSlice_isEmpty h)
|
||||
exact ForwardSliceSearcher.le_of_matchesAt (by simpa)
|
||||
(matchesAt_iff_toSlice.1 h')
|
||||
|
||||
theorem matchesAt_iff_matchesAt_toSlice {pat : String} {s : Slice}
|
||||
{pos : s.Pos} : MatchesAt pat pos ↔ MatchesAt pat.toSlice pos := by
|
||||
simp [matchesAt_iff_exists_isLongestMatchAt, isLongestMatchAt_iff_isLongestMatchAt_toSlice]
|
||||
|
||||
theorem revMatchesAt_iff_revMatchesAt_toSlice {pat : String} {s : Slice}
|
||||
{pos : s.Pos} : RevMatchesAt pat pos ↔ RevMatchesAt pat.toSlice pos := by
|
||||
simp [revMatchesAt_iff_exists_isLongestRevMatchAt,
|
||||
isLongestRevMatchAt_iff_isLongestRevMatchAt_toSlice]
|
||||
|
||||
theorem toSearcher_eq {pat : String} {s : Slice} :
|
||||
ToForwardSearcher.toSearcher pat s = ToForwardSearcher.toSearcher pat.toSlice s := (rfl)
|
||||
|
||||
@@ -275,6 +545,21 @@ theorem isValidSearchFrom_iff_isValidSearchFrom_toSlice {pat : String}
|
||||
| matched => simp_all [IsValidSearchFrom.matched, isLongestMatchAt_iff_isLongestMatchAt_toSlice]
|
||||
| mismatched => simp_all [IsValidSearchFrom.mismatched, matchesAt_iff_matchesAt_toSlice]
|
||||
|
||||
theorem isValidRevSearchFrom_iff_isValidRevSearchFrom_toSlice {pat : String}
|
||||
{s : Slice} {pos : s.Pos} {l : List (SearchStep s)} :
|
||||
IsValidRevSearchFrom pat pos l ↔ IsValidRevSearchFrom pat.toSlice pos l := by
|
||||
refine ⟨fun h => ?_, fun h => ?_⟩
|
||||
· induction h with
|
||||
| startPos => simpa using IsValidRevSearchFrom.startPos
|
||||
| matched => simp_all [IsValidRevSearchFrom.matched,
|
||||
isLongestRevMatchAt_iff_isLongestRevMatchAt_toSlice]
|
||||
| mismatched => simp_all [IsValidRevSearchFrom.mismatched, revMatchesAt_iff_revMatchesAt_toSlice]
|
||||
· induction h with
|
||||
| startPos => simpa using IsValidRevSearchFrom.startPos
|
||||
| matched => simp_all [IsValidRevSearchFrom.matched,
|
||||
isLongestRevMatchAt_iff_isLongestRevMatchAt_toSlice]
|
||||
| mismatched => simp_all [IsValidRevSearchFrom.mismatched, revMatchesAt_iff_revMatchesAt_toSlice]
|
||||
|
||||
end ForwardStringSearcher
|
||||
|
||||
end String.Slice.Pattern.Model
|
||||
|
||||
@@ -56,7 +56,7 @@ theorem skipPrefix?_eq_some_iff {pat s : Slice} {pos : s.Pos} :
|
||||
simp only [reduceCtorEq, false_iff]
|
||||
intro heq
|
||||
have := h (s.sliceFrom pos).copy
|
||||
simp [← heq, pos.splits.eq_append] at this
|
||||
simp [← heq, -sliceTo_append_sliceFrom, pos.splits.eq_append] at this
|
||||
|
||||
theorem isSome_skipPrefix? {pat s : Slice} : (skipPrefix? pat s).isSome = startsWith pat s := by
|
||||
fun_cases skipPrefix? <;> simp_all
|
||||
@@ -76,12 +76,11 @@ namespace Model.ForwardSliceSearcher
|
||||
|
||||
open Pattern.ForwardSliceSearcher
|
||||
|
||||
public theorem lawfulForwardPatternModel {pat : Slice} (hpat : pat.isEmpty = false) :
|
||||
LawfulForwardPatternModel pat where
|
||||
skipPrefixOfNonempty?_eq h := rfl
|
||||
startsWith_eq s := isSome_skipPrefix?.symm
|
||||
public instance {pat : Slice} : LawfulForwardPatternModel pat where
|
||||
skipPrefixOfNonempty?_eq _ := rfl
|
||||
startsWith_eq _ := isSome_skipPrefix?.symm
|
||||
skipPrefix?_eq_some_iff pos := by
|
||||
simp [ForwardPattern.skipPrefix?, skipPrefix?_eq_some_iff, isLongestMatch_iff hpat]
|
||||
simp [ForwardPattern.skipPrefix?, skipPrefix?_eq_some_iff, isLongestMatch_iff]
|
||||
|
||||
end Model.ForwardSliceSearcher
|
||||
|
||||
@@ -89,15 +88,107 @@ namespace Model.ForwardStringSearcher
|
||||
|
||||
open Pattern.ForwardSliceSearcher
|
||||
|
||||
public theorem lawfulForwardPatternModel {pat : String} (hpat : pat ≠ "") :
|
||||
LawfulForwardPatternModel pat where
|
||||
skipPrefixOfNonempty?_eq h := rfl
|
||||
startsWith_eq s := isSome_skipPrefix?.symm
|
||||
public instance {pat : String} : LawfulForwardPatternModel pat where
|
||||
skipPrefixOfNonempty?_eq _ := rfl
|
||||
startsWith_eq _ := isSome_skipPrefix?.symm
|
||||
skipPrefix?_eq_some_iff pos := by
|
||||
simp [ForwardPattern.skipPrefix?, skipPrefix?_eq_some_iff, isLongestMatch_iff hpat]
|
||||
simp [ForwardPattern.skipPrefix?, skipPrefix?_eq_some_iff, isLongestMatch_iff]
|
||||
|
||||
end Model.ForwardStringSearcher
|
||||
|
||||
namespace BackwardSliceSearcher
|
||||
|
||||
theorem endsWith_iff {pat s : Slice} : endsWith pat s ↔ ∃ t, s.copy = t ++ pat.copy := by
|
||||
rw [endsWith]
|
||||
simp [Internal.memcmpSlice_eq_true_iff, utf8ByteSize_eq_size_toByteArray_copy, -size_toByteArray]
|
||||
generalize pat.copy = pat
|
||||
generalize s.copy = s
|
||||
refine ⟨fun ⟨h₁, h₂⟩ => ?_, ?_⟩
|
||||
· rw [Nat.sub_add_cancel h₁] at h₂
|
||||
suffices (s.rawEndPos.unoffsetBy pat.rawEndPos).IsValid s by
|
||||
have h₃ : (s.sliceFrom (s.pos _ this)).copy = pat := by
|
||||
rw [← toByteArray_inj, (s.pos _ this).splits.toByteArray_right_eq]
|
||||
simpa [offset_pos, Pos.Raw.byteIdx_unoffsetBy, byteIdx_rawEndPos]
|
||||
have := (s.pos _ this).splits
|
||||
rw [h₃] at this
|
||||
exact ⟨_, this.eq_append⟩
|
||||
rw [Pos.Raw.isValid_iff_isValidUTF8_extract_utf8ByteSize]
|
||||
refine ⟨by simp [Pos.Raw.le_iff, Pos.Raw.byteIdx_unoffsetBy], ?_⟩
|
||||
simp only [size_toByteArray] at h₂
|
||||
simpa [Pos.Raw.byteIdx_unoffsetBy, byteIdx_rawEndPos, h₂] using pat.isValidUTF8
|
||||
· rintro ⟨t, rfl⟩
|
||||
exact ⟨by simp, by rw [Nat.sub_add_cancel (by simp)]; exact
|
||||
ByteArray.extract_append_eq_right (by simp) (by simp)⟩
|
||||
|
||||
theorem skipSuffix?_eq_some_iff {pat s : Slice} {pos : s.Pos} :
|
||||
skipSuffix? pat s = some pos ↔ (s.sliceFrom pos).copy = pat.copy := by
|
||||
fun_cases skipSuffix? with
|
||||
| case1 h =>
|
||||
simp only [Option.some.injEq]
|
||||
obtain ⟨t, ht⟩ := endsWith_iff.1 h
|
||||
have hpc : pat.copy.utf8ByteSize = pat.utf8ByteSize := Slice.utf8ByteSize_copy
|
||||
have hsz : s.utf8ByteSize = t.utf8ByteSize + pat.utf8ByteSize := by
|
||||
have := congrArg String.utf8ByteSize ht
|
||||
simp only [utf8ByteSize_append, Slice.utf8ByteSize_copy] at this
|
||||
exact this
|
||||
have hoff : (s.endPos.offset.unoffsetBy pat.rawEndPos) = t.rawEndPos := by
|
||||
ext
|
||||
simp only [offset_endPos, Pos.Raw.byteIdx_unoffsetBy, byteIdx_rawEndPos,
|
||||
String.byteIdx_rawEndPos]
|
||||
omega
|
||||
have hval : (s.endPos.offset.unoffsetBy pat.rawEndPos).IsValidForSlice s :=
|
||||
Pos.Raw.isValidForSlice_iff_exists_append.mpr ⟨t, pat.copy, ht, hoff⟩
|
||||
have hsp : (s.pos _ hval).Splits t pat.copy := ⟨ht, hoff⟩
|
||||
rw [Slice.pos!_eq_pos hval]
|
||||
exact ⟨(· ▸ hsp.copy_sliceFrom_eq),
|
||||
fun h => hsp.pos_eq_of_eq_right (h ▸ pos.splits)⟩
|
||||
| case2 h =>
|
||||
simp only [endsWith_iff, not_exists] at h
|
||||
simp only [reduceCtorEq, false_iff]
|
||||
intro heq
|
||||
have := h (s.sliceTo pos).copy
|
||||
simp [← heq, -sliceTo_append_sliceFrom, pos.splits.eq_append] at this
|
||||
|
||||
theorem isSome_skipSuffix? {pat s : Slice} : (skipSuffix? pat s).isSome = endsWith pat s := by
|
||||
fun_cases skipSuffix? <;> simp_all
|
||||
|
||||
public theorem endsWith_of_isEmpty {pat s : Slice} (hpat : pat.isEmpty = true) :
|
||||
BackwardPattern.endsWith pat s = true := by
|
||||
suffices pat.copy = "" by simp [BackwardPattern.endsWith, endsWith_iff, this]
|
||||
simpa
|
||||
|
||||
public theorem skipSuffix?_of_isEmpty {pat s : Slice} (hpat : pat.isEmpty = true) :
|
||||
BackwardPattern.skipSuffix? pat s = some s.endPos := by
|
||||
simpa [BackwardPattern.skipSuffix?, skipSuffix?_eq_some_iff]
|
||||
|
||||
end BackwardSliceSearcher
|
||||
|
||||
namespace Model.BackwardSliceSearcher
|
||||
|
||||
open Pattern.BackwardSliceSearcher
|
||||
|
||||
public instance {pat : Slice} : LawfulBackwardPatternModel pat where
|
||||
skipSuffixOfNonempty?_eq _ := rfl
|
||||
endsWith_eq _ := isSome_skipSuffix?.symm
|
||||
skipSuffix?_eq_some_iff pos := by
|
||||
simp [BackwardPattern.skipSuffix?, skipSuffix?_eq_some_iff,
|
||||
ForwardSliceSearcher.isLongestRevMatch_iff]
|
||||
|
||||
end Model.BackwardSliceSearcher
|
||||
|
||||
namespace Model.BackwardStringSearcher
|
||||
|
||||
open Pattern.BackwardSliceSearcher
|
||||
|
||||
public instance {pat : String} : LawfulBackwardPatternModel pat where
|
||||
skipSuffixOfNonempty?_eq _ := rfl
|
||||
endsWith_eq _ := isSome_skipSuffix?.symm
|
||||
skipSuffix?_eq_some_iff pos := by
|
||||
simp [BackwardPattern.skipSuffix?, skipSuffix?_eq_some_iff,
|
||||
ForwardStringSearcher.isLongestRevMatch_iff]
|
||||
|
||||
end Model.BackwardStringSearcher
|
||||
|
||||
end Pattern
|
||||
|
||||
public theorem startsWith_string_eq_startsWith_toSlice {pat : String} {s : Slice} :
|
||||
@@ -116,19 +207,22 @@ public theorem Pattern.ForwardPattern.skipPrefix?_string_eq_skipPrefix?_toSlice
|
||||
{pat : String} {s : Slice} :
|
||||
skipPrefix? pat s = skipPrefix? pat.toSlice s := (rfl)
|
||||
|
||||
public theorem Pos.skip?_string_eq_skip?_toSlice {pat : String} {s : Slice} {pos : s.Pos} :
|
||||
pos.skip? pat = pos.skip? pat.toSlice := (rfl)
|
||||
|
||||
public theorem Pos.skipWhile_string_eq_skipWhile_toSlice {pat : String} {s : Slice}
|
||||
(curr : s.Pos) :
|
||||
Pos.skipWhile curr pat = Pos.skipWhile curr pat.toSlice := by
|
||||
fun_induction Pos.skipWhile curr pat with
|
||||
| case1 pos nextCurr h₁ h₂ ih =>
|
||||
conv => rhs; rw [Pos.skipWhile]
|
||||
simp [← Pattern.ForwardPattern.skipPrefix?_string_eq_skipPrefix?_toSlice, h₁, h₂, ih]
|
||||
simp [← Pos.skip?_string_eq_skip?_toSlice, h₁, h₂, ih]
|
||||
| case2 pos nextCurr h ih =>
|
||||
conv => rhs; rw [Pos.skipWhile]
|
||||
simp [← Pattern.ForwardPattern.skipPrefix?_string_eq_skipPrefix?_toSlice, h, ih]
|
||||
simp [← Pos.skip?_string_eq_skip?_toSlice, h, ih]
|
||||
| case3 pos h =>
|
||||
conv => rhs; rw [Pos.skipWhile]
|
||||
simp [← Pattern.ForwardPattern.skipPrefix?_string_eq_skipPrefix?_toSlice]
|
||||
simp [← Pos.skip?_string_eq_skip?_toSlice, h]
|
||||
|
||||
public theorem skipPrefixWhile_string_eq_skipPrefixWhile_toSlice {pat : String} {s : Slice} :
|
||||
s.skipPrefixWhile pat = s.skipPrefixWhile pat.toSlice :=
|
||||
@@ -144,7 +238,7 @@ public theorem takeWhile_string_eq_takeWhile_toSlice {pat : String} {s : Slice}
|
||||
|
||||
public theorem all_string_eq_all_toSlice {pat : String} {s : Slice} :
|
||||
s.all pat = s.all pat.toSlice := by
|
||||
simp only [all, dropWhile_string_eq_dropWhile_toSlice]
|
||||
simp only [all, skipPrefixWhile_string_eq_skipPrefixWhile_toSlice]
|
||||
|
||||
public theorem endsWith_string_eq_endsWith_toSlice {pat : String} {s : Slice} :
|
||||
s.endsWith pat = s.endsWith pat.toSlice := (rfl)
|
||||
@@ -162,19 +256,22 @@ public theorem Pattern.BackwardPattern.skipSuffix?_string_eq_skipSuffix?_toSlice
|
||||
{pat : String} {s : Slice} :
|
||||
skipSuffix? pat s = skipSuffix? pat.toSlice s := (rfl)
|
||||
|
||||
public theorem Pos.revSkip?_string_eq_revSkip?_toSlice {pat : String} {s : Slice} {pos : s.Pos} :
|
||||
pos.revSkip? pat = pos.revSkip? pat.toSlice := (rfl)
|
||||
|
||||
public theorem Pos.revSkipWhile_string_eq_revSkipWhile_toSlice {pat : String} {s : Slice}
|
||||
(curr : s.Pos) :
|
||||
Pos.revSkipWhile curr pat = Pos.revSkipWhile curr pat.toSlice := by
|
||||
fun_induction Pos.revSkipWhile curr pat with
|
||||
| case1 pos nextCurr h₁ h₂ ih =>
|
||||
conv => rhs; rw [Pos.revSkipWhile]
|
||||
simp [← Pattern.BackwardPattern.skipSuffix?_string_eq_skipSuffix?_toSlice, h₁, h₂, ih]
|
||||
simp [← Pos.revSkip?_string_eq_revSkip?_toSlice, h₁, h₂, ih]
|
||||
| case2 pos nextCurr h ih =>
|
||||
conv => rhs; rw [Pos.revSkipWhile]
|
||||
simp [← Pattern.BackwardPattern.skipSuffix?_string_eq_skipSuffix?_toSlice, h, ih]
|
||||
simp [← Pos.revSkip?_string_eq_revSkip?_toSlice, h, ih]
|
||||
| case3 pos h =>
|
||||
conv => rhs; rw [Pos.revSkipWhile]
|
||||
simp [← Pattern.BackwardPattern.skipSuffix?_string_eq_skipSuffix?_toSlice]
|
||||
simp [← Pos.revSkip?_string_eq_revSkip?_toSlice, h]
|
||||
|
||||
public theorem skipSuffixWhile_string_eq_skipSuffixWhile_toSlice {pat : String} {s : Slice} :
|
||||
s.skipSuffixWhile pat = s.skipSuffixWhile pat.toSlice :=
|
||||
@@ -188,4 +285,8 @@ public theorem takeEndWhile_string_eq_takeEndWhile_toSlice {pat : String} {s : S
|
||||
s.takeEndWhile pat = s.takeEndWhile pat.toSlice := by
|
||||
simp only [takeEndWhile]; exact congrArg _ skipSuffixWhile_string_eq_skipSuffixWhile_toSlice
|
||||
|
||||
public theorem revAll_string_eq_revAll_toSlice {pat : String} {s : Slice} :
|
||||
s.revAll pat = s.revAll pat.toSlice := by
|
||||
simp [revAll, skipSuffixWhile_string_eq_skipSuffixWhile_toSlice]
|
||||
|
||||
end String.Slice
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -11,6 +11,8 @@ public import Init.Data.String.TakeDrop
|
||||
import Init.Data.String.Lemmas.Pattern.TakeDrop.Basic
|
||||
import Init.Data.String.Lemmas.Pattern.Char
|
||||
import Init.Data.Option.Lemmas
|
||||
import Init.Data.String.Lemmas.FindPos
|
||||
import Init.Data.List.Sublist
|
||||
|
||||
public section
|
||||
|
||||
@@ -40,6 +42,126 @@ theorem startsWith_char_eq_head? {c : Char} {s : Slice} :
|
||||
simp only [← toList_inj, toList_append, toList_singleton, List.cons_append, List.nil_append]
|
||||
cases h : s.copy.toList <;> simp_all [← ofList_inj]
|
||||
|
||||
theorem startsWith_char_iff_exists_append {c : Char} {s : Slice} :
|
||||
s.startsWith c ↔ ∃ t, s.copy = singleton c ++ t := by
|
||||
simp only [startsWith_char_eq_head?, beq_iff_eq, List.head?_eq_some_iff, ← toList_inj,
|
||||
toList_append, toList_singleton, List.cons_append, List.nil_append]
|
||||
exact ⟨fun ⟨t, ht⟩ => ⟨ofList t, by simp [ht]⟩, fun ⟨t, ht⟩ => ⟨t.toList, by simp [ht]⟩⟩
|
||||
|
||||
theorem startsWith_char_eq_false_iff_forall_append {c : Char} {s : Slice} :
|
||||
s.startsWith c = false ↔ ∀ t, s.copy ≠ singleton c ++ t := by
|
||||
simp [← Bool.not_eq_true, startsWith_char_iff_exists_append]
|
||||
|
||||
theorem eq_append_of_dropPrefix?_char_eq_some {c : Char} {s res : Slice} (h : s.dropPrefix? c = some res) :
|
||||
s.copy = singleton c ++ res.copy := by
|
||||
simpa [PatternModel.Matches] using Pattern.Model.eq_append_of_dropPrefix?_eq_some h
|
||||
|
||||
theorem Pos.skip?_char_eq_some_iff {c : Char} {s : Slice} {pos res : s.Pos} :
|
||||
pos.skip? c = some res ↔ ∃ h, res = pos.next h ∧ pos.get h = c := by
|
||||
simp [Pattern.Model.Pos.skip?_eq_some_iff, Char.isLongestMatchAt_iff]
|
||||
|
||||
@[simp]
|
||||
theorem Pos.skip?_char_eq_none_iff {c : Char} {s : Slice} {pos : s.Pos} :
|
||||
pos.skip? c = none ↔ ∀ h, pos.get h ≠ c := by
|
||||
simp [Pattern.Model.Pos.skip?_eq_none_iff, Char.matchesAt_iff]
|
||||
|
||||
theorem Pos.get_skipWhile_char_ne {c : Char} {s : Slice} {pos : s.Pos} {h} :
|
||||
(pos.skipWhile c).get h ≠ c := by
|
||||
have := Pattern.Model.Pos.not_matchesAt_skipWhile c pos
|
||||
simp_all [Char.matchesAt_iff]
|
||||
|
||||
theorem Pos.skipWhile_char_eq_self_iff_get {c : Char} {s : Slice} {pos : s.Pos} :
|
||||
pos.skipWhile c = pos ↔ ∀ h, pos.get h ≠ c := by
|
||||
simp [Pattern.Model.Pos.skipWhile_eq_self_iff, Char.matchesAt_iff]
|
||||
|
||||
theorem Pos.get_eq_of_lt_skipWhile_char {c : Char} {s : Slice} {pos pos' : s.Pos}
|
||||
(h₁ : pos ≤ pos') (h₂ : pos' < pos.skipWhile c) : pos'.get (ne_endPos_of_lt h₂) = c :=
|
||||
(Char.isLongestMatchAtChain_iff.1 (Pattern.Model.Pos.isLongestMatchAtChain_skipWhile c pos)).2 _ h₁ h₂
|
||||
|
||||
theorem get_skipPrefixWhile_char_ne {c : Char} {s : Slice} {h} :
|
||||
(s.skipPrefixWhile c).get h ≠ c := by
|
||||
simp [skipPrefixWhile_eq_skipWhile_startPos, Pos.get_skipWhile_char_ne]
|
||||
|
||||
theorem get_eq_of_lt_skipPrefixWhile_char {c : Char} {s : Slice} {pos : s.Pos} (h : pos < s.skipPrefixWhile c) :
|
||||
pos.get (Pos.ne_endPos_of_lt h) = c :=
|
||||
Pos.get_eq_of_lt_skipWhile_char (Pos.startPos_le _) (by rwa [skipPrefixWhile_eq_skipWhile_startPos] at h)
|
||||
|
||||
@[simp]
|
||||
theorem all_char_iff {c : Char} {s : Slice} : s.all c ↔ s.copy.toList = List.replicate s.copy.length c := by
|
||||
rw [Bool.eq_iff_iff]
|
||||
simp [Pattern.Model.all_eq_true_iff, Char.isLongestMatchAtChain_startPos_endPos_iff_toList]
|
||||
|
||||
theorem Pos.revSkip?_char_eq_some_iff {c : Char} {s : Slice} {pos res : s.Pos} :
|
||||
pos.revSkip? c = some res ↔ ∃ h, res = pos.prev h ∧ (pos.prev h).get (by simp) = c := by
|
||||
simp [Pattern.Model.Pos.revSkip?_eq_some_iff, Char.isLongestRevMatchAt_iff]
|
||||
|
||||
@[simp]
|
||||
theorem Pos.revSkip?_char_eq_none_iff {c : Char} {s : Slice} {pos : s.Pos} :
|
||||
pos.revSkip? c = none ↔ ∀ h, (pos.prev h).get (by simp) ≠ c := by
|
||||
simp [Pattern.Model.Pos.revSkip?_eq_none_iff, Char.revMatchesAt_iff]
|
||||
|
||||
theorem Pos.get_revSkipWhile_char_ne {c : Char} {s : Slice} {pos : s.Pos} {h} :
|
||||
((pos.revSkipWhile c).prev h).get (by simp) ≠ c := by
|
||||
have := Pattern.Model.Pos.not_revMatchesAt_revSkipWhile c pos
|
||||
simp_all [Char.revMatchesAt_iff]
|
||||
|
||||
theorem Pos.revSkipWhile_char_eq_self_iff_get {c : Char} {s : Slice} {pos : s.Pos} :
|
||||
pos.revSkipWhile c = pos ↔ ∀ h, (pos.prev h).get (by simp) ≠ c := by
|
||||
simp [Pattern.Model.Pos.revSkipWhile_eq_self_iff, Char.revMatchesAt_iff]
|
||||
|
||||
theorem Pos.get_eq_of_revSkipWhile_le_char {c : Char} {s : Slice} {pos pos' : s.Pos}
|
||||
(h₁ : pos' < pos) (h₂ : pos.revSkipWhile c ≤ pos') : pos'.get (Pos.ne_endPos_of_lt h₁) = c :=
|
||||
(Char.isLongestRevMatchAtChain_iff.1 (Pattern.Model.Pos.isLongestRevMatchAtChain_revSkipWhile c pos)).2 _ h₂ h₁
|
||||
|
||||
theorem get_skipSuffixWhile_char_ne {c : Char} {s : Slice} {h} :
|
||||
((s.skipSuffixWhile c).prev h).get (by simp) ≠ c := by
|
||||
simp [skipSuffixWhile_eq_revSkipWhile_endPos, Pos.get_revSkipWhile_char_ne]
|
||||
|
||||
theorem get_eq_of_skipSuffixWhile_le_char {c : Char} {s : Slice} {pos : s.Pos}
|
||||
(h : s.skipSuffixWhile c ≤ pos) (h' : pos < s.endPos) :
|
||||
pos.get (Pos.ne_endPos_of_lt h') = c :=
|
||||
Pos.get_eq_of_revSkipWhile_le_char h' (by rwa [skipSuffixWhile_eq_revSkipWhile_endPos] at h)
|
||||
|
||||
@[simp]
|
||||
theorem revAll_char_iff {c : Char} {s : Slice} : s.revAll c ↔ s.copy.toList = List.replicate s.copy.length c := by
|
||||
rw [Bool.eq_iff_iff]
|
||||
simp [Pattern.Model.revAll_eq_true_iff, Char.isLongestRevMatchAtChain_startPos_endPos_iff_toList]
|
||||
|
||||
theorem skipSuffix?_char_eq_some_iff {c : Char} {s : Slice} {pos : s.Pos} :
|
||||
s.skipSuffix? c = some pos ↔ ∃ h, pos = s.endPos.prev h ∧ (s.endPos.prev h).get (by simp) = c := by
|
||||
rw [Pattern.Model.skipSuffix?_eq_some_iff, Char.isLongestRevMatch_iff]
|
||||
|
||||
theorem endsWith_char_iff_get {c : Char} {s : Slice} :
|
||||
s.endsWith c ↔ ∃ h, (s.endPos.prev h).get (by simp) = c := by
|
||||
simp [Pattern.Model.endsWith_iff, Char.revMatchesAt_iff]
|
||||
|
||||
theorem endsWith_char_eq_false_iff_get {c : Char} {s : Slice} :
|
||||
s.endsWith c = false ↔ ∀ h, (s.endPos.prev h).get (by simp) ≠ c := by
|
||||
simp [Pattern.Model.endsWith_eq_false_iff, Char.revMatchesAt_iff]
|
||||
|
||||
theorem endsWith_char_iff_exists_append {c : Char} {s : Slice} :
|
||||
s.endsWith c ↔ ∃ t, s.copy = t ++ singleton c := by
|
||||
rw [Pattern.Model.endsWith_iff, Char.revMatchesAt_iff_splits]
|
||||
simp only [splits_endPos_iff, exists_eq_right, eq_comm (a := s.copy)]
|
||||
|
||||
theorem endsWith_char_eq_getLast? {c : Char} {s : Slice} :
|
||||
s.endsWith c = (s.copy.toList.getLast? == some c) := by
|
||||
rw [Bool.eq_iff_iff, endsWith_char_iff_exists_append, beq_iff_eq,
|
||||
← List.singleton_suffix_iff_getLast?_eq_some, List.suffix_iff_exists_eq_append]
|
||||
constructor
|
||||
· rintro ⟨t, ht⟩
|
||||
exact ⟨t.toList, by rw [ht, toList_append, toList_singleton]⟩
|
||||
· rintro ⟨l, hl⟩
|
||||
exact ⟨ofList l, by rw [← toList_inj, toList_append, toList_singleton, toList_ofList]; exact hl⟩
|
||||
|
||||
theorem endsWith_char_eq_false_iff_forall_append {c : Char} {s : Slice} :
|
||||
s.endsWith c = false ↔ ∀ t, s.copy ≠ t ++ singleton c := by
|
||||
simp [← Bool.not_eq_true, endsWith_char_iff_exists_append]
|
||||
|
||||
theorem eq_append_of_dropSuffix?_char_eq_some {c : Char} {s res : Slice} (h : s.dropSuffix? c = some res) :
|
||||
s.copy = res.copy ++ singleton c := by
|
||||
simpa [PatternModel.Matches] using Pattern.Model.eq_append_of_dropSuffix?_eq_some h
|
||||
|
||||
end Slice
|
||||
|
||||
theorem skipPrefix?_char_eq_some_iff {c : Char} {s : String} {pos : s.Pos} :
|
||||
@@ -49,14 +171,57 @@ theorem skipPrefix?_char_eq_some_iff {c : Char} {s : String} {pos : s.Pos} :
|
||||
|
||||
theorem startsWith_char_iff_get {c : Char} {s : String} :
|
||||
s.startsWith c ↔ ∃ h, s.startPos.get h = c := by
|
||||
simp [startsWith_eq_startsWith_toSlice, Slice.startsWith_char_iff_get]
|
||||
simp [← startsWith_toSlice, Slice.startsWith_char_iff_get]
|
||||
|
||||
theorem startsWith_char_eq_false_iff_get {c : Char} {s : String} :
|
||||
s.startsWith c = false ↔ ∀ h, s.startPos.get h ≠ c := by
|
||||
simp [startsWith_eq_startsWith_toSlice, Slice.startsWith_char_eq_false_iff_get]
|
||||
simp [← startsWith_toSlice, Slice.startsWith_char_eq_false_iff_get]
|
||||
|
||||
theorem startsWith_char_eq_head? {c : Char} {s : String} :
|
||||
s.startsWith c = (s.toList.head? == some c) := by
|
||||
simp [startsWith_eq_startsWith_toSlice, Slice.startsWith_char_eq_head?]
|
||||
simp [← startsWith_toSlice, Slice.startsWith_char_eq_head?]
|
||||
|
||||
theorem startsWith_char_iff_exists_append {c : Char} {s : String} :
|
||||
s.startsWith c ↔ ∃ t, s = singleton c ++ t := by
|
||||
simp [← startsWith_toSlice, Slice.startsWith_char_iff_exists_append]
|
||||
|
||||
theorem startsWith_char_eq_false_iff_forall_append {c : Char} {s : String} :
|
||||
s.startsWith c = false ↔ ∀ t, s ≠ singleton c ++ t := by
|
||||
simp [← Bool.not_eq_true, startsWith_char_iff_exists_append]
|
||||
|
||||
theorem eq_append_of_dropPrefix?_char_eq_some {c : Char} {s : String} {res : Slice} (h : s.dropPrefix? c = some res) :
|
||||
s = singleton c ++ res.copy := by
|
||||
rw [dropPrefix?_eq_dropPrefix?_toSlice] at h
|
||||
simpa using Slice.eq_append_of_dropPrefix?_char_eq_some h
|
||||
|
||||
theorem skipSuffix?_char_eq_some_iff {c : Char} {s : String} {pos : s.Pos} :
|
||||
s.skipSuffix? c = some pos ↔ ∃ h, pos = s.endPos.prev h ∧ (s.endPos.prev h).get (by simp) = c := by
|
||||
simp [skipSuffix?_eq_skipSuffix?_toSlice, Slice.skipSuffix?_char_eq_some_iff, ← Pos.toSlice_inj,
|
||||
Pos.prev_toSlice]
|
||||
|
||||
theorem endsWith_char_iff_get {c : Char} {s : String} :
|
||||
s.endsWith c ↔ ∃ h, (s.endPos.prev h).get (by simp) = c := by
|
||||
simp [← endsWith_toSlice, Slice.endsWith_char_iff_get, Pos.prev_toSlice]
|
||||
|
||||
theorem endsWith_char_eq_false_iff_get {c : Char} {s : String} :
|
||||
s.endsWith c = false ↔ ∀ h, (s.endPos.prev h).get (by simp) ≠ c := by
|
||||
simp [← endsWith_toSlice, Slice.endsWith_char_eq_false_iff_get, Pos.prev_toSlice]
|
||||
|
||||
theorem endsWith_char_eq_getLast? {c : Char} {s : String} :
|
||||
s.endsWith c = (s.toList.getLast? == some c) := by
|
||||
simp [← endsWith_toSlice, Slice.endsWith_char_eq_getLast?]
|
||||
|
||||
theorem endsWith_char_iff_exists_append {c : Char} {s : String} :
|
||||
s.endsWith c ↔ ∃ t, s = t ++ singleton c := by
|
||||
simp [← endsWith_toSlice, Slice.endsWith_char_iff_exists_append]
|
||||
|
||||
theorem endsWith_char_eq_false_iff_forall_append {c : Char} {s : String} :
|
||||
s.endsWith c = false ↔ ∀ t, s ≠ t ++ singleton c := by
|
||||
simp [← Bool.not_eq_true, endsWith_char_iff_exists_append]
|
||||
|
||||
theorem eq_append_of_dropSuffix?_char_eq_some {c : Char} {s : String} {res : Slice} (h : s.dropSuffix? c = some res) :
|
||||
s = res.copy ++ singleton c := by
|
||||
rw [dropSuffix?_eq_dropSuffix?_toSlice] at h
|
||||
simpa using Slice.eq_append_of_dropSuffix?_char_eq_some h
|
||||
|
||||
end String
|
||||
|
||||
@@ -8,10 +8,16 @@ module
|
||||
prelude
|
||||
public import Init.Data.String.Slice
|
||||
public import Init.Data.String.TakeDrop
|
||||
public import Init.Data.String.Lemmas.Order
|
||||
import Init.Data.String.Lemmas.Pattern.TakeDrop.Basic
|
||||
import Init.Data.String.Lemmas.Pattern.Pred
|
||||
import Init.Data.Option.Lemmas
|
||||
import Init.Data.String.Lemmas.FindPos
|
||||
import Init.Data.String.Lemmas.Intercalate
|
||||
import Init.ByCases
|
||||
import Init.Data.Order.Lemmas
|
||||
import Init.Data.String.OrderInstances
|
||||
import Init.Data.String.Lemmas.Basic
|
||||
|
||||
public section
|
||||
|
||||
@@ -43,6 +49,85 @@ theorem startsWith_bool_eq_head? {p : Char → Bool} {s : Slice} :
|
||||
· obtain ⟨t, ht⟩ := s.splits_startPos.exists_eq_singleton_append h
|
||||
simp [h, ht]
|
||||
|
||||
theorem eq_append_of_dropPrefix?_bool_eq_some {p : Char → Bool} {s res : Slice} (h : s.dropPrefix? p = some res) :
|
||||
∃ c, s.copy = singleton c ++ res.copy ∧ p c = true := by
|
||||
obtain ⟨_, ⟨c, ⟨rfl, h₁⟩⟩, h₂⟩ := by simpa [PatternModel.Matches] using Pattern.Model.eq_append_of_dropPrefix?_eq_some h
|
||||
exact ⟨_, h₂, h₁⟩
|
||||
|
||||
@[simp]
|
||||
theorem Pos.skip?_bool_eq_some_iff {p : Char → Bool} {s : Slice} {pos res : s.Pos} :
|
||||
pos.skip? p = some res ↔ ∃ h, res = pos.next h ∧ p (pos.get h) := by
|
||||
simp [Pattern.Model.Pos.skip?_eq_some_iff, CharPred.isLongestMatchAt_iff]
|
||||
|
||||
@[simp]
|
||||
theorem Pos.skip?_bool_eq_none_iff {p : Char → Bool} {s : Slice} {pos : s.Pos} :
|
||||
pos.skip? p = none ↔ ∀ h, p (pos.get h) = false := by
|
||||
simp [Pattern.Model.Pos.skip?_eq_none_iff, CharPred.matchesAt_iff]
|
||||
|
||||
theorem Pos.apply_skipWhile_bool_eq_false {p : Char → Bool} {s : Slice} {pos : s.Pos} {h} :
|
||||
p ((pos.skipWhile p).get h) = false := by
|
||||
have := Pattern.Model.Pos.not_matchesAt_skipWhile p pos
|
||||
simp_all [CharPred.matchesAt_iff]
|
||||
|
||||
theorem Pos.skipWhile_bool_eq_self_iff_get {p : Char → Bool} {s : Slice} {pos : s.Pos} :
|
||||
pos.skipWhile p = pos ↔ ∀ h, p (pos.get h) = false := by
|
||||
simp [Pattern.Model.Pos.skipWhile_eq_self_iff, CharPred.matchesAt_iff]
|
||||
|
||||
theorem Pos.apply_eq_true_of_lt_skipWhile_bool {p : Char → Bool} {s : Slice} {pos pos' : s.Pos}
|
||||
(h₁ : pos ≤ pos') (h₂ : pos' < pos.skipWhile p) : p (pos'.get (ne_endPos_of_lt h₂)) = true :=
|
||||
(CharPred.isLongestMatchAtChain_iff.1 (Pattern.Model.Pos.isLongestMatchAtChain_skipWhile p pos)).2 _ h₁ h₂
|
||||
|
||||
theorem apply_skipPrefixWhile_bool_eq_false {p : Char → Bool} {s : Slice} {h} :
|
||||
p ((s.skipPrefixWhile p).get h) = false := by
|
||||
simp [skipPrefixWhile_eq_skipWhile_startPos, Pos.apply_skipWhile_bool_eq_false]
|
||||
|
||||
theorem apply_eq_true_of_lt_skipPrefixWhile_bool {p : Char → Bool} {s : Slice} {pos : s.Pos} (h : pos < s.skipPrefixWhile p) :
|
||||
p (pos.get (Pos.ne_endPos_of_lt h)) = true :=
|
||||
Pos.apply_eq_true_of_lt_skipWhile_bool (Pos.startPos_le _) (skipPrefixWhile_eq_skipWhile_startPos ▸ h)
|
||||
|
||||
@[simp]
|
||||
theorem all_bool_eq {p : Char → Bool} {s : Slice} : s.all p = s.copy.toList.all p := by
|
||||
rw [Bool.eq_iff_iff, Pattern.Model.all_eq_true_iff,
|
||||
CharPred.isLongestMatchAtChain_startPos_endPos_iff_toList, List.all_eq_true]
|
||||
|
||||
@[simp]
|
||||
theorem Pos.skip?_prop_eq_some_iff {P : Char → Prop} [DecidablePred P] {s : Slice} {pos res : s.Pos} :
|
||||
pos.skip? P = some res ↔ ∃ h, res = pos.next h ∧ P (pos.get h) := by
|
||||
simp [Pos.skip?_prop_eq_skip?_decide, skip?_bool_eq_some_iff]
|
||||
|
||||
@[simp]
|
||||
theorem Pos.skip?_prop_eq_none_iff {P : Char → Prop} [DecidablePred P] {s : Slice} {pos : s.Pos} :
|
||||
pos.skip? P = none ↔ ∀ h, ¬ P (pos.get h) := by
|
||||
simp [Pos.skip?_prop_eq_skip?_decide, skip?_bool_eq_none_iff]
|
||||
|
||||
theorem Pos.apply_skipWhile_prop {P : Char → Prop} [DecidablePred P] {s : Slice} {pos : s.Pos} {h} :
|
||||
¬ P ((pos.skipWhile P).get h) := by
|
||||
have := Pattern.Model.Pos.not_matchesAt_skipWhile P pos
|
||||
simp_all [CharPred.Decidable.matchesAt_iff]
|
||||
|
||||
theorem Pos.skipWhile_prop_eq_self_iff_get {P : Char → Prop} [DecidablePred P] {s : Slice} {pos : s.Pos} :
|
||||
pos.skipWhile P = pos ↔ ∀ h, ¬ P (pos.get h) := by
|
||||
simp [Pos.skipWhile_prop_eq_skipWhile_decide, skipWhile_bool_eq_self_iff_get]
|
||||
|
||||
theorem Pos.apply_of_lt_skipWhile_prop {P : Char → Prop} [DecidablePred P] {s : Slice} {pos pos' : s.Pos}
|
||||
(h₁ : pos ≤ pos') (h₂ : pos' < pos.skipWhile P) : P (pos'.get (ne_endPos_of_lt h₂)) := by
|
||||
simp [Pos.skipWhile_prop_eq_skipWhile_decide] at h₂
|
||||
simpa using apply_eq_true_of_lt_skipWhile_bool h₁ h₂
|
||||
|
||||
theorem apply_skipPrefixWhile_prop {P : Char → Prop} [DecidablePred P] {s : Slice} {h} :
|
||||
¬ P ((s.skipPrefixWhile P).get h) := by
|
||||
simp [skipPrefixWhile_eq_skipWhile_startPos, Pos.apply_skipWhile_prop]
|
||||
|
||||
theorem apply_of_lt_skipPrefixWhile_prop {P : Char → Prop} [DecidablePred P] {s : Slice} {pos : s.Pos}
|
||||
(h : pos < s.skipPrefixWhile P) : P (pos.get (Pos.ne_endPos_of_lt h)) := by
|
||||
simp [skipPrefixWhile_prop_eq_skipPrefixWhile_decide] at h
|
||||
simpa using apply_eq_true_of_lt_skipPrefixWhile_bool h
|
||||
|
||||
@[simp]
|
||||
theorem all_prop_eq {P : Char → Prop} [DecidablePred P] {s : Slice} :
|
||||
s.all P = s.copy.toList.all (decide <| P ·) := by
|
||||
simp [all_prop_eq_all_decide]
|
||||
|
||||
theorem skipPrefix?_prop_eq_some_iff {P : Char → Prop} [DecidablePred P] {s : Slice} {pos : s.Pos} :
|
||||
s.skipPrefix? P = some pos ↔ ∃ h, pos = s.startPos.next h ∧ P (s.startPos.get h) := by
|
||||
simp [skipPrefix?_prop_eq_skipPrefix?_decide, skipPrefix?_bool_eq_some_iff]
|
||||
@@ -59,6 +144,136 @@ theorem startsWith_prop_eq_head? {P : Char → Prop} [DecidablePred P] {s : Slic
|
||||
s.startsWith P = s.copy.toList.head?.any (decide <| P ·) := by
|
||||
simp [startsWith_prop_eq_startsWith_decide, startsWith_bool_eq_head?]
|
||||
|
||||
theorem eq_append_of_dropPrefix?_prop_eq_some {P : Char → Prop} [DecidablePred P] {s res : Slice} (h : s.dropPrefix? P = some res) :
|
||||
∃ c, s.copy = singleton c ++ res.copy ∧ P c := by
|
||||
rw [dropPrefix?_prop_eq_dropPrefix?_decide] at h
|
||||
simpa using eq_append_of_dropPrefix?_bool_eq_some h
|
||||
|
||||
theorem skipSuffix?_bool_eq_some_iff {p : Char → Bool} {s : Slice} {pos : s.Pos} :
|
||||
s.skipSuffix? p = some pos ↔ ∃ h, pos = s.endPos.prev h ∧ p ((s.endPos.prev h).get (by simp)) = true := by
|
||||
rw [Pattern.Model.skipSuffix?_eq_some_iff, CharPred.isLongestRevMatch_iff]
|
||||
|
||||
theorem endsWith_bool_iff_get {p : Char → Bool} {s : Slice} :
|
||||
s.endsWith p ↔ ∃ h, p ((s.endPos.prev h).get (by simp)) = true := by
|
||||
simp [Pattern.Model.endsWith_iff, CharPred.revMatchesAt_iff]
|
||||
|
||||
theorem endsWith_bool_eq_false_iff_get {p : Char → Bool} {s : Slice} :
|
||||
s.endsWith p = false ↔ ∀ h, p ((s.endPos.prev h).get (by simp)) = false := by
|
||||
simp [Pattern.Model.endsWith_eq_false_iff, CharPred.revMatchesAt_iff]
|
||||
|
||||
theorem endsWith_bool_eq_getLast? {p : Char → Bool} {s : Slice} :
|
||||
s.endsWith p = s.copy.toList.getLast?.any p := by
|
||||
rw [Bool.eq_iff_iff, Pattern.Model.endsWith_iff, CharPred.revMatchesAt_iff]
|
||||
by_cases h : s.endPos = s.startPos
|
||||
· refine ⟨fun ⟨h', _⟩ => by simp_all, ?_⟩
|
||||
have : s.copy = "" := by simp_all [Slice.startPos_eq_endPos_iff.mp h.symm]
|
||||
simp [this]
|
||||
· obtain ⟨t, ht⟩ := s.splits_endPos.exists_eq_append_singleton_of_ne_startPos h
|
||||
simp [h, ht]
|
||||
|
||||
theorem eq_append_of_dropSuffix?_bool_eq_some {p : Char → Bool} {s res : Slice} (h : s.dropSuffix? p = some res) :
|
||||
∃ c, s.copy = res.copy ++ singleton c ∧ p c = true := by
|
||||
obtain ⟨_, ⟨c, ⟨rfl, h₁⟩⟩, h₂⟩ := by simpa [PatternModel.Matches] using Pattern.Model.eq_append_of_dropSuffix?_eq_some h
|
||||
exact ⟨_, h₂, h₁⟩
|
||||
|
||||
theorem skipSuffix?_prop_eq_some_iff {P : Char → Prop} [DecidablePred P] {s : Slice} {pos : s.Pos} :
|
||||
s.skipSuffix? P = some pos ↔ ∃ h, pos = s.endPos.prev h ∧ P ((s.endPos.prev h).get (by simp)) := by
|
||||
simp [skipSuffix?_prop_eq_skipSuffix?_decide, skipSuffix?_bool_eq_some_iff]
|
||||
|
||||
theorem endsWith_prop_iff_get {P : Char → Prop} [DecidablePred P] {s : Slice} :
|
||||
s.endsWith P ↔ ∃ h, P ((s.endPos.prev h).get (by simp)) := by
|
||||
simp [endsWith_prop_eq_endsWith_decide, endsWith_bool_iff_get]
|
||||
|
||||
theorem endsWith_prop_eq_false_iff_get {P : Char → Prop} [DecidablePred P] {s : Slice} :
|
||||
s.endsWith P = false ↔ ∀ h, ¬ P ((s.endPos.prev h).get (by simp)) := by
|
||||
simp [endsWith_prop_eq_endsWith_decide, endsWith_bool_eq_false_iff_get]
|
||||
|
||||
theorem endsWith_prop_eq_getLast? {P : Char → Prop} [DecidablePred P] {s : Slice} :
|
||||
s.endsWith P = s.copy.toList.getLast?.any (decide <| P ·) := by
|
||||
simp [endsWith_prop_eq_endsWith_decide, endsWith_bool_eq_getLast?]
|
||||
|
||||
theorem eq_append_of_dropSuffix?_prop_eq_some {P : Char → Prop} [DecidablePred P] {s res : Slice} (h : s.dropSuffix? P = some res) :
|
||||
∃ c, s.copy = res.copy ++ singleton c ∧ P c := by
|
||||
rw [dropSuffix?_prop_eq_dropSuffix?_decide] at h
|
||||
simpa using eq_append_of_dropSuffix?_bool_eq_some h
|
||||
|
||||
@[simp]
|
||||
theorem Pos.revSkip?_bool_eq_some_iff {p : Char → Bool} {s : Slice} {pos res : s.Pos} :
|
||||
pos.revSkip? p = some res ↔ ∃ h, res = pos.prev h ∧ p ((pos.prev h).get (by simp)) := by
|
||||
simp [Pattern.Model.Pos.revSkip?_eq_some_iff, CharPred.isLongestRevMatchAt_iff]
|
||||
|
||||
@[simp]
|
||||
theorem Pos.revSkip?_bool_eq_none_iff {p : Char → Bool} {s : Slice} {pos : s.Pos} :
|
||||
pos.revSkip? p = none ↔ ∀ h, p ((pos.prev h).get (by simp)) = false := by
|
||||
simp [Pattern.Model.Pos.revSkip?_eq_none_iff, CharPred.revMatchesAt_iff]
|
||||
|
||||
theorem Pos.apply_revSkipWhile_bool_eq_false {p : Char → Bool} {s : Slice} {pos : s.Pos} {h} :
|
||||
p (((pos.revSkipWhile p).prev h).get (by simp)) = false := by
|
||||
have := Pattern.Model.Pos.not_revMatchesAt_revSkipWhile p pos
|
||||
simp_all [CharPred.revMatchesAt_iff]
|
||||
|
||||
theorem Pos.revSkipWhile_bool_eq_self_iff_get {p : Char → Bool} {s : Slice} {pos : s.Pos} :
|
||||
pos.revSkipWhile p = pos ↔ ∀ h, p ((pos.prev h).get (by simp)) = false := by
|
||||
simp [Pattern.Model.Pos.revSkipWhile_eq_self_iff, CharPred.revMatchesAt_iff]
|
||||
|
||||
theorem Pos.apply_eq_true_of_revSkipWhile_le_bool {p : Char → Bool} {s : Slice} {pos pos' : s.Pos}
|
||||
(h₁ : pos' < pos) (h₂ : pos.revSkipWhile p ≤ pos') : p (pos'.get (Pos.ne_endPos_of_lt h₁)) = true :=
|
||||
(CharPred.isLongestRevMatchAtChain_iff.1 (Pattern.Model.Pos.isLongestRevMatchAtChain_revSkipWhile p pos)).2 _ h₂ h₁
|
||||
|
||||
theorem apply_skipSuffixWhile_bool_eq_false {p : Char → Bool} {s : Slice} {h} :
|
||||
p (((s.skipSuffixWhile p).prev h).get (by simp)) = false := by
|
||||
simp [skipSuffixWhile_eq_revSkipWhile_endPos, Pos.apply_revSkipWhile_bool_eq_false]
|
||||
|
||||
theorem apply_eq_true_of_skipSuffixWhile_le_bool {p : Char → Bool} {s : Slice} {pos : s.Pos}
|
||||
(h : s.skipSuffixWhile p ≤ pos) (h' : pos < s.endPos) :
|
||||
p (pos.get (Pos.ne_endPos_of_lt h')) = true :=
|
||||
Pos.apply_eq_true_of_revSkipWhile_le_bool h' (skipSuffixWhile_eq_revSkipWhile_endPos ▸ h)
|
||||
|
||||
@[simp]
|
||||
theorem revAll_bool_eq {p : Char → Bool} {s : Slice} : s.revAll p = s.copy.toList.all p := by
|
||||
rw [Bool.eq_iff_iff, Pattern.Model.revAll_eq_true_iff,
|
||||
CharPred.isLongestRevMatchAtChain_startPos_endPos_iff_toList, List.all_eq_true]
|
||||
|
||||
@[simp]
|
||||
theorem Pos.revSkip?_prop_eq_some_iff {P : Char → Prop} [DecidablePred P] {s : Slice} {pos res : s.Pos} :
|
||||
pos.revSkip? P = some res ↔ ∃ h, res = pos.prev h ∧ P ((pos.prev h).get (by simp)) := by
|
||||
simp [Pos.revSkip?_prop_eq_revSkip?_decide, revSkip?_bool_eq_some_iff]
|
||||
|
||||
@[simp]
|
||||
theorem Pos.revSkip?_prop_eq_none_iff {P : Char → Prop} [DecidablePred P] {s : Slice} {pos : s.Pos} :
|
||||
pos.revSkip? P = none ↔ ∀ h, ¬ P ((pos.prev h).get (by simp)) := by
|
||||
simp [Pos.revSkip?_prop_eq_revSkip?_decide, revSkip?_bool_eq_none_iff]
|
||||
|
||||
theorem Pos.apply_revSkipWhile_prop {P : Char → Prop} [DecidablePred P] {s : Slice} {pos : s.Pos} {h} :
|
||||
¬ P (((pos.revSkipWhile P).prev h).get (by simp)) := by
|
||||
have := Pattern.Model.Pos.not_revMatchesAt_revSkipWhile P pos
|
||||
simp_all [CharPred.Decidable.revMatchesAt_iff]
|
||||
|
||||
theorem Pos.revSkipWhile_prop_eq_self_iff_get {P : Char → Prop} [DecidablePred P] {s : Slice} {pos : s.Pos} :
|
||||
pos.revSkipWhile P = pos ↔ ∀ h, ¬ P ((pos.prev h).get (by simp)) := by
|
||||
simp [Pos.revSkipWhile_prop_eq_revSkipWhile_decide, revSkipWhile_bool_eq_self_iff_get]
|
||||
|
||||
theorem Pos.apply_of_revSkipWhile_le_prop {P : Char → Prop} [DecidablePred P] {s : Slice} {pos pos' : s.Pos}
|
||||
(h₁ : pos' < pos) (h₂ : pos.revSkipWhile P ≤ pos') : P (pos'.get (Pos.ne_endPos_of_lt h₁)) := by
|
||||
have h₂' : pos.revSkipWhile (decide <| P ·) ≤ pos' :=
|
||||
Pos.revSkipWhile_prop_eq_revSkipWhile_decide (p := P) pos ▸ h₂
|
||||
simpa using Pos.apply_eq_true_of_revSkipWhile_le_bool h₁ h₂'
|
||||
|
||||
theorem apply_skipSuffixWhile_prop {P : Char → Prop} [DecidablePred P] {s : Slice} {h} :
|
||||
¬ P (((s.skipSuffixWhile P).prev h).get (by simp)) := by
|
||||
have := Pattern.Model.Pos.not_revMatchesAt_revSkipWhile P s.endPos
|
||||
simp_all [CharPred.Decidable.revMatchesAt_iff, skipSuffixWhile_eq_revSkipWhile_endPos]
|
||||
|
||||
theorem apply_of_skipSuffixWhile_le_prop {P : Char → Prop} [DecidablePred P] {s : Slice} {pos : s.Pos}
|
||||
(h : s.skipSuffixWhile P ≤ pos) (h' : pos < s.endPos) :
|
||||
P (pos.get (Pos.ne_endPos_of_lt h')) :=
|
||||
Pos.apply_of_revSkipWhile_le_prop h' (skipSuffixWhile_eq_revSkipWhile_endPos (pat := P) ▸ h)
|
||||
|
||||
@[simp]
|
||||
theorem revAll_prop_eq {P : Char → Prop} [DecidablePred P] {s : Slice} :
|
||||
s.revAll P = s.copy.toList.all (decide <| P ·) := by
|
||||
simp [revAll_prop_eq_revAll_decide, revAll_bool_eq]
|
||||
|
||||
end Slice
|
||||
|
||||
theorem skipPrefix?_bool_eq_some_iff {p : Char → Bool} {s : String} {pos : s.Pos} :
|
||||
@@ -68,15 +283,57 @@ theorem skipPrefix?_bool_eq_some_iff {p : Char → Bool} {s : String} {pos : s.P
|
||||
|
||||
theorem startsWith_bool_iff_get {p : Char → Bool} {s : String} :
|
||||
s.startsWith p ↔ ∃ h, p (s.startPos.get h) = true := by
|
||||
simp [startsWith_eq_startsWith_toSlice, Slice.startsWith_bool_iff_get]
|
||||
simp [← startsWith_toSlice, Slice.startsWith_bool_iff_get]
|
||||
|
||||
theorem startsWith_bool_eq_false_iff_get {p : Char → Bool} {s : String} :
|
||||
s.startsWith p = false ↔ ∀ h, p (s.startPos.get h) = false := by
|
||||
simp [startsWith_eq_startsWith_toSlice, Slice.startsWith_bool_eq_false_iff_get]
|
||||
simp [← startsWith_toSlice, Slice.startsWith_bool_eq_false_iff_get]
|
||||
|
||||
theorem startsWith_bool_eq_head? {p : Char → Bool} {s : String} :
|
||||
s.startsWith p = s.toList.head?.any p := by
|
||||
simp [startsWith_eq_startsWith_toSlice, Slice.startsWith_bool_eq_head?]
|
||||
simp [← startsWith_toSlice, Slice.startsWith_bool_eq_head?]
|
||||
|
||||
theorem eq_append_of_dropPrefix?_bool_eq_some {p : Char → Bool} {s : String} {res : Slice} (h : s.dropPrefix? p = some res) :
|
||||
∃ c, s = singleton c ++ res.copy ∧ p c = true := by
|
||||
rw [dropPrefix?_eq_dropPrefix?_toSlice] at h
|
||||
simpa using Slice.eq_append_of_dropPrefix?_bool_eq_some h
|
||||
|
||||
@[simp]
|
||||
theorem Pos.skip?_bool_eq_some_iff {p : Char → Bool} {s : String} {pos res : s.Pos} :
|
||||
pos.skip? p = some res ↔ ∃ h, res = pos.next h ∧ p (pos.get h) := by
|
||||
simp [skip?_eq_skip?_toSlice, ← toSlice_inj, toSlice_next]
|
||||
|
||||
@[simp]
|
||||
theorem Pos.skip?_bool_eq_none_iff {p : Char → Bool} {s : String} {pos : s.Pos} :
|
||||
pos.skip? p = none ↔ ∀ h, p (pos.get h) = false := by
|
||||
simp [skip?_eq_skip?_toSlice]
|
||||
|
||||
theorem Pos.apply_skipWhile_bool_eq_false {p : Char → Bool} {s : String} {pos : s.Pos} {h} :
|
||||
p ((pos.skipWhile p).get h) = false := by
|
||||
simp [skipWhile_eq_skipWhile_toSlice, Slice.Pos.apply_skipWhile_bool_eq_false]
|
||||
|
||||
theorem Pos.skipWhile_bool_eq_self_iff_get {p : Char → Bool} {s : String} {pos : s.Pos} :
|
||||
pos.skipWhile p = pos ↔ ∀ h, p (pos.get h) = false := by
|
||||
simp [skipWhile_eq_skipWhile_toSlice, ← toSlice_inj, Slice.Pos.skipWhile_bool_eq_self_iff_get]
|
||||
|
||||
theorem Pos.apply_eq_true_of_lt_skipWhile_bool {p : Char → Bool} {s : String} {pos pos' : s.Pos}
|
||||
(h₁ : pos ≤ pos') (h₂ : pos' < pos.skipWhile p) : p (pos'.get (ne_endPos_of_lt h₂)) = true := by
|
||||
rw [Pos.get_eq_get_toSlice]
|
||||
exact Slice.Pos.apply_eq_true_of_lt_skipWhile_bool (toSlice_le_toSlice_iff.2 h₁)
|
||||
(by simpa [skipWhile_eq_skipWhile_toSlice] using h₂)
|
||||
|
||||
theorem apply_skipPrefixWhile_bool_eq_false {p : Char → Bool} {s : String} {h} :
|
||||
p ((s.skipPrefixWhile p).get h) = false := by
|
||||
simp [skipPrefixWhile_eq_skipPrefixWhile_toSlice, Slice.apply_skipPrefixWhile_bool_eq_false]
|
||||
|
||||
theorem apply_eq_true_of_lt_skipPrefixWhile_bool {p : Char → Bool} {s : String} {pos : s.Pos} (h : pos < s.skipPrefixWhile p) :
|
||||
p (pos.get (Pos.ne_endPos_of_lt h)) = true := by
|
||||
rw [Pos.get_eq_get_toSlice]
|
||||
exact Slice.apply_eq_true_of_lt_skipPrefixWhile_bool (by simpa [skipPrefixWhile_eq_skipPrefixWhile_toSlice] using h)
|
||||
|
||||
@[simp]
|
||||
theorem all_bool_eq {p : Char → Bool} {s : String} : s.all p = s.toList.all p := by
|
||||
simp [← all_toSlice]
|
||||
|
||||
theorem skipPrefix?_prop_eq_some_iff {P : Char → Prop} [DecidablePred P] {s : String} {pos : s.Pos} :
|
||||
s.skipPrefix? P = some pos ↔ ∃ h, pos = s.startPos.next h ∧ P (s.startPos.get h) := by
|
||||
@@ -85,14 +342,198 @@ theorem skipPrefix?_prop_eq_some_iff {P : Char → Prop} [DecidablePred P] {s :
|
||||
|
||||
theorem startsWith_prop_iff_get {P : Char → Prop} [DecidablePred P] {s : String} :
|
||||
s.startsWith P ↔ ∃ h, P (s.startPos.get h) := by
|
||||
simp [startsWith_eq_startsWith_toSlice, Slice.startsWith_prop_iff_get]
|
||||
simp [← startsWith_toSlice, Slice.startsWith_prop_iff_get]
|
||||
|
||||
theorem startsWith_prop_eq_false_iff_get {P : Char → Prop} [DecidablePred P] {s : String} :
|
||||
s.startsWith P = false ↔ ∀ h, ¬ P (s.startPos.get h) := by
|
||||
simp [startsWith_eq_startsWith_toSlice, Slice.startsWith_prop_eq_false_iff_get]
|
||||
simp [← startsWith_toSlice, Slice.startsWith_prop_eq_false_iff_get]
|
||||
|
||||
theorem startsWith_prop_eq_head? {P : Char → Prop} [DecidablePred P] {s : String} :
|
||||
s.startsWith P = s.toList.head?.any (decide <| P ·) := by
|
||||
simp [startsWith_eq_startsWith_toSlice, Slice.startsWith_prop_eq_head?]
|
||||
simp [← startsWith_toSlice, Slice.startsWith_prop_eq_head?]
|
||||
|
||||
theorem eq_append_of_dropPrefix?_prop_eq_some {P : Char → Prop} [DecidablePred P] {s : String} {res : Slice}
|
||||
(h : s.dropPrefix? P = some res) : ∃ c, s = singleton c ++ res.copy ∧ P c := by
|
||||
rw [dropPrefix?_eq_dropPrefix?_toSlice] at h
|
||||
simpa using Slice.eq_append_of_dropPrefix?_prop_eq_some h
|
||||
|
||||
theorem skipSuffix?_bool_eq_some_iff {p : Char → Bool} {s : String} {pos : s.Pos} :
|
||||
s.skipSuffix? p = some pos ↔ ∃ h, pos = s.endPos.prev h ∧ p ((s.endPos.prev h).get (by simp)) = true := by
|
||||
simp [skipSuffix?_eq_skipSuffix?_toSlice, Slice.skipSuffix?_bool_eq_some_iff, ← Pos.toSlice_inj,
|
||||
Pos.prev_toSlice]
|
||||
|
||||
theorem endsWith_bool_iff_get {p : Char → Bool} {s : String} :
|
||||
s.endsWith p ↔ ∃ h, p ((s.endPos.prev h).get (by simp)) = true := by
|
||||
simp [← endsWith_toSlice, Slice.endsWith_bool_iff_get, Pos.prev_toSlice]
|
||||
|
||||
theorem endsWith_bool_eq_false_iff_get {p : Char → Bool} {s : String} :
|
||||
s.endsWith p = false ↔ ∀ h, p ((s.endPos.prev h).get (by simp)) = false := by
|
||||
simp [← endsWith_toSlice, Slice.endsWith_bool_eq_false_iff_get, Pos.prev_toSlice]
|
||||
|
||||
theorem endsWith_bool_eq_getLast? {p : Char → Bool} {s : String} :
|
||||
s.endsWith p = s.toList.getLast?.any p := by
|
||||
simp [← endsWith_toSlice, Slice.endsWith_bool_eq_getLast?]
|
||||
|
||||
theorem eq_append_of_dropSuffix?_bool_eq_some {p : Char → Bool} {s : String} {res : Slice} (h : s.dropSuffix? p = some res) :
|
||||
∃ c, s = res.copy ++ singleton c ∧ p c = true := by
|
||||
rw [dropSuffix?_eq_dropSuffix?_toSlice] at h
|
||||
simpa using Slice.eq_append_of_dropSuffix?_bool_eq_some h
|
||||
|
||||
theorem skipSuffix?_prop_eq_some_iff {P : Char → Prop} [DecidablePred P] {s : String} {pos : s.Pos} :
|
||||
s.skipSuffix? P = some pos ↔ ∃ h, pos = s.endPos.prev h ∧ P ((s.endPos.prev h).get (by simp)) := by
|
||||
simp [skipSuffix?_eq_skipSuffix?_toSlice, Slice.skipSuffix?_prop_eq_some_iff, ← Pos.toSlice_inj,
|
||||
Pos.prev_toSlice]
|
||||
|
||||
theorem endsWith_prop_iff_get {P : Char → Prop} [DecidablePred P] {s : String} :
|
||||
s.endsWith P ↔ ∃ h, P ((s.endPos.prev h).get (by simp)) := by
|
||||
simp [← endsWith_toSlice, Slice.endsWith_prop_iff_get, Pos.prev_toSlice]
|
||||
|
||||
theorem endsWith_prop_eq_false_iff_get {P : Char → Prop} [DecidablePred P] {s : String} :
|
||||
s.endsWith P = false ↔ ∀ h, ¬ P ((s.endPos.prev h).get (by simp)) := by
|
||||
simp [← endsWith_toSlice, Slice.endsWith_prop_eq_false_iff_get, Pos.prev_toSlice]
|
||||
|
||||
theorem endsWith_prop_eq_getLast? {P : Char → Prop} [DecidablePred P] {s : String} :
|
||||
s.endsWith P = s.toList.getLast?.any (decide <| P ·) := by
|
||||
simp [← endsWith_toSlice, Slice.endsWith_prop_eq_getLast?]
|
||||
|
||||
theorem eq_append_of_dropSuffix?_prop_eq_some {P : Char → Prop} [DecidablePred P] {s : String} {res : Slice}
|
||||
(h : s.dropSuffix? P = some res) : ∃ c, s = res.copy ++ singleton c ∧ P c := by
|
||||
rw [dropSuffix?_eq_dropSuffix?_toSlice] at h
|
||||
simpa using Slice.eq_append_of_dropSuffix?_prop_eq_some h
|
||||
|
||||
@[simp]
|
||||
theorem Pos.skip?_prop_eq_some_iff {P : Char → Prop} [DecidablePred P] {s : String} {pos res : s.Pos} :
|
||||
pos.skip? P = some res ↔ ∃ h, res = pos.next h ∧ P (pos.get h) := by
|
||||
simp [skip?_eq_skip?_toSlice, ← toSlice_inj, toSlice_next]
|
||||
|
||||
@[simp]
|
||||
theorem Pos.skip?_prop_eq_none_iff {P : Char → Prop} [DecidablePred P] {s : String} {pos : s.Pos} :
|
||||
pos.skip? P = none ↔ ∀ h, ¬ P (pos.get h) := by
|
||||
simp [skip?_eq_skip?_toSlice]
|
||||
|
||||
theorem Pos.apply_skipWhile_prop {P : Char → Prop} [DecidablePred P] {s : String} {pos : s.Pos} {h} :
|
||||
¬ P ((pos.skipWhile P).get h) := by
|
||||
simp [skipWhile_eq_skipWhile_toSlice, Slice.Pos.apply_skipWhile_prop]
|
||||
|
||||
theorem Pos.skipWhile_prop_eq_self_iff_get {P : Char → Prop} [DecidablePred P] {s : String} {pos : s.Pos} :
|
||||
pos.skipWhile P = pos ↔ ∀ h, ¬ P (pos.get h) := by
|
||||
simp [skipWhile_eq_skipWhile_toSlice, ← toSlice_inj, Slice.Pos.skipWhile_prop_eq_self_iff_get]
|
||||
|
||||
theorem Pos.apply_of_lt_skipWhile_prop {P : Char → Prop} [DecidablePred P] {s : String} {pos pos' : s.Pos}
|
||||
(h₁ : pos ≤ pos') (h₂ : pos' < pos.skipWhile P) : P (pos'.get (ne_endPos_of_lt h₂)) := by
|
||||
rw [Pos.get_eq_get_toSlice]
|
||||
exact Slice.Pos.apply_of_lt_skipWhile_prop (toSlice_le_toSlice_iff.2 h₁)
|
||||
(by simpa [skipWhile_eq_skipWhile_toSlice] using h₂)
|
||||
|
||||
theorem apply_skipPrefixWhile_prop {P : Char → Prop} [DecidablePred P] {s : String} {h} :
|
||||
¬ P ((s.skipPrefixWhile P).get h) := by
|
||||
simp [skipPrefixWhile_eq_skipPrefixWhile_toSlice, Slice.apply_skipPrefixWhile_prop]
|
||||
|
||||
theorem apply_of_lt_skipPrefixWhile_prop {P : Char → Prop} [DecidablePred P] {s : String} {pos : s.Pos}
|
||||
(h : pos < s.skipPrefixWhile P) : P (pos.get (Pos.ne_endPos_of_lt h)) := by
|
||||
rw [Pos.get_eq_get_toSlice]
|
||||
exact Slice.apply_of_lt_skipPrefixWhile_prop (by simpa [skipPrefixWhile_eq_skipPrefixWhile_toSlice] using h)
|
||||
|
||||
@[simp]
|
||||
theorem all_prop_eq {P : Char → Prop} [DecidablePred P] {s : String} :
|
||||
s.all P = s.toList.all (decide <| P ·) := by
|
||||
simp [← all_toSlice]
|
||||
|
||||
@[simp]
|
||||
theorem Pos.revSkip?_bool_eq_some_iff {p : Char → Bool} {s : String} {pos res : s.Pos} :
|
||||
pos.revSkip? p = some res ↔ ∃ h, res = pos.prev h ∧ p ((pos.prev h).get (by simp)) := by
|
||||
simp [revSkip?_eq_revSkip?_toSlice, ← toSlice_inj, toSlice_prev, get_eq_get_toSlice]
|
||||
|
||||
@[simp]
|
||||
theorem Pos.revSkip?_bool_eq_none_iff {p : Char → Bool} {s : String} {pos : s.Pos} :
|
||||
pos.revSkip? p = none ↔ ∀ h, p ((pos.prev h).get (by simp)) = false := by
|
||||
simp [revSkip?_eq_revSkip?_toSlice, Pos.prev_toSlice]
|
||||
|
||||
theorem Pos.apply_revSkipWhile_bool_eq_false {p : Char → Bool} {s : String} {pos : s.Pos} {h} :
|
||||
p (((pos.revSkipWhile p).prev h).get (by simp)) = false := by
|
||||
have h' : pos.toSlice.revSkipWhile p ≠ s.toSlice.startPos := by
|
||||
simpa [Pos.revSkipWhile_eq_revSkipWhile_toSlice, ← toSlice_inj] using h
|
||||
have := Slice.Pos.apply_revSkipWhile_bool_eq_false (pos := pos.toSlice) (h := h')
|
||||
simpa [Pos.revSkipWhile_eq_revSkipWhile_toSlice, Pos.prev_ofToSlice]
|
||||
|
||||
theorem Pos.revSkipWhile_bool_eq_self_iff_get {p : Char → Bool} {s : String} {pos : s.Pos} :
|
||||
pos.revSkipWhile p = pos ↔ ∀ h, p ((pos.prev h).get (by simp)) = false := by
|
||||
simp [Pos.revSkipWhile_eq_revSkipWhile_toSlice, ← toSlice_inj, Slice.Pos.revSkipWhile_bool_eq_self_iff_get,
|
||||
Pos.prev_toSlice]
|
||||
|
||||
theorem Pos.apply_eq_true_of_revSkipWhile_le_bool {p : Char → Bool} {s : String} {pos pos' : s.Pos}
|
||||
(h₁ : pos' < pos) (h₂ : pos.revSkipWhile p ≤ pos') : p (pos'.get (ne_endPos_of_lt h₁)) = true := by
|
||||
rw [Pos.get_eq_get_toSlice]
|
||||
exact Slice.Pos.apply_eq_true_of_revSkipWhile_le_bool
|
||||
(Pos.toSlice_lt_toSlice_iff.2 h₁)
|
||||
(by simpa [Pos.revSkipWhile_eq_revSkipWhile_toSlice, Pos.ofToSlice_le_iff] using h₂)
|
||||
|
||||
theorem apply_skipSuffixWhile_bool_eq_false {p : Char → Bool} {s : String} {h} :
|
||||
p (((s.skipSuffixWhile p).prev h).get (by simp)) = false := by
|
||||
have h' : s.toSlice.skipSuffixWhile p ≠ s.toSlice.startPos := by
|
||||
simpa [skipSuffixWhile_eq_skipSuffixWhile_toSlice, ← Pos.toSlice_inj] using h
|
||||
have := Slice.apply_skipSuffixWhile_bool_eq_false (s := s.toSlice) (h := h')
|
||||
simpa [skipSuffixWhile_eq_skipSuffixWhile_toSlice, Pos.prev_ofToSlice]
|
||||
|
||||
theorem apply_eq_true_of_skipSuffixWhile_le_bool {p : Char → Bool} {s : String} {pos : s.Pos}
|
||||
(h : s.skipSuffixWhile p ≤ pos) (h' : pos < s.endPos) :
|
||||
p (pos.get (Pos.ne_endPos_of_lt h')) = true := by
|
||||
rw [Pos.get_eq_get_toSlice]
|
||||
exact Slice.apply_eq_true_of_skipSuffixWhile_le_bool
|
||||
(by simpa [skipSuffixWhile_eq_skipSuffixWhile_toSlice, Pos.ofToSlice_le_iff] using h)
|
||||
(by simpa [Pos.toSlice_lt_toSlice_iff] using h')
|
||||
|
||||
@[simp]
|
||||
theorem revAll_bool_eq {p : Char → Bool} {s : String} : s.revAll p = s.toList.all p := by
|
||||
simp [← revAll_toSlice]
|
||||
|
||||
@[simp]
|
||||
theorem Pos.revSkip?_prop_eq_some_iff {P : Char → Prop} [DecidablePred P] {s : String} {pos res : s.Pos} :
|
||||
pos.revSkip? P = some res ↔ ∃ h, res = pos.prev h ∧ P ((pos.prev h).get (by simp)) := by
|
||||
simp [revSkip?_eq_revSkip?_toSlice, ← toSlice_inj, toSlice_prev, get_eq_get_toSlice]
|
||||
|
||||
@[simp]
|
||||
theorem Pos.revSkip?_prop_eq_none_iff {P : Char → Prop} [DecidablePred P] {s : String} {pos : s.Pos} :
|
||||
pos.revSkip? P = none ↔ ∀ h, ¬ P ((pos.prev h).get (by simp)) := by
|
||||
simp [revSkip?_eq_revSkip?_toSlice, Pos.prev_toSlice]
|
||||
|
||||
theorem Pos.apply_revSkipWhile_prop {P : Char → Prop} [DecidablePred P] {s : String} {pos : s.Pos} {h} :
|
||||
¬ P (((pos.revSkipWhile P).prev h).get (by simp)) := by
|
||||
have h' : pos.toSlice.revSkipWhile P ≠ s.toSlice.startPos := by
|
||||
simpa [Pos.revSkipWhile_eq_revSkipWhile_toSlice, ← toSlice_inj] using h
|
||||
have := Slice.Pos.apply_revSkipWhile_prop (pos := pos.toSlice) (h := h')
|
||||
simpa [Pos.revSkipWhile_eq_revSkipWhile_toSlice, Pos.prev_ofToSlice]
|
||||
|
||||
theorem Pos.revSkipWhile_prop_eq_self_iff_get {P : Char → Prop} [DecidablePred P] {s : String} {pos : s.Pos} :
|
||||
pos.revSkipWhile P = pos ↔ ∀ h, ¬ P ((pos.prev h).get (by simp)) := by
|
||||
simp [Pos.revSkipWhile_eq_revSkipWhile_toSlice, ← toSlice_inj,
|
||||
Slice.Pos.revSkipWhile_prop_eq_self_iff_get, Pos.prev_toSlice]
|
||||
|
||||
theorem Pos.apply_of_revSkipWhile_le_prop {P : Char → Prop} [DecidablePred P] {s : String} {pos pos' : s.Pos}
|
||||
(h₁ : pos' < pos) (h₂ : pos.revSkipWhile P ≤ pos') : P (pos'.get (ne_endPos_of_lt h₁)) := by
|
||||
rw [Pos.get_eq_get_toSlice]
|
||||
exact Slice.Pos.apply_of_revSkipWhile_le_prop
|
||||
(Pos.toSlice_lt_toSlice_iff.2 h₁)
|
||||
(by simpa [Pos.revSkipWhile_eq_revSkipWhile_toSlice, Pos.ofToSlice_le_iff] using h₂)
|
||||
|
||||
theorem apply_skipSuffixWhile_prop {P : Char → Prop} [DecidablePred P] {s : String} {h} :
|
||||
¬ P (((s.skipSuffixWhile P).prev h).get (by simp)) := by
|
||||
have h' : s.toSlice.skipSuffixWhile P ≠ s.toSlice.startPos := by
|
||||
simpa [skipSuffixWhile_eq_skipSuffixWhile_toSlice, ← Pos.toSlice_inj] using h
|
||||
have := Slice.apply_skipSuffixWhile_prop (s := s.toSlice) (h := h')
|
||||
simpa [skipSuffixWhile_eq_skipSuffixWhile_toSlice, Pos.prev_ofToSlice]
|
||||
|
||||
theorem apply_of_skipSuffixWhile_le_prop {P : Char → Prop} [DecidablePred P] {s : String} {pos : s.Pos}
|
||||
(h : s.skipSuffixWhile P ≤ pos) (h' : pos < s.endPos) :
|
||||
P (pos.get (Pos.ne_endPos_of_lt h')) := by
|
||||
rw [Pos.get_eq_get_toSlice]
|
||||
exact Slice.apply_of_skipSuffixWhile_le_prop
|
||||
(by simpa [skipSuffixWhile_eq_skipSuffixWhile_toSlice, Pos.ofToSlice_le_iff] using h)
|
||||
(by simpa [Pos.toSlice_lt_toSlice_iff] using h')
|
||||
|
||||
@[simp]
|
||||
theorem revAll_prop_eq {P : Char → Prop} [DecidablePred P] {s : String} :
|
||||
s.revAll P = s.toList.all (decide <| P ·) := by
|
||||
simp [← revAll_toSlice]
|
||||
|
||||
end String
|
||||
|
||||
@@ -13,6 +13,7 @@ import Init.Data.String.Lemmas.Pattern.TakeDrop.Basic
|
||||
import Init.Data.String.Lemmas.Pattern.String
|
||||
import Init.Data.List.Sublist
|
||||
import Init.Data.Option.Lemmas
|
||||
import Init.Data.String.Lemmas.Basic
|
||||
|
||||
public section
|
||||
|
||||
@@ -22,53 +23,160 @@ namespace String
|
||||
|
||||
namespace Slice
|
||||
|
||||
theorem skipPrefix?_slice_of_isEmpty {pat s : Slice} (hpat : pat.isEmpty = true) :
|
||||
s.skipPrefix? pat = some s.startPos := by
|
||||
rw [skipPrefix?_eq_forwardPatternSkipPrefix?, ForwardSliceSearcher.skipPrefix?_of_isEmpty hpat]
|
||||
|
||||
@[simp]
|
||||
theorem skipPrefix?_slice_eq_some_iff {pat s : Slice} {pos : s.Pos} :
|
||||
s.skipPrefix? pat = some pos ↔ ∃ t, pos.Splits pat.copy t := by
|
||||
match h : pat.isEmpty with
|
||||
| false =>
|
||||
have := ForwardSliceSearcher.lawfulForwardPatternModel h
|
||||
rw [Pattern.Model.skipPrefix?_eq_some_iff, ForwardSliceSearcher.isLongestMatch_iff_splits h]
|
||||
| true =>
|
||||
rw [skipPrefix?_eq_forwardPatternSkipPrefix?, ForwardSliceSearcher.skipPrefix?_of_isEmpty h]
|
||||
simp [(show pat.copy = "" by simpa), eq_comm]
|
||||
rw [Pattern.Model.skipPrefix?_eq_some_iff, ForwardSliceSearcher.isLongestMatch_iff_splits]
|
||||
|
||||
theorem startsWith_slice_of_isEmpty {pat s : Slice} (hpat : pat.isEmpty = true) :
|
||||
s.startsWith pat = true := by
|
||||
rw [startsWith_eq_forwardPatternStartsWith, ForwardSliceSearcher.startsWith_of_isEmpty hpat]
|
||||
|
||||
@[simp]
|
||||
theorem startsWith_slice_iff {pat s : Slice} :
|
||||
s.startsWith pat ↔ pat.copy.toList <+: s.copy.toList := by
|
||||
match h : pat.isEmpty with
|
||||
| false =>
|
||||
have := ForwardSliceSearcher.lawfulForwardPatternModel h
|
||||
simp only [Model.startsWith_iff, ForwardSliceSearcher.matchesAt_iff_splits h,
|
||||
splits_startPos_iff, exists_and_left, exists_eq_left]
|
||||
simp only [← toList_inj, toList_append, List.prefix_iff_exists_append_eq]
|
||||
exact ⟨fun ⟨t, ht⟩ => ⟨t.toList, by simp [ht]⟩, fun ⟨t, ht⟩ => ⟨String.ofList t, by simp [← ht]⟩⟩
|
||||
| true =>
|
||||
rw [startsWith_eq_forwardPatternStartsWith, ForwardSliceSearcher.startsWith_of_isEmpty h]
|
||||
simp [(show pat.copy = "" by simpa)]
|
||||
simp only [Model.startsWith_iff, ForwardSliceSearcher.matchesAt_iff_splits,
|
||||
splits_startPos_iff, exists_and_left, exists_eq_left]
|
||||
simp only [← toList_inj, toList_append, List.prefix_iff_exists_append_eq]
|
||||
exact ⟨fun ⟨t, ht⟩ => ⟨t.toList, by simp [ht]⟩, fun ⟨t, ht⟩ => ⟨String.ofList t, by simp [← ht]⟩⟩
|
||||
|
||||
@[simp]
|
||||
theorem startsWith_slice_eq_false_iff {pat s : Slice} :
|
||||
s.startsWith pat = false ↔ ¬ (pat.copy.toList <+: s.copy.toList) := by
|
||||
simp [← Bool.not_eq_true, startsWith_slice_iff]
|
||||
|
||||
theorem dropPrefix?_slice_of_isEmpty {pat s : Slice} (hpat : pat.isEmpty = true) :
|
||||
s.dropPrefix? pat = some s := by
|
||||
simp [dropPrefix?_eq_map_skipPrefix?, skipPrefix?_slice_of_isEmpty hpat]
|
||||
|
||||
theorem eq_append_of_dropPrefix?_slice_eq_some {pat s res : Slice} (h : s.dropPrefix? pat = some res) :
|
||||
s.copy = pat.copy ++ res.copy := by
|
||||
have := Pattern.Model.eq_append_of_dropPrefix?_eq_some h
|
||||
simp only [PatternModel.Matches] at this
|
||||
obtain ⟨_, ⟨-, rfl⟩, h⟩ := this
|
||||
exact h
|
||||
|
||||
@[simp]
|
||||
theorem all_slice_iff {pat s : Slice} : s.all pat ↔ ∃ n, s.copy = String.join (List.replicate n pat.copy) := by
|
||||
simp [Pattern.Model.all_eq_true_iff, ForwardSliceSearcher.isLongestMatchAtChain_startPos_endPos_iff]
|
||||
|
||||
@[simp]
|
||||
theorem revAll_slice_iff {pat s : Slice} : s.revAll pat ↔ ∃ n, s.copy = String.join (List.replicate n pat.copy) := by
|
||||
simp [Pattern.Model.revAll_eq_true_iff, ForwardSliceSearcher.isLongestRevMatchAtChain_startPos_endPos_iff]
|
||||
|
||||
@[simp]
|
||||
theorem skipPrefix?_string_eq_some_iff {pat : String} {s : Slice} {pos : s.Pos} :
|
||||
s.skipPrefix? pat = some pos ↔ ∃ t, pos.Splits pat t := by
|
||||
simp [skipPrefix?_string_eq_skipPrefix?_toSlice]
|
||||
|
||||
@[simp]
|
||||
theorem skipPrefix?_string_empty {s : Slice} : s.skipPrefix? "" = some s.startPos := by
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem startsWith_string_iff {pat : String} {s : Slice} :
|
||||
s.startsWith pat ↔ pat.toList <+: s.copy.toList := by
|
||||
simp [startsWith_string_eq_startsWith_toSlice]
|
||||
|
||||
@[simp]
|
||||
theorem startsWith_string_empty {s : Slice} : s.startsWith "" = true := by
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem startsWith_string_eq_false_iff {pat : String} {s : Slice} :
|
||||
s.startsWith pat = false ↔ ¬ (pat.toList <+: s.copy.toList) := by
|
||||
simp [startsWith_string_eq_startsWith_toSlice]
|
||||
|
||||
@[simp]
|
||||
theorem dropPrefix?_string_empty {s : Slice} : s.dropPrefix? "" = some s := by
|
||||
simpa [dropPrefix?_string_eq_dropPrefix?_toSlice] using dropPrefix?_slice_of_isEmpty (by simp)
|
||||
|
||||
theorem eq_append_of_dropPrefix?_string_eq_some {pat : String} {s res : Slice} (h : s.dropPrefix? pat = some res) :
|
||||
s.copy = pat ++ res.copy := by
|
||||
rw [dropPrefix?_string_eq_dropPrefix?_toSlice] at h
|
||||
simpa using eq_append_of_dropPrefix?_slice_eq_some h
|
||||
|
||||
|
||||
theorem skipSuffix?_slice_of_isEmpty {pat s : Slice} (hpat : pat.isEmpty = true) :
|
||||
s.skipSuffix? pat = some s.endPos := by
|
||||
rw [skipSuffix?_eq_backwardPatternSkipSuffix?, BackwardSliceSearcher.skipSuffix?_of_isEmpty hpat]
|
||||
|
||||
@[simp]
|
||||
theorem skipSuffix?_slice_eq_some_iff {pat s : Slice} {pos : s.Pos} :
|
||||
s.skipSuffix? pat = some pos ↔ ∃ t, pos.Splits t pat.copy := by
|
||||
rw [Pattern.Model.skipSuffix?_eq_some_iff, ForwardSliceSearcher.isLongestRevMatch_iff_splits]
|
||||
|
||||
theorem endsWith_slice_of_isEmpty {pat s : Slice} (hpat : pat.isEmpty = true) :
|
||||
s.endsWith pat = true := by
|
||||
rw [endsWith_eq_backwardPatternEndsWith, BackwardSliceSearcher.endsWith_of_isEmpty hpat]
|
||||
|
||||
@[simp]
|
||||
theorem endsWith_slice_iff {pat s : Slice} :
|
||||
s.endsWith pat ↔ pat.copy.toList <:+ s.copy.toList := by
|
||||
simp only [Model.endsWith_iff, ForwardSliceSearcher.revMatchesAt_iff_splits,
|
||||
splits_endPos_iff, exists_eq_right]
|
||||
simp only [← toList_inj, toList_append, List.suffix_iff_exists_append_eq]
|
||||
exact ⟨fun ⟨t, ht⟩ => ⟨t.toList, by simp [ht]⟩, fun ⟨t, ht⟩ => ⟨String.ofList t, by simp [← ht]⟩⟩
|
||||
|
||||
@[simp]
|
||||
theorem endsWith_slice_eq_false_iff {pat s : Slice} :
|
||||
s.endsWith pat = false ↔ ¬ (pat.copy.toList <:+ s.copy.toList) := by
|
||||
simp [← Bool.not_eq_true, endsWith_slice_iff]
|
||||
|
||||
theorem dropSuffix?_slice_of_isEmpty {pat s : Slice} (hpat : pat.isEmpty = true) :
|
||||
s.dropSuffix? pat = some s := by
|
||||
simp [dropSuffix?_eq_map_skipSuffix?, skipSuffix?_slice_of_isEmpty hpat]
|
||||
|
||||
theorem eq_append_of_dropSuffix?_slice_eq_some {pat s res : Slice} (h : s.dropSuffix? pat = some res) :
|
||||
s.copy = res.copy ++ pat.copy := by
|
||||
have := Pattern.Model.eq_append_of_dropSuffix?_eq_some h
|
||||
simp only [PatternModel.Matches] at this
|
||||
obtain ⟨_, ⟨-, rfl⟩, h⟩ := this
|
||||
exact h
|
||||
|
||||
@[simp]
|
||||
theorem skipSuffix?_string_eq_some_iff' {pat : String} {s : Slice} {pos : s.Pos} :
|
||||
s.skipSuffix? pat = some pos ↔ ∃ t, pos.Splits t pat := by
|
||||
simp [skipSuffix?_string_eq_skipSuffix?_toSlice]
|
||||
|
||||
@[simp]
|
||||
theorem skipSuffix?_string_empty {s : Slice} : s.skipSuffix? "" = some s.endPos := by
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem endsWith_string_iff {pat : String} {s : Slice} :
|
||||
s.endsWith pat ↔ pat.toList <:+ s.copy.toList := by
|
||||
simp [endsWith_string_eq_endsWith_toSlice]
|
||||
|
||||
@[simp]
|
||||
theorem endsWith_string_empty {s : Slice} : s.endsWith "" = true := by
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem endsWith_string_eq_false_iff {pat : String} {s : Slice} :
|
||||
s.endsWith pat = false ↔ ¬ (pat.toList <:+ s.copy.toList) := by
|
||||
simp [endsWith_string_eq_endsWith_toSlice]
|
||||
|
||||
@[simp]
|
||||
theorem dropSuffix?_string_empty {s : Slice} : s.dropSuffix? "" = some s := by
|
||||
simpa [dropSuffix?_string_eq_dropSuffix?_toSlice] using dropSuffix?_slice_of_isEmpty (by simp)
|
||||
|
||||
theorem eq_append_of_dropSuffix?_string_eq_some {pat : String} {s res : Slice} (h : s.dropSuffix? pat = some res) :
|
||||
s.copy = res.copy ++ pat := by
|
||||
rw [dropSuffix?_string_eq_dropSuffix?_toSlice] at h
|
||||
simpa using eq_append_of_dropSuffix?_slice_eq_some h
|
||||
|
||||
end Slice
|
||||
|
||||
theorem skipPrefix?_slice_of_isEmpty {pat : Slice} {s : String} (hpat : pat.isEmpty = true) :
|
||||
s.skipPrefix? pat = some s.startPos := by
|
||||
rw [skipPrefix?_eq_skipPrefix?_toSlice, Slice.skipPrefix?_slice_of_isEmpty hpat]
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem skipPrefix?_slice_eq_some_iff {pat : Slice} {s : String} {pos : s.Pos} :
|
||||
s.skipPrefix? pat = some pos ↔ ∃ t, pos.Splits pat.copy t := by
|
||||
@@ -78,15 +186,32 @@ theorem skipPrefix?_slice_eq_some_iff {pat : Slice} {s : String} {pos : s.Pos} :
|
||||
rintro ⟨pos, h, rfl⟩
|
||||
simpa
|
||||
|
||||
theorem startsWith_slice_of_isEmpty {pat : Slice} {s : String} (hpat : pat.isEmpty = true) :
|
||||
s.startsWith pat = true := by
|
||||
rw [startsWith_eq_startsWith_toSlice, Slice.startsWith_slice_of_isEmpty hpat]
|
||||
|
||||
@[simp]
|
||||
theorem startsWith_slice_iff {pat : Slice} {s : String} :
|
||||
s.startsWith pat ↔ pat.copy.toList <+: s.toList := by
|
||||
simp [startsWith_eq_startsWith_toSlice]
|
||||
simp [← startsWith_toSlice]
|
||||
|
||||
@[simp]
|
||||
theorem startsWith_slice_eq_false_iff {pat : Slice} {s : String} :
|
||||
s.startsWith pat = false ↔ ¬ (pat.copy.toList <+: s.toList) := by
|
||||
simp [startsWith_eq_startsWith_toSlice]
|
||||
simp [← startsWith_toSlice]
|
||||
|
||||
theorem dropPrefix?_slice_of_isEmpty {pat : Slice} {s : String} (hpat : pat.isEmpty = true) :
|
||||
s.dropPrefix? pat = some s.toSlice := by
|
||||
rw [dropPrefix?_eq_dropPrefix?_toSlice, Slice.dropPrefix?_slice_of_isEmpty hpat]
|
||||
|
||||
theorem eq_append_of_dropPrefix?_slice_eq_some {pat res : Slice} {s : String} (h : s.dropPrefix? pat = some res) :
|
||||
s = pat.copy ++ res.copy := by
|
||||
rw [dropPrefix?_eq_dropPrefix?_toSlice] at h
|
||||
simpa using Slice.eq_append_of_dropPrefix?_slice_eq_some h
|
||||
|
||||
@[simp]
|
||||
theorem skipPrefix?_string_empty {s : String} : s.skipPrefix? "" = some s.startPos := by
|
||||
simp [skipPrefix?_eq_skipPrefix?_toSlice]
|
||||
|
||||
@[simp]
|
||||
theorem skipPrefix?_string_eq_some_iff {pat s : String} {pos : s.Pos} :
|
||||
@@ -97,14 +222,27 @@ theorem skipPrefix?_string_eq_some_iff {pat s : String} {pos : s.Pos} :
|
||||
rintro ⟨pos, h, rfl⟩
|
||||
simpa
|
||||
|
||||
@[simp]
|
||||
theorem startsWith_string_empty {s : String} : s.startsWith "" = true := by
|
||||
simp [← startsWith_toSlice]
|
||||
|
||||
@[simp]
|
||||
theorem startsWith_string_iff {pat s : String} :
|
||||
s.startsWith pat ↔ pat.toList <+: s.toList := by
|
||||
simp [startsWith_eq_startsWith_toSlice]
|
||||
simp [← startsWith_toSlice]
|
||||
|
||||
@[simp]
|
||||
theorem startsWith_string_eq_false_iff {pat s : String} :
|
||||
s.startsWith pat = false ↔ ¬ (pat.toList <+: s.toList) := by
|
||||
simp [startsWith_eq_startsWith_toSlice]
|
||||
simp [← startsWith_toSlice]
|
||||
|
||||
@[simp]
|
||||
theorem dropPrefix?_string_empty {s : String} : s.dropPrefix? "" = some s.toSlice := by
|
||||
simp [← dropPrefix?_toSlice]
|
||||
|
||||
theorem eq_append_of_dropPrefix?_string_eq_some {s pat : String} {res : Slice} (h : s.dropPrefix? pat = some res) :
|
||||
s = pat ++ res.copy := by
|
||||
rw [dropPrefix?_eq_dropPrefix?_toSlice] at h
|
||||
simpa using Slice.eq_append_of_dropPrefix?_string_eq_some h
|
||||
|
||||
end String
|
||||
|
||||
@@ -8,6 +8,8 @@ module
|
||||
prelude
|
||||
public import Init.Data.String.Search
|
||||
import all Init.Data.String.Search
|
||||
import Init.Data.String.Lemmas.Slice
|
||||
import Init.Data.String.Lemmas.FindPos
|
||||
|
||||
public section
|
||||
|
||||
@@ -28,4 +30,42 @@ theorem Pos.le_find {s : String} (pos : s.Pos) (pattern : ρ) [ToForwardSearcher
|
||||
pos ≤ pos.find pattern := by
|
||||
simp [Pos.find, ← toSlice_le]
|
||||
|
||||
@[simp]
|
||||
theorem front?_toSlice {s : String} : s.toSlice.front? = s.front? :=
|
||||
(rfl)
|
||||
|
||||
theorem front?_eq_get? {s : String} : s.front? = s.startPos.get? := by
|
||||
simp [← front?_toSlice, ← Pos.get?_toSlice, Slice.front?_eq_get?]
|
||||
|
||||
theorem front?_eq {s : String} : s.front? = s.toList.head? := by
|
||||
simp [← front?_toSlice, Slice.front?_eq]
|
||||
|
||||
@[simp]
|
||||
theorem front_toSlice {s : String} : s.toSlice.front = s.front :=
|
||||
(rfl)
|
||||
|
||||
@[simp]
|
||||
theorem front_eq {s : String} : s.front = s.front?.getD default := by
|
||||
simp [← front_toSlice, Slice.front_eq]
|
||||
|
||||
@[simp]
|
||||
theorem back?_toSlice {s : String} : s.toSlice.back? = s.back? :=
|
||||
(rfl)
|
||||
|
||||
theorem back?_eq_get? {s : String} : s.back? = s.endPos.prev?.bind Pos.get? := by
|
||||
simp only [← back?_toSlice, Slice.back?_eq_get?, endPos_toSlice, Slice.Pos.prev?_eq_dif,
|
||||
startPos_toSlice, Pos.toSlice_inj, Pos.prev?_eq_dif]
|
||||
split <;> simp [← Pos.get?_toSlice, Pos.toSlice_prev]
|
||||
|
||||
theorem back?_eq {s : String} : s.back? = s.toList.getLast? := by
|
||||
simp [← back?_toSlice, Slice.back?_eq]
|
||||
|
||||
@[simp]
|
||||
theorem back_toSlice {s : String} : s.toSlice.back = s.back :=
|
||||
(rfl)
|
||||
|
||||
@[simp]
|
||||
theorem back_eq {s : String} : s.back = s.back?.getD default := by
|
||||
simp [← back_toSlice, Slice.back_eq]
|
||||
|
||||
end String
|
||||
|
||||
@@ -11,6 +11,8 @@ import all Init.Data.String.Slice
|
||||
import Init.Data.String.Lemmas.Pattern.Memcmp
|
||||
import Init.Data.String.Lemmas.Basic
|
||||
import Init.Data.ByteArray.Lemmas
|
||||
import Init.Data.String.Lemmas.IsEmpty
|
||||
import Init.Data.String.Lemmas.FindPos
|
||||
|
||||
public section
|
||||
|
||||
@@ -33,9 +35,104 @@ theorem beq_eq_true_iff {s t : Slice} : s == t ↔ s.copy = t.copy := by
|
||||
theorem beq_eq_false_iff {s t : Slice} : (s == t) = false ↔ s.copy ≠ t.copy := by
|
||||
simp [← Bool.not_eq_true]
|
||||
|
||||
theorem beq_eq_decide {s t : Slice} : (s == t) = decide (s.copy = t.copy) := by
|
||||
cases h : s == t <;> simp_all
|
||||
theorem beq_eq_decide {s t : Slice} : (s == t) = decide (s.copy = t.copy) :=
|
||||
Bool.eq_iff_iff.2 (by simp)
|
||||
|
||||
instance : EquivBEq String.Slice :=
|
||||
equivBEq_of_iff_apply_eq copy (by simp)
|
||||
|
||||
theorem beq_list_iff {l l' : List String.Slice} : l == l' ↔ l.map copy = l'.map copy := by
|
||||
induction l generalizing l' <;> cases l' <;> simp_all
|
||||
|
||||
theorem beq_list_eq_false_iff {l l' : List String.Slice} :
|
||||
(l == l') = false ↔ l.map copy ≠ l'.map copy := by
|
||||
simp [← Bool.not_eq_true, beq_list_iff]
|
||||
|
||||
theorem beq_list_eq_decide {l l' : List String.Slice} :
|
||||
(l == l') = decide (l.map copy = l'.map copy) :=
|
||||
Bool.eq_iff_iff.2 (by simp [beq_list_iff])
|
||||
|
||||
end BEq
|
||||
|
||||
end String.Slice
|
||||
namespace Pos
|
||||
|
||||
theorem get?_eq_dif {s : Slice} {p : s.Pos} : p.get? = if h : p = s.endPos then none else some (p.get h) :=
|
||||
(rfl)
|
||||
|
||||
theorem get?_eq_some_get {s : Slice} {p : s.Pos} (h : p ≠ s.endPos) : p.get? = some (p.get h) := by
|
||||
simp [Pos.get?, h]
|
||||
|
||||
@[simp]
|
||||
theorem get?_eq_none_iff {s : Slice} {p : s.Pos} : p.get? = none ↔ p = s.endPos := by
|
||||
simp [Pos.get?]
|
||||
|
||||
theorem get?_eq_none {s : Slice} {p : s.Pos} (h : p = s.endPos) : p.get? = none :=
|
||||
get?_eq_none_iff.2 h
|
||||
|
||||
@[simp]
|
||||
theorem get?_endPos {s : Slice} : s.endPos.get? = none := by
|
||||
simp
|
||||
|
||||
end Pos
|
||||
|
||||
end Slice
|
||||
|
||||
namespace Pos
|
||||
|
||||
theorem get?_toSlice {s : String} {p : s.Pos} : p.toSlice.get? = p.get? :=
|
||||
(rfl)
|
||||
|
||||
theorem get?_eq_dif {s : String} {p : s.Pos} : p.get? = if h : p = s.endPos then none else some (p.get h) := by
|
||||
simp [← get?_toSlice, Slice.Pos.get?_eq_dif]
|
||||
|
||||
theorem get?_eq_some_get {s : String} {p : s.Pos} (h : p ≠ s.endPos) : p.get? = some (p.get h) := by
|
||||
simpa [← get?_toSlice] using Slice.Pos.get?_eq_some_get (by simpa)
|
||||
|
||||
@[simp]
|
||||
theorem get?_eq_none_iff {s : String} {p : s.Pos} : p.get? = none ↔ p = s.endPos := by
|
||||
simp [← get?_toSlice]
|
||||
|
||||
theorem get?_eq_none {s : String} {p : s.Pos} (h : p = s.endPos) : p.get? = none :=
|
||||
get?_eq_none_iff.2 h
|
||||
|
||||
@[simp]
|
||||
theorem get?_endPos {s : String} : s.endPos.get? = none := by
|
||||
simp
|
||||
|
||||
end Pos
|
||||
|
||||
namespace Slice
|
||||
|
||||
theorem front?_eq_get? {s : Slice} : s.front? = s.startPos.get? :=
|
||||
(rfl)
|
||||
|
||||
theorem front?_eq {s : Slice} : s.front? = s.copy.toList.head? := by
|
||||
simp only [front?_eq_get?, Pos.get?_eq_dif]
|
||||
split
|
||||
· simp_all [startPos_eq_endPos_iff, eq_comm (a := none)]
|
||||
· rename_i h
|
||||
obtain ⟨t, ht⟩ := s.splits_startPos.exists_eq_singleton_append h
|
||||
simp [ht]
|
||||
|
||||
@[simp]
|
||||
theorem front_eq {s : Slice} : s.front = s.front?.getD default := by
|
||||
simp [front]
|
||||
|
||||
theorem back?_eq_get? {s : Slice} : s.back? = s.endPos.prev?.bind Pos.get? :=
|
||||
(rfl)
|
||||
|
||||
theorem back?_eq {s : Slice} : s.back? = s.copy.toList.getLast? := by
|
||||
simp [back?_eq_get?, Pos.prev?_eq_dif]
|
||||
split
|
||||
· simp_all [startPos_eq_endPos_iff, eq_comm (a := s.endPos), eq_comm (a := none)]
|
||||
· rename_i h
|
||||
obtain ⟨t, ht⟩ := s.splits_endPos.exists_eq_append_singleton_of_ne_startPos h
|
||||
simp [ht, Pos.get?_eq_some_get]
|
||||
|
||||
@[simp]
|
||||
theorem back_eq {s : Slice} : s.back = s.back?.getD default := by
|
||||
simp [back]
|
||||
|
||||
end Slice
|
||||
|
||||
end String
|
||||
|
||||
@@ -17,6 +17,8 @@ import Init.Data.String.OrderInstances
|
||||
import Init.Data.Nat.Order
|
||||
import Init.Omega
|
||||
import Init.Data.String.Lemmas.FindPos
|
||||
import Init.Data.List.TakeDrop
|
||||
import Init.Data.List.Nat.TakeDrop
|
||||
|
||||
/-!
|
||||
# `Splits` predicates on `String.Pos` and `String.Slice.Pos`.
|
||||
@@ -97,6 +99,11 @@ theorem Pos.splits {s : String} (p : s.Pos) :
|
||||
eq_append := by simp [← toByteArray_inj, Slice.toByteArray_copy, ← size_toByteArray]
|
||||
offset_eq_rawEndPos := by simp
|
||||
|
||||
@[simp]
|
||||
theorem sliceTo_append_sliceFrom {s : String} {pos : s.Pos} :
|
||||
(s.sliceTo pos).copy ++ (s.sliceFrom pos).copy = s :=
|
||||
pos.splits.eq_append.symm
|
||||
|
||||
theorem Slice.Pos.splits {s : Slice} (p : s.Pos) :
|
||||
p.Splits (s.sliceTo p).copy (s.sliceFrom p).copy where
|
||||
eq_append := copy_eq_copy_sliceTo
|
||||
@@ -365,7 +372,7 @@ theorem Slice.Pos.Splits.of_prev {s : Slice} {p : s.Pos} {hp}
|
||||
obtain ⟨rfl, rfl, rfl⟩ := by simpa using h.eq (splits_prev p hp)
|
||||
exact splits_prev_right p hp
|
||||
|
||||
theorem Slice.sliceTo_copy_eq_iff_exists_splits {s : Slice} {p : s.Pos} {t₁ : String} :
|
||||
theorem Slice.copy_sliceTo_eq_iff_exists_splits {s : Slice} {p : s.Pos} {t₁ : String} :
|
||||
(s.sliceTo p).copy = t₁ ↔ ∃ t₂, p.Splits t₁ t₂ := by
|
||||
refine ⟨?_, ?_⟩
|
||||
· rintro rfl
|
||||
@@ -373,13 +380,37 @@ theorem Slice.sliceTo_copy_eq_iff_exists_splits {s : Slice} {p : s.Pos} {t₁ :
|
||||
· rintro ⟨t₂, h⟩
|
||||
exact p.splits.eq_left h
|
||||
|
||||
theorem sliceTo_copy_eq_iff_exists_splits {s : String} {p : s.Pos} {t₁ : String} :
|
||||
(s.sliceTo p).copy = t₁ ↔ ∃ t₂, p.Splits t₁ t₂ := by
|
||||
theorem Slice.copy_sliceTo_eq_iff_splits {s : Slice} {p : s.Pos} {t₁ : String} :
|
||||
(s.sliceTo p).copy = t₁ ↔ p.Splits t₁ (s.sliceFrom p).copy :=
|
||||
⟨fun h => h ▸ p.splits, p.splits.eq_left⟩
|
||||
|
||||
theorem Slice.copy_sliceFrom_eq_iff_exists_splits {s : Slice} {p : s.Pos} {t₂ : String} :
|
||||
(s.sliceFrom p).copy = t₂ ↔ ∃ t₁, p.Splits t₁ t₂ := by
|
||||
refine ⟨?_, ?_⟩
|
||||
· rintro rfl
|
||||
exact ⟨_, p.splits⟩
|
||||
· rintro ⟨t₂, h⟩
|
||||
exact p.splits.eq_left h
|
||||
exact p.splits.eq_right h
|
||||
|
||||
theorem Slice.copy_sliceFrom_eq_iff_splits {s : Slice} {p : s.Pos} {t₂ : String} :
|
||||
(s.sliceFrom p).copy = t₂ ↔ p.Splits (s.sliceTo p).copy t₂ :=
|
||||
⟨fun h => h ▸ p.splits, p.splits.eq_right⟩
|
||||
|
||||
theorem copy_sliceTo_eq_iff_exists_splits {s : String} {p : s.Pos} {t₁ : String} :
|
||||
(s.sliceTo p).copy = t₁ ↔ ∃ t₂, p.Splits t₁ t₂ := by
|
||||
simp [← Pos.splits_toSlice_iff, ← Slice.copy_sliceTo_eq_iff_exists_splits]
|
||||
|
||||
theorem copy_sliceTo_eq_iff_splits {s : String} {p : s.Pos} {t₁ : String} :
|
||||
(s.sliceTo p).copy = t₁ ↔ p.Splits t₁ (s.sliceFrom p).copy :=
|
||||
⟨fun h => h ▸ p.splits, p.splits.eq_left⟩
|
||||
|
||||
theorem copy_sliceFrom_eq_iff_exists_splits {s : String} {p : s.Pos} {t₂ : String} :
|
||||
(s.sliceFrom p).copy = t₂ ↔ ∃ t₁, p.Splits t₁ t₂ := by
|
||||
simp [← Pos.splits_toSlice_iff, ← Slice.copy_sliceFrom_eq_iff_exists_splits]
|
||||
|
||||
theorem copy_sliceFrom_eq_iff_splits {s : String} {p : s.Pos} {t₂ : String} :
|
||||
(s.sliceFrom p).copy = t₂ ↔ p.Splits (s.sliceTo p).copy t₂ :=
|
||||
⟨fun h => h ▸ p.splits, p.splits.eq_right⟩
|
||||
|
||||
theorem Pos.Splits.offset_eq_decreaseBy {s : String} {p : s.Pos} (h : p.Splits t₁ t₂) :
|
||||
p.offset = s.rawEndPos.decreaseBy t₂.utf8ByteSize := by
|
||||
@@ -425,8 +456,7 @@ theorem Slice.splits_singleton_iff {s : Slice} {p : s.Pos} {c : Char} {t : Strin
|
||||
simp [startPos_ne_endPos_iff, ← copy_ne_empty_iff, h.eq_append]
|
||||
have spl : (s.startPos.next this).Splits (singleton c) t := by
|
||||
rw [← empty_append (s := singleton c)]
|
||||
apply Pos.Splits.next
|
||||
simp [h.eq_append]
|
||||
exact Pos.Splits.next (by simp [h.eq_append])
|
||||
refine ⟨this, ⟨h.pos_eq spl, ?_, h.eq_append⟩⟩
|
||||
rw [← empty_append (s := singleton c)] at spl
|
||||
exact spl.get_eq_of_singleton
|
||||
@@ -440,6 +470,27 @@ theorem splits_singleton_iff {s : String} {p : s.Pos} {c : Char} {t : String} :
|
||||
rw [← Pos.splits_toSlice_iff, Slice.splits_singleton_iff]
|
||||
simp [← Pos.ofToSlice_inj]
|
||||
|
||||
theorem Slice.splits_singleton_right_iff {s : Slice} {p : s.Pos} {c : Char} {t : String} :
|
||||
p.Splits t (singleton c) ↔
|
||||
∃ h, p = s.endPos.prev h ∧ (s.endPos.prev h).get (by simp) = c ∧ s.copy = t ++ singleton c := by
|
||||
refine ⟨fun h => ?_, ?_⟩
|
||||
· have : s.endPos ≠ s.startPos := by
|
||||
simp [ne_comm (a := s.endPos), startPos_ne_endPos_iff, ← copy_ne_empty_iff, h.eq_append]
|
||||
have spl : (s.endPos.prev this).Splits t (singleton c) := by
|
||||
rw [← append_empty (s := singleton c)]
|
||||
exact Pos.Splits.prev (by simp [h.eq_append])
|
||||
refine ⟨this, ⟨h.pos_eq spl, ?_, h.eq_append⟩⟩
|
||||
exact (h.eq_append ▸ Pos.next_prev (h := this) ▸ s.splits_endPos).get_eq_of_singleton
|
||||
· rintro ⟨h, ⟨rfl, rfl, h'⟩⟩
|
||||
rw [← String.append_empty (s := singleton _)]
|
||||
exact Pos.Splits.prev (by simp [h'])
|
||||
|
||||
theorem splits_singleton_right_iff {s : String} {p : s.Pos} {c : Char} {t : String} :
|
||||
p.Splits t (singleton c) ↔
|
||||
∃ h, p = s.endPos.prev h ∧ (s.endPos.prev h).get (by simp) = c ∧ s = t ++ singleton c := by
|
||||
rw [← Pos.splits_toSlice_iff, Slice.splits_singleton_right_iff]
|
||||
simp [← Pos.ofToSlice_inj, Pos.prev_toSlice]
|
||||
|
||||
theorem Slice.splits_next_startPos {s : Slice} {h : s.startPos ≠ s.endPos} :
|
||||
(s.startPos.next h).Splits
|
||||
(singleton (s.startPos.get h)) (s.sliceFrom (s.startPos.next h)).copy := by
|
||||
@@ -454,6 +505,20 @@ theorem splits_next_startPos {s : String} {h : s.startPos ≠ s.endPos} :
|
||||
rw [← Pos.splits_toSlice_iff]
|
||||
apply (Slice.splits_next_startPos).of_eq <;> simp [String.Pos.next_toSlice]
|
||||
|
||||
theorem Slice.splits_prev_endPos {s : Slice} {h : s.endPos ≠ s.startPos} :
|
||||
(s.endPos.prev h).Splits
|
||||
(s.sliceTo (s.endPos.prev h)).copy (singleton ((s.endPos.prev h).get (by simp))) := by
|
||||
rw [← String.append_empty (s := singleton _)]
|
||||
apply Slice.Pos.Splits.prev
|
||||
have := Slice.Pos.splits_prev_right s.endPos h
|
||||
rwa [copy_sliceFrom_endPos] at this
|
||||
|
||||
theorem splits_prev_endPos {s : String} {h : s.endPos ≠ s.startPos} :
|
||||
(s.endPos.prev h).Splits
|
||||
(s.sliceTo (s.endPos.prev h)).copy (singleton ((s.endPos.prev h).get (by simp))) := by
|
||||
rw [← Pos.splits_toSlice_iff]
|
||||
apply (Slice.splits_prev_endPos).of_eq <;> simp [String.Pos.prev_toSlice, h]
|
||||
|
||||
theorem Slice.Pos.Splits.toByteArray_eq_left {s : Slice} {p : s.Pos} {t₁ t₂ : String} (h : p.Splits t₁ t₂) :
|
||||
t₁.toByteArray = s.copy.toByteArray.extract 0 p.offset.byteIdx := by
|
||||
rw [h.eq_left p.splits]
|
||||
@@ -597,6 +662,28 @@ theorem Pos.splits_append_rawEndPos {s t : String} :
|
||||
eq_append := rfl
|
||||
offset_eq_rawEndPos := rfl
|
||||
|
||||
/--
|
||||
Given a slice `s` such that `s.copy = t₁ ++ t₂`, obtain the position sitting between `t₁` and `t₂`.
|
||||
-/
|
||||
def Slice.Pos.ofEqAppend {s : Slice} {t₁ t₂ : String} (h : s.copy = t₁ ++ t₂) : s.Pos :=
|
||||
s.pos t₁.rawEndPos
|
||||
(by simpa [← Pos.Raw.isValid_copy_iff, h] using ((Pos.Raw.isValid_rawEndPos).append_right t₂))
|
||||
|
||||
theorem Slice.Pos.splits_ofEqAppend {s : Slice} {t₁ t₂ : String} (h : s.copy = t₁ ++ t₂) :
|
||||
(ofEqAppend h).Splits t₁ t₂ where
|
||||
eq_append := h
|
||||
offset_eq_rawEndPos := by simp [ofEqAppend]
|
||||
|
||||
/--
|
||||
Given a string `s` such that `s = t₁ ++ t₂`, obtain the position sitting between `t₁` and `t₂`.
|
||||
-/
|
||||
def Pos.ofEqAppend {s t₁ t₂ : String} (h : s = t₁ ++ t₂) : s.Pos :=
|
||||
((t₁ ++ t₂).pos t₁.rawEndPos ((Pos.Raw.isValid_rawEndPos).append_right t₂)).cast h.symm
|
||||
|
||||
theorem Pos.splits_ofEqAppend {s t₁ t₂ : String} (h : s = t₁ ++ t₂) : (ofEqAppend h).Splits t₁ t₂ where
|
||||
eq_append := h
|
||||
offset_eq_rawEndPos := by simp [ofEqAppend]
|
||||
|
||||
theorem Pos.Splits.copy_sliceTo_eq {s : String} {p : s.Pos} (h : p.Splits t₁ t₂) :
|
||||
(s.sliceTo p).copy = t₁ :=
|
||||
p.splits.eq_left h
|
||||
@@ -649,4 +736,91 @@ theorem Slice.splits_slice {s : Slice} {p₀ p₁ : s.Pos} (h) (p : (s.slice p
|
||||
p.Splits (s.slice p₀ (Pos.ofSlice p) Pos.le_ofSlice).copy (s.slice (Pos.ofSlice p) p₁ Pos.ofSlice_le).copy := by
|
||||
simpa using p.splits
|
||||
|
||||
theorem Slice.Pos.Splits.nextn {s : Slice} {t₁ t₂ : String} {p : s.Pos} (h : p.Splits t₁ t₂) (n : Nat) :
|
||||
(p.nextn n).Splits (t₁ ++ String.ofList (t₂.toList.take n)) (String.ofList (t₂.toList.drop n)) := by
|
||||
induction n generalizing p t₁ t₂ with
|
||||
| zero => simpa
|
||||
| succ n ih =>
|
||||
rw [Pos.nextn_add_one]
|
||||
split
|
||||
· simp_all
|
||||
· obtain ⟨t₂, rfl⟩ := h.exists_eq_singleton_append ‹_›
|
||||
simpa [← append_assoc] using ih h.next
|
||||
|
||||
theorem Slice.splits_nextn_startPos (s : Slice) (n : Nat) :
|
||||
(s.startPos.nextn n).Splits (String.ofList (s.copy.toList.take n)) (String.ofList (s.copy.toList.drop n)) := by
|
||||
simpa using s.splits_startPos.nextn n
|
||||
|
||||
theorem Pos.Splits.nextn {s t₁ t₂ : String} {p : s.Pos} (h : p.Splits t₁ t₂) (i : Nat) :
|
||||
(p.nextn i).Splits (t₁ ++ String.ofList (t₂.toList.take i)) (String.ofList (t₂.toList.drop i)) := by
|
||||
simpa [← splits_toSlice_iff, toSlice_nextn] using h.toSlice.nextn i
|
||||
|
||||
theorem splits_nextn_startPos (s : String) (n : Nat) :
|
||||
(s.startPos.nextn n).Splits (String.ofList (s.toList.take n)) (String.ofList (s.toList.drop n)) := by
|
||||
simpa using s.splits_startPos.nextn n
|
||||
|
||||
theorem Slice.Pos.Splits.prevn {s : Slice} {t₁ t₂ : String} {p : s.Pos} (h : p.Splits t₁ t₂) (n : Nat) :
|
||||
(p.prevn n).Splits (String.ofList (t₁.toList.take (t₁.length - n))) (String.ofList (t₁.toList.drop (t₁.length - n)) ++ t₂) := by
|
||||
induction n generalizing p t₁ t₂ with
|
||||
| zero => simpa [← String.length_toList]
|
||||
| succ n ih =>
|
||||
rw [Pos.prevn_add_one]
|
||||
split
|
||||
· simp_all
|
||||
· obtain ⟨t₂, rfl⟩ := h.exists_eq_append_singleton_of_ne_startPos ‹_›
|
||||
simpa [Nat.add_sub_add_right, List.take_append, List.drop_append, ← append_assoc] using ih h.prev
|
||||
|
||||
theorem Slice.splits_prevn_endPos (s : Slice) (n : Nat) :
|
||||
(s.endPos.prevn n).Splits (String.ofList (s.copy.toList.take (s.copy.length - n)))
|
||||
(String.ofList (s.copy.toList.drop (s.copy.length - n))) := by
|
||||
simpa using s.splits_endPos.prevn n
|
||||
|
||||
theorem Pos.Splits.prevn {s t₁ t₂ : String} {p : s.Pos} (h : p.Splits t₁ t₂) (n : Nat) :
|
||||
(p.prevn n).Splits (String.ofList (t₁.toList.take (t₁.length - n))) (String.ofList (t₁.toList.drop (t₁.length - n)) ++ t₂) := by
|
||||
simpa [← splits_toSlice_iff, toSlice_prevn] using h.toSlice.prevn n
|
||||
|
||||
theorem splits_prevn_endPos (s : String) (n : Nat) :
|
||||
(s.endPos.prevn n).Splits (String.ofList (s.toList.take (s.length - n))) (String.ofList (s.toList.drop (s.length - n))) := by
|
||||
simpa using s.splits_endPos.prevn n
|
||||
|
||||
@[simp]
|
||||
theorem Slice.copy_sliceFrom_cast {s t : Slice} (hst : s.copy = t.copy) {pos : s.Pos} :
|
||||
(t.sliceFrom (pos.cast hst)).copy = (s.sliceFrom pos).copy := by
|
||||
simpa [copy_sliceFrom_eq_iff_exists_splits] using ⟨_, pos.splits⟩
|
||||
|
||||
@[simp]
|
||||
theorem Slice.copy_sliceTo_cast {s t : Slice} (hst : s.copy = t.copy) {pos : s.Pos} :
|
||||
(t.sliceTo (pos.cast hst)).copy = (s.sliceTo pos).copy := by
|
||||
simpa [copy_sliceTo_eq_iff_exists_splits] using ⟨_, pos.splits⟩
|
||||
|
||||
@[simp]
|
||||
theorem copy_sliceFrom_cast {s t : String} (hst : s = t) {pos : s.Pos} :
|
||||
(t.sliceFrom (pos.cast hst)).copy = (s.sliceFrom pos).copy := by
|
||||
simpa [copy_sliceFrom_eq_iff_exists_splits] using ⟨_, pos.splits⟩
|
||||
|
||||
@[simp]
|
||||
theorem copy_sliceTo_cast {s t : String} (hst : s = t) {pos : s.Pos} :
|
||||
(t.sliceTo (pos.cast hst)).copy = (s.sliceTo pos).copy := by
|
||||
simpa [copy_sliceTo_eq_iff_exists_splits] using ⟨_, pos.splits⟩
|
||||
|
||||
theorem Slice.Pos.sliceFrom_cast {s t : Slice} {hst : s.copy = t.copy} (p q : s.Pos) {h} :
|
||||
Slice.Pos.sliceFrom (p.cast hst) (q.cast hst) h =
|
||||
(Slice.Pos.sliceFrom p q (by simpa using h)).cast (by simp) := by
|
||||
ext1; simp
|
||||
|
||||
theorem Slice.Pos.sliceTo_cast {s t : Slice} {hst : s.copy = t.copy} (p q : s.Pos) {h} :
|
||||
Slice.Pos.sliceTo (p.cast hst) (q.cast hst) h =
|
||||
(Slice.Pos.sliceTo p q (by simpa using h)).cast (by simp) := by
|
||||
ext1; simp
|
||||
|
||||
theorem Pos.sliceFrom_cast {s t : String} {hst : s = t} (p q : s.Pos) {h} :
|
||||
Pos.sliceFrom (p.cast hst) (q.cast hst) h =
|
||||
(Pos.sliceFrom p q (by simpa using h)).cast (by simp) := by
|
||||
ext1; simp
|
||||
|
||||
theorem Pos.sliceTo_cast {s t : String} {hst : s = t} (p q : s.Pos) {h} :
|
||||
Pos.sliceTo (p.cast hst) (q.cast hst) h =
|
||||
(Pos.sliceTo p q (by simpa using h)).cast (by simp) := by
|
||||
ext1; simp
|
||||
|
||||
end String
|
||||
|
||||
49
src/Init/Data/String/Lemmas/StringOrder.lean
Normal file
49
src/Init/Data/String/Lemmas/StringOrder.lean
Normal file
@@ -0,0 +1,49 @@
|
||||
/-
|
||||
Copyright (c) 2024 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.Data.String.Basic
|
||||
public import Init.Data.Order.Classes
|
||||
import Init.Data.List.Lex
|
||||
import Init.Data.Char.Lemmas
|
||||
import Init.Data.Char.Order
|
||||
import Init.Data.Order.Factories
|
||||
import Init.Data.Order.Lemmas
|
||||
|
||||
public section
|
||||
|
||||
open Std
|
||||
|
||||
namespace String
|
||||
|
||||
@[simp] protected theorem not_le {a b : String} : ¬ a ≤ b ↔ b < a := Decidable.not_not
|
||||
@[simp] protected theorem not_lt {a b : String} : ¬ a < b ↔ b ≤ a := Iff.rfl
|
||||
@[simp] protected theorem le_refl (a : String) : a ≤ a := List.le_refl _
|
||||
@[simp] protected theorem lt_irrefl (a : String) : ¬ a < a := List.lt_irrefl _
|
||||
|
||||
attribute [local instance] Char.notLTTrans Char.ltTrichotomous Char.ltAsymm
|
||||
|
||||
protected theorem le_trans {a b c : String} : a ≤ b → b ≤ c → a ≤ c := List.le_trans
|
||||
protected theorem lt_trans {a b c : String} : a < b → b < c → a < c := List.lt_trans
|
||||
protected theorem le_total (a b : String) : a ≤ b ∨ b ≤ a := List.le_total _ _
|
||||
protected theorem le_antisymm {a b : String} : a ≤ b → b ≤ a → a = b := fun h₁ h₂ => String.ext (List.le_antisymm (as := a.toList) (bs := b.toList) h₁ h₂)
|
||||
protected theorem lt_asymm {a b : String} (h : a < b) : ¬ b < a := List.lt_asymm h
|
||||
protected theorem ne_of_lt {a b : String} (h : a < b) : a ≠ b := by
|
||||
have := String.lt_irrefl a
|
||||
intro h; subst h; contradiction
|
||||
|
||||
instance instIsLinearOrder : IsLinearOrder String := by
|
||||
apply IsLinearOrder.of_le
|
||||
case le_antisymm => constructor; apply String.le_antisymm
|
||||
case le_trans => constructor; apply String.le_trans
|
||||
case le_total => constructor; apply String.le_total
|
||||
|
||||
instance : LawfulOrderLT String where
|
||||
lt_iff a b := by
|
||||
simp [← String.not_le, Decidable.imp_iff_not_or, Std.Total.total]
|
||||
|
||||
end String
|
||||
86
src/Init/Data/String/Lemmas/TakeDrop.lean
Normal file
86
src/Init/Data/String/Lemmas/TakeDrop.lean
Normal file
@@ -0,0 +1,86 @@
|
||||
/-
|
||||
Copyright (c) 2026 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Author: Julia Markus Himmel
|
||||
-/
|
||||
module
|
||||
|
||||
prelude
|
||||
public import Init.Data.String.TakeDrop
|
||||
import all Init.Data.String.Slice
|
||||
import all Init.Data.String.TakeDrop
|
||||
import Init.Data.String.Lemmas.Splits
|
||||
|
||||
public section
|
||||
|
||||
namespace String
|
||||
|
||||
namespace Slice
|
||||
|
||||
theorem drop_eq_sliceFrom {s : Slice} {n : Nat} : s.drop n = s.sliceFrom (s.startPos.nextn n) :=
|
||||
(rfl)
|
||||
|
||||
@[simp]
|
||||
theorem toList_copy_drop {s : Slice} {n : Nat} : (s.drop n).copy.toList = s.copy.toList.drop n := by
|
||||
simp [drop_eq_sliceFrom, (s.splits_nextn_startPos n).copy_sliceFrom_eq]
|
||||
|
||||
theorem dropEnd_eq_sliceTo {s : Slice} {n : Nat} : s.dropEnd n = s.sliceTo (s.endPos.prevn n) :=
|
||||
(rfl)
|
||||
|
||||
@[simp]
|
||||
theorem toList_copy_dropEnd {s : Slice} {n : Nat} :
|
||||
(s.dropEnd n).copy.toList = s.copy.toList.take (s.copy.length - n) := by
|
||||
simp [dropEnd_eq_sliceTo, (s.splits_prevn_endPos n).copy_sliceTo_eq]
|
||||
|
||||
theorem take_eq_sliceTo {s : Slice} {n : Nat} : s.take n = s.sliceTo (s.startPos.nextn n) :=
|
||||
(rfl)
|
||||
|
||||
@[simp]
|
||||
theorem toList_copy_take {s : Slice} {n : Nat} : (s.take n).copy.toList = s.copy.toList.take n := by
|
||||
simp [take_eq_sliceTo, (s.splits_nextn_startPos n).copy_sliceTo_eq]
|
||||
|
||||
theorem takeEnd_eq_sliceFrom {s : Slice} {n : Nat} : s.takeEnd n = s.sliceFrom (s.endPos.prevn n) :=
|
||||
(rfl)
|
||||
|
||||
@[simp]
|
||||
theorem toList_copy_takeEnd {s : Slice} {n : Nat} :
|
||||
(s.takeEnd n).copy.toList = s.copy.toList.drop (s.copy.length - n) := by
|
||||
simp [takeEnd_eq_sliceFrom, (s.splits_prevn_endPos n).copy_sliceFrom_eq]
|
||||
|
||||
end Slice
|
||||
|
||||
@[simp]
|
||||
theorem drop_toSlice {s : String} {n : Nat} : s.toSlice.drop n = s.drop n :=
|
||||
(rfl)
|
||||
|
||||
@[simp]
|
||||
theorem toList_copy_drop {s : String} {n : Nat} : (s.drop n).copy.toList = s.toList.drop n := by
|
||||
simp [← drop_toSlice]
|
||||
|
||||
@[simp]
|
||||
theorem dropEnd_toSlice {s : String} {n : Nat} : s.toSlice.dropEnd n = s.dropEnd n :=
|
||||
(rfl)
|
||||
|
||||
@[simp]
|
||||
theorem toList_copy_dropEnd {s : String} {n : Nat} :
|
||||
(s.dropEnd n).copy.toList = s.toList.take (s.length - n) := by
|
||||
simp [← dropEnd_toSlice]
|
||||
|
||||
@[simp]
|
||||
theorem take_toSlice {s : String} {n : Nat} : s.toSlice.take n = s.take n :=
|
||||
(rfl)
|
||||
|
||||
@[simp]
|
||||
theorem toList_copy_take {s : String} {n : Nat} : (s.take n).copy.toList = s.toList.take n := by
|
||||
simp [← take_toSlice]
|
||||
|
||||
@[simp]
|
||||
theorem takeEnd_toSlice {s : String} {n : Nat} : s.toSlice.takeEnd n = s.takeEnd n :=
|
||||
(rfl)
|
||||
|
||||
@[simp]
|
||||
theorem toList_copy_takeEnd {s : String} {n : Nat} :
|
||||
(s.takeEnd n).copy.toList = s.toList.drop (s.length - n) := by
|
||||
simp [← takeEnd_toSlice]
|
||||
|
||||
end String
|
||||
@@ -96,6 +96,44 @@ theorem endPos_ofSliceFrom {s : Slice} {p : s.Pos} {st : SearchStep (s.sliceFrom
|
||||
st.ofSliceFrom.endPos = Slice.Pos.ofSliceFrom st.endPos := by
|
||||
cases st <;> simp [ofSliceFrom]
|
||||
|
||||
/--
|
||||
Converts a {lean}`SearchStep s` into a {lean}`SearchStep t` by applying {name}`Slice.Pos.cast` to the
|
||||
start and end position.
|
||||
-/
|
||||
@[inline]
|
||||
def cast {s t : Slice} (hst : s.copy = t.copy) : SearchStep s → SearchStep t
|
||||
| .rejected startPos endPos => .rejected (startPos.cast hst) (endPos.cast hst)
|
||||
| .matched startPos endPos => .matched (startPos.cast hst) (endPos.cast hst)
|
||||
|
||||
@[simp]
|
||||
theorem cast_rejected {s t : Slice} {hst : s.copy = t.copy} {startPos endPos : s.Pos} :
|
||||
(SearchStep.rejected startPos endPos).cast hst = .rejected (startPos.cast hst) (endPos.cast hst) :=
|
||||
(rfl)
|
||||
|
||||
@[simp]
|
||||
theorem cast_matched {s t : Slice} {hst : s.copy = t.copy} {startPos endPos : s.Pos} :
|
||||
(SearchStep.matched startPos endPos).cast hst = .matched (startPos.cast hst) (endPos.cast hst) :=
|
||||
(rfl)
|
||||
|
||||
@[simp]
|
||||
theorem startPos_cast {s t : Slice} (hst : s.copy = t.copy) {st : SearchStep s} :
|
||||
(st.cast hst).startPos = st.startPos.cast hst := by
|
||||
cases st <;> simp
|
||||
|
||||
@[simp]
|
||||
theorem endPos_cast {s t : Slice} (hst : s.copy = t.copy) {st : SearchStep s} :
|
||||
(st.cast hst).endPos = st.endPos.cast hst := by
|
||||
cases st <;> simp
|
||||
|
||||
@[simp]
|
||||
theorem cast_rfl {s : Slice} {st : SearchStep s} : st.cast rfl = st := by
|
||||
cases st <;> simp
|
||||
|
||||
@[simp]
|
||||
theorem cast_cast {s t u : Slice} {hst : s.copy = t.copy} {htu : t.copy = u.copy} {st : SearchStep s} :
|
||||
(st.cast hst).cast htu = st.cast (hst.trans htu) := by
|
||||
cases st <;> simp
|
||||
|
||||
end SearchStep
|
||||
|
||||
/--
|
||||
@@ -117,7 +155,7 @@ class ForwardPattern {ρ : Type} (pat : ρ) where
|
||||
-/
|
||||
startsWith : (s : Slice) → Bool := fun s => (skipPrefix? s).isSome
|
||||
|
||||
@[deprecated ForwardPattern.dropPrefix? (since := "2026-03-19")]
|
||||
@[deprecated ForwardPattern.skipPrefix? (since := "2026-03-19")]
|
||||
def ForwardPattern.dropPrefix? {ρ : Type} (pat : ρ) [ForwardPattern pat] (s : Slice) : Option s.Pos :=
|
||||
ForwardPattern.skipPrefix? pat s
|
||||
|
||||
|
||||
@@ -47,8 +47,8 @@ instance {c : Char} : LawfulBackwardPattern c where
|
||||
skipSuffixOfNonempty?_eq h := LawfulBackwardPattern.skipSuffixOfNonempty?_eq (pat := (· == c)) h
|
||||
endsWith_eq s := LawfulBackwardPattern.endsWith_eq (pat := (· == c)) s
|
||||
|
||||
instance {c : Char} : ToBackwardSearcher c (ToBackwardSearcher.DefaultBackwardSearcher c) :=
|
||||
.defaultImplementation
|
||||
instance {c : Char} : ToBackwardSearcher c (ToBackwardSearcher.DefaultBackwardSearcher (· == c)) where
|
||||
toSearcher s := ToBackwardSearcher.toSearcher (· == c) s
|
||||
|
||||
end Char
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user