mirror of
https://github.com/leanprover/lean4.git
synced 2026-03-22 12:54:06 +00:00
Compare commits
85 Commits
finRange
...
generalize
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8534409834 | ||
|
|
4dd182c554 | ||
|
|
762c5758f5 | ||
|
|
6447fda253 | ||
|
|
279f36b4cc | ||
|
|
d2853ecbc4 | ||
|
|
6e60d13084 | ||
|
|
019f8e175f | ||
|
|
c366a291ca | ||
|
|
1400b95ffb | ||
|
|
00c7b85261 | ||
|
|
f6e88e5a05 | ||
|
|
88573c802d | ||
|
|
faf07e58db | ||
|
|
c5181569f9 | ||
|
|
77211029da | ||
|
|
da9a0c4190 | ||
|
|
b9bf94313a | ||
|
|
2a891a3889 | ||
|
|
00718c3959 | ||
|
|
473274f145 | ||
|
|
7b98fbece4 | ||
|
|
24b412ebe3 | ||
|
|
cb600ed9b4 | ||
|
|
57d83c835e | ||
|
|
ce27d49e31 | ||
|
|
8a7889d602 | ||
|
|
69340297be | ||
|
|
222abdd43d | ||
|
|
490be9282e | ||
|
|
cda6d5c67a | ||
|
|
904404303b | ||
|
|
f6bc6b2eb1 | ||
|
|
d9d54c1f99 | ||
|
|
b2336fd980 | ||
|
|
f156f22d7c | ||
|
|
3c348d4526 | ||
|
|
0b8f50f78d | ||
|
|
0d89f0194b | ||
|
|
e157fcbcd1 | ||
|
|
95dbac26cf | ||
|
|
be63c8280e | ||
|
|
6fcf35e930 | ||
|
|
b3e0c9c3fa | ||
|
|
3c5e612dc5 | ||
|
|
29e84fa7ea | ||
|
|
6bf8ff32f0 | ||
|
|
62b8238782 | ||
|
|
0a2a8e8aa4 | ||
|
|
23236ef520 | ||
|
|
b2f70dad52 | ||
|
|
819cb879e1 | ||
|
|
3ee2842e77 | ||
|
|
7b8504cf06 | ||
|
|
ca96922b4b | ||
|
|
a1c3a36433 | ||
|
|
734ea3071d | ||
|
|
f3f00451c8 | ||
|
|
27df5e968a | ||
|
|
3752241edd | ||
|
|
39bffb6fda | ||
|
|
86f303774a | ||
|
|
d97af36867 | ||
|
|
27cc0c8039 | ||
|
|
c9ee66fb1f | ||
|
|
5c7e027b03 | ||
|
|
4969ec9cdb | ||
|
|
827062f807 | ||
|
|
6d495586a1 | ||
|
|
10d1d2cc25 | ||
|
|
2325f5c7b9 | ||
|
|
88e3a2b1ab | ||
|
|
b378fe98a7 | ||
|
|
5f1ff42a15 | ||
|
|
30d01f7a9a | ||
|
|
81b85d8e2f | ||
|
|
5982a6d230 | ||
|
|
ac1197ff59 | ||
|
|
609346f5e0 | ||
|
|
04f80a1f9f | ||
|
|
7e9dd5668b | ||
|
|
79f050b816 | ||
|
|
af4a3f2251 | ||
|
|
7692343720 | ||
|
|
597ef8cfee |
3
.github/workflows/check-prelude.yml
vendored
3
.github/workflows/check-prelude.yml
vendored
@@ -14,6 +14,7 @@ jobs:
|
||||
sparse-checkout: |
|
||||
src/Lean
|
||||
src/Std
|
||||
src/lake/Lake
|
||||
- name: Check Prelude
|
||||
run: |
|
||||
failed_files=""
|
||||
@@ -21,7 +22,7 @@ jobs:
|
||||
if ! grep -q "^prelude$" "$file"; then
|
||||
failed_files="$failed_files$file\n"
|
||||
fi
|
||||
done < <(find src/Lean src/Std -name '*.lean' -print0)
|
||||
done < <(find src/Lean src/Std src/lake/Lake -name '*.lean' -print0)
|
||||
if [ -n "$failed_files" ]; then
|
||||
echo -e "The following files should use 'prelude':\n$failed_files"
|
||||
exit 1
|
||||
|
||||
4
.github/workflows/pr-release.yml
vendored
4
.github/workflows/pr-release.yml
vendored
@@ -34,7 +34,7 @@ jobs:
|
||||
- name: Download artifact from the previous workflow.
|
||||
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
|
||||
id: download-artifact
|
||||
uses: dawidd6/action-download-artifact@v6 # https://github.com/marketplace/actions/download-workflow-artifact
|
||||
uses: dawidd6/action-download-artifact@v7 # https://github.com/marketplace/actions/download-workflow-artifact
|
||||
with:
|
||||
run_id: ${{ github.event.workflow_run.id }}
|
||||
path: artifacts
|
||||
@@ -111,7 +111,7 @@ jobs:
|
||||
|
||||
- name: 'Setup jq'
|
||||
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
|
||||
uses: dcarbone/install-jq-action@v2.1.0
|
||||
uses: dcarbone/install-jq-action@v3.0.1
|
||||
|
||||
# Check that the most recently nightly coincides with 'git merge-base HEAD master'
|
||||
- name: Check merge-base and nightly-testing-YYYY-MM-DD
|
||||
|
||||
@@ -8,11 +8,16 @@ This file contains work-in-progress notes for the upcoming release, as well as p
|
||||
Please check the [releases](https://github.com/leanprover/lean4/releases) page for the current status
|
||||
of each version.
|
||||
|
||||
v4.15.0
|
||||
v4.16.0
|
||||
----------
|
||||
|
||||
Development in progress.
|
||||
|
||||
v4.15.0
|
||||
----------
|
||||
|
||||
Release candidate, release notes will be copied from the branch `releases/v4.15.0` once completed.
|
||||
|
||||
v4.14.0
|
||||
----------
|
||||
|
||||
@@ -88,7 +93,7 @@ v4.13.0
|
||||
* [#4768](https://github.com/leanprover/lean4/pull/4768) fixes a parse error when `..` appears with a `.` on the next line
|
||||
|
||||
* Metaprogramming
|
||||
* [#3090](https://github.com/leanprover/lean4/pull/3090) handles level parameters in `Meta.evalExpr` (@eric-wieser)
|
||||
* [#3090](https://github.com/leanprover/lean4/pull/3090) handles level parameters in `Meta.evalExpr` (@eric-wieser)
|
||||
* [#5401](https://github.com/leanprover/lean4/pull/5401) instance for `Inhabited (TacticM α)` (@alexkeizer)
|
||||
* [#5412](https://github.com/leanprover/lean4/pull/5412) expose Kernel.check for debugging purposes
|
||||
* [#5556](https://github.com/leanprover/lean4/pull/5556) improves the "invalid projection" type inference error in `inferType`.
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
[0829/202002.254:ERROR:crashpad_client_win.cc(868)] not connected
|
||||
@@ -103,10 +103,21 @@ your PR using rebase merge, bypassing the merge queue.
|
||||
As written above, changes in meta code in the current stage usually will only
|
||||
affect later stages. This is an issue in two specific cases.
|
||||
|
||||
* For the special case of *quotations*, it is desirable to have changes in builtin parsers affect them immediately: when the changes in the parser become active in the next stage, builtin macros implemented via quotations should generate syntax trees compatible with the new parser, and quotation patterns in builtin macros and elaborators should be able to match syntax created by the new parser and macros.
|
||||
Since quotations capture the syntax tree structure during execution of the current stage and turn it into code for the next stage, we need to run the current stage's builtin parsers in quotations via the interpreter for this to work.
|
||||
Caveats:
|
||||
* We activate this behavior by default when building stage 1 by setting `-Dinternal.parseQuotWithCurrentStage=true`.
|
||||
We force-disable it inside `macro/macro_rules/elab/elab_rules` via `suppressInsideQuot` as they are guaranteed not to run in the next stage and may need to be run in the current one, so the stage 0 parser is the correct one to use for them.
|
||||
It may be necessary to extend this disabling to functions that contain quotations and are (exclusively) used by one of the mentioned commands. A function using quotations should never be used by both builtin and non-builtin macros/elaborators. Example: https://github.com/leanprover/lean4/blob/f70b7e5722da6101572869d87832494e2f8534b7/src/Lean/Elab/Tactic/Config.lean#L118-L122
|
||||
* The parser needs to be reachable via an `import` statement, otherwise the version of the previous stage will silently be used.
|
||||
* Only the parser code (`Parser.fn`) is affected; all metadata such as leading tokens is taken from the previous stage.
|
||||
|
||||
For an example, see https://github.com/leanprover/lean4/commit/f9dcbbddc48ccab22c7674ba20c5f409823b4cc1#diff-371387aed38bb02bf7761084fd9460e4168ae16d1ffe5de041b47d3ad2d22422R13
|
||||
|
||||
* For *non-builtin* meta code such as `notation`s or `macro`s in
|
||||
`Notation.lean`, we expect changes to affect the current file and all later
|
||||
files of the same stage immediately, just like outside the stdlib. To ensure
|
||||
this, we need to build the stage using `-Dinterpreter.prefer_native=false` -
|
||||
this, we build stage 1 using `-Dinterpreter.prefer_native=false` -
|
||||
otherwise, when executing a macro, the interpreter would notice that there is
|
||||
already a native symbol available for this function and run it instead of the
|
||||
new IR, but the symbol is from the previous stage!
|
||||
@@ -124,26 +135,11 @@ affect later stages. This is an issue in two specific cases.
|
||||
further stages (e.g. after an `update-stage0`) will then need to be compiled
|
||||
with the flag set to `false` again since they will expect the new signature.
|
||||
|
||||
For an example, see https://github.com/leanprover/lean4/commit/da4c46370d85add64ef7ca5e7cc4638b62823fbb.
|
||||
When enabling `prefer_native`, we usually want to *disable* `parseQuotWithCurrentStage` as it would otherwise make quotations use the interpreter after all.
|
||||
However, there is a specific case where we want to set both options to `true`: when we make changes to a non-builtin parser like `simp` that has a builtin elaborator, we cannot have the new parser be active outside of quotations in stage 1 as the builtin elaborator from stage 0 would not understand them; on the other hand, we need quotations in e.g. the builtin `simp` elaborator to produce the new syntax in the next stage.
|
||||
As this issue usually affects only tactics, enabling `debug.byAsSorry` instead of `prefer_native` can be a simpler solution.
|
||||
|
||||
* For the special case of *quotations*, it is desirable to have changes in
|
||||
built-in parsers affect them immediately: when the changes in the parser
|
||||
become active in the next stage, macros implemented via quotations should
|
||||
generate syntax trees compatible with the new parser, and quotation patterns
|
||||
in macro and elaborators should be able to match syntax created by the new
|
||||
parser and macros. Since quotations capture the syntax tree structure during
|
||||
execution of the current stage and turn it into code for the next stage, we
|
||||
need to run the current stage's built-in parsers in quotation via the
|
||||
interpreter for this to work. Caveats:
|
||||
* Since interpreting full parsers is not nearly as cheap and we rarely change
|
||||
built-in syntax, this needs to be opted in using `-Dinternal.parseQuotWithCurrentStage=true`.
|
||||
* The parser needs to be reachable via an `import` statement, otherwise the
|
||||
version of the previous stage will silently be used.
|
||||
* Only the parser code (`Parser.fn`) is affected; all metadata such as leading
|
||||
tokens is taken from the previous stage.
|
||||
|
||||
For an example, see https://github.com/leanprover/lean4/commit/f9dcbbddc48ccab22c7674ba20c5f409823b4cc1#diff-371387aed38bb02bf7761084fd9460e4168ae16d1ffe5de041b47d3ad2d22422
|
||||
(from before the flag defaulted to `false`).
|
||||
For a `prefer_native` example, see https://github.com/leanprover/lean4/commit/da4c46370d85add64ef7ca5e7cc4638b62823fbb.
|
||||
|
||||
To modify either of these flags both for building and editing the stdlib, adjust
|
||||
the code in `stage0/src/stdlib_flags.h`. The flags will automatically be reset
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
source ../../tests/common.sh
|
||||
|
||||
exec_check lean -Dlinter.all=false "$f"
|
||||
exec_check_raw lean -Dlinter.all=false "$f"
|
||||
|
||||
@@ -139,7 +139,7 @@ You might be wondering, how does the context actually move through the `ReaderM`
|
||||
add an input argument to a function by modifying its return type? There is a special command in
|
||||
Lean that will show you the reduced types:
|
||||
-/
|
||||
#reduce ReaderM Environment String -- Environment → String
|
||||
#reduce (types := true) ReaderM Environment String -- Environment → String
|
||||
/-!
|
||||
And you can see here that this type is actually a function! It's a function that takes an
|
||||
`Environment` as input and returns a `String`.
|
||||
@@ -196,4 +196,4 @@ entirely.
|
||||
|
||||
Now it's time to move on to [StateM Monad](states.lean.md) which is like a `ReaderM` that is
|
||||
also updatable.
|
||||
-/
|
||||
-/
|
||||
|
||||
12
script/mathlib-bench
Executable file
12
script/mathlib-bench
Executable file
@@ -0,0 +1,12 @@
|
||||
#! /bin/env bash
|
||||
# Open a Mathlib4 PR for benchmarking a given Lean 4 PR
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
[ $# -eq 1 ] || (echo "usage: $0 <lean4 PR #>"; exit 1)
|
||||
|
||||
LEAN_PR=$1
|
||||
PR_RESPONSE=$(gh api repos/leanprover-community/mathlib4/pulls -X POST -f head=lean-pr-testing-$LEAN_PR -f base=nightly-testing -f title="leanprover/lean4#$LEAN_PR benchmarking" -f draft=true -f body="ignore me")
|
||||
PR_NUMBER=$(echo "$PR_RESPONSE" | jq '.number')
|
||||
echo "opened https://github.com/leanprover-community/mathlib4/pull/$PR_NUMBER"
|
||||
gh api repos/leanprover-community/mathlib4/issues/$PR_NUMBER/comments -X POST -f body="!bench" > /dev/null
|
||||
@@ -10,7 +10,7 @@ endif()
|
||||
include(ExternalProject)
|
||||
project(LEAN CXX C)
|
||||
set(LEAN_VERSION_MAJOR 4)
|
||||
set(LEAN_VERSION_MINOR 15)
|
||||
set(LEAN_VERSION_MINOR 16)
|
||||
set(LEAN_VERSION_PATCH 0)
|
||||
set(LEAN_VERSION_IS_RELEASE 0) # This number is 1 in the release revision, and 0 otherwise.
|
||||
set(LEAN_SPECIAL_VERSION_DESC "" CACHE STRING "Additional version description like 'nightly-2018-03-11'")
|
||||
@@ -51,6 +51,8 @@ option(LLVM "LLVM" OFF)
|
||||
option(USE_GITHASH "GIT_HASH" ON)
|
||||
# When ON we install LICENSE files to CMAKE_INSTALL_PREFIX
|
||||
option(INSTALL_LICENSE "INSTALL_LICENSE" ON)
|
||||
# When ON we install a copy of cadical
|
||||
option(INSTALL_CADICAL "Install a copy of cadical" ON)
|
||||
# When ON thread storage is automatically finalized, it assumes platform support pthreads.
|
||||
# This option is important when using Lean as library that is invoked from a different programming language (e.g., Haskell).
|
||||
option(AUTO_THREAD_FINALIZATION "AUTO_THREAD_FINALIZATION" ON)
|
||||
@@ -120,7 +122,7 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
|
||||
# From https://emscripten.org/docs/compiling/WebAssembly.html#backends:
|
||||
# > The simple and safe thing is to pass all -s flags at both compile and link time.
|
||||
set(EMSCRIPTEN_SETTINGS "-s ALLOW_MEMORY_GROWTH=1 -fwasm-exceptions -pthread -flto")
|
||||
string(APPEND LEANC_EXTRA_FLAGS " -pthread")
|
||||
string(APPEND LEANC_EXTRA_CC_FLAGS " -pthread")
|
||||
string(APPEND LEAN_EXTRA_CXX_FLAGS " -D LEAN_EMSCRIPTEN ${EMSCRIPTEN_SETTINGS}")
|
||||
string(APPEND LEAN_EXTRA_LINKER_FLAGS " ${EMSCRIPTEN_SETTINGS}")
|
||||
endif()
|
||||
@@ -155,11 +157,11 @@ if ((${MULTI_THREAD} MATCHES "ON") AND (${CMAKE_SYSTEM_NAME} MATCHES "Darwin"))
|
||||
endif ()
|
||||
|
||||
# We want explicit stack probes in huge Lean stack frames for robust stack overflow detection
|
||||
string(APPEND LEANC_EXTRA_FLAGS " -fstack-clash-protection")
|
||||
string(APPEND LEANC_EXTRA_CC_FLAGS " -fstack-clash-protection")
|
||||
|
||||
# This makes signed integer overflow guaranteed to match 2's complement.
|
||||
string(APPEND CMAKE_CXX_FLAGS " -fwrapv")
|
||||
string(APPEND LEANC_EXTRA_FLAGS " -fwrapv")
|
||||
string(APPEND LEANC_EXTRA_CC_FLAGS " -fwrapv")
|
||||
|
||||
if(NOT MULTI_THREAD)
|
||||
message(STATUS "Disabled multi-thread support, it will not be safe to run multiple threads in parallel")
|
||||
@@ -449,7 +451,7 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||
string(APPEND TOOLCHAIN_SHARED_LINKER_FLAGS " -Wl,-Bsymbolic")
|
||||
endif()
|
||||
string(APPEND CMAKE_CXX_FLAGS " -fPIC -ftls-model=initial-exec")
|
||||
string(APPEND LEANC_EXTRA_FLAGS " -fPIC")
|
||||
string(APPEND LEANC_EXTRA_CC_FLAGS " -fPIC")
|
||||
string(APPEND TOOLCHAIN_SHARED_LINKER_FLAGS " -Wl,-rpath=\\$$ORIGIN/..:\\$$ORIGIN")
|
||||
string(APPEND LAKESHARED_LINKER_FLAGS " -Wl,--whole-archive ${CMAKE_BINARY_DIR}/lib/temp/libLake.a.export -Wl,--no-whole-archive")
|
||||
string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,-rpath=\\\$ORIGIN/../lib:\\\$ORIGIN/../lib/lean")
|
||||
@@ -462,7 +464,7 @@ elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,-rpath,@executable_path/../lib -Wl,-rpath,@executable_path/../lib/lean")
|
||||
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
|
||||
string(APPEND CMAKE_CXX_FLAGS " -fPIC")
|
||||
string(APPEND LEANC_EXTRA_FLAGS " -fPIC")
|
||||
string(APPEND LEANC_EXTRA_CC_FLAGS " -fPIC")
|
||||
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
|
||||
string(APPEND LAKESHARED_LINKER_FLAGS " -Wl,--out-implib,${CMAKE_BINARY_DIR}/lib/lean/libLake_shared.dll.a -Wl,--whole-archive ${CMAKE_BINARY_DIR}/lib/temp/libLake.a.export -Wl,--no-whole-archive")
|
||||
endif()
|
||||
@@ -477,7 +479,7 @@ if(NOT(${CMAKE_SYSTEM_NAME} MATCHES "Windows") AND NOT(${CMAKE_SYSTEM_NAME} MATC
|
||||
string(APPEND CMAKE_EXE_LINKER_FLAGS " -rdynamic")
|
||||
# hide all other symbols
|
||||
string(APPEND CMAKE_CXX_FLAGS " -fvisibility=hidden -fvisibility-inlines-hidden")
|
||||
string(APPEND LEANC_EXTRA_FLAGS " -fvisibility=hidden")
|
||||
string(APPEND LEANC_EXTRA_CC_FLAGS " -fvisibility=hidden")
|
||||
endif()
|
||||
|
||||
# On Windows, add bcrypt for random number generation
|
||||
@@ -542,9 +544,10 @@ include_directories(${CMAKE_BINARY_DIR}/include) # config.h etc., "public" head
|
||||
string(TOUPPER "${CMAKE_BUILD_TYPE}" uppercase_CMAKE_BUILD_TYPE)
|
||||
string(APPEND LEANC_OPTS " ${CMAKE_CXX_FLAGS_${uppercase_CMAKE_BUILD_TYPE}}")
|
||||
|
||||
# Do embed flag for finding system libraries in dev builds
|
||||
# Do embed flag for finding system headers and libraries in dev builds
|
||||
if(CMAKE_OSX_SYSROOT AND NOT LEAN_STANDALONE)
|
||||
string(APPEND LEANC_EXTRA_FLAGS " ${CMAKE_CXX_SYSROOT_FLAG}${CMAKE_OSX_SYSROOT}")
|
||||
string(APPEND LEANC_EXTRA_CC_FLAGS " ${CMAKE_CXX_SYSROOT_FLAG}${CMAKE_OSX_SYSROOT}")
|
||||
string(APPEND LEAN_EXTRA_LINKER_FLAGS " ${CMAKE_CXX_SYSROOT_FLAG}${CMAKE_OSX_SYSROOT}")
|
||||
endif()
|
||||
|
||||
add_subdirectory(initialize)
|
||||
@@ -616,7 +619,7 @@ else()
|
||||
OUTPUT_NAME leancpp)
|
||||
endif()
|
||||
|
||||
if((${STAGE} GREATER 0) AND CADICAL)
|
||||
if((${STAGE} GREATER 0) AND CADICAL AND INSTALL_CADICAL)
|
||||
add_custom_target(copy-cadical
|
||||
COMMAND cmake -E copy_if_different "${CADICAL}" "${CMAKE_BINARY_DIR}/bin/cadical${CMAKE_EXECUTABLE_SUFFIX}")
|
||||
add_dependencies(leancpp copy-cadical)
|
||||
@@ -738,7 +741,7 @@ file(COPY ${LEAN_SOURCE_DIR}/bin/leanmake DESTINATION ${CMAKE_BINARY_DIR}/bin)
|
||||
|
||||
install(DIRECTORY "${CMAKE_BINARY_DIR}/bin/" USE_SOURCE_PERMISSIONS DESTINATION bin)
|
||||
|
||||
if (${STAGE} GREATER 0 AND CADICAL)
|
||||
if (${STAGE} GREATER 0 AND CADICAL AND INSTALL_CADICAL)
|
||||
install(PROGRAMS "${CADICAL}" DESTINATION bin)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -20,3 +20,5 @@ import Init.Data.Array.MapIdx
|
||||
import Init.Data.Array.Set
|
||||
import Init.Data.Array.Monadic
|
||||
import Init.Data.Array.FinRange
|
||||
import Init.Data.Array.Perm
|
||||
import Init.Data.Array.Find
|
||||
|
||||
@@ -251,7 +251,7 @@ theorem getElem?_attach {xs : Array α} {i : Nat} :
|
||||
theorem getElem_attachWith {xs : Array α} {P : α → Prop} {H : ∀ a ∈ xs, P a}
|
||||
{i : Nat} (h : i < (xs.attachWith P H).size) :
|
||||
(xs.attachWith P H)[i] = ⟨xs[i]'(by simpa using h), H _ (getElem_mem (by simpa using h))⟩ :=
|
||||
getElem_pmap ..
|
||||
getElem_pmap _ _ h
|
||||
|
||||
@[simp]
|
||||
theorem getElem_attach {xs : Array α} {i : Nat} (h : i < xs.attach.size) :
|
||||
|
||||
@@ -474,6 +474,10 @@ def findSomeM? {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f
|
||||
| _ => pure ⟨⟩
|
||||
return none
|
||||
|
||||
/--
|
||||
Note that the universe level is contrained to `Type` here,
|
||||
to avoid having to have the predicate live in `p : α → m (ULift Bool)`.
|
||||
-/
|
||||
@[inline]
|
||||
def findM? {α : Type} {m : Type → Type} [Monad m] (p : α → m Bool) (as : Array α) : m (Option α) := do
|
||||
for a in as do
|
||||
@@ -585,8 +589,12 @@ def zipWithIndex (arr : Array α) : Array (α × Nat) :=
|
||||
arr.mapIdx fun i a => (a, i)
|
||||
|
||||
@[inline]
|
||||
def find? {α : Type} (p : α → Bool) (as : Array α) : Option α :=
|
||||
Id.run <| as.findM? p
|
||||
def find? {α : Type u} (p : α → Bool) (as : Array α) : Option α :=
|
||||
Id.run do
|
||||
for a in as do
|
||||
if p a then
|
||||
return a
|
||||
return none
|
||||
|
||||
@[inline]
|
||||
def findSome? {α : Type u} {β : Type v} (f : α → Option β) (as : Array α) : Option β :=
|
||||
|
||||
@@ -21,15 +21,14 @@ import Init.TacticsExtra
|
||||
## Theorems about `Array`.
|
||||
-/
|
||||
|
||||
|
||||
/-! ### Preliminaries about `Array` needed for `List.toArray` lemmas.
|
||||
|
||||
This section contains only the bare minimum lemmas about `Array`
|
||||
that we need to write lemmas about `List.toArray`.
|
||||
-/
|
||||
namespace Array
|
||||
|
||||
@[simp] theorem mem_toArray {a : α} {l : List α} : a ∈ l.toArray ↔ a ∈ l := by
|
||||
simp [mem_def]
|
||||
|
||||
@[simp] theorem getElem_mk {xs : List α} {i : Nat} (h : i < xs.length) : (Array.mk xs)[i] = xs[i] := rfl
|
||||
|
||||
theorem getElem_eq_getElem_toList {a : Array α} (h : i < a.size) : a[i] = a.toList[i] := rfl
|
||||
|
||||
theorem getElem?_eq_getElem {a : Array α} {i : Nat} (h : i < a.size) : a[i]? = some a[i] :=
|
||||
getElem?_pos ..
|
||||
|
||||
@@ -39,96 +38,34 @@ theorem getElem?_eq_getElem {a : Array α} {i : Nat} (h : i < a.size) : a[i]? =
|
||||
· rw [getElem?_neg a i h]
|
||||
simp_all
|
||||
|
||||
@[simp] theorem none_eq_getElem?_iff {a : Array α} {i : Nat} : none = a[i]? ↔ a.size ≤ i := by
|
||||
simp [eq_comm (a := none)]
|
||||
|
||||
theorem getElem?_eq {a : Array α} {i : Nat} :
|
||||
a[i]? = if h : i < a.size then some a[i] else none := by
|
||||
split
|
||||
· simp_all [getElem?_eq_getElem]
|
||||
· simp_all
|
||||
|
||||
theorem getElem?_eq_some_iff {a : Array α} : a[i]? = some b ↔ ∃ h : i < a.size, a[i] = b := by
|
||||
simp [getElem?_eq]
|
||||
|
||||
theorem some_eq_getElem?_iff {a : Array α} : some b = a[i]? ↔ ∃ h : i < a.size, a[i] = b := by
|
||||
rw [eq_comm, getElem?_eq_some_iff]
|
||||
|
||||
theorem getElem?_eq_getElem?_toList (a : Array α) (i : Nat) : a[i]? = a.toList[i]? := by
|
||||
rw [getElem?_eq]
|
||||
split <;> simp_all
|
||||
|
||||
theorem getElem_push_lt (a : Array α) (x : α) (i : Nat) (h : i < a.size) :
|
||||
have : i < (a.push x).size := by simp [*, Nat.lt_succ_of_le, Nat.le_of_lt]
|
||||
(a.push x)[i] = a[i] := by
|
||||
simp only [push, getElem_eq_getElem_toList, List.concat_eq_append, List.getElem_append_left, h]
|
||||
|
||||
@[simp] theorem getElem_push_eq (a : Array α) (x : α) : (a.push x)[a.size] = x := by
|
||||
simp only [push, getElem_eq_getElem_toList, List.concat_eq_append]
|
||||
rw [List.getElem_append_right] <;> simp [getElem_eq_getElem_toList, Nat.zero_lt_one]
|
||||
|
||||
theorem getElem_push (a : Array α) (x : α) (i : Nat) (h : i < (a.push x).size) :
|
||||
(a.push x)[i] = if h : i < a.size then a[i] else x := by
|
||||
by_cases h' : i < a.size
|
||||
· simp [getElem_push_lt, h']
|
||||
· simp at h
|
||||
simp [getElem_push_lt, Nat.le_antisymm (Nat.le_of_lt_succ h) (Nat.ge_of_not_lt h')]
|
||||
|
||||
@[deprecated getElem_push (since := "2024-10-21")] abbrev get_push := @getElem_push
|
||||
@[deprecated getElem_push_lt (since := "2024-10-21")] abbrev get_push_lt := @getElem_push_lt
|
||||
@[deprecated getElem_push_eq (since := "2024-10-21")] abbrev get_push_eq := @getElem_push_eq
|
||||
|
||||
@[simp] theorem mem_push {a : Array α} {x y : α} : x ∈ a.push y ↔ x ∈ a ∨ x = y := by
|
||||
simp [mem_def]
|
||||
|
||||
theorem mem_push_self {a : Array α} {x : α} : x ∈ a.push x :=
|
||||
mem_push.2 (Or.inr rfl)
|
||||
|
||||
theorem mem_push_of_mem {a : Array α} {x : α} (y : α) (h : x ∈ a) : x ∈ a.push y :=
|
||||
mem_push.2 (Or.inl h)
|
||||
|
||||
theorem getElem_of_mem {a} {l : Array α} (h : a ∈ l) : ∃ (n : Nat) (h : n < l.size), l[n]'h = a := by
|
||||
cases l
|
||||
simp [List.getElem_of_mem (by simpa using h)]
|
||||
|
||||
theorem getElem?_of_mem {a} {l : Array α} (h : a ∈ l) : ∃ n : Nat, l[n]? = some a :=
|
||||
let ⟨n, _, e⟩ := getElem_of_mem h; ⟨n, e ▸ getElem?_eq_getElem _⟩
|
||||
|
||||
theorem mem_of_getElem? {l : Array α} {n : Nat} {a : α} (e : l[n]? = some a) : a ∈ l :=
|
||||
let ⟨_, e⟩ := getElem?_eq_some_iff.1 e; e ▸ getElem_mem ..
|
||||
|
||||
theorem mem_iff_getElem {a} {l : Array α} : a ∈ l ↔ ∃ (n : Nat) (h : n < l.size), l[n]'h = a :=
|
||||
⟨getElem_of_mem, fun ⟨_, _, e⟩ => e ▸ getElem_mem ..⟩
|
||||
|
||||
theorem mem_iff_getElem? {a} {l : Array α} : a ∈ l ↔ ∃ n : Nat, l[n]? = some a := by
|
||||
simp [getElem?_eq_some_iff, mem_iff_getElem]
|
||||
|
||||
theorem forall_getElem {l : Array α} {p : α → Prop} :
|
||||
(∀ (n : Nat) h, p (l[n]'h)) ↔ ∀ a, a ∈ l → p a := by
|
||||
cases l; simp [List.forall_getElem]
|
||||
@[simp] theorem get_eq_getElem (a : Array α) (i : Nat) (h) : a.get i h = a[i] := rfl
|
||||
|
||||
@[simp] theorem get!_eq_getElem! [Inhabited α] (a : Array α) (i : Nat) : a.get! i = a[i]! := by
|
||||
simp [getElem!_def, get!, getD]
|
||||
split <;> rename_i h
|
||||
· simp [getElem?_eq_getElem h]
|
||||
rfl
|
||||
· simp [getElem?_eq_none_iff.2 (by simpa using h)]
|
||||
|
||||
theorem singleton_inj : #[a] = #[b] ↔ a = b := by
|
||||
simp
|
||||
|
||||
theorem singleton_eq_toArray_singleton (a : α) : #[a] = [a].toArray := rfl
|
||||
@[simp] theorem mem_toArray {a : α} {l : List α} : a ∈ l.toArray ↔ a ∈ l := by
|
||||
simp [mem_def]
|
||||
|
||||
end Array
|
||||
|
||||
namespace List
|
||||
|
||||
open Array
|
||||
|
||||
/-! ### Lemmas about `List.toArray`.
|
||||
|
||||
We prefer to pull `List.toArray` outwards.
|
||||
-/
|
||||
namespace List
|
||||
|
||||
open Array
|
||||
|
||||
theorem toArray_inj {a b : List α} (h : a.toArray = b.toArray) : a = b := by
|
||||
cases a with
|
||||
| nil => simpa using h
|
||||
| cons a as =>
|
||||
cases b with
|
||||
| nil => simp at h
|
||||
| cons b bs => simpa using h
|
||||
|
||||
@[simp] theorem size_toArrayAux {a : List α} {b : Array α} :
|
||||
(a.toArrayAux b).size = b.size + a.length := by
|
||||
@@ -293,7 +230,13 @@ theorem findRevM?_toArray [Monad m] [LawfulMonad m] (f : α → m Bool) (l : Lis
|
||||
|
||||
@[simp] theorem find?_toArray (f : α → Bool) (l : List α) :
|
||||
l.toArray.find? f = l.find? f := by
|
||||
rw [Array.find?, ← findM?_id, findM?_toArray, Id.run]
|
||||
rw [Array.find?]
|
||||
simp only [Id.run, Id, Id.pure_eq, Id.bind_eq, forIn_toArray]
|
||||
induction l with
|
||||
| nil => simp
|
||||
| cons a l ih =>
|
||||
simp only [forIn_cons, Id.pure_eq, Id.bind_eq, find?]
|
||||
by_cases f a <;> simp_all
|
||||
|
||||
theorem isPrefixOfAux_toArray_succ [BEq α] (l₁ l₂ : List α) (hle : l₁.length ≤ l₂.length) (i : Nat) :
|
||||
Array.isPrefixOfAux l₁.toArray l₂.toArray hle (i + 1) =
|
||||
@@ -419,10 +362,248 @@ theorem zipWithAll_go_toArray (as : List α) (bs : List β) (f : Option α → O
|
||||
Array.zipWithAll as.toArray bs.toArray f = (List.zipWithAll f as bs).toArray := by
|
||||
simp [Array.zipWithAll, zipWithAll_go_toArray]
|
||||
|
||||
@[simp] theorem toArray_appendList (l₁ l₂ : List α) :
|
||||
l₁.toArray ++ l₂ = (l₁ ++ l₂).toArray := by
|
||||
apply ext'
|
||||
simp
|
||||
|
||||
@[simp] theorem pop_toArray (l : List α) : l.toArray.pop = l.dropLast.toArray := by
|
||||
apply ext'
|
||||
simp
|
||||
|
||||
theorem takeWhile_go_succ (p : α → Bool) (a : α) (l : List α) (i : Nat) :
|
||||
takeWhile.go p (a :: l).toArray (i+1) r = takeWhile.go p l.toArray i r := by
|
||||
rw [takeWhile.go, takeWhile.go]
|
||||
simp only [size_toArray, length_cons, Nat.add_lt_add_iff_right, Array.get_eq_getElem,
|
||||
getElem_toArray, getElem_cons_succ]
|
||||
split
|
||||
rw [takeWhile_go_succ]
|
||||
rfl
|
||||
|
||||
theorem takeWhile_go_toArray (p : α → Bool) (l : List α) (i : Nat) :
|
||||
Array.takeWhile.go p l.toArray i r = r ++ (takeWhile p (l.drop i)).toArray := by
|
||||
induction l generalizing i r with
|
||||
| nil => simp [takeWhile.go]
|
||||
| cons a l ih =>
|
||||
rw [takeWhile.go]
|
||||
cases i with
|
||||
| zero =>
|
||||
simp [takeWhile_go_succ, ih, takeWhile_cons]
|
||||
split <;> simp
|
||||
| succ i =>
|
||||
simp only [size_toArray, length_cons, Nat.add_lt_add_iff_right, Array.get_eq_getElem,
|
||||
getElem_toArray, getElem_cons_succ, drop_succ_cons]
|
||||
split <;> rename_i h₁
|
||||
· rw [takeWhile_go_succ, ih]
|
||||
rw [← getElem_cons_drop_succ_eq_drop h₁, takeWhile_cons]
|
||||
split <;> simp_all
|
||||
· simp_all [drop_eq_nil_of_le]
|
||||
|
||||
@[simp] theorem takeWhile_toArray (p : α → Bool) (l : List α) :
|
||||
l.toArray.takeWhile p = (l.takeWhile p).toArray := by
|
||||
simp [Array.takeWhile, takeWhile_go_toArray]
|
||||
|
||||
end List
|
||||
|
||||
|
||||
namespace Array
|
||||
|
||||
/-! ## Preliminaries -/
|
||||
|
||||
/-! ### toList -/
|
||||
|
||||
theorem toList_inj {a b : Array α} (h : a.toList = b.toList) : a = b := by
|
||||
cases a; cases b; simpa using h
|
||||
|
||||
/-! ### empty -/
|
||||
|
||||
@[simp] theorem empty_eq {xs : Array α} : #[] = xs ↔ xs = #[] := by
|
||||
cases xs <;> simp
|
||||
|
||||
/-! ### size -/
|
||||
|
||||
theorem eq_empty_of_size_eq_zero (h : l.size = 0) : l = #[] := by
|
||||
cases l
|
||||
simp_all
|
||||
|
||||
theorem ne_empty_of_size_eq_add_one (h : l.size = n + 1) : l ≠ #[] := by
|
||||
cases l
|
||||
simpa using List.ne_nil_of_length_eq_add_one h
|
||||
|
||||
theorem ne_empty_of_size_pos (h : 0 < l.size) : l ≠ #[] := by
|
||||
cases l
|
||||
simpa using List.ne_nil_of_length_pos h
|
||||
|
||||
@[simp] theorem size_eq_zero : l.size = 0 ↔ l = #[] :=
|
||||
⟨eq_empty_of_size_eq_zero, fun h => h ▸ rfl⟩
|
||||
|
||||
theorem size_pos_of_mem {a : α} {l : Array α} (h : a ∈ l) : 0 < l.size := by
|
||||
cases l
|
||||
simp only [mem_toArray] at h
|
||||
simpa using List.length_pos_of_mem h
|
||||
|
||||
theorem exists_mem_of_size_pos {l : Array α} (h : 0 < l.size) : ∃ a, a ∈ l := by
|
||||
cases l
|
||||
simpa using List.exists_mem_of_length_pos h
|
||||
|
||||
theorem size_pos_iff_exists_mem {l : Array α} : 0 < l.size ↔ ∃ a, a ∈ l :=
|
||||
⟨exists_mem_of_size_pos, fun ⟨_, h⟩ => size_pos_of_mem h⟩
|
||||
|
||||
theorem exists_mem_of_size_eq_add_one {l : Array α} (h : l.size = n + 1) : ∃ a, a ∈ l := by
|
||||
cases l
|
||||
simpa using List.exists_mem_of_length_eq_add_one h
|
||||
|
||||
theorem size_pos {l : Array α} : 0 < l.size ↔ l ≠ #[] :=
|
||||
Nat.pos_iff_ne_zero.trans (not_congr size_eq_zero)
|
||||
|
||||
theorem size_eq_one {l : Array α} : l.size = 1 ↔ ∃ a, l = #[a] := by
|
||||
cases l
|
||||
simpa using List.length_eq_one
|
||||
|
||||
/-! ### push -/
|
||||
|
||||
theorem push_ne_empty {a : α} {xs : Array α} : xs.push a ≠ #[] := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[simp] theorem push_ne_self {a : α} {xs : Array α} : xs.push a ≠ xs := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[simp] theorem ne_push_self {a : α} {xs : Array α} : xs ≠ xs.push a := by
|
||||
rw [ne_eq, eq_comm]
|
||||
simp
|
||||
|
||||
theorem back_eq_of_push_eq {a b : α} {xs ys : Array α} (h : xs.push a = ys.push b) : a = b := by
|
||||
cases xs
|
||||
cases ys
|
||||
simp only [List.push_toArray, mk.injEq] at h
|
||||
replace h := List.append_inj_right' h (by simp)
|
||||
simpa using h
|
||||
|
||||
theorem pop_eq_of_push_eq {a b : α} {xs ys : Array α} (h : xs.push a = ys.push b) : xs = ys := by
|
||||
cases xs
|
||||
cases ys
|
||||
simp at h
|
||||
replace h := List.append_inj_left' h (by simp)
|
||||
simp [h]
|
||||
|
||||
theorem push_inj_left {a : α} {xs ys : Array α} : xs.push a = ys.push a ↔ xs = ys :=
|
||||
⟨pop_eq_of_push_eq, fun h => by simp [h]⟩
|
||||
|
||||
theorem push_inj_right {a b : α} {xs : Array α} : xs.push a = xs.push b ↔ a = b :=
|
||||
⟨back_eq_of_push_eq, fun h => by simp [h]⟩
|
||||
|
||||
theorem push_eq_push {a b : α} {xs ys : Array α} : xs.push a = ys.push b ↔ a = b ∧ xs = ys := by
|
||||
constructor
|
||||
· intro h
|
||||
exact ⟨back_eq_of_push_eq h, pop_eq_of_push_eq h⟩
|
||||
· rintro ⟨rfl, rfl⟩
|
||||
rfl
|
||||
|
||||
theorem exists_push_of_ne_empty {xs : Array α} (h : xs ≠ #[]) :
|
||||
∃ (ys : Array α) (a : α), xs = ys.push a := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp only [ne_eq, mk.injEq] at h
|
||||
exact ⟨(xs.take (xs.length - 1)).toArray, xs.getLast h, by simp⟩
|
||||
|
||||
theorem ne_empty_iff_exists_push {xs : Array α} :
|
||||
xs ≠ #[] ↔ ∃ (ys : Array α) (a : α), xs = ys.push a :=
|
||||
⟨exists_push_of_ne_empty, fun ⟨_, _, eq⟩ => eq.symm ▸ push_ne_empty⟩
|
||||
|
||||
theorem exists_push_of_size_pos {xs : Array α} (h : 0 < xs.size) :
|
||||
∃ (ys : Array α) (a : α), xs = ys.push a := by
|
||||
replace h : xs ≠ #[] := size_pos.mp h
|
||||
exact exists_push_of_ne_empty h
|
||||
|
||||
theorem size_pos_iff_exists_push {xs : Array α} :
|
||||
0 < xs.size ↔ ∃ (ys : Array α) (a : α), xs = ys.push a :=
|
||||
⟨exists_push_of_size_pos, fun ⟨_, _, eq⟩ => by simp [eq]⟩
|
||||
|
||||
theorem exists_push_of_size_eq_add_one {xs : Array α} (h : xs.size = n + 1) :
|
||||
∃ (ys : Array α) (a : α), xs = ys.push a :=
|
||||
exists_push_of_size_pos (by simp [h])
|
||||
|
||||
/-! ## L[i] and L[i]? -/
|
||||
|
||||
@[deprecated List.getElem_toArray (since := "2024-11-29")]
|
||||
theorem getElem_mk {xs : List α} {i : Nat} (h : i < xs.length) : (Array.mk xs)[i] = xs[i] := rfl
|
||||
|
||||
theorem getElem_eq_getElem_toList {a : Array α} (h : i < a.size) : a[i] = a.toList[i] := rfl
|
||||
|
||||
@[simp] theorem none_eq_getElem?_iff {a : Array α} {i : Nat} : none = a[i]? ↔ a.size ≤ i := by
|
||||
simp [eq_comm (a := none)]
|
||||
|
||||
theorem getElem?_eq {a : Array α} {i : Nat} :
|
||||
a[i]? = if h : i < a.size then some a[i] else none := by
|
||||
split
|
||||
· simp_all [getElem?_eq_getElem]
|
||||
· simp_all
|
||||
|
||||
theorem getElem?_eq_some_iff {a : Array α} : a[i]? = some b ↔ ∃ h : i < a.size, a[i] = b := by
|
||||
simp [getElem?_eq]
|
||||
|
||||
theorem some_eq_getElem?_iff {a : Array α} : some b = a[i]? ↔ ∃ h : i < a.size, a[i] = b := by
|
||||
rw [eq_comm, getElem?_eq_some_iff]
|
||||
|
||||
theorem getElem?_eq_getElem?_toList (a : Array α) (i : Nat) : a[i]? = a.toList[i]? := by
|
||||
rw [getElem?_eq]
|
||||
split <;> simp_all
|
||||
|
||||
theorem getElem_push_lt (a : Array α) (x : α) (i : Nat) (h : i < a.size) :
|
||||
have : i < (a.push x).size := by simp [*, Nat.lt_succ_of_le, Nat.le_of_lt]
|
||||
(a.push x)[i] = a[i] := by
|
||||
simp only [push, getElem_eq_getElem_toList, List.concat_eq_append, List.getElem_append_left, h]
|
||||
|
||||
@[simp] theorem getElem_push_eq (a : Array α) (x : α) : (a.push x)[a.size] = x := by
|
||||
simp only [push, getElem_eq_getElem_toList, List.concat_eq_append]
|
||||
rw [List.getElem_append_right] <;> simp [getElem_eq_getElem_toList, Nat.zero_lt_one]
|
||||
|
||||
theorem getElem_push (a : Array α) (x : α) (i : Nat) (h : i < (a.push x).size) :
|
||||
(a.push x)[i] = if h : i < a.size then a[i] else x := by
|
||||
by_cases h' : i < a.size
|
||||
· simp [getElem_push_lt, h']
|
||||
· simp at h
|
||||
simp [getElem_push_lt, Nat.le_antisymm (Nat.le_of_lt_succ h) (Nat.ge_of_not_lt h')]
|
||||
|
||||
@[deprecated getElem_push (since := "2024-10-21")] abbrev get_push := @getElem_push
|
||||
@[deprecated getElem_push_lt (since := "2024-10-21")] abbrev get_push_lt := @getElem_push_lt
|
||||
@[deprecated getElem_push_eq (since := "2024-10-21")] abbrev get_push_eq := @getElem_push_eq
|
||||
|
||||
@[simp] theorem mem_push {a : Array α} {x y : α} : x ∈ a.push y ↔ x ∈ a ∨ x = y := by
|
||||
simp [mem_def]
|
||||
|
||||
theorem mem_push_self {a : Array α} {x : α} : x ∈ a.push x :=
|
||||
mem_push.2 (Or.inr rfl)
|
||||
|
||||
theorem mem_push_of_mem {a : Array α} {x : α} (y : α) (h : x ∈ a) : x ∈ a.push y :=
|
||||
mem_push.2 (Or.inl h)
|
||||
|
||||
theorem getElem_of_mem {a} {l : Array α} (h : a ∈ l) : ∃ (n : Nat) (h : n < l.size), l[n]'h = a := by
|
||||
cases l
|
||||
simp [List.getElem_of_mem (by simpa using h)]
|
||||
|
||||
theorem getElem?_of_mem {a} {l : Array α} (h : a ∈ l) : ∃ n : Nat, l[n]? = some a :=
|
||||
let ⟨n, _, e⟩ := getElem_of_mem h; ⟨n, e ▸ getElem?_eq_getElem _⟩
|
||||
|
||||
theorem mem_of_getElem? {l : Array α} {n : Nat} {a : α} (e : l[n]? = some a) : a ∈ l :=
|
||||
let ⟨_, e⟩ := getElem?_eq_some_iff.1 e; e ▸ getElem_mem ..
|
||||
|
||||
theorem mem_iff_getElem {a} {l : Array α} : a ∈ l ↔ ∃ (n : Nat) (h : n < l.size), l[n]'h = a :=
|
||||
⟨getElem_of_mem, fun ⟨_, _, e⟩ => e ▸ getElem_mem ..⟩
|
||||
|
||||
theorem mem_iff_getElem? {a} {l : Array α} : a ∈ l ↔ ∃ n : Nat, l[n]? = some a := by
|
||||
simp [getElem?_eq_some_iff, mem_iff_getElem]
|
||||
|
||||
theorem forall_getElem {l : Array α} {p : α → Prop} :
|
||||
(∀ (n : Nat) h, p (l[n]'h)) ↔ ∀ a, a ∈ l → p a := by
|
||||
cases l; simp [List.forall_getElem]
|
||||
|
||||
theorem singleton_inj : #[a] = #[b] ↔ a = b := by
|
||||
simp
|
||||
|
||||
theorem singleton_eq_toArray_singleton (a : α) : #[a] = [a].toArray := rfl
|
||||
|
||||
@[simp] theorem singleton_def (v : α) : singleton v = #[v] := rfl
|
||||
|
||||
-- This is a duplicate of `List.toArray_toList`.
|
||||
@@ -496,6 +677,11 @@ where
|
||||
simp only [← length_toList]
|
||||
simp
|
||||
|
||||
@[simp] theorem mapM_empty [Monad m] (f : α → m β) : mapM f #[] = pure #[] := by
|
||||
rw [mapM, mapM.map]; rfl
|
||||
|
||||
@[simp] theorem map_empty (f : α → β) : map f #[] = #[] := mapM_empty f
|
||||
|
||||
@[simp] theorem appendList_nil (arr : Array α) : arr ++ ([] : List α) = arr := Array.ext' (by simp)
|
||||
|
||||
@[simp] theorem appendList_cons (arr : Array α) (a : α) (l : List α) :
|
||||
@@ -535,8 +721,6 @@ theorem size_uset (a : Array α) (v i h) : (uset a i v h).size = a.size := by si
|
||||
|
||||
/-! # get -/
|
||||
|
||||
@[simp] theorem get_eq_getElem (a : Array α) (i : Nat) (h) : a.get i h = a[i] := rfl
|
||||
|
||||
theorem getElem?_lt
|
||||
(a : Array α) {i : Nat} (h : i < a.size) : a[i]? = some a[i] := dif_pos h
|
||||
|
||||
@@ -601,11 +785,26 @@ theorem getElem_set (a : Array α) (i : Nat) (h' : i < a.size) (v : α) (j : Nat
|
||||
else
|
||||
simp [setIfInBounds, h]
|
||||
|
||||
theorem getElem_setIfInBounds (a : Array α) (i : Nat) (v : α) (j : Nat)
|
||||
(hj : j < (setIfInBounds a i v).size) :
|
||||
(setIfInBounds a i v)[j]'hj = if i = j then v else a[j]'(by simpa using hj) := by
|
||||
simp only [setIfInBounds]
|
||||
split
|
||||
· simp [getElem_set]
|
||||
· simp only [size_setIfInBounds] at hj
|
||||
rw [if_neg]
|
||||
omega
|
||||
|
||||
@[simp] theorem getElem_setIfInBounds_eq (a : Array α) {i : Nat} (v : α) (h : _) :
|
||||
(setIfInBounds a i v)[i]'h = v := by
|
||||
simp at h
|
||||
simp only [setIfInBounds, h, ↓reduceDIte, getElem_set_eq]
|
||||
|
||||
@[simp] theorem getElem_setIfInBounds_ne (a : Array α) {i : Nat} (v : α) {j : Nat}
|
||||
(hj : j < (setIfInBounds a i v).size) (h : i ≠ j) :
|
||||
(setIfInBounds a i v)[j]'hj = a[j]'(by simpa using hj) := by
|
||||
simp [getElem_setIfInBounds, h]
|
||||
|
||||
@[simp]
|
||||
theorem getElem?_setIfInBounds_eq (a : Array α) {i : Nat} (p : i < a.size) (v : α) :
|
||||
(a.setIfInBounds i v)[i]? = some v := by
|
||||
@@ -807,11 +1006,6 @@ theorem get_set (a : Array α) (i : Nat) (hi : i < a.size) (j : Nat) (hj : j < a
|
||||
(h : i ≠ j) : (a.set i v)[j]'(by simp [*]) = a[j] := by
|
||||
simp only [set, getElem_eq_getElem_toList, List.getElem_set_ne h]
|
||||
|
||||
theorem getElem_setIfInBounds (a : Array α) (i : Nat) (v : α) (h : i < (setIfInBounds a i v).size) :
|
||||
(setIfInBounds a i v)[i] = v := by
|
||||
simp at h
|
||||
simp only [setIfInBounds, h, ↓reduceDIte, getElem_set_eq]
|
||||
|
||||
theorem set_set (a : Array α) (i : Nat) (h) (v v' : α) :
|
||||
(a.set i v h).set i v' (by simp [h]) = a.set i v' := by simp [set, List.set_set]
|
||||
|
||||
@@ -855,11 +1049,6 @@ theorem swapAt!_def (a : Array α) (i : Nat) (v : α) (h : i < a.size) :
|
||||
a.pop[i] = a[i]'(Nat.lt_of_lt_of_le (a.size_pop ▸ hi) (Nat.sub_le _ _)) :=
|
||||
List.getElem_dropLast ..
|
||||
|
||||
theorem eq_empty_of_size_eq_zero {as : Array α} (h : as.size = 0) : as = #[] := by
|
||||
apply ext
|
||||
· simp [h]
|
||||
· intros; contradiction
|
||||
|
||||
theorem eq_push_pop_back!_of_size_ne_zero [Inhabited α] {as : Array α} (h : as.size ≠ 0) :
|
||||
as = as.pop.push as.back! := by
|
||||
apply ext
|
||||
@@ -1376,6 +1565,18 @@ theorem getElem?_append {as bs : Array α} {n : Nat} :
|
||||
· exact getElem?_append_left h
|
||||
· exact getElem?_append_right (by simpa using h)
|
||||
|
||||
@[simp] theorem toArray_eq_append_iff {xs : List α} {as bs : Array α} :
|
||||
xs.toArray = as ++ bs ↔ xs = as.toList ++ bs.toList := by
|
||||
cases as
|
||||
cases bs
|
||||
simp
|
||||
|
||||
@[simp] theorem append_eq_toArray_iff {as bs : Array α} {xs : List α} :
|
||||
as ++ bs = xs.toArray ↔ as.toList ++ bs.toList = xs := by
|
||||
cases as
|
||||
cases bs
|
||||
simp
|
||||
|
||||
/-! ### flatten -/
|
||||
|
||||
@[simp] theorem toList_flatten {l : Array (Array α)} :
|
||||
@@ -1670,8 +1871,6 @@ instance [DecidableEq α] (a : α) (as : Array α) : Decidable (a ∈ as) :=
|
||||
|
||||
/-! ### swap -/
|
||||
|
||||
open Fin
|
||||
|
||||
@[simp] theorem getElem_swap_right (a : Array α) {i j : Nat} {hi hj} :
|
||||
(a.swap i j hi hj)[j]'(by simpa using hj) = a[i] := by
|
||||
simp [swap_def, getElem_set]
|
||||
@@ -1690,7 +1889,7 @@ theorem getElem_swap' (a : Array α) (i j : Nat) {hi hj} (k : Nat) (hk : k < a.s
|
||||
· simp_all only [getElem_swap_left]
|
||||
· split <;> simp_all
|
||||
|
||||
theorem getElem_swap (a : Array α) (i j : Nat) {hi hj}(k : Nat) (hk : k < (a.swap i j).size) :
|
||||
theorem getElem_swap (a : Array α) (i j : Nat) {hi hj} (k : Nat) (hk : k < (a.swap i j).size) :
|
||||
(a.swap i j hi hj)[k] = if k = i then a[j] else if k = j then a[i] else a[k]'(by simp_all) := by
|
||||
apply getElem_swap'
|
||||
|
||||
@@ -1753,6 +1952,13 @@ theorem eraseIdx_eq_eraseIdxIfInBounds {a : Array α} {i : Nat} (h : i < a.size)
|
||||
(as.zip bs).size = min as.size bs.size :=
|
||||
as.size_zipWith bs Prod.mk
|
||||
|
||||
@[simp] theorem getElem_zipWith (as : Array α) (bs : Array β) (f : α → β → γ) (i : Nat)
|
||||
(hi : i < (as.zipWith bs f).size) :
|
||||
(as.zipWith bs f)[i] = f (as[i]'(by simp at hi; omega)) (bs[i]'(by simp at hi; omega)) := by
|
||||
cases as
|
||||
cases bs
|
||||
simp
|
||||
|
||||
/-! ### findSomeM?, findM?, findSome?, find? -/
|
||||
|
||||
@[simp] theorem findSomeM?_toList [Monad m] [LawfulMonad m] (p : α → m (Option β)) (as : Array α) :
|
||||
@@ -1812,11 +2018,6 @@ Our goal is to have `simp` "pull `List.toArray` outwards" as much as possible.
|
||||
apply ext'
|
||||
simp
|
||||
|
||||
@[simp] theorem toArray_appendList (l₁ l₂ : List α) :
|
||||
l₁.toArray ++ l₂ = (l₁ ++ l₂).toArray := by
|
||||
apply ext'
|
||||
simp
|
||||
|
||||
@[simp] theorem set_toArray (l : List α) (i : Fin l.toArray.size) (a : α) :
|
||||
l.toArray.set i a = (l.set i a).toArray := by
|
||||
apply ext'
|
||||
@@ -1880,10 +2081,6 @@ theorem all_toArray (p : α → Bool) (l : List α) : l.toArray.all p = l.all p
|
||||
apply ext'
|
||||
simp
|
||||
|
||||
@[simp] theorem pop_toArray (l : List α) : l.toArray.pop = l.dropLast.toArray := by
|
||||
apply ext'
|
||||
simp
|
||||
|
||||
@[simp] theorem reverse_toArray (l : List α) : l.toArray.reverse = l.reverse.toArray := by
|
||||
apply ext'
|
||||
simp
|
||||
@@ -1929,38 +2126,6 @@ theorem filterMap_toArray (f : α → Option β) (l : List α) :
|
||||
@[simp] theorem toArray_ofFn (f : Fin n → α) : (ofFn f).toArray = Array.ofFn f := by
|
||||
ext <;> simp
|
||||
|
||||
theorem takeWhile_go_succ (p : α → Bool) (a : α) (l : List α) (i : Nat) :
|
||||
takeWhile.go p (a :: l).toArray (i+1) r = takeWhile.go p l.toArray i r := by
|
||||
rw [takeWhile.go, takeWhile.go]
|
||||
simp only [size_toArray, length_cons, Nat.add_lt_add_iff_right, Array.get_eq_getElem,
|
||||
getElem_toArray, getElem_cons_succ]
|
||||
split
|
||||
rw [takeWhile_go_succ]
|
||||
rfl
|
||||
|
||||
theorem takeWhile_go_toArray (p : α → Bool) (l : List α) (i : Nat) :
|
||||
Array.takeWhile.go p l.toArray i r = r ++ (takeWhile p (l.drop i)).toArray := by
|
||||
induction l generalizing i r with
|
||||
| nil => simp [takeWhile.go]
|
||||
| cons a l ih =>
|
||||
rw [takeWhile.go]
|
||||
cases i with
|
||||
| zero =>
|
||||
simp [takeWhile_go_succ, ih, takeWhile_cons]
|
||||
split <;> simp
|
||||
| succ i =>
|
||||
simp only [size_toArray, length_cons, Nat.add_lt_add_iff_right, Array.get_eq_getElem,
|
||||
getElem_toArray, getElem_cons_succ, drop_succ_cons]
|
||||
split <;> rename_i h₁
|
||||
· rw [takeWhile_go_succ, ih]
|
||||
rw [← getElem_cons_drop_succ_eq_drop h₁, takeWhile_cons]
|
||||
split <;> simp_all
|
||||
· simp_all [drop_eq_nil_of_le]
|
||||
|
||||
@[simp] theorem takeWhile_toArray (p : α → Bool) (l : List α) :
|
||||
l.toArray.takeWhile p = (l.takeWhile p).toArray := by
|
||||
simp [Array.takeWhile, takeWhile_go_toArray]
|
||||
|
||||
@[simp] theorem eraseIdx_toArray (l : List α) (i : Nat) (h : i < l.toArray.size) :
|
||||
l.toArray.eraseIdx i h = (l.eraseIdx i).toArray := by
|
||||
rw [Array.eraseIdx]
|
||||
@@ -2094,6 +2259,11 @@ theorem foldr_map' (g : α → β) (f : α → α → α) (f' : β → β → β
|
||||
cases as
|
||||
simp
|
||||
|
||||
@[simp] theorem getElem_reverse (as : Array α) (i : Nat) (hi : i < as.reverse.size) :
|
||||
(as.reverse)[i] = as[as.size - 1 - i]'(by simp at hi; omega) := by
|
||||
cases as
|
||||
simp [Array.getElem_reverse]
|
||||
|
||||
/-! ### findSomeRevM?, findRevM?, findSomeRev?, findRev? -/
|
||||
|
||||
@[simp] theorem findSomeRevM?_eq_findSomeM?_reverse
|
||||
|
||||
65
src/Init/Data/Array/Perm.lean
Normal file
65
src/Init/Data/Array/Perm.lean
Normal file
@@ -0,0 +1,65 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Kim Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.List.Nat.Perm
|
||||
import Init.Data.Array.Lemmas
|
||||
|
||||
namespace Array
|
||||
|
||||
open List
|
||||
|
||||
/--
|
||||
`Perm as bs` asserts that `as` and `bs` are permutations of each other.
|
||||
|
||||
This is a wrapper around `List.Perm`, and for now has much less API.
|
||||
For more complicated verification, use `perm_iff_toList_perm` and the `List` API.
|
||||
-/
|
||||
def Perm (as bs : Array α) : Prop :=
|
||||
as.toList ~ bs.toList
|
||||
|
||||
@[inherit_doc] scoped infixl:50 " ~ " => Perm
|
||||
|
||||
theorem perm_iff_toList_perm {as bs : Array α} : as ~ bs ↔ as.toList ~ bs.toList := Iff.rfl
|
||||
|
||||
@[simp] theorem perm_toArray (as bs : List α) : as.toArray ~ bs.toArray ↔ as ~ bs := by
|
||||
simp [perm_iff_toList_perm]
|
||||
|
||||
@[simp, refl] protected theorem Perm.refl (l : Array α) : l ~ l := by
|
||||
cases l
|
||||
simp
|
||||
|
||||
protected theorem Perm.rfl {l : List α} : l ~ l := .refl _
|
||||
|
||||
theorem Perm.of_eq {l₁ l₂ : Array α} (h : l₁ = l₂) : l₁ ~ l₂ := h ▸ .rfl
|
||||
|
||||
protected theorem Perm.symm {l₁ l₂ : Array α} (h : l₁ ~ l₂) : l₂ ~ l₁ := by
|
||||
cases l₁; cases l₂
|
||||
simp only [perm_toArray] at h
|
||||
simpa using h.symm
|
||||
|
||||
protected theorem Perm.trans {l₁ l₂ l₃ : Array α} (h₁ : l₁ ~ l₂) (h₂ : l₂ ~ l₃) : l₁ ~ l₃ := by
|
||||
cases l₁; cases l₂; cases l₃
|
||||
simp only [perm_toArray] at h₁ h₂
|
||||
simpa using h₁.trans h₂
|
||||
|
||||
instance : Trans (Perm (α := α)) (Perm (α := α)) (Perm (α := α)) where
|
||||
trans h₁ h₂ := Perm.trans h₁ h₂
|
||||
|
||||
theorem perm_comm {l₁ l₂ : Array α} : l₁ ~ l₂ ↔ l₂ ~ l₁ := ⟨Perm.symm, Perm.symm⟩
|
||||
|
||||
theorem Perm.push (x y : α) {l₁ l₂ : Array α} (p : l₁ ~ l₂) :
|
||||
(l₁.push x).push y ~ (l₂.push y).push x := by
|
||||
cases l₁; cases l₂
|
||||
simp only [perm_toArray] at p
|
||||
simp only [push_toArray, List.append_assoc, singleton_append, perm_toArray]
|
||||
exact p.append (Perm.swap' _ _ Perm.nil)
|
||||
|
||||
theorem swap_perm {as : Array α} {i j : Nat} (h₁ : i < as.size) (h₂ : j < as.size) :
|
||||
as.swap i j ~ as := by
|
||||
simp only [swap, perm_iff_toList_perm, toList_set]
|
||||
apply set_set_perm
|
||||
|
||||
end Array
|
||||
@@ -4,46 +4,46 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Array.Basic
|
||||
import Init.Data.Vector.Basic
|
||||
import Init.Data.Ord
|
||||
|
||||
namespace Array
|
||||
-- TODO: remove the [Inhabited α] parameters as soon as we have the tactic framework for automating proof generation and using Array.fget
|
||||
|
||||
def qpartition (as : Array α) (lt : α → α → Bool) (lo hi : Nat) : Nat × Array α :=
|
||||
if h : as.size = 0 then (0, as) else have : Inhabited α := ⟨as[0]'(by revert h; cases as.size <;> simp)⟩ -- TODO: remove
|
||||
private def qpartition {n} (as : Vector α n) (lt : α → α → Bool) (lo hi : Nat)
|
||||
(hlo : lo < n := by omega) (hhi : hi < n := by omega) : {n : Nat // lo ≤ n} × Vector α n :=
|
||||
let mid := (lo + hi) / 2
|
||||
let as := if lt (as.get! mid) (as.get! lo) then as.swapIfInBounds lo mid else as
|
||||
let as := if lt (as.get! hi) (as.get! lo) then as.swapIfInBounds lo hi else as
|
||||
let as := if lt (as.get! mid) (as.get! hi) then as.swapIfInBounds mid hi else as
|
||||
let pivot := as.get! hi
|
||||
let rec loop (as : Array α) (i j : Nat) :=
|
||||
let as := if lt as[mid] as[lo] then as.swap lo mid else as
|
||||
let as := if lt as[hi] as[lo] then as.swap lo hi else as
|
||||
let as := if lt as[mid] as[hi] then as.swap mid hi else as
|
||||
let pivot := as[hi]
|
||||
let rec loop (as : Vector α n) (i j : Nat)
|
||||
(ilo : lo ≤ i := by omega) (jh : j < n := by omega) (w : i ≤ j := by omega) :=
|
||||
if h : j < hi then
|
||||
if lt (as.get! j) pivot then
|
||||
let as := as.swapIfInBounds i j
|
||||
loop as (i+1) (j+1)
|
||||
if lt as[j] pivot then
|
||||
loop (as.swap i j) (i+1) (j+1)
|
||||
else
|
||||
loop as i (j+1)
|
||||
else
|
||||
let as := as.swapIfInBounds i hi
|
||||
(i, as)
|
||||
termination_by hi - j
|
||||
decreasing_by all_goals simp_wf; decreasing_trivial_pre_omega
|
||||
(⟨i, ilo⟩, as.swap i hi)
|
||||
loop as lo lo
|
||||
|
||||
@[inline] partial def qsort (as : Array α) (lt : α → α → Bool) (low := 0) (high := as.size - 1) : Array α :=
|
||||
let rec @[specialize] sort (as : Array α) (low high : Nat) :=
|
||||
if low < high then
|
||||
let p := qpartition as lt low high;
|
||||
-- TODO: fix `partial` support in the equation compiler, it breaks if we use `let (mid, as) := partition as lt low high`
|
||||
let mid := p.1
|
||||
let as := p.2
|
||||
if mid >= high then as
|
||||
@[inline] def qsort (as : Array α) (lt : α → α → Bool := by exact (· < ·))
|
||||
(low := 0) (high := as.size - 1) : Array α :=
|
||||
let rec @[specialize] sort {n} (as : Vector α n) (lo hi : Nat)
|
||||
(hlo : lo < n := by omega) (hhi : hi < n := by omega) :=
|
||||
if h₁ : lo < hi then
|
||||
let ⟨⟨mid, hmid⟩, as⟩ := qpartition as lt lo hi
|
||||
if h₂ : mid ≥ hi then
|
||||
as
|
||||
else
|
||||
let as := sort as low mid
|
||||
sort as (mid+1) high
|
||||
sort (sort as lo mid) (mid+1) hi
|
||||
else as
|
||||
sort as low high
|
||||
if h : as.size = 0 then
|
||||
as
|
||||
else
|
||||
let low := min low (as.size - 1)
|
||||
let high := min high (as.size - 1)
|
||||
sort ⟨as, rfl⟩ low high |>.toArray
|
||||
|
||||
set_option linter.unusedVariables.funArgs false in
|
||||
/--
|
||||
|
||||
@@ -351,17 +351,17 @@ end relations
|
||||
section cast
|
||||
|
||||
/-- `cast eq x` embeds `x` into an equal `BitVec` type. -/
|
||||
@[inline] def cast (eq : n = m) (x : BitVec n) : BitVec m := .ofNatLt x.toNat (eq ▸ x.isLt)
|
||||
@[inline] protected def cast (eq : n = m) (x : BitVec n) : BitVec m := .ofNatLt x.toNat (eq ▸ x.isLt)
|
||||
|
||||
@[simp] theorem cast_ofNat {n m : Nat} (h : n = m) (x : Nat) :
|
||||
cast h (BitVec.ofNat n x) = BitVec.ofNat m x := by
|
||||
(BitVec.ofNat n x).cast h = BitVec.ofNat m x := by
|
||||
subst h; rfl
|
||||
|
||||
@[simp] theorem cast_cast {n m k : Nat} (h₁ : n = m) (h₂ : m = k) (x : BitVec n) :
|
||||
cast h₂ (cast h₁ x) = cast (h₁ ▸ h₂) x :=
|
||||
(x.cast h₁).cast h₂ = x.cast (h₁ ▸ h₂) :=
|
||||
rfl
|
||||
|
||||
@[simp] theorem cast_eq {n : Nat} (h : n = n) (x : BitVec n) : cast h x = x := rfl
|
||||
@[simp] theorem cast_eq {n : Nat} (h : n = n) (x : BitVec n) : x.cast h = x := rfl
|
||||
|
||||
/--
|
||||
Extraction of bits `start` to `start + len - 1` from a bit vector of size `n` to yield a
|
||||
|
||||
@@ -410,21 +410,21 @@ theorem toNat_ge_of_msb_true {x : BitVec n} (p : BitVec.msb x = true) : x.toNat
|
||||
|
||||
/-! ### cast -/
|
||||
|
||||
@[simp, bv_toNat] theorem toNat_cast (h : w = v) (x : BitVec w) : (cast h x).toNat = x.toNat := rfl
|
||||
@[simp, bv_toNat] theorem toNat_cast (h : w = v) (x : BitVec w) : (x.cast h).toNat = x.toNat := rfl
|
||||
@[simp] theorem toFin_cast (h : w = v) (x : BitVec w) :
|
||||
(cast h x).toFin = x.toFin.cast (by rw [h]) :=
|
||||
(x.cast h).toFin = x.toFin.cast (by rw [h]) :=
|
||||
rfl
|
||||
|
||||
@[simp] theorem getLsbD_cast (h : w = v) (x : BitVec w) : (cast h x).getLsbD i = x.getLsbD i := by
|
||||
@[simp] theorem getLsbD_cast (h : w = v) (x : BitVec w) : (x.cast h).getLsbD i = x.getLsbD i := by
|
||||
subst h; simp
|
||||
|
||||
@[simp] theorem getMsbD_cast (h : w = v) (x : BitVec w) : (cast h x).getMsbD i = x.getMsbD i := by
|
||||
@[simp] theorem getMsbD_cast (h : w = v) (x : BitVec w) : (x.cast h).getMsbD i = x.getMsbD i := by
|
||||
subst h; simp
|
||||
|
||||
@[simp] theorem getElem_cast (h : w = v) (x : BitVec w) (p : i < v) : (cast h x)[i] = x[i] := by
|
||||
@[simp] theorem getElem_cast (h : w = v) (x : BitVec w) (p : i < v) : (x.cast h)[i] = x[i] := by
|
||||
subst h; simp
|
||||
|
||||
@[simp] theorem msb_cast (h : w = v) (x : BitVec w) : (cast h x).msb = x.msb := by
|
||||
@[simp] theorem msb_cast (h : w = v) (x : BitVec w) : (x.cast h).msb = x.msb := by
|
||||
simp [BitVec.msb]
|
||||
|
||||
/-! ### toInt/ofInt -/
|
||||
@@ -658,7 +658,7 @@ theorem getElem?_setWidth (m : Nat) (x : BitVec n) (i : Nat) :
|
||||
<;> omega
|
||||
|
||||
@[simp] theorem cast_setWidth (h : v = v') (x : BitVec w) :
|
||||
cast h (setWidth v x) = setWidth v' x := by
|
||||
(x.setWidth v).cast h = x.setWidth v' := by
|
||||
subst h
|
||||
ext
|
||||
simp
|
||||
@@ -671,7 +671,7 @@ theorem getElem?_setWidth (m : Nat) (x : BitVec n) (i : Nat) :
|
||||
revert p
|
||||
cases getLsbD x i <;> simp; omega
|
||||
|
||||
@[simp] theorem setWidth_cast {h : w = v} : (cast h x).setWidth k = x.setWidth k := by
|
||||
@[simp] theorem setWidth_cast {x : BitVec w} {h : w = v} : (x.cast h).setWidth k = x.setWidth k := by
|
||||
apply eq_of_getLsbD_eq
|
||||
simp
|
||||
|
||||
@@ -1102,19 +1102,19 @@ theorem not_eq_comm {x y : BitVec w} : ~~~ x = y ↔ x = ~~~ y := by
|
||||
|
||||
/-! ### cast -/
|
||||
|
||||
@[simp] theorem not_cast {x : BitVec w} (h : w = w') : ~~~(cast h x) = cast h (~~~x) := by
|
||||
@[simp] theorem not_cast {x : BitVec w} (h : w = w') : ~~~(x.cast h) = (~~~x).cast h := by
|
||||
ext
|
||||
simp_all [lt_of_getLsbD]
|
||||
|
||||
@[simp] theorem and_cast {x y : BitVec w} (h : w = w') : cast h x &&& cast h y = cast h (x &&& y) := by
|
||||
@[simp] theorem and_cast {x y : BitVec w} (h : w = w') : x.cast h &&& y.cast h = (x &&& y).cast h := by
|
||||
ext
|
||||
simp_all [lt_of_getLsbD]
|
||||
|
||||
@[simp] theorem or_cast {x y : BitVec w} (h : w = w') : cast h x ||| cast h y = cast h (x ||| y) := by
|
||||
@[simp] theorem or_cast {x y : BitVec w} (h : w = w') : x.cast h ||| y.cast h = (x ||| y).cast h := by
|
||||
ext
|
||||
simp_all [lt_of_getLsbD]
|
||||
|
||||
@[simp] theorem xor_cast {x y : BitVec w} (h : w = w') : cast h x ^^^ cast h y = cast h (x ^^^ y) := by
|
||||
@[simp] theorem xor_cast {x y : BitVec w} (h : w = w') : x.cast h ^^^ y.cast h = (x ^^^ y).cast h := by
|
||||
ext
|
||||
simp_all [lt_of_getLsbD]
|
||||
|
||||
@@ -1316,6 +1316,61 @@ theorem toNat_ushiftRight_lt (x : BitVec w) (n : Nat) (hn : n ≤ w) :
|
||||
· apply hn
|
||||
· apply Nat.pow_pos (by decide)
|
||||
|
||||
|
||||
/-- Shifting right by `n`, which is larger than the bitwidth `w` produces `0. -/
|
||||
theorem ushiftRight_eq_zero {x : BitVec w} {n : Nat} (hn : w ≤ n) :
|
||||
x >>> n = 0#w := by
|
||||
simp only [toNat_eq, toNat_ushiftRight, toNat_ofNat, Nat.zero_mod]
|
||||
have : 2^w ≤ 2^n := Nat.pow_le_pow_of_le Nat.one_lt_two hn
|
||||
rw [Nat.shiftRight_eq_div_pow, Nat.div_eq_of_lt (by omega)]
|
||||
|
||||
|
||||
/--
|
||||
Unsigned shift right by at least one bit makes the interpretations of the bitvector as an `Int` or `Nat` agree,
|
||||
because it makes the value of the bitvector less than or equal to `2^(w-1)`.
|
||||
-/
|
||||
theorem toInt_ushiftRight_of_lt {x : BitVec w} {n : Nat} (hn : 0 < n) :
|
||||
(x >>> n).toInt = x.toNat >>> n := by
|
||||
rw [toInt_eq_toNat_cond]
|
||||
simp only [toNat_ushiftRight, ite_eq_left_iff, Nat.not_lt]
|
||||
intros h
|
||||
by_cases hn : n ≤ w
|
||||
· have h1 := Nat.mul_lt_mul_of_pos_left (toNat_ushiftRight_lt x n hn) Nat.two_pos
|
||||
simp only [toNat_ushiftRight, Nat.zero_lt_succ, Nat.mul_lt_mul_left] at h1
|
||||
have : 2 ^ (w - n).succ ≤ 2^ w := Nat.pow_le_pow_of_le (by decide) (by omega)
|
||||
have := show 2 * x.toNat >>> n < 2 ^ w by
|
||||
omega
|
||||
omega
|
||||
· have : x.toNat >>> n = 0 := by
|
||||
apply Nat.shiftRight_eq_zero
|
||||
have : 2^w ≤ 2^n := Nat.pow_le_pow_of_le (by decide) (by omega)
|
||||
omega
|
||||
simp [this] at h
|
||||
omega
|
||||
|
||||
/--
|
||||
Unsigned shift right by at least one bit makes the interpretations of the bitvector as an `Int` or `Nat` agree,
|
||||
because it makes the value of the bitvector less than or equal to `2^(w-1)`.
|
||||
In the case when `n = 0`, then the shift right value equals the integer interpretation.
|
||||
-/
|
||||
@[simp]
|
||||
theorem toInt_ushiftRight {x : BitVec w} {n : Nat} :
|
||||
(x >>> n).toInt = if n = 0 then x.toInt else x.toNat >>> n := by
|
||||
by_cases hn : n = 0
|
||||
· simp [hn]
|
||||
· rw [toInt_ushiftRight_of_lt (by omega), toInt_eq_toNat_cond]
|
||||
simp [hn]
|
||||
|
||||
@[simp]
|
||||
theorem toFin_uShiftRight {x : BitVec w} {n : Nat} :
|
||||
(x >>> n).toFin = x.toFin / (Fin.ofNat' (2^w) (2^n)) := by
|
||||
apply Fin.eq_of_val_eq
|
||||
by_cases hn : n < w
|
||||
· simp [Nat.shiftRight_eq_div_pow, Nat.mod_eq_of_lt (Nat.pow_lt_pow_of_lt Nat.one_lt_two hn)]
|
||||
· simp only [Nat.not_lt] at hn
|
||||
rw [ushiftRight_eq_zero (by omega)]
|
||||
simp [Nat.dvd_iff_mod_eq_zero.mp (Nat.pow_dvd_pow 2 hn)]
|
||||
|
||||
@[simp]
|
||||
theorem getMsbD_ushiftRight {x : BitVec w} {i n : Nat} :
|
||||
(x >>> n).getMsbD i = (decide (i < w) && (!decide (i < n) && x.getMsbD (i - n))) := by
|
||||
@@ -1622,14 +1677,16 @@ theorem signExtend_eq (x : BitVec w) : x.signExtend w = x := by
|
||||
/-- Sign extending to a larger bitwidth depends on the msb.
|
||||
If the msb is false, then the result equals the original value.
|
||||
If the msb is true, then we add a value of `(2^v - 2^w)`, which arises from the sign extension. -/
|
||||
theorem toNat_signExtend_of_le (x : BitVec w) {v : Nat} (hv : w ≤ v) :
|
||||
private theorem toNat_signExtend_of_le (x : BitVec w) {v : Nat} (hv : w ≤ v) :
|
||||
(x.signExtend v).toNat = x.toNat + if x.msb then 2^v - 2^w else 0 := by
|
||||
apply Nat.eq_of_testBit_eq
|
||||
intro i
|
||||
have ⟨k, hk⟩ := Nat.exists_eq_add_of_le hv
|
||||
rw [hk, testBit_toNat, getLsbD_signExtend, Nat.pow_add, ← Nat.mul_sub_one, Nat.add_comm (x.toNat)]
|
||||
by_cases hx : x.msb
|
||||
· simp [hx, Nat.testBit_mul_pow_two_add _ x.isLt, testBit_toNat]
|
||||
· simp only [hx, Bool.if_true_right, ↓reduceIte,
|
||||
Nat.testBit_mul_pow_two_add _ x.isLt,
|
||||
testBit_toNat, Nat.testBit_two_pow_sub_one]
|
||||
-- Case analysis on i being in the intervals [0..w), [w..w + k), [w+k..∞)
|
||||
have hi : i < w ∨ (w ≤ i ∧ i < w + k) ∨ w + k ≤ i := by omega
|
||||
rcases hi with hi | hi | hi
|
||||
@@ -1637,7 +1694,8 @@ theorem toNat_signExtend_of_le (x : BitVec w) {v : Nat} (hv : w ≤ v) :
|
||||
· simp [hi]; omega
|
||||
· simp [hi, show ¬ (i < w + k) by omega, show ¬ (i < w) by omega]
|
||||
omega
|
||||
· simp [hx, Nat.testBit_mul_pow_two_add _ x.isLt, testBit_toNat]
|
||||
· simp only [hx, Bool.if_false_right,
|
||||
Bool.false_eq_true, ↓reduceIte, Nat.zero_add, testBit_toNat]
|
||||
have hi : i < w ∨ (w ≤ i ∧ i < w + k) ∨ w + k ≤ i := by omega
|
||||
rcases hi with hi | hi | hi
|
||||
· simp [hi]; omega
|
||||
@@ -1739,7 +1797,7 @@ theorem msb_append {x : BitVec w} {y : BitVec v} :
|
||||
rw [getLsbD_append] -- Why does this not work with `simp [getLsbD_append]`?
|
||||
simp
|
||||
|
||||
@[simp] theorem zero_width_append (x : BitVec 0) (y : BitVec v) : x ++ y = cast (by omega) y := by
|
||||
@[simp] theorem zero_width_append (x : BitVec 0) (y : BitVec v) : x ++ y = y.cast (by omega) := by
|
||||
ext
|
||||
rw [getLsbD_append]
|
||||
simpa using lt_of_getLsbD
|
||||
@@ -1749,7 +1807,7 @@ theorem msb_append {x : BitVec w} {y : BitVec v} :
|
||||
simp only [getLsbD_append, getLsbD_zero, Bool.cond_self]
|
||||
|
||||
@[simp] theorem cast_append_right (h : w + v = w + v') (x : BitVec w) (y : BitVec v) :
|
||||
cast h (x ++ y) = x ++ cast (by omega) y := by
|
||||
(x ++ y).cast h = x ++ y.cast (by omega) := by
|
||||
ext
|
||||
simp only [getLsbD_cast, getLsbD_append, cond_eq_if, decide_eq_true_eq]
|
||||
split <;> split
|
||||
@@ -1760,7 +1818,7 @@ theorem msb_append {x : BitVec w} {y : BitVec v} :
|
||||
omega
|
||||
|
||||
@[simp] theorem cast_append_left (h : w + v = w' + v) (x : BitVec w) (y : BitVec v) :
|
||||
cast h (x ++ y) = cast (by omega) x ++ y := by
|
||||
(x ++ y).cast h = x.cast (by omega) ++ y := by
|
||||
ext
|
||||
simp [getLsbD_append]
|
||||
|
||||
@@ -1999,6 +2057,46 @@ theorem getElem_concat (x : BitVec w) (b : Bool) (i : Nat) (h : i < w + 1) :
|
||||
(concat x b)[i + 1] = x[i] := by
|
||||
simp [getElem_concat, h, getLsbD_eq_getElem]
|
||||
|
||||
@[simp]
|
||||
theorem getMsbD_concat {i w : Nat} {b : Bool} {x : BitVec w} :
|
||||
(x.concat b).getMsbD i = if i < w then x.getMsbD i else decide (i = w) && b := by
|
||||
simp only [getMsbD_eq_getLsbD, Nat.add_sub_cancel, getLsbD_concat]
|
||||
by_cases h₀ : i = w
|
||||
· simp [h₀]
|
||||
· by_cases h₁ : i < w
|
||||
· simp [h₀, h₁, show ¬ w - i = 0 by omega, show i < w + 1 by omega, Nat.sub_sub, Nat.add_comm]
|
||||
· simp only [show w - i = 0 by omega, ↓reduceIte, h₁, h₀, decide_false, Bool.false_and,
|
||||
Bool.and_eq_false_imp, decide_eq_true_eq]
|
||||
intro
|
||||
omega
|
||||
|
||||
@[simp]
|
||||
theorem msb_concat {w : Nat} {b : Bool} {x : BitVec w} :
|
||||
(x.concat b).msb = if 0 < w then x.msb else b := by
|
||||
simp only [BitVec.msb, getMsbD_eq_getLsbD, Nat.zero_lt_succ, decide_true, Nat.add_one_sub_one,
|
||||
Nat.sub_zero, Bool.true_and]
|
||||
by_cases h₀ : 0 < w
|
||||
· simp only [Nat.lt_add_one, getLsbD_eq_getElem, getElem_concat, h₀, ↓reduceIte, decide_true,
|
||||
Bool.true_and, ite_eq_right_iff]
|
||||
intro
|
||||
omega
|
||||
· simp [h₀, show w = 0 by omega]
|
||||
|
||||
@[simp] theorem toInt_concat (x : BitVec w) (b : Bool) :
|
||||
(concat x b).toInt = if w = 0 then -b.toInt else x.toInt * 2 + b.toInt := by
|
||||
simp only [BitVec.toInt, toNat_concat]
|
||||
cases w
|
||||
· cases b <;> simp [eq_nil x]
|
||||
· cases b <;> simp <;> omega
|
||||
|
||||
@[simp] theorem toFin_concat (x : BitVec w) (b : Bool) :
|
||||
(concat x b).toFin = Fin.mk (x.toNat * 2 + b.toNat) (by
|
||||
have := Bool.toNat_lt b
|
||||
simp [← Nat.two_pow_pred_add_two_pow_pred, Bool.toNat_lt b]
|
||||
omega
|
||||
) := by
|
||||
simp [← Fin.val_inj]
|
||||
|
||||
@[simp] theorem not_concat (x : BitVec w) (b : Bool) : ~~~(concat x b) = concat (~~~x) !b := by
|
||||
ext i; cases i using Fin.succRecOn <;> simp [*, Nat.succ_lt_succ]
|
||||
|
||||
@@ -2014,6 +2112,10 @@ theorem getElem_concat (x : BitVec w) (b : Bool) (i : Nat) (h : i < w + 1) :
|
||||
(concat x a) ^^^ (concat y b) = concat (x ^^^ y) (a ^^ b) := by
|
||||
ext i; cases i using Fin.succRecOn <;> simp
|
||||
|
||||
@[simp] theorem zero_concat_false : concat 0#w false = 0#(w + 1) := by
|
||||
ext
|
||||
simp [getLsbD_concat]
|
||||
|
||||
/-! ### shiftConcat -/
|
||||
|
||||
theorem getLsbD_shiftConcat (x : BitVec w) (b : Bool) (i : Nat) :
|
||||
@@ -2059,35 +2161,6 @@ theorem toNat_shiftConcat_lt_of_lt {x : BitVec w} {b : Bool} {k : Nat}
|
||||
have := Bool.toNat_lt b
|
||||
omega
|
||||
|
||||
@[simp] theorem zero_concat_false : concat 0#w false = 0#(w + 1) := by
|
||||
ext
|
||||
simp [getLsbD_concat]
|
||||
|
||||
@[simp]
|
||||
theorem getMsbD_concat {i w : Nat} {b : Bool} {x : BitVec w} :
|
||||
(x.concat b).getMsbD i = if i < w then x.getMsbD i else decide (i = w) && b := by
|
||||
simp only [getMsbD_eq_getLsbD, Nat.add_sub_cancel, getLsbD_concat]
|
||||
by_cases h₀ : i = w
|
||||
· simp [h₀]
|
||||
· by_cases h₁ : i < w
|
||||
· simp [h₀, h₁, show ¬ w - i = 0 by omega, show i < w + 1 by omega, Nat.sub_sub, Nat.add_comm]
|
||||
· simp only [show w - i = 0 by omega, ↓reduceIte, h₁, h₀, decide_false, Bool.false_and,
|
||||
Bool.and_eq_false_imp, decide_eq_true_eq]
|
||||
intro
|
||||
omega
|
||||
|
||||
@[simp]
|
||||
theorem msb_concat {w : Nat} {b : Bool} {x : BitVec w} :
|
||||
(x.concat b).msb = if 0 < w then x.msb else b := by
|
||||
simp only [BitVec.msb, getMsbD_eq_getLsbD, Nat.zero_lt_succ, decide_true, Nat.add_one_sub_one,
|
||||
Nat.sub_zero, Bool.true_and]
|
||||
by_cases h₀ : 0 < w
|
||||
· simp only [Nat.lt_add_one, getLsbD_eq_getElem, getElem_concat, h₀, ↓reduceIte, decide_true,
|
||||
Bool.true_and, ite_eq_right_iff]
|
||||
intro
|
||||
omega
|
||||
· simp [h₀, show w = 0 by omega]
|
||||
|
||||
/-! ### add -/
|
||||
|
||||
theorem add_def {n} (x y : BitVec n) : x + y = .ofNat n (x.toNat + y.toNat) := rfl
|
||||
@@ -2758,12 +2831,6 @@ theorem getElem_rotateLeft {x : BitVec w} {r i : Nat} (h : i < w) :
|
||||
if h' : i < r % w then x[(w - (r % w) + i)] else x[i - (r % w)] := by
|
||||
simp [← BitVec.getLsbD_eq_getElem, h]
|
||||
|
||||
/-- If `w ≤ x < 2 * w`, then `x % w = x - w` -/
|
||||
theorem mod_eq_sub_of_le_of_lt {x w : Nat} (x_le : w ≤ x) (x_lt : x < 2 * w) :
|
||||
x % w = x - w := by
|
||||
rw [Nat.mod_eq_sub_mod, Nat.mod_eq_of_lt (by omega)]
|
||||
omega
|
||||
|
||||
theorem getMsbD_rotateLeftAux_of_lt {x : BitVec w} {r : Nat} {i : Nat} (hi : i < w - r) :
|
||||
(x.rotateLeftAux r).getMsbD i = x.getMsbD (r + i) := by
|
||||
rw [rotateLeftAux, getMsbD_or]
|
||||
@@ -2773,6 +2840,20 @@ theorem getMsbD_rotateLeftAux_of_ge {x : BitVec w} {r : Nat} {i : Nat} (hi : i
|
||||
(x.rotateLeftAux r).getMsbD i = (decide (i < w) && x.getMsbD (i - (w - r))) := by
|
||||
simp [rotateLeftAux, getMsbD_or, show i + r ≥ w by omega, show ¬i < w - r by omega]
|
||||
|
||||
/--
|
||||
If a number `w * n ≤ i < w * (n + 1)`, then `i - w * n` equals `i % w`.
|
||||
This is true by subtracting `w * n` from the inequality, giving
|
||||
`0 ≤ i - w * n < w`, which uniquely identifies `i % w`.
|
||||
-/
|
||||
private theorem Nat.sub_mul_eq_mod_of_lt_of_le (hlo : w * n ≤ i) (hhi : i < w * (n + 1)) :
|
||||
i - w * n = i % w := by
|
||||
rw [Nat.mod_def]
|
||||
congr
|
||||
symm
|
||||
apply Nat.div_eq_of_lt_le
|
||||
(by rw [Nat.mul_comm]; omega)
|
||||
(by rw [Nat.mul_comm]; omega)
|
||||
|
||||
/-- When `r < w`, we give a formula for `(x.rotateLeft r).getMsbD i`. -/
|
||||
theorem getMsbD_rotateLeft_of_lt {n w : Nat} {x : BitVec w} (hi : r < w):
|
||||
(x.rotateLeft r).getMsbD n = (decide (n < w) && x.getMsbD ((r + n) % w)) := by
|
||||
@@ -2785,8 +2866,8 @@ theorem getMsbD_rotateLeft_of_lt {n w : Nat} {x : BitVec w} (hi : r < w):
|
||||
by_cases h₁ : n < w + 1
|
||||
· simp only [h₁, decide_true, Bool.true_and]
|
||||
have h₂ : (r + n) < 2 * (w + 1) := by omega
|
||||
rw [mod_eq_sub_of_le_of_lt (by omega) (by omega)]
|
||||
congr 1
|
||||
rw [← Nat.sub_mul_eq_mod_of_lt_of_le (n := 1) (by omega) (by omega), Nat.mul_one]
|
||||
omega
|
||||
· simp [h₁]
|
||||
|
||||
@@ -3103,20 +3184,6 @@ theorem replicate_succ_eq {x : BitVec w} :
|
||||
(x ++ replicate n x).cast (by rw [Nat.mul_succ]; omega) := by
|
||||
simp [replicate]
|
||||
|
||||
/--
|
||||
If a number `w * n ≤ i < w * (n + 1)`, then `i - w * n` equals `i % w`.
|
||||
This is true by subtracting `w * n` from the inequality, giving
|
||||
`0 ≤ i - w * n < w`, which uniquely identifies `i % w`.
|
||||
-/
|
||||
private theorem Nat.sub_mul_eq_mod_of_lt_of_le (hlo : w * n ≤ i) (hhi : i < w * (n + 1)) :
|
||||
i - w * n = i % w := by
|
||||
rw [Nat.mod_def]
|
||||
congr
|
||||
symm
|
||||
apply Nat.div_eq_of_lt_le
|
||||
(by rw [Nat.mul_comm]; omega)
|
||||
(by rw [Nat.mul_comm]; omega)
|
||||
|
||||
@[simp]
|
||||
theorem getLsbD_replicate {n w : Nat} (x : BitVec w) :
|
||||
(x.replicate n).getLsbD i =
|
||||
@@ -3222,6 +3289,11 @@ theorem toInt_neg_of_ne_intMin {x : BitVec w} (rs : x ≠ intMin w) :
|
||||
have := @Nat.two_pow_pred_mul_two w (by omega)
|
||||
split <;> split <;> omega
|
||||
|
||||
theorem toInt_neg_eq_ite {x : BitVec w} :
|
||||
(-x).toInt = if x = intMin w then x.toInt else -(x.toInt) := by
|
||||
by_cases hx : x = intMin w <;>
|
||||
simp [hx, neg_intMin, toInt_neg_of_ne_intMin]
|
||||
|
||||
theorem msb_intMin {w : Nat} : (intMin w).msb = decide (0 < w) := by
|
||||
simp only [msb_eq_decide, toNat_intMin, decide_eq_decide]
|
||||
by_cases h : 0 < w <;> simp_all
|
||||
@@ -3355,6 +3427,73 @@ theorem getMsbD_abs {i : Nat} {x : BitVec w} :
|
||||
getMsbD (x.abs) i = if x.msb then getMsbD (-x) i else getMsbD x i := by
|
||||
by_cases h : x.msb <;> simp [BitVec.abs, h]
|
||||
|
||||
/-
|
||||
The absolute value of `x : BitVec w` is naively a case split on the sign of `x`.
|
||||
However, recall that when `x = intMin w`, `-x = x`.
|
||||
Thus, the full value of `abs x` is computed by the case split:
|
||||
- If `x : BitVec w` is `intMin`, then its absolute value is also `intMin w`, and
|
||||
thus `toInt` will equal `intMin.toInt`.
|
||||
- Otherwise, if `x` is negative, then `x.abs.toInt = (-x).toInt`.
|
||||
- If `x` is positive, then it is equal to `x.abs.toInt = x.toInt`.
|
||||
-/
|
||||
theorem toInt_abs_eq_ite {x : BitVec w} :
|
||||
x.abs.toInt =
|
||||
if x = intMin w then (intMin w).toInt
|
||||
else if x.msb then -x.toInt
|
||||
else x.toInt := by
|
||||
by_cases hx : x = intMin w
|
||||
· simp [hx]
|
||||
· simp [hx]
|
||||
by_cases hx₂ : x.msb
|
||||
· simp [hx₂, abs_eq, toInt_neg_of_ne_intMin hx]
|
||||
· simp [hx₂, abs_eq]
|
||||
|
||||
|
||||
|
||||
/-
|
||||
The absolute value of `x : BitVec w` is a case split on the sign of `x`, when `x ≠ intMin w`.
|
||||
This is a variant of `toInt_abs_eq_ite`.
|
||||
-/
|
||||
theorem toInt_abs_eq_ite_of_ne_intMin {x : BitVec w} (hx : x ≠ intMin w) :
|
||||
x.abs.toInt = if x.msb then -x.toInt else x.toInt := by
|
||||
simp [toInt_abs_eq_ite, hx]
|
||||
|
||||
|
||||
/--
|
||||
The absolute value of `x : BitVec w`, interpreted as an integer, is a case split:
|
||||
- When `x = intMin w`, then `x.abs = intMin w`
|
||||
- Otherwise, `x.abs.toInt` equals the absolute value (`x.toInt.natAbs`).
|
||||
|
||||
This is a simpler version of `BitVec.toInt_abs_eq_ite`, which hides a case split on `x.msb`.
|
||||
-/
|
||||
theorem toInt_abs_eq_natAbs {x : BitVec w} : x.abs.toInt =
|
||||
if x = intMin w then (intMin w).toInt else x.toInt.natAbs := by
|
||||
rw [toInt_abs_eq_ite]
|
||||
by_cases hx : x = intMin w
|
||||
· simp [hx]
|
||||
· simp [hx]
|
||||
by_cases h : x.msb
|
||||
· simp only [h, ↓reduceIte]
|
||||
have : x.toInt < 0 := by
|
||||
rw [toInt_neg_iff]
|
||||
have := msb_eq_true_iff_two_mul_ge.mp h
|
||||
omega
|
||||
omega
|
||||
· simp only [h, Bool.false_eq_true, ↓reduceIte]
|
||||
have : 0 ≤ x.toInt := by
|
||||
rw [toInt_pos_iff]
|
||||
exact msb_eq_false_iff_two_mul_lt.mp (by simp [h])
|
||||
omega
|
||||
|
||||
/-
|
||||
The absolute value of `(x : BitVec w)`, when interpreted as an integer,
|
||||
is the absolute value of `x.toInt` when `(x ≠ intMin)`.
|
||||
-/
|
||||
theorem toInt_abs_eq_natAbs_of_ne_intMin {x : BitVec w} (hx : x ≠ intMin w) :
|
||||
x.abs.toInt = x.toInt.natAbs := by
|
||||
simp [toInt_abs_eq_natAbs, hx]
|
||||
|
||||
|
||||
/-! ### Decidable quantifiers -/
|
||||
|
||||
theorem forall_zero_iff {P : BitVec 0 → Prop} :
|
||||
|
||||
@@ -384,6 +384,15 @@ theorem toNat_lt (b : Bool) : b.toNat < 2 :=
|
||||
@[simp] theorem toNat_eq_one {b : Bool} : b.toNat = 1 ↔ b = true := by
|
||||
cases b <;> simp
|
||||
|
||||
/-! ## toInt -/
|
||||
|
||||
/-- convert a `Bool` to an `Int`, `false -> 0`, `true -> 1` -/
|
||||
def toInt (b : Bool) : Int := cond b 1 0
|
||||
|
||||
@[simp] theorem toInt_false : false.toInt = 0 := rfl
|
||||
|
||||
@[simp] theorem toInt_true : true.toInt = 1 := rfl
|
||||
|
||||
/-! ### ite -/
|
||||
|
||||
@[simp] theorem if_true_left (p : Prop) [h : Decidable p] (f : Bool) :
|
||||
|
||||
@@ -8,6 +8,8 @@ import Init.Data.Queue
|
||||
import Init.System.Promise
|
||||
import Init.System.Mutex
|
||||
|
||||
set_option linter.deprecated false
|
||||
|
||||
namespace IO
|
||||
|
||||
/--
|
||||
@@ -15,6 +17,7 @@ Internal state of an `Channel`.
|
||||
|
||||
We maintain the invariant that at all times either `consumers` or `values` is empty.
|
||||
-/
|
||||
@[deprecated "Use Std.Channel.State from Std.Sync.Channel instead" (since := "2024-12-02")]
|
||||
structure Channel.State (α : Type) where
|
||||
values : Std.Queue α := ∅
|
||||
consumers : Std.Queue (Promise (Option α)) := ∅
|
||||
@@ -27,12 +30,14 @@ FIFO channel with unbounded buffer, where `recv?` returns a `Task`.
|
||||
A channel can be closed. Once it is closed, all `send`s are ignored, and
|
||||
`recv?` returns `none` once the queue is empty.
|
||||
-/
|
||||
@[deprecated "Use Std.Channel from Std.Sync.Channel instead" (since := "2024-12-02")]
|
||||
def Channel (α : Type) : Type := Mutex (Channel.State α)
|
||||
|
||||
instance : Nonempty (Channel α) :=
|
||||
inferInstanceAs (Nonempty (Mutex _))
|
||||
|
||||
/-- Creates a new `Channel`. -/
|
||||
@[deprecated "Use Std.Channel.new from Std.Sync.Channel instead" (since := "2024-12-02")]
|
||||
def Channel.new : BaseIO (Channel α) :=
|
||||
Mutex.new {}
|
||||
|
||||
@@ -41,6 +46,7 @@ Sends a message on an `Channel`.
|
||||
|
||||
This function does not block.
|
||||
-/
|
||||
@[deprecated "Use Std.Channel.send from Std.Sync.Channel instead" (since := "2024-12-02")]
|
||||
def Channel.send (ch : Channel α) (v : α) : BaseIO Unit :=
|
||||
ch.atomically do
|
||||
let st ← get
|
||||
@@ -54,6 +60,7 @@ def Channel.send (ch : Channel α) (v : α) : BaseIO Unit :=
|
||||
/--
|
||||
Closes an `Channel`.
|
||||
-/
|
||||
@[deprecated "Use Std.Channel.close from Std.Sync.Channel instead" (since := "2024-12-02")]
|
||||
def Channel.close (ch : Channel α) : BaseIO Unit :=
|
||||
ch.atomically do
|
||||
let st ← get
|
||||
@@ -67,6 +74,7 @@ Every message is only received once.
|
||||
|
||||
Returns `none` if the channel is closed and the queue is empty.
|
||||
-/
|
||||
@[deprecated "Use Std.Channel.recv? from Std.Sync.Channel instead" (since := "2024-12-02")]
|
||||
def Channel.recv? (ch : Channel α) : BaseIO (Task (Option α)) :=
|
||||
ch.atomically do
|
||||
let st ← get
|
||||
@@ -85,6 +93,7 @@ def Channel.recv? (ch : Channel α) : BaseIO (Task (Option α)) :=
|
||||
|
||||
Note that if this function is called twice, each `forAsync` only gets half the messages.
|
||||
-/
|
||||
@[deprecated "Use Std.Channel.forAsync from Std.Sync.Channel instead" (since := "2024-12-02")]
|
||||
partial def Channel.forAsync (f : α → BaseIO Unit) (ch : Channel α)
|
||||
(prio : Task.Priority := .default) : BaseIO (Task Unit) := do
|
||||
BaseIO.bindTask (prio := prio) (← ch.recv?) fun
|
||||
@@ -96,11 +105,13 @@ Receives all currently queued messages from the channel.
|
||||
|
||||
Those messages are dequeued and will not be returned by `recv?`.
|
||||
-/
|
||||
@[deprecated "Use Std.Channel.recvAllCurrent from Std.Sync.Channel instead" (since := "2024-12-02")]
|
||||
def Channel.recvAllCurrent (ch : Channel α) : BaseIO (Array α) :=
|
||||
ch.atomically do
|
||||
modifyGet fun st => (st.values.toArray, { st with values := ∅ })
|
||||
|
||||
/-- Type tag for synchronous (blocking) operations on a `Channel`. -/
|
||||
@[deprecated "Use Std.Channel.Sync from Std.Sync.Channel instead" (since := "2024-12-02")]
|
||||
def Channel.Sync := Channel
|
||||
|
||||
/--
|
||||
@@ -110,6 +121,7 @@ For example, `ch.sync.recv?` blocks until the next message,
|
||||
and `for msg in ch.sync do ...` iterates synchronously over the channel.
|
||||
These functions should only be used in dedicated threads.
|
||||
-/
|
||||
@[deprecated "Use Std.Channel.sync from Std.Sync.Channel instead" (since := "2024-12-02")]
|
||||
def Channel.sync (ch : Channel α) : Channel.Sync α := ch
|
||||
|
||||
/--
|
||||
@@ -118,9 +130,11 @@ Synchronously receives a message from the channel.
|
||||
Every message is only received once.
|
||||
Returns `none` if the channel is closed and the queue is empty.
|
||||
-/
|
||||
@[deprecated "Use Std.Channel.Sync.recv? from Std.Sync.Channel instead" (since := "2024-12-02")]
|
||||
def Channel.Sync.recv? (ch : Channel.Sync α) : BaseIO (Option α) := do
|
||||
IO.wait (← Channel.recv? ch)
|
||||
|
||||
@[deprecated "Use Std.Channel.Sync.forIn from Std.Sync.Channel instead" (since := "2024-12-02")]
|
||||
private partial def Channel.Sync.forIn [Monad m] [MonadLiftT BaseIO m]
|
||||
(ch : Channel.Sync α) (f : α → β → m (ForInStep β)) : β → m β := fun b => do
|
||||
match ← ch.recv? with
|
||||
|
||||
@@ -36,12 +36,6 @@ def succ : Fin n → Fin (n + 1)
|
||||
|
||||
variable {n : Nat}
|
||||
|
||||
/--
|
||||
Returns `a` modulo `n + 1` as a `Fin n.succ`.
|
||||
-/
|
||||
protected def ofNat {n : Nat} (a : Nat) : Fin (n + 1) :=
|
||||
⟨a % (n+1), Nat.mod_lt _ (Nat.zero_lt_succ _)⟩
|
||||
|
||||
/--
|
||||
Returns `a` modulo `n` as a `Fin n`.
|
||||
|
||||
@@ -50,9 +44,12 @@ The assumption `NeZero n` ensures that `Fin n` is nonempty.
|
||||
protected def ofNat' (n : Nat) [NeZero n] (a : Nat) : Fin n :=
|
||||
⟨a % n, Nat.mod_lt _ (pos_of_neZero n)⟩
|
||||
|
||||
-- We intend to deprecate `Fin.ofNat` in favor of `Fin.ofNat'` (and later rename).
|
||||
-- This is waiting on https://github.com/leanprover/lean4/pull/5323
|
||||
-- attribute [deprecated Fin.ofNat' (since := "2024-09-16")] Fin.ofNat
|
||||
/--
|
||||
Returns `a` modulo `n + 1` as a `Fin n.succ`.
|
||||
-/
|
||||
@[deprecated Fin.ofNat' (since := "2024-11-27")]
|
||||
protected def ofNat {n : Nat} (a : Nat) : Fin (n + 1) :=
|
||||
⟨a % (n+1), Nat.mod_lt _ (Nat.zero_lt_succ _)⟩
|
||||
|
||||
private theorem mlt {b : Nat} : {a : Nat} → a < n → b % n < n
|
||||
| 0, h => Nat.mod_lt _ h
|
||||
@@ -179,7 +176,7 @@ protected theorem pos (i : Fin n) : 0 < n :=
|
||||
@[inline] def castLE (h : n ≤ m) (i : Fin n) : Fin m := ⟨i, Nat.lt_of_lt_of_le i.2 h⟩
|
||||
|
||||
/-- `cast eq i` embeds `i` into an equal `Fin` type. -/
|
||||
@[inline] def cast (eq : n = m) (i : Fin n) : Fin m := ⟨i, eq ▸ i.2⟩
|
||||
@[inline] protected def cast (eq : n = m) (i : Fin n) : Fin m := ⟨i, eq ▸ i.2⟩
|
||||
|
||||
/-- `castAdd m i` embeds `i : Fin n` in `Fin (n+m)`. See also `Fin.natAdd` and `Fin.addNat`. -/
|
||||
@[inline] def castAdd (m) : Fin n → Fin (n + m) :=
|
||||
|
||||
@@ -13,14 +13,14 @@ namespace Fin
|
||||
/-- Folds over `Fin n` from the left: `foldl 3 f x = f (f (f x 0) 1) 2`. -/
|
||||
@[inline] def foldl (n) (f : α → Fin n → α) (init : α) : α := loop init 0 where
|
||||
/-- Inner loop for `Fin.foldl`. `Fin.foldl.loop n f x i = f (f (f x i) ...) (n-1)` -/
|
||||
@[semireducible] loop (x : α) (i : Nat) : α :=
|
||||
@[semireducible, specialize] loop (x : α) (i : Nat) : α :=
|
||||
if h : i < n then loop (f x ⟨i, h⟩) (i+1) else x
|
||||
termination_by n - i
|
||||
|
||||
/-- Folds over `Fin n` from the right: `foldr 3 f x = f 0 (f 1 (f 2 x))`. -/
|
||||
@[inline] def foldr (n) (f : Fin n → α → α) (init : α) : α := loop n (Nat.le_refl n) init where
|
||||
/-- Inner loop for `Fin.foldr`. `Fin.foldr.loop n f i x = f 0 (f ... (f (i-1) x))` -/
|
||||
loop : (i : _) → i ≤ n → α → α
|
||||
@[specialize] loop : (i : _) → i ≤ n → α → α
|
||||
| 0, _, x => x
|
||||
| i+1, h, x => loop i (Nat.le_of_lt h) (f ⟨i, h⟩ x)
|
||||
termination_by structural i => i
|
||||
@@ -47,7 +47,7 @@ Fin.foldlM n f x₀ = do
|
||||
pure xₙ
|
||||
```
|
||||
-/
|
||||
loop (x : α) (i : Nat) : m α := do
|
||||
@[semireducible, specialize] loop (x : α) (i : Nat) : m α := do
|
||||
if h : i < n then f x ⟨i, h⟩ >>= (loop · (i+1)) else pure x
|
||||
termination_by n - i
|
||||
decreasing_by decreasing_trivial_pre_omega
|
||||
@@ -76,7 +76,7 @@ Fin.foldrM n f xₙ = do
|
||||
pure x₀
|
||||
```
|
||||
-/
|
||||
loop : {i // i ≤ n} → α → m α
|
||||
@[semireducible, specialize] loop : {i // i ≤ n} → α → m α
|
||||
| ⟨0, _⟩, x => pure x
|
||||
| ⟨i+1, h⟩, x => f ⟨i, h⟩ x >>= loop ⟨i, Nat.le_of_lt h⟩
|
||||
|
||||
@@ -125,7 +125,7 @@ theorem foldrM_loop [Monad m] [LawfulMonad m] (f : Fin (n+1) → α → m α) (x
|
||||
| zero =>
|
||||
rw [foldrM_loop_zero, foldrM_loop_succ, pure_bind]
|
||||
conv => rhs; rw [←bind_pure (f 0 x)]
|
||||
congr; funext; exact foldrM_loop_zero ..
|
||||
congr; funext
|
||||
| succ i ih =>
|
||||
rw [foldrM_loop_succ, foldrM_loop_succ, bind_assoc]
|
||||
congr; funext; exact ih ..
|
||||
|
||||
@@ -370,25 +370,25 @@ theorem succ_succ_ne_one (a : Fin n) : Fin.succ (Fin.succ a) ≠ 1 :=
|
||||
Fin.castLE mn ∘ Fin.castLE km = Fin.castLE (Nat.le_trans km mn) :=
|
||||
funext (castLE_castLE km mn)
|
||||
|
||||
@[simp] theorem coe_cast (h : n = m) (i : Fin n) : (cast h i : Nat) = i := rfl
|
||||
@[simp] theorem coe_cast (h : n = m) (i : Fin n) : (i.cast h : Nat) = i := rfl
|
||||
|
||||
@[simp] theorem cast_last {n' : Nat} {h : n + 1 = n' + 1} : cast h (last n) = last n' :=
|
||||
@[simp] theorem cast_last {n' : Nat} {h : n + 1 = n' + 1} : (last n).cast h = last n' :=
|
||||
Fin.ext (by rw [coe_cast, val_last, val_last, Nat.succ.inj h])
|
||||
|
||||
@[simp] theorem cast_mk (h : n = m) (i : Nat) (hn : i < n) : cast h ⟨i, hn⟩ = ⟨i, h ▸ hn⟩ := rfl
|
||||
@[simp] theorem cast_mk (h : n = m) (i : Nat) (hn : i < n) : Fin.cast h ⟨i, hn⟩ = ⟨i, h ▸ hn⟩ := rfl
|
||||
|
||||
@[simp] theorem cast_refl (n : Nat) (h : n = n) : cast h = id := by
|
||||
@[simp] theorem cast_refl (n : Nat) (h : n = n) : Fin.cast h = id := by
|
||||
ext
|
||||
simp
|
||||
|
||||
@[simp] theorem cast_trans {k : Nat} (h : n = m) (h' : m = k) {i : Fin n} :
|
||||
cast h' (cast h i) = cast (Eq.trans h h') i := rfl
|
||||
(i.cast h).cast h' = i.cast (Eq.trans h h') := rfl
|
||||
|
||||
theorem castLE_of_eq {m n : Nat} (h : m = n) {h' : m ≤ n} : castLE h' = Fin.cast h := rfl
|
||||
|
||||
@[simp] theorem coe_castAdd (m : Nat) (i : Fin n) : (castAdd m i : Nat) = i := rfl
|
||||
|
||||
@[simp] theorem castAdd_zero : (castAdd 0 : Fin n → Fin (n + 0)) = cast rfl := rfl
|
||||
@[simp] theorem castAdd_zero : (castAdd 0 : Fin n → Fin (n + 0)) = Fin.cast rfl := rfl
|
||||
|
||||
theorem castAdd_lt {m : Nat} (n : Nat) (i : Fin m) : (castAdd n i : Nat) < m := by simp
|
||||
|
||||
@@ -406,37 +406,37 @@ theorem castAdd_cast {n n' : Nat} (m : Nat) (i : Fin n') (h : n' = n) :
|
||||
castAdd m (Fin.cast h i) = Fin.cast (congrArg (. + m) h) (castAdd m i) := Fin.ext rfl
|
||||
|
||||
theorem cast_castAdd_left {n n' m : Nat} (i : Fin n') (h : n' + m = n + m) :
|
||||
cast h (castAdd m i) = castAdd m (cast (Nat.add_right_cancel h) i) := rfl
|
||||
(i.castAdd m).cast h = (i.cast (Nat.add_right_cancel h)).castAdd m := rfl
|
||||
|
||||
@[simp] theorem cast_castAdd_right {n m m' : Nat} (i : Fin n) (h : n + m' = n + m) :
|
||||
cast h (castAdd m' i) = castAdd m i := rfl
|
||||
(i.castAdd m').cast h = i.castAdd m := rfl
|
||||
|
||||
theorem castAdd_castAdd {m n p : Nat} (i : Fin m) :
|
||||
castAdd p (castAdd n i) = cast (Nat.add_assoc ..).symm (castAdd (n + p) i) := rfl
|
||||
(i.castAdd n).castAdd p = (i.castAdd (n + p)).cast (Nat.add_assoc ..).symm := rfl
|
||||
|
||||
/-- The cast of the successor is the successor of the cast. See `Fin.succ_cast_eq` for rewriting in
|
||||
the reverse direction. -/
|
||||
@[simp] theorem cast_succ_eq {n' : Nat} (i : Fin n) (h : n.succ = n'.succ) :
|
||||
cast h i.succ = (cast (Nat.succ.inj h) i).succ := rfl
|
||||
i.succ.cast h = (i.cast (Nat.succ.inj h)).succ := rfl
|
||||
|
||||
theorem succ_cast_eq {n' : Nat} (i : Fin n) (h : n = n') :
|
||||
(cast h i).succ = cast (by rw [h]) i.succ := rfl
|
||||
(i.cast h).succ = i.succ.cast (by rw [h]) := rfl
|
||||
|
||||
@[simp] theorem coe_castSucc (i : Fin n) : (Fin.castSucc i : Nat) = i := rfl
|
||||
@[simp] theorem coe_castSucc (i : Fin n) : (i.castSucc : Nat) = i := rfl
|
||||
|
||||
@[simp] theorem castSucc_mk (n i : Nat) (h : i < n) : castSucc ⟨i, h⟩ = ⟨i, Nat.lt.step h⟩ := rfl
|
||||
|
||||
@[simp] theorem cast_castSucc {n' : Nat} {h : n + 1 = n' + 1} {i : Fin n} :
|
||||
cast h (castSucc i) = castSucc (cast (Nat.succ.inj h) i) := rfl
|
||||
i.castSucc.cast h = (i.cast (Nat.succ.inj h)).castSucc := rfl
|
||||
|
||||
theorem castSucc_lt_succ (i : Fin n) : Fin.castSucc i < i.succ :=
|
||||
theorem castSucc_lt_succ (i : Fin n) : i.castSucc < i.succ :=
|
||||
lt_def.2 <| by simp only [coe_castSucc, val_succ, Nat.lt_succ_self]
|
||||
|
||||
theorem le_castSucc_iff {i : Fin (n + 1)} {j : Fin n} : i ≤ Fin.castSucc j ↔ i < j.succ := by
|
||||
theorem le_castSucc_iff {i : Fin (n + 1)} {j : Fin n} : i ≤ j.castSucc ↔ i < j.succ := by
|
||||
simpa only [lt_def, le_def] using Nat.add_one_le_add_one_iff.symm
|
||||
|
||||
theorem castSucc_lt_iff_succ_le {n : Nat} {i : Fin n} {j : Fin (n + 1)} :
|
||||
Fin.castSucc i < j ↔ i.succ ≤ j := .rfl
|
||||
i.castSucc < j ↔ i.succ ≤ j := .rfl
|
||||
|
||||
@[simp] theorem succ_last (n : Nat) : (last n).succ = last n.succ := rfl
|
||||
|
||||
@@ -444,48 +444,48 @@ theorem castSucc_lt_iff_succ_le {n : Nat} {i : Fin n} {j : Fin (n + 1)} :
|
||||
i.succ = last (n + 1) ↔ i = last n := by rw [← succ_last, succ_inj]
|
||||
|
||||
@[simp] theorem castSucc_castLT (i : Fin (n + 1)) (h : (i : Nat) < n) :
|
||||
castSucc (castLT i h) = i := rfl
|
||||
(castLT i h).castSucc = i := rfl
|
||||
|
||||
@[simp] theorem castLT_castSucc {n : Nat} (a : Fin n) (h : (a : Nat) < n) :
|
||||
castLT (castSucc a) h = a := rfl
|
||||
castLT a.castSucc h = a := rfl
|
||||
|
||||
@[simp] theorem castSucc_lt_castSucc_iff {a b : Fin n} :
|
||||
Fin.castSucc a < Fin.castSucc b ↔ a < b := .rfl
|
||||
a.castSucc < b.castSucc ↔ a < b := .rfl
|
||||
|
||||
theorem castSucc_inj {a b : Fin n} : castSucc a = castSucc b ↔ a = b := by simp [Fin.ext_iff]
|
||||
theorem castSucc_inj {a b : Fin n} : a.castSucc = b.castSucc ↔ a = b := by simp [Fin.ext_iff]
|
||||
|
||||
theorem castSucc_lt_last (a : Fin n) : castSucc a < last n := a.is_lt
|
||||
theorem castSucc_lt_last (a : Fin n) : a.castSucc < last n := a.is_lt
|
||||
|
||||
@[simp] theorem castSucc_zero : castSucc (0 : Fin (n + 1)) = 0 := rfl
|
||||
|
||||
@[simp] theorem castSucc_one {n : Nat} : castSucc (1 : Fin (n + 2)) = 1 := rfl
|
||||
|
||||
/-- `castSucc i` is positive when `i` is positive -/
|
||||
theorem castSucc_pos {i : Fin (n + 1)} (h : 0 < i) : 0 < castSucc i := by
|
||||
theorem castSucc_pos {i : Fin (n + 1)} (h : 0 < i) : 0 < i.castSucc := by
|
||||
simpa [lt_def] using h
|
||||
|
||||
@[simp] theorem castSucc_eq_zero_iff {a : Fin (n + 1)} : castSucc a = 0 ↔ a = 0 := by simp [Fin.ext_iff]
|
||||
@[simp] theorem castSucc_eq_zero_iff {a : Fin (n + 1)} : a.castSucc = 0 ↔ a = 0 := by simp [Fin.ext_iff]
|
||||
|
||||
theorem castSucc_ne_zero_iff {a : Fin (n + 1)} : castSucc a ≠ 0 ↔ a ≠ 0 :=
|
||||
theorem castSucc_ne_zero_iff {a : Fin (n + 1)} : a.castSucc ≠ 0 ↔ a ≠ 0 :=
|
||||
not_congr <| castSucc_eq_zero_iff
|
||||
|
||||
theorem castSucc_fin_succ (n : Nat) (j : Fin n) :
|
||||
castSucc (Fin.succ j) = Fin.succ (castSucc j) := by simp [Fin.ext_iff]
|
||||
j.succ.castSucc = (j.castSucc).succ := by simp [Fin.ext_iff]
|
||||
|
||||
@[simp]
|
||||
theorem coeSucc_eq_succ {a : Fin n} : castSucc a + 1 = a.succ := by
|
||||
theorem coeSucc_eq_succ {a : Fin n} : a.castSucc + 1 = a.succ := by
|
||||
cases n
|
||||
· exact a.elim0
|
||||
· simp [Fin.ext_iff, add_def, Nat.mod_eq_of_lt (Nat.succ_lt_succ a.is_lt)]
|
||||
|
||||
theorem lt_succ {a : Fin n} : castSucc a < a.succ := by
|
||||
theorem lt_succ {a : Fin n} : a.castSucc < a.succ := by
|
||||
rw [castSucc, lt_def, coe_castAdd, val_succ]; exact Nat.lt_succ_self a.val
|
||||
|
||||
theorem exists_castSucc_eq {n : Nat} {i : Fin (n + 1)} : (∃ j, castSucc j = i) ↔ i ≠ last n :=
|
||||
⟨fun ⟨j, hj⟩ => hj ▸ Fin.ne_of_lt j.castSucc_lt_last,
|
||||
fun hi => ⟨i.castLT <| Fin.val_lt_last hi, rfl⟩⟩
|
||||
|
||||
theorem succ_castSucc {n : Nat} (i : Fin n) : i.castSucc.succ = castSucc i.succ := rfl
|
||||
theorem succ_castSucc {n : Nat} (i : Fin n) : i.castSucc.succ = i.succ.castSucc := rfl
|
||||
|
||||
@[simp] theorem coe_addNat (m : Nat) (i : Fin n) : (addNat i m : Nat) = i + m := rfl
|
||||
|
||||
@@ -502,17 +502,17 @@ theorem le_coe_addNat (m : Nat) (i : Fin n) : m ≤ addNat i m :=
|
||||
addNat ⟨i, hi⟩ n = ⟨i + n, Nat.add_lt_add_right hi n⟩ := rfl
|
||||
|
||||
@[simp] theorem cast_addNat_zero {n n' : Nat} (i : Fin n) (h : n + 0 = n') :
|
||||
cast h (addNat i 0) = cast ((Nat.add_zero _).symm.trans h) i := rfl
|
||||
(addNat i 0).cast h = i.cast ((Nat.add_zero _).symm.trans h) := rfl
|
||||
|
||||
/-- For rewriting in the reverse direction, see `Fin.cast_addNat_left`. -/
|
||||
theorem addNat_cast {n n' m : Nat} (i : Fin n') (h : n' = n) :
|
||||
addNat (cast h i) m = cast (congrArg (. + m) h) (addNat i m) := rfl
|
||||
addNat (i.cast h) m = (addNat i m).cast (congrArg (. + m) h) := rfl
|
||||
|
||||
theorem cast_addNat_left {n n' m : Nat} (i : Fin n') (h : n' + m = n + m) :
|
||||
cast h (addNat i m) = addNat (cast (Nat.add_right_cancel h) i) m := rfl
|
||||
(addNat i m).cast h = addNat (i.cast (Nat.add_right_cancel h)) m := rfl
|
||||
|
||||
@[simp] theorem cast_addNat_right {n m m' : Nat} (i : Fin n) (h : n + m' = n + m) :
|
||||
cast h (addNat i m') = addNat i m :=
|
||||
(addNat i m').cast h = addNat i m :=
|
||||
Fin.ext <| (congrArg ((· + ·) (i : Nat)) (Nat.add_left_cancel h) : _)
|
||||
|
||||
@[simp] theorem coe_natAdd (n : Nat) {m : Nat} (i : Fin m) : (natAdd n i : Nat) = n + i := rfl
|
||||
@@ -522,46 +522,46 @@ theorem cast_addNat_left {n n' m : Nat} (i : Fin n') (h : n' + m = n + m) :
|
||||
|
||||
theorem le_coe_natAdd (m : Nat) (i : Fin n) : m ≤ natAdd m i := Nat.le_add_right ..
|
||||
|
||||
@[simp] theorem natAdd_zero {n : Nat} : natAdd 0 = cast (Nat.zero_add n).symm := by ext; simp
|
||||
@[simp] theorem natAdd_zero {n : Nat} : natAdd 0 = Fin.cast (Nat.zero_add n).symm := by ext; simp
|
||||
|
||||
/-- For rewriting in the reverse direction, see `Fin.cast_natAdd_right`. -/
|
||||
theorem natAdd_cast {n n' : Nat} (m : Nat) (i : Fin n') (h : n' = n) :
|
||||
natAdd m (cast h i) = cast (congrArg _ h) (natAdd m i) := rfl
|
||||
natAdd m (i.cast h) = (natAdd m i).cast (congrArg _ h) := rfl
|
||||
|
||||
theorem cast_natAdd_right {n n' m : Nat} (i : Fin n') (h : m + n' = m + n) :
|
||||
cast h (natAdd m i) = natAdd m (cast (Nat.add_left_cancel h) i) := rfl
|
||||
(natAdd m i).cast h = natAdd m (i.cast (Nat.add_left_cancel h)) := rfl
|
||||
|
||||
@[simp] theorem cast_natAdd_left {n m m' : Nat} (i : Fin n) (h : m' + n = m + n) :
|
||||
cast h (natAdd m' i) = natAdd m i :=
|
||||
(natAdd m' i).cast h = natAdd m i :=
|
||||
Fin.ext <| (congrArg (· + (i : Nat)) (Nat.add_right_cancel h) : _)
|
||||
|
||||
theorem castAdd_natAdd (p m : Nat) {n : Nat} (i : Fin n) :
|
||||
castAdd p (natAdd m i) = cast (Nat.add_assoc ..).symm (natAdd m (castAdd p i)) := rfl
|
||||
castAdd p (natAdd m i) = (natAdd m (castAdd p i)).cast (Nat.add_assoc ..).symm := rfl
|
||||
|
||||
theorem natAdd_castAdd (p m : Nat) {n : Nat} (i : Fin n) :
|
||||
natAdd m (castAdd p i) = cast (Nat.add_assoc ..) (castAdd p (natAdd m i)) := rfl
|
||||
natAdd m (castAdd p i) = (castAdd p (natAdd m i)).cast (Nat.add_assoc ..) := rfl
|
||||
|
||||
theorem natAdd_natAdd (m n : Nat) {p : Nat} (i : Fin p) :
|
||||
natAdd m (natAdd n i) = cast (Nat.add_assoc ..) (natAdd (m + n) i) :=
|
||||
natAdd m (natAdd n i) = (natAdd (m + n) i).cast (Nat.add_assoc ..) :=
|
||||
Fin.ext <| (Nat.add_assoc ..).symm
|
||||
|
||||
@[simp]
|
||||
theorem cast_natAdd_zero {n n' : Nat} (i : Fin n) (h : 0 + n = n') :
|
||||
cast h (natAdd 0 i) = cast ((Nat.zero_add _).symm.trans h) i :=
|
||||
(natAdd 0 i).cast h = i.cast ((Nat.zero_add _).symm.trans h) :=
|
||||
Fin.ext <| Nat.zero_add _
|
||||
|
||||
@[simp]
|
||||
theorem cast_natAdd (n : Nat) {m : Nat} (i : Fin m) :
|
||||
cast (Nat.add_comm ..) (natAdd n i) = addNat i n := Fin.ext <| Nat.add_comm ..
|
||||
(natAdd n i).cast (Nat.add_comm ..) = addNat i n := Fin.ext <| Nat.add_comm ..
|
||||
|
||||
@[simp]
|
||||
theorem cast_addNat {n : Nat} (m : Nat) (i : Fin n) :
|
||||
cast (Nat.add_comm ..) (addNat i m) = natAdd m i := Fin.ext <| Nat.add_comm ..
|
||||
(addNat i m).cast (Nat.add_comm ..) = natAdd m i := Fin.ext <| Nat.add_comm ..
|
||||
|
||||
@[simp] theorem natAdd_last {m n : Nat} : natAdd n (last m) = last (n + m) := rfl
|
||||
|
||||
@[simp] theorem addNat_last (n : Nat) :
|
||||
addNat (last n) m = cast (by omega) (last (n + m)) := by
|
||||
addNat (last n) m = (last (n + m)).cast (by omega) := by
|
||||
ext
|
||||
simp
|
||||
|
||||
@@ -657,7 +657,7 @@ theorem pred_add_one (i : Fin (n + 2)) (h : (i : Nat) < n + 1) :
|
||||
subNat m (addNat i m) h = i := Fin.ext <| Nat.add_sub_cancel i m
|
||||
|
||||
@[simp] theorem natAdd_subNat_cast {i : Fin (n + m)} (h : n ≤ i) :
|
||||
natAdd n (subNat n (cast (Nat.add_comm ..) i) h) = i := by simp [← cast_addNat]
|
||||
natAdd n (subNat n (i.cast (Nat.add_comm ..)) h) = i := by simp [← cast_addNat]
|
||||
|
||||
/-! ### recursion and induction principles -/
|
||||
|
||||
|
||||
@@ -34,4 +34,8 @@ theorem shiftRight_eq_div_pow (m : Int) (n : Nat) :
|
||||
theorem zero_shiftRight (n : Nat) : (0 : Int) >>> n = 0 := by
|
||||
simp [Int.shiftRight_eq_div_pow]
|
||||
|
||||
@[simp]
|
||||
theorem shiftRight_zero (n : Int) : n >>> 0 = n := by
|
||||
simp [Int.shiftRight_eq_div_pow]
|
||||
|
||||
end Int
|
||||
|
||||
@@ -29,6 +29,8 @@ At that time, we did not rename `div` and `mod` to `tdiv` and `tmod` (along with
|
||||
In September 2024, we decided to do this rename (with deprecations in place),
|
||||
and later we intend to rename `ediv` and `emod` to `div` and `mod`, as nearly all users will only
|
||||
ever need to use these functions and their associated lemmas.
|
||||
|
||||
In December 2024, we removed `tdiv` and `tmod`, but have not yet renamed `ediv` and `emod`.
|
||||
-/
|
||||
|
||||
/-! ### T-rounding division -/
|
||||
@@ -71,8 +73,6 @@ def tdiv : (@& Int) → (@& Int) → Int
|
||||
| -[m +1], ofNat n => -ofNat (succ m / n)
|
||||
| -[m +1], -[n +1] => ofNat (succ m / succ n)
|
||||
|
||||
@[deprecated tdiv (since := "2024-09-11")] abbrev div := tdiv
|
||||
|
||||
/-- Integer modulo. This function uses the
|
||||
[*"T-rounding"*][t-rounding] (**T**runcation-rounding) convention
|
||||
to pair with `Int.tdiv`, meaning that `tmod a b + b * (tdiv a b) = a`
|
||||
@@ -107,8 +107,6 @@ def tmod : (@& Int) → (@& Int) → Int
|
||||
| -[m +1], ofNat n => -ofNat (succ m % n)
|
||||
| -[m +1], -[n +1] => -ofNat (succ m % succ n)
|
||||
|
||||
@[deprecated tmod (since := "2024-09-11")] abbrev mod := tmod
|
||||
|
||||
/-! ### F-rounding division
|
||||
This pair satisfies `fdiv x y = floor (x / y)`.
|
||||
-/
|
||||
@@ -251,8 +249,6 @@ instance : Mod Int where
|
||||
|
||||
theorem ofNat_tdiv (m n : Nat) : ↑(m / n) = tdiv ↑m ↑n := rfl
|
||||
|
||||
@[deprecated ofNat_tdiv (since := "2024-09-11")] abbrev ofNat_div := ofNat_tdiv
|
||||
|
||||
theorem ofNat_fdiv : ∀ m n : Nat, ↑(m / n) = fdiv ↑m ↑n
|
||||
| 0, _ => by simp [fdiv]
|
||||
| succ _, _ => rfl
|
||||
|
||||
@@ -125,7 +125,7 @@ theorem eq_one_of_mul_eq_one_right {a b : Int} (H : 0 ≤ a) (H' : a * b = 1) :
|
||||
eq_one_of_dvd_one H ⟨b, H'.symm⟩
|
||||
|
||||
theorem eq_one_of_mul_eq_one_left {a b : Int} (H : 0 ≤ b) (H' : a * b = 1) : b = 1 :=
|
||||
eq_one_of_mul_eq_one_right H <| by rw [Int.mul_comm, H']
|
||||
eq_one_of_mul_eq_one_right (b := a) H <| by rw [Int.mul_comm, H']
|
||||
|
||||
/-! ### *div zero -/
|
||||
|
||||
@@ -1315,65 +1315,3 @@ theorem bmod_natAbs_plus_one (x : Int) (w : 1 < x.natAbs) : bmod x (x.natAbs + 1
|
||||
all_goals decide
|
||||
· exact ofNat_nonneg x
|
||||
· exact succ_ofNat_pos (x + 1)
|
||||
|
||||
/-! ### Deprecations -/
|
||||
|
||||
@[deprecated Int.zero_tdiv (since := "2024-09-11")] protected abbrev zero_div := @Int.zero_tdiv
|
||||
@[deprecated Int.tdiv_zero (since := "2024-09-11")] protected abbrev div_zero := @Int.tdiv_zero
|
||||
@[deprecated tdiv_eq_ediv (since := "2024-09-11")] abbrev div_eq_ediv := @tdiv_eq_ediv
|
||||
@[deprecated fdiv_eq_tdiv (since := "2024-09-11")] abbrev fdiv_eq_div := @fdiv_eq_tdiv
|
||||
@[deprecated zero_tmod (since := "2024-09-11")] abbrev zero_mod := @zero_tmod
|
||||
@[deprecated tmod_zero (since := "2024-09-11")] abbrev mod_zero := @tmod_zero
|
||||
@[deprecated tmod_add_tdiv (since := "2024-09-11")] abbrev mod_add_div := @tmod_add_tdiv
|
||||
@[deprecated tdiv_add_tmod (since := "2024-09-11")] abbrev div_add_mod := @tdiv_add_tmod
|
||||
@[deprecated tmod_add_tdiv' (since := "2024-09-11")] abbrev mod_add_div' := @tmod_add_tdiv'
|
||||
@[deprecated tdiv_add_tmod' (since := "2024-09-11")] abbrev div_add_mod' := @tdiv_add_tmod'
|
||||
@[deprecated tmod_def (since := "2024-09-11")] abbrev mod_def := @tmod_def
|
||||
@[deprecated tmod_eq_emod (since := "2024-09-11")] abbrev mod_eq_emod := @tmod_eq_emod
|
||||
@[deprecated fmod_eq_tmod (since := "2024-09-11")] abbrev fmod_eq_mod := @fmod_eq_tmod
|
||||
@[deprecated Int.tdiv_one (since := "2024-09-11")] protected abbrev div_one := @Int.tdiv_one
|
||||
@[deprecated Int.tdiv_neg (since := "2024-09-11")] protected abbrev div_neg := @Int.tdiv_neg
|
||||
@[deprecated Int.neg_tdiv (since := "2024-09-11")] protected abbrev neg_div := @Int.neg_tdiv
|
||||
@[deprecated Int.neg_tdiv_neg (since := "2024-09-11")] protected abbrev neg_div_neg := @Int.neg_tdiv_neg
|
||||
@[deprecated Int.tdiv_nonneg (since := "2024-09-11")] protected abbrev div_nonneg := @Int.tdiv_nonneg
|
||||
@[deprecated Int.tdiv_nonpos (since := "2024-09-11")] protected abbrev div_nonpos := @Int.tdiv_nonpos
|
||||
@[deprecated Int.tdiv_eq_zero_of_lt (since := "2024-09-11")] abbrev div_eq_zero_of_lt := @Int.tdiv_eq_zero_of_lt
|
||||
@[deprecated Int.mul_tdiv_cancel (since := "2024-09-11")] protected abbrev mul_div_cancel := @Int.mul_tdiv_cancel
|
||||
@[deprecated Int.mul_tdiv_cancel_left (since := "2024-09-11")] protected abbrev mul_div_cancel_left := @Int.mul_tdiv_cancel_left
|
||||
@[deprecated Int.tdiv_self (since := "2024-09-11")] protected abbrev div_self := @Int.tdiv_self
|
||||
@[deprecated Int.mul_tdiv_cancel_of_tmod_eq_zero (since := "2024-09-11")] abbrev mul_div_cancel_of_mod_eq_zero := @Int.mul_tdiv_cancel_of_tmod_eq_zero
|
||||
@[deprecated Int.tdiv_mul_cancel_of_tmod_eq_zero (since := "2024-09-11")] abbrev div_mul_cancel_of_mod_eq_zero := @Int.tdiv_mul_cancel_of_tmod_eq_zero
|
||||
@[deprecated Int.dvd_of_tmod_eq_zero (since := "2024-09-11")] abbrev dvd_of_mod_eq_zero := @Int.dvd_of_tmod_eq_zero
|
||||
@[deprecated Int.mul_tdiv_assoc (since := "2024-09-11")] protected abbrev mul_div_assoc := @Int.mul_tdiv_assoc
|
||||
@[deprecated Int.mul_tdiv_assoc' (since := "2024-09-11")] protected abbrev mul_div_assoc' := @Int.mul_tdiv_assoc'
|
||||
@[deprecated Int.tdiv_dvd_tdiv (since := "2024-09-11")] abbrev div_dvd_div := @Int.tdiv_dvd_tdiv
|
||||
@[deprecated Int.natAbs_tdiv (since := "2024-09-11")] abbrev natAbs_div := @Int.natAbs_tdiv
|
||||
@[deprecated Int.tdiv_eq_of_eq_mul_right (since := "2024-09-11")] protected abbrev div_eq_of_eq_mul_right := @Int.tdiv_eq_of_eq_mul_right
|
||||
@[deprecated Int.eq_tdiv_of_mul_eq_right (since := "2024-09-11")] protected abbrev eq_div_of_mul_eq_right := @Int.eq_tdiv_of_mul_eq_right
|
||||
@[deprecated Int.ofNat_tmod (since := "2024-09-11")] abbrev ofNat_mod := @Int.ofNat_tmod
|
||||
@[deprecated Int.tmod_one (since := "2024-09-11")] abbrev mod_one := @Int.tmod_one
|
||||
@[deprecated Int.tmod_eq_of_lt (since := "2024-09-11")] abbrev mod_eq_of_lt := @Int.tmod_eq_of_lt
|
||||
@[deprecated Int.tmod_lt_of_pos (since := "2024-09-11")] abbrev mod_lt_of_pos := @Int.tmod_lt_of_pos
|
||||
@[deprecated Int.tmod_nonneg (since := "2024-09-11")] abbrev mod_nonneg := @Int.tmod_nonneg
|
||||
@[deprecated Int.tmod_neg (since := "2024-09-11")] abbrev mod_neg := @Int.tmod_neg
|
||||
@[deprecated Int.mul_tmod_left (since := "2024-09-11")] abbrev mul_mod_left := @Int.mul_tmod_left
|
||||
@[deprecated Int.mul_tmod_right (since := "2024-09-11")] abbrev mul_mod_right := @Int.mul_tmod_right
|
||||
@[deprecated Int.tmod_eq_zero_of_dvd (since := "2024-09-11")] abbrev mod_eq_zero_of_dvd := @Int.tmod_eq_zero_of_dvd
|
||||
@[deprecated Int.dvd_iff_tmod_eq_zero (since := "2024-09-11")] abbrev dvd_iff_mod_eq_zero := @Int.dvd_iff_tmod_eq_zero
|
||||
@[deprecated Int.neg_mul_tmod_right (since := "2024-09-11")] abbrev neg_mul_mod_right := @Int.neg_mul_tmod_right
|
||||
@[deprecated Int.neg_mul_tmod_left (since := "2024-09-11")] abbrev neg_mul_mod_left := @Int.neg_mul_tmod_left
|
||||
@[deprecated Int.tdiv_mul_cancel (since := "2024-09-11")] protected abbrev div_mul_cancel := @Int.tdiv_mul_cancel
|
||||
@[deprecated Int.mul_tdiv_cancel' (since := "2024-09-11")] protected abbrev mul_div_cancel' := @Int.mul_tdiv_cancel'
|
||||
@[deprecated Int.eq_mul_of_tdiv_eq_right (since := "2024-09-11")] protected abbrev eq_mul_of_div_eq_right := @Int.eq_mul_of_tdiv_eq_right
|
||||
@[deprecated Int.tmod_self (since := "2024-09-11")] abbrev mod_self := @Int.tmod_self
|
||||
@[deprecated Int.neg_tmod_self (since := "2024-09-11")] abbrev neg_mod_self := @Int.neg_tmod_self
|
||||
@[deprecated Int.lt_tdiv_add_one_mul_self (since := "2024-09-11")] abbrev lt_div_add_one_mul_self := @Int.lt_tdiv_add_one_mul_self
|
||||
@[deprecated Int.tdiv_eq_iff_eq_mul_right (since := "2024-09-11")] protected abbrev div_eq_iff_eq_mul_right := @Int.tdiv_eq_iff_eq_mul_right
|
||||
@[deprecated Int.tdiv_eq_iff_eq_mul_left (since := "2024-09-11")] protected abbrev div_eq_iff_eq_mul_left := @Int.tdiv_eq_iff_eq_mul_left
|
||||
@[deprecated Int.eq_mul_of_tdiv_eq_left (since := "2024-09-11")] protected abbrev eq_mul_of_div_eq_left := @Int.eq_mul_of_tdiv_eq_left
|
||||
@[deprecated Int.tdiv_eq_of_eq_mul_left (since := "2024-09-11")] protected abbrev div_eq_of_eq_mul_left := @Int.tdiv_eq_of_eq_mul_left
|
||||
@[deprecated Int.eq_zero_of_tdiv_eq_zero (since := "2024-09-11")] protected abbrev eq_zero_of_div_eq_zero := @Int.eq_zero_of_tdiv_eq_zero
|
||||
@[deprecated Int.tdiv_left_inj (since := "2024-09-11")] protected abbrev div_left_inj := @Int.tdiv_left_inj
|
||||
@[deprecated Int.tdiv_sign (since := "2024-09-11")] abbrev div_sign := @Int.tdiv_sign
|
||||
@[deprecated Int.sign_eq_tdiv_abs (since := "2024-09-11")] protected abbrev sign_eq_div_abs := @Int.sign_eq_tdiv_abs
|
||||
@[deprecated Int.tdiv_eq_ediv_of_dvd (since := "2024-09-11")] abbrev div_eq_ediv_of_dvd := @Int.tdiv_eq_ediv_of_dvd
|
||||
|
||||
@@ -155,7 +155,8 @@ def mapMono (as : List α) (f : α → α) : List α :=
|
||||
|
||||
/-! ## Additional lemmas required for bootstrapping `Array`. -/
|
||||
|
||||
theorem getElem_append_left {as bs : List α} (h : i < as.length) {h'} : (as ++ bs)[i] = as[i] := by
|
||||
theorem getElem_append_left {as bs : List α} (h : i < as.length) {h' : i < (as ++ bs).length} :
|
||||
(as ++ bs)[i] = as[i] := by
|
||||
induction as generalizing i with
|
||||
| nil => trivial
|
||||
| cons a as ih =>
|
||||
|
||||
@@ -162,6 +162,10 @@ theorem countP_filterMap (p : β → Bool) (f : α → Option β) (l : List α)
|
||||
|
||||
@[deprecated countP_flatten (since := "2024-10-14")] abbrev countP_join := @countP_flatten
|
||||
|
||||
theorem countP_flatMap (p : β → Bool) (l : List α) (f : α → List β) :
|
||||
countP p (l.flatMap f) = sum (map (countP p ∘ f) l) := by
|
||||
rw [List.flatMap, countP_flatten, map_map]
|
||||
|
||||
@[simp] theorem countP_reverse (l : List α) : countP p l.reverse = countP p l := by
|
||||
simp [countP_eq_length_filter, filter_reverse]
|
||||
|
||||
@@ -326,6 +330,9 @@ theorem count_filterMap {α} [BEq β] (b : β) (f : α → Option β) (l : List
|
||||
· simp
|
||||
· simp
|
||||
|
||||
theorem count_flatMap {α} [BEq β] (l : List α) (f : α → List β) (x : β) :
|
||||
count x (l.flatMap f) = sum (map (count x ∘ f) l) := countP_flatMap _ _ _
|
||||
|
||||
theorem count_erase (a b : α) :
|
||||
∀ l : List α, count a (l.erase b) = count a l - if b == a then 1 else 0
|
||||
| [] => by simp
|
||||
|
||||
@@ -83,44 +83,12 @@ open Nat
|
||||
@[simp] theorem nil_eq {α} {xs : List α} : [] = xs ↔ xs = [] := by
|
||||
cases xs <;> simp
|
||||
|
||||
/-! ### cons -/
|
||||
|
||||
theorem cons_ne_nil (a : α) (l : List α) : a :: l ≠ [] := nofun
|
||||
|
||||
@[simp]
|
||||
theorem cons_ne_self (a : α) (l : List α) : a :: l ≠ l := mt (congrArg length) (Nat.succ_ne_self _)
|
||||
|
||||
@[simp] theorem ne_cons_self {a : α} {l : List α} : l ≠ a :: l := by
|
||||
rw [ne_eq, eq_comm]
|
||||
simp
|
||||
|
||||
theorem head_eq_of_cons_eq (H : h₁ :: t₁ = h₂ :: t₂) : h₁ = h₂ := (cons.inj H).1
|
||||
|
||||
theorem tail_eq_of_cons_eq (H : h₁ :: t₁ = h₂ :: t₂) : t₁ = t₂ := (cons.inj H).2
|
||||
|
||||
theorem cons_inj_right (a : α) {l l' : List α} : a :: l = a :: l' ↔ l = l' :=
|
||||
⟨tail_eq_of_cons_eq, congrArg _⟩
|
||||
|
||||
@[deprecated cons_inj_right (since := "2024-06-15")] abbrev cons_inj := @cons_inj_right
|
||||
|
||||
theorem cons_eq_cons {a b : α} {l l' : List α} : a :: l = b :: l' ↔ a = b ∧ l = l' :=
|
||||
List.cons.injEq .. ▸ .rfl
|
||||
|
||||
theorem exists_cons_of_ne_nil : ∀ {l : List α}, l ≠ [] → ∃ b L, l = b :: L
|
||||
| c :: l', _ => ⟨c, l', rfl⟩
|
||||
|
||||
theorem singleton_inj {α : Type _} {a b : α} : [a] = [b] ↔ a = b := by
|
||||
simp
|
||||
|
||||
/-! ### length -/
|
||||
|
||||
theorem eq_nil_of_length_eq_zero (_ : length l = 0) : l = [] := match l with | [] => rfl
|
||||
|
||||
theorem ne_nil_of_length_eq_add_one (_ : length l = n + 1) : l ≠ [] := fun _ => nomatch l
|
||||
|
||||
@[deprecated ne_nil_of_length_eq_add_one (since := "2024-06-16")]
|
||||
abbrev ne_nil_of_length_eq_succ := @ne_nil_of_length_eq_add_one
|
||||
|
||||
theorem ne_nil_of_length_pos (_ : 0 < length l) : l ≠ [] := fun _ => nomatch l
|
||||
|
||||
@[simp] theorem length_eq_zero : length l = 0 ↔ l = [] :=
|
||||
@@ -156,6 +124,36 @@ theorem length_pos {l : List α} : 0 < length l ↔ l ≠ [] :=
|
||||
theorem length_eq_one {l : List α} : length l = 1 ↔ ∃ a, l = [a] :=
|
||||
⟨fun h => match l, h with | [_], _ => ⟨_, rfl⟩, fun ⟨_, h⟩ => by simp [h]⟩
|
||||
|
||||
/-! ### cons -/
|
||||
|
||||
theorem cons_ne_nil (a : α) (l : List α) : a :: l ≠ [] := nofun
|
||||
|
||||
@[simp]
|
||||
theorem cons_ne_self (a : α) (l : List α) : a :: l ≠ l := mt (congrArg length) (Nat.succ_ne_self _)
|
||||
|
||||
@[simp] theorem ne_cons_self {a : α} {l : List α} : l ≠ a :: l := by
|
||||
rw [ne_eq, eq_comm]
|
||||
simp
|
||||
|
||||
theorem head_eq_of_cons_eq (H : h₁ :: t₁ = h₂ :: t₂) : h₁ = h₂ := (cons.inj H).1
|
||||
|
||||
theorem tail_eq_of_cons_eq (H : h₁ :: t₁ = h₂ :: t₂) : t₁ = t₂ := (cons.inj H).2
|
||||
|
||||
theorem cons_inj_right (a : α) {l l' : List α} : a :: l = a :: l' ↔ l = l' :=
|
||||
⟨tail_eq_of_cons_eq, congrArg _⟩
|
||||
|
||||
theorem cons_eq_cons {a b : α} {l l' : List α} : a :: l = b :: l' ↔ a = b ∧ l = l' :=
|
||||
List.cons.injEq .. ▸ .rfl
|
||||
|
||||
theorem exists_cons_of_ne_nil : ∀ {l : List α}, l ≠ [] → ∃ b L, l = b :: L
|
||||
| c :: l', _ => ⟨c, l', rfl⟩
|
||||
|
||||
theorem ne_nil_iff_exists_cons {l : List α} : l ≠ [] ↔ ∃ b L, l = b :: L :=
|
||||
⟨exists_cons_of_ne_nil, fun ⟨_, _, eq⟩ => eq.symm ▸ cons_ne_nil _ _⟩
|
||||
|
||||
theorem singleton_inj {α : Type _} {a b : α} : [a] = [b] ↔ a = b := by
|
||||
simp
|
||||
|
||||
/-! ## L[i] and L[i]? -/
|
||||
|
||||
/-! ### `get` and `get?`.
|
||||
@@ -163,57 +161,29 @@ theorem length_eq_one {l : List α} : length l = 1 ↔ ∃ a, l = [a] :=
|
||||
We simplify `l.get i` to `l[i.1]'i.2` and `l.get? i` to `l[i]?`.
|
||||
-/
|
||||
|
||||
theorem get_cons_zero : get (a::l) (0 : Fin (l.length + 1)) = a := rfl
|
||||
@[simp] theorem get_eq_getElem (l : List α) (i : Fin l.length) : l.get i = l[i.1]'i.2 := rfl
|
||||
|
||||
theorem get_cons_succ {as : List α} {h : i + 1 < (a :: as).length} :
|
||||
(a :: as).get ⟨i+1, h⟩ = as.get ⟨i, Nat.lt_of_succ_lt_succ h⟩ := rfl
|
||||
|
||||
theorem get_cons_succ' {as : List α} {i : Fin as.length} :
|
||||
(a :: as).get i.succ = as.get i := rfl
|
||||
|
||||
@[deprecated "Deprecated without replacement." (since := "2024-07-09")]
|
||||
theorem get_cons_cons_one : (a₁ :: a₂ :: as).get (1 : Fin (as.length + 2)) = a₂ := rfl
|
||||
|
||||
theorem get_mk_zero : ∀ {l : List α} (h : 0 < l.length), l.get ⟨0, h⟩ = l.head (length_pos.mp h)
|
||||
| _::_, _ => rfl
|
||||
|
||||
theorem get?_zero (l : List α) : l.get? 0 = l.head? := by cases l <;> rfl
|
||||
|
||||
theorem get?_len_le : ∀ {l : List α} {n}, length l ≤ n → l.get? n = none
|
||||
theorem get?_eq_none : ∀ {l : List α} {n}, length l ≤ n → l.get? n = none
|
||||
| [], _, _ => rfl
|
||||
| _ :: l, _+1, h => get?_len_le (l := l) <| Nat.le_of_succ_le_succ h
|
||||
| _ :: l, _+1, h => get?_eq_none (l := l) <| Nat.le_of_succ_le_succ h
|
||||
|
||||
theorem get?_eq_get : ∀ {l : List α} {n} (h : n < l.length), l.get? n = some (get l ⟨n, h⟩)
|
||||
| _ :: _, 0, _ => rfl
|
||||
| _ :: l, _+1, _ => get?_eq_get (l := l) _
|
||||
|
||||
theorem get?_eq_some : l.get? n = some a ↔ ∃ h, get l ⟨n, h⟩ = a :=
|
||||
theorem get?_eq_some_iff : l.get? n = some a ↔ ∃ h, get l ⟨n, h⟩ = a :=
|
||||
⟨fun e =>
|
||||
have : n < length l := Nat.gt_of_not_le fun hn => by cases get?_len_le hn ▸ e
|
||||
have : n < length l := Nat.gt_of_not_le fun hn => by cases get?_eq_none hn ▸ e
|
||||
⟨this, by rwa [get?_eq_get this, Option.some.injEq] at e⟩,
|
||||
fun ⟨_, e⟩ => e ▸ get?_eq_get _⟩
|
||||
|
||||
theorem get?_eq_none : l.get? n = none ↔ length l ≤ n :=
|
||||
⟨fun e => Nat.ge_of_not_lt (fun h' => by cases e ▸ get?_eq_some.2 ⟨h', rfl⟩), get?_len_le⟩
|
||||
theorem get?_eq_none_iff : l.get? n = none ↔ length l ≤ n :=
|
||||
⟨fun e => Nat.ge_of_not_lt (fun h' => by cases e ▸ get?_eq_some_iff.2 ⟨h', rfl⟩), get?_eq_none⟩
|
||||
|
||||
@[simp] theorem get?_eq_getElem? (l : List α) (i : Nat) : l.get? i = l[i]? := by
|
||||
simp only [getElem?, decidableGetElem?]; split
|
||||
simp only [getElem?_def]; split
|
||||
· exact (get?_eq_get ‹_›)
|
||||
· exact (get?_eq_none.2 <| Nat.not_lt.1 ‹_›)
|
||||
|
||||
@[simp] theorem get_eq_getElem (l : List α) (i : Fin l.length) : l.get i = l[i.1]'i.2 := rfl
|
||||
|
||||
theorem getElem?_eq_some {l : List α} : l[i]? = some a ↔ ∃ h : i < l.length, l[i]'h = a := by
|
||||
simpa using get?_eq_some
|
||||
|
||||
/--
|
||||
If one has `l.get i` in an expression (with `i : Fin l.length`) and `h : l = l'`,
|
||||
`rw [h]` will give a "motive it not type correct" error, as it cannot rewrite the
|
||||
`i : Fin l.length` to `Fin l'.length` directly. The theorem `get_of_eq` can be used to make
|
||||
such a rewrite, with `rw [get_of_eq h]`.
|
||||
-/
|
||||
theorem get_of_eq {l l' : List α} (h : l = l') (i : Fin l.length) :
|
||||
get l i = get l' ⟨i, h ▸ i.2⟩ := by cases h; rfl
|
||||
· exact (get?_eq_none_iff.2 <| Nat.not_lt.1 ‹_›)
|
||||
|
||||
/-! ### getD
|
||||
|
||||
@@ -224,42 +194,29 @@ Because of this, there is only minimal API for `getD`.
|
||||
@[simp] theorem getD_eq_getElem?_getD (l) (n) (a : α) : getD l n a = (l[n]?).getD a := by
|
||||
simp [getD]
|
||||
|
||||
@[deprecated getD_eq_getElem?_getD (since := "2024-06-12")]
|
||||
theorem getD_eq_get? : ∀ l n (a : α), getD l n a = (get? l n).getD a := by simp
|
||||
|
||||
/-! ### get!
|
||||
|
||||
We simplify `l.get! n` to `l[n]!`.
|
||||
-/
|
||||
|
||||
theorem get!_of_get? [Inhabited α] : ∀ {l : List α} {n}, get? l n = some a → get! l n = a
|
||||
| _a::_, 0, rfl => rfl
|
||||
| _::l, _+1, e => get!_of_get? (l := l) e
|
||||
|
||||
theorem get!_eq_getD [Inhabited α] : ∀ (l : List α) n, l.get! n = l.getD n default
|
||||
| [], _ => rfl
|
||||
| _a::_, 0 => rfl
|
||||
| _a::l, n+1 => get!_eq_getD l n
|
||||
|
||||
theorem get!_len_le [Inhabited α] : ∀ {l : List α} {n}, length l ≤ n → l.get! n = (default : α)
|
||||
| [], _, _ => rfl
|
||||
| _ :: l, _+1, h => get!_len_le (l := l) <| Nat.le_of_succ_le_succ h
|
||||
|
||||
@[simp] theorem get!_eq_getElem! [Inhabited α] (l : List α) (n) : l.get! n = l[n]! := by
|
||||
simp [get!_eq_getD]
|
||||
rfl
|
||||
|
||||
/-! ### getElem! -/
|
||||
/-! ### getElem!
|
||||
|
||||
@[simp] theorem getElem!_nil [Inhabited α] {n : Nat} : ([] : List α)[n]! = default := rfl
|
||||
We simplify `l[n]!` to `(l[n]?).getD default`.
|
||||
-/
|
||||
|
||||
@[simp] theorem getElem!_cons_zero [Inhabited α] {l : List α} : (a::l)[0]! = a := by
|
||||
rw [getElem!_pos] <;> simp
|
||||
|
||||
@[simp] theorem getElem!_cons_succ [Inhabited α] {l : List α} : (a::l)[n+1]! = l[n]! := by
|
||||
by_cases h : n < l.length
|
||||
· rw [getElem!_pos, getElem!_pos] <;> simp_all [Nat.succ_lt_succ_iff]
|
||||
· rw [getElem!_neg, getElem!_neg] <;> simp_all [Nat.succ_lt_succ_iff]
|
||||
@[simp] theorem getElem!_eq_getElem?_getD [Inhabited α] (l : List α) (n : Nat) :
|
||||
l[n]! = (l[n]?).getD (default : α) := by
|
||||
simp only [getElem!_def]
|
||||
split <;> simp_all
|
||||
|
||||
/-! ### getElem? and getElem -/
|
||||
|
||||
@@ -267,23 +224,19 @@ theorem get!_len_le [Inhabited α] : ∀ {l : List α} {n}, length l ≤ n → l
|
||||
simp only [getElem?_def, h, ↓reduceDIte]
|
||||
|
||||
theorem getElem?_eq_some_iff {l : List α} : l[n]? = some a ↔ ∃ h : n < l.length, l[n] = a := by
|
||||
simp only [← get?_eq_getElem?, get?_eq_some, get_eq_getElem]
|
||||
simp only [← get?_eq_getElem?, get?_eq_some_iff, get_eq_getElem]
|
||||
|
||||
theorem some_eq_getElem?_iff {l : List α} : some a = l[n]? ↔ ∃ h : n < l.length, l[n] = a := by
|
||||
rw [eq_comm, getElem?_eq_some_iff]
|
||||
|
||||
@[simp] theorem getElem?_eq_none_iff : l[n]? = none ↔ length l ≤ n := by
|
||||
simp only [← get?_eq_getElem?, get?_eq_none]
|
||||
simp only [← get?_eq_getElem?, get?_eq_none_iff]
|
||||
|
||||
@[simp] theorem none_eq_getElem?_iff {l : List α} {n : Nat} : none = l[n]? ↔ length l ≤ n := by
|
||||
simp [eq_comm (a := none)]
|
||||
|
||||
theorem getElem?_eq_none (h : length l ≤ n) : l[n]? = none := getElem?_eq_none_iff.mpr h
|
||||
|
||||
theorem getElem?_eq (l : List α) (i : Nat) :
|
||||
l[i]? = if h : i < l.length then some l[i] else none := by
|
||||
split <;> simp_all
|
||||
|
||||
@[simp] theorem some_getElem_eq_getElem?_iff {α} (xs : List α) (i : Nat) (h : i < xs.length) :
|
||||
(some xs[i] = xs[i]?) ↔ True := by
|
||||
simp [h]
|
||||
@@ -300,9 +253,6 @@ theorem getElem_eq_getElem?_get (l : List α) (i : Nat) (h : i < l.length) :
|
||||
l[i] = l[i]?.get (by simp [getElem?_eq_getElem, h]) := by
|
||||
simp [getElem_eq_iff]
|
||||
|
||||
@[deprecated getElem_eq_getElem?_get (since := "2024-09-04")] abbrev getElem_eq_getElem? :=
|
||||
@getElem_eq_getElem?_get
|
||||
|
||||
@[simp] theorem getElem?_nil {n : Nat} : ([] : List α)[n]? = none := rfl
|
||||
|
||||
theorem getElem?_cons_zero {l : List α} : (a::l)[0]? = some a := by simp
|
||||
@@ -314,11 +264,6 @@ theorem getElem?_cons_zero {l : List α} : (a::l)[0]? = some a := by simp
|
||||
theorem getElem?_cons : (a :: l)[i]? = if i = 0 then some a else l[i-1]? := by
|
||||
cases i <;> simp
|
||||
|
||||
theorem getElem?_len_le : ∀ {l : List α} {n}, length l ≤ n → l[n]? = none
|
||||
| [], _, _ => rfl
|
||||
| _ :: l, _+1, h => by
|
||||
rw [getElem?_cons_succ, getElem?_len_le (l := l) <| Nat.le_of_succ_le_succ h]
|
||||
|
||||
/--
|
||||
If one has `l[i]` in an expression and `h : l = l'`,
|
||||
`rw [h]` will give a "motive it not type correct" error, as it cannot rewrite the
|
||||
@@ -332,20 +277,10 @@ theorem getElem_of_eq {l l' : List α} (h : l = l') {i : Nat} (w : i < l.length)
|
||||
match i, h with
|
||||
| 0, _ => rfl
|
||||
|
||||
@[deprecated getElem_singleton (since := "2024-06-12")]
|
||||
theorem get_singleton (a : α) (n : Fin 1) : get [a] n = a := by simp
|
||||
|
||||
theorem getElem_zero {l : List α} (h : 0 < l.length) : l[0] = l.head (length_pos.mp h) :=
|
||||
match l, h with
|
||||
| _ :: _, _ => rfl
|
||||
|
||||
theorem getElem!_of_getElem? [Inhabited α] : ∀ {l : List α} {n : Nat}, l[n]? = some a → l[n]! = a
|
||||
| _a::_, 0, _ => by
|
||||
rw [getElem!_pos] <;> simp_all
|
||||
| _::l, _+1, e => by
|
||||
simp at e
|
||||
simp_all [getElem!_of_getElem? (l := l) e]
|
||||
|
||||
@[ext] theorem ext_getElem? {l₁ l₂ : List α} (h : ∀ n : Nat, l₁[n]? = l₂[n]?) : l₁ = l₂ :=
|
||||
ext_get? fun n => by simp_all
|
||||
|
||||
@@ -356,11 +291,7 @@ theorem ext_getElem {l₁ l₂ : List α} (hl : length l₁ = length l₂)
|
||||
simp_all [getElem?_eq_getElem]
|
||||
else by
|
||||
have h₁ := Nat.le_of_not_lt h₁
|
||||
rw [getElem?_len_le h₁, getElem?_len_le]; rwa [← hl]
|
||||
|
||||
theorem ext_get {l₁ l₂ : List α} (hl : length l₁ = length l₂)
|
||||
(h : ∀ n h₁ h₂, get l₁ ⟨n, h₁⟩ = get l₂ ⟨n, h₂⟩) : l₁ = l₂ :=
|
||||
ext_getElem hl (by simp_all)
|
||||
rw [getElem?_eq_none h₁, getElem?_eq_none]; rwa [← hl]
|
||||
|
||||
@[simp] theorem getElem_concat_length : ∀ (l : List α) (a : α) (i) (_ : i = l.length) (w), (l ++ [a])[i]'w = a
|
||||
| [], a, _, h, _ => by subst h; simp
|
||||
@@ -369,19 +300,11 @@ theorem ext_get {l₁ l₂ : List α} (hl : length l₁ = length l₂)
|
||||
theorem getElem?_concat_length (l : List α) (a : α) : (l ++ [a])[l.length]? = some a := by
|
||||
simp
|
||||
|
||||
@[deprecated getElem?_concat_length (since := "2024-06-12")]
|
||||
theorem get?_concat_length (l : List α) (a : α) : (l ++ [a]).get? l.length = some a := by simp
|
||||
theorem isSome_getElem? {l : List α} {n : Nat} : l[n]?.isSome ↔ n < l.length := by
|
||||
simp
|
||||
|
||||
@[simp] theorem isSome_getElem? {l : List α} {n : Nat} : l[n]?.isSome ↔ n < l.length := by
|
||||
by_cases h : n < l.length
|
||||
· simp_all
|
||||
· simp [h]
|
||||
simp_all
|
||||
|
||||
@[simp] theorem isNone_getElem? {l : List α} {n : Nat} : l[n]?.isNone ↔ l.length ≤ n := by
|
||||
by_cases h : n < l.length
|
||||
· simp_all
|
||||
· simp [h]
|
||||
theorem isNone_getElem? {l : List α} {n : Nat} : l[n]?.isNone ↔ l.length ≤ n := by
|
||||
simp
|
||||
|
||||
/-! ### mem -/
|
||||
|
||||
@@ -493,42 +416,19 @@ theorem getElem_of_mem : ∀ {a} {l : List α}, a ∈ l → ∃ (n : Nat) (h : n
|
||||
| _, _ :: _, .head .. => ⟨0, Nat.succ_pos _, rfl⟩
|
||||
| _, _ :: _, .tail _ m => let ⟨n, h, e⟩ := getElem_of_mem m; ⟨n+1, Nat.succ_lt_succ h, e⟩
|
||||
|
||||
theorem get_of_mem {a} {l : List α} (h : a ∈ l) : ∃ n, get l n = a := by
|
||||
obtain ⟨n, h, e⟩ := getElem_of_mem h
|
||||
exact ⟨⟨n, h⟩, e⟩
|
||||
|
||||
theorem getElem?_of_mem {a} {l : List α} (h : a ∈ l) : ∃ n : Nat, l[n]? = some a :=
|
||||
let ⟨n, _, e⟩ := getElem_of_mem h; ⟨n, e ▸ getElem?_eq_getElem _⟩
|
||||
|
||||
theorem get?_of_mem {a} {l : List α} (h : a ∈ l) : ∃ n, l.get? n = some a :=
|
||||
let ⟨⟨n, _⟩, e⟩ := get_of_mem h; ⟨n, e ▸ get?_eq_get _⟩
|
||||
|
||||
theorem get_mem : ∀ (l : List α) n, get l n ∈ l
|
||||
| _ :: _, ⟨0, _⟩ => .head ..
|
||||
| _ :: l, ⟨_+1, _⟩ => .tail _ (get_mem l ..)
|
||||
theorem getElem?_of_mem {a} {l : List α} (h : a ∈ l) : ∃ n : Nat, l[n]? = some a := by
|
||||
let ⟨n, _, e⟩ := getElem_of_mem h
|
||||
exact ⟨n, e ▸ getElem?_eq_getElem _⟩
|
||||
|
||||
theorem mem_of_getElem? {l : List α} {n : Nat} {a : α} (e : l[n]? = some a) : a ∈ l :=
|
||||
let ⟨_, e⟩ := getElem?_eq_some_iff.1 e; e ▸ getElem_mem ..
|
||||
|
||||
@[deprecated mem_of_getElem? (since := "2024-09-06")] abbrev getElem?_mem := @mem_of_getElem?
|
||||
|
||||
theorem mem_of_get? {l : List α} {n a} (e : l.get? n = some a) : a ∈ l :=
|
||||
let ⟨_, e⟩ := get?_eq_some.1 e; e ▸ get_mem ..
|
||||
|
||||
@[deprecated mem_of_get? (since := "2024-09-06")] abbrev get?_mem := @mem_of_get?
|
||||
|
||||
theorem mem_iff_getElem {a} {l : List α} : a ∈ l ↔ ∃ (n : Nat) (h : n < l.length), l[n]'h = a :=
|
||||
⟨getElem_of_mem, fun ⟨_, _, e⟩ => e ▸ getElem_mem ..⟩
|
||||
|
||||
theorem mem_iff_get {a} {l : List α} : a ∈ l ↔ ∃ n, get l n = a :=
|
||||
⟨get_of_mem, fun ⟨_, e⟩ => e ▸ get_mem ..⟩
|
||||
|
||||
theorem mem_iff_getElem? {a} {l : List α} : a ∈ l ↔ ∃ n : Nat, l[n]? = some a := by
|
||||
simp [getElem?_eq_some_iff, mem_iff_getElem]
|
||||
|
||||
theorem mem_iff_get? {a} {l : List α} : a ∈ l ↔ ∃ n, l.get? n = some a := by
|
||||
simp [getElem?_eq_some_iff, Fin.exists_iff, mem_iff_get]
|
||||
|
||||
theorem forall_getElem {l : List α} {p : α → Prop} :
|
||||
(∀ (n : Nat) h, p (l[n]'h)) ↔ ∀ a, a ∈ l → p a := by
|
||||
induction l with
|
||||
@@ -579,18 +479,6 @@ theorem isEmpty_iff_length_eq_zero {l : List α} : l.isEmpty ↔ l.length = 0 :=
|
||||
|
||||
/-! ### any / all -/
|
||||
|
||||
theorem any_beq [BEq α] [LawfulBEq α] {l : List α} : (l.any fun x => a == x) ↔ a ∈ l := by
|
||||
induction l <;> simp_all
|
||||
|
||||
theorem any_beq' [BEq α] [LawfulBEq α] {l : List α} : (l.any fun x => x == a) ↔ a ∈ l := by
|
||||
induction l <;> simp_all [eq_comm (a := a)]
|
||||
|
||||
theorem all_bne [BEq α] [LawfulBEq α] {l : List α} : (l.all fun x => a != x) ↔ a ∉ l := by
|
||||
induction l <;> simp_all
|
||||
|
||||
theorem all_bne' [BEq α] [LawfulBEq α] {l : List α} : (l.all fun x => x != a) ↔ a ∉ l := by
|
||||
induction l <;> simp_all [eq_comm (a := a)]
|
||||
|
||||
theorem any_eq {l : List α} : l.any p = decide (∃ x, x ∈ l ∧ p x) := by induction l <;> simp [*]
|
||||
|
||||
theorem all_eq {l : List α} : l.all p = decide (∀ x, x ∈ l → p x) := by induction l <;> simp [*]
|
||||
@@ -615,6 +503,18 @@ theorem decide_forall_mem {l : List α} {p : α → Prop} [DecidablePred p] :
|
||||
@[simp] theorem all_eq_false {l : List α} : l.all p = false ↔ ∃ x, x ∈ l ∧ ¬p x := by
|
||||
simp [all_eq]
|
||||
|
||||
theorem any_beq [BEq α] [LawfulBEq α] {l : List α} : (l.any fun x => a == x) ↔ a ∈ l := by
|
||||
simp
|
||||
|
||||
theorem any_beq' [BEq α] [LawfulBEq α] {l : List α} : (l.any fun x => x == a) ↔ a ∈ l := by
|
||||
simp
|
||||
|
||||
theorem all_bne [BEq α] [LawfulBEq α] {l : List α} : (l.all fun x => a != x) ↔ a ∉ l := by
|
||||
induction l <;> simp_all
|
||||
|
||||
theorem all_bne' [BEq α] [LawfulBEq α] {l : List α} : (l.all fun x => x != a) ↔ a ∉ l := by
|
||||
induction l <;> simp_all [eq_comm (a := a)]
|
||||
|
||||
/-! ### set -/
|
||||
|
||||
-- As `List.set` is defined in `Init.Prelude`, we write the basic simplification lemmas here.
|
||||
@@ -632,19 +532,10 @@ theorem decide_forall_mem {l : List α} {p : α → Prop} [DecidablePred p] :
|
||||
| _ :: _, 0 => by simp
|
||||
| _ :: l, i + 1 => by simp [getElem_set_self]
|
||||
|
||||
@[deprecated getElem_set_self (since := "2024-09-04")] abbrev getElem_set_eq := @getElem_set_self
|
||||
|
||||
@[deprecated getElem_set_self (since := "2024-06-12")]
|
||||
theorem get_set_eq {l : List α} {i : Nat} {a : α} (h : i < (l.set i a).length) :
|
||||
(l.set i a).get ⟨i, h⟩ = a := by
|
||||
simp
|
||||
|
||||
@[simp] theorem getElem?_set_self {l : List α} {i : Nat} {a : α} (h : i < l.length) :
|
||||
(l.set i a)[i]? = some a := by
|
||||
simp_all [getElem?_eq_some_iff]
|
||||
|
||||
@[deprecated getElem?_set_self (since := "2024-09-04")] abbrev getElem?_set_eq := @getElem?_set_self
|
||||
|
||||
/-- This differs from `getElem?_set_self` by monadically mapping `Function.const _ a` over the `Option`
|
||||
returned by `l[i]?`. -/
|
||||
theorem getElem?_set_self' {l : List α} {i : Nat} {a : α} :
|
||||
@@ -666,12 +557,6 @@ theorem getElem?_set_self' {l : List α} {i : Nat} {a : α} :
|
||||
have g : i ≠ j := h ∘ congrArg (· + 1)
|
||||
simp [getElem_set_ne g]
|
||||
|
||||
@[deprecated getElem_set_ne (since := "2024-06-12")]
|
||||
theorem get_set_ne {l : List α} {i j : Nat} (h : i ≠ j) {a : α}
|
||||
(hj : j < (l.set i a).length) :
|
||||
(l.set i a).get ⟨j, hj⟩ = l.get ⟨j, by simp at hj; exact hj⟩ := by
|
||||
simp [h]
|
||||
|
||||
@[simp] theorem getElem?_set_ne {l : List α} {i j : Nat} (h : i ≠ j) {a : α} :
|
||||
(l.set i a)[j]? = l[j]? := by
|
||||
by_cases hj : j < (l.set i a).length
|
||||
@@ -686,11 +571,6 @@ theorem getElem_set {l : List α} {m n} {a} (h) :
|
||||
else
|
||||
simp [h]
|
||||
|
||||
@[deprecated getElem_set (since := "2024-06-12")]
|
||||
theorem get_set {l : List α} {m n} {a : α} (h) :
|
||||
(set l m a).get ⟨n, h⟩ = if m = n then a else l.get ⟨n, length_set .. ▸ h⟩ := by
|
||||
simp [getElem_set]
|
||||
|
||||
theorem getElem?_set {l : List α} {i j : Nat} {a : α} :
|
||||
(l.set i a)[j]? = if i = j then if i < l.length then some a else none else l[j]? := by
|
||||
if h : i = j then
|
||||
@@ -710,6 +590,14 @@ theorem getElem?_set' {l : List α} {i j : Nat} {a : α} :
|
||||
· simp only [getElem?_set_self', Option.map_eq_map, ↓reduceIte, *]
|
||||
· simp only [ne_eq, not_false_eq_true, getElem?_set_ne, ↓reduceIte, *]
|
||||
|
||||
@[simp] theorem set_getElem_self {as : List α} {i : Nat} (h : i < as.length) :
|
||||
as.set i as[i] = as := by
|
||||
apply ext_getElem
|
||||
· simp
|
||||
· intro n h₁ h₂
|
||||
rw [getElem_set]
|
||||
split <;> simp_all
|
||||
|
||||
theorem set_eq_of_length_le {l : List α} {n : Nat} (h : l.length ≤ n) {a : α} :
|
||||
l.set n a = l := by
|
||||
induction l generalizing n with
|
||||
@@ -724,8 +612,6 @@ theorem set_eq_of_length_le {l : List α} {n : Nat} (h : l.length ≤ n) {a : α
|
||||
@[simp] theorem set_eq_nil_iff {l : List α} (n : Nat) (a : α) : l.set n a = [] ↔ l = [] := by
|
||||
cases l <;> cases n <;> simp [set]
|
||||
|
||||
@[deprecated set_eq_nil_iff (since := "2024-09-05")] abbrev set_eq_nil := @set_eq_nil_iff
|
||||
|
||||
theorem set_comm (a b : α) : ∀ {n m : Nat} (l : List α), n ≠ m →
|
||||
(l.set n a).set m b = (l.set m b).set n a
|
||||
| _, _, [], _ => by simp
|
||||
@@ -1064,7 +950,7 @@ theorem getLast_eq_getElem : ∀ (l : List α) (h : l ≠ []),
|
||||
| _ :: _ :: _, _ => by
|
||||
simp [getLast, get, Nat.succ_sub_succ, getLast_eq_getElem]
|
||||
|
||||
theorem getElem_length_sub_one_eq_getLast (l : List α) (h) :
|
||||
theorem getElem_length_sub_one_eq_getLast (l : List α) (h : l.length - 1 < l.length) :
|
||||
l[l.length - 1] = getLast l (by cases l; simp at h; simp) := by
|
||||
rw [← getLast_eq_getElem]
|
||||
|
||||
@@ -1192,7 +1078,8 @@ theorem head_eq_getElem (l : List α) (h : l ≠ []) : head l h = l[0]'(length_p
|
||||
| nil => simp at h
|
||||
| cons _ _ => simp
|
||||
|
||||
theorem getElem_zero_eq_head (l : List α) (h) : l[0] = head l (by simpa [length_pos] using h) := by
|
||||
theorem getElem_zero_eq_head (l : List α) (h : 0 < l.length) :
|
||||
l[0] = head l (by simpa [length_pos] using h) := by
|
||||
cases l with
|
||||
| nil => simp at h
|
||||
| cons _ _ => simp
|
||||
@@ -1784,7 +1671,7 @@ theorem filterMap_eq_cons_iff {l} {b} {bs} :
|
||||
@[simp] theorem cons_append_fun (a : α) (as : List α) :
|
||||
(fun bs => ((a :: as) ++ bs)) = fun bs => a :: (as ++ bs) := rfl
|
||||
|
||||
theorem getElem_append {l₁ l₂ : List α} (n : Nat) (h) :
|
||||
theorem getElem_append {l₁ l₂ : List α} (n : Nat) (h : n < (l₁ ++ l₂).length) :
|
||||
(l₁ ++ l₂)[n] = if h' : n < l₁.length then l₁[n] else l₂[n - l₁.length]'(by simp at h h'; exact Nat.sub_lt_left_of_lt_add h' h) := by
|
||||
split <;> rename_i h'
|
||||
· rw [getElem_append_left h']
|
||||
@@ -2326,6 +2213,11 @@ theorem flatMap_def (l : List α) (f : α → List β) : l.flatMap f = flatten (
|
||||
|
||||
@[simp] theorem flatMap_id (l : List (List α)) : List.flatMap l id = l.flatten := by simp [flatMap_def]
|
||||
|
||||
@[simp]
|
||||
theorem length_flatMap (l : List α) (f : α → List β) :
|
||||
length (l.flatMap f) = sum (map (length ∘ f) l) := by
|
||||
rw [List.flatMap, length_flatten, map_map]
|
||||
|
||||
@[simp] theorem mem_flatMap {f : α → List β} {b} {l : List α} : b ∈ l.flatMap f ↔ ∃ a, a ∈ l ∧ b ∈ f a := by
|
||||
simp [flatMap_def, mem_flatten]
|
||||
exact ⟨fun ⟨_, ⟨a, h₁, rfl⟩, h₂⟩ => ⟨a, h₁, h₂⟩, fun ⟨a, h₁, h₂⟩ => ⟨_, ⟨a, h₁, rfl⟩, h₂⟩⟩
|
||||
@@ -2982,7 +2874,7 @@ are often used for theorems about `Array.pop`.
|
||||
@[simp] theorem getElem_dropLast : ∀ (xs : List α) (i : Nat) (h : i < xs.dropLast.length),
|
||||
xs.dropLast[i] = xs[i]'(Nat.lt_of_lt_of_le h (length_dropLast .. ▸ Nat.pred_le _))
|
||||
| _::_::_, 0, _ => rfl
|
||||
| _::_::_, i+1, _ => getElem_dropLast _ i _
|
||||
| _::_::_, i+1, h => getElem_dropLast _ i (Nat.add_one_lt_add_one_iff.mp h)
|
||||
|
||||
@[deprecated getElem_dropLast (since := "2024-06-12")]
|
||||
theorem get_dropLast (xs : List α) (i : Fin xs.dropLast.length) :
|
||||
@@ -3357,10 +3249,10 @@ theorem any_eq_not_all_not (l : List α) (p : α → Bool) : l.any p = !l.all (!
|
||||
theorem all_eq_not_any_not (l : List α) (p : α → Bool) : l.all p = !l.any (!p .) := by
|
||||
simp only [not_any_eq_all_not, Bool.not_not]
|
||||
|
||||
@[simp] theorem any_map {l : List α} {p : α → Bool} : (l.map f).any p = l.any (p ∘ f) := by
|
||||
@[simp] theorem any_map {l : List α} {p : β → Bool} : (l.map f).any p = l.any (p ∘ f) := by
|
||||
induction l with simp | cons _ _ ih => rw [ih]
|
||||
|
||||
@[simp] theorem all_map {l : List α} {p : α → Bool} : (l.map f).all p = l.all (p ∘ f) := by
|
||||
@[simp] theorem all_map {l : List α} {p : β → Bool} : (l.map f).all p = l.all (p ∘ f) := by
|
||||
induction l with simp | cons _ _ ih => rw [ih]
|
||||
|
||||
@[simp] theorem any_filter {l : List α} {p q : α → Bool} :
|
||||
@@ -3445,17 +3337,137 @@ theorem all_eq_not_any_not (l : List α) (p : α → Bool) : l.all p = !l.any (!
|
||||
(l.insert a).all f = (f a && l.all f) := by
|
||||
simp [all_eq]
|
||||
|
||||
/-! ### Legacy lemmas about `get`, `get?`, and `get!`.
|
||||
|
||||
Hopefully these should not be needed, in favour of lemmas about `xs[i]`, `xs[i]?`, and `xs[i]!`,
|
||||
to which these simplify.
|
||||
|
||||
We may consider deprecating or downstreaming these lemmas.
|
||||
-/
|
||||
|
||||
theorem get_cons_zero : get (a::l) (0 : Fin (l.length + 1)) = a := rfl
|
||||
|
||||
theorem get_cons_succ {as : List α} {h : i + 1 < (a :: as).length} :
|
||||
(a :: as).get ⟨i+1, h⟩ = as.get ⟨i, Nat.lt_of_succ_lt_succ h⟩ := rfl
|
||||
|
||||
theorem get_cons_succ' {as : List α} {i : Fin as.length} :
|
||||
(a :: as).get i.succ = as.get i := rfl
|
||||
|
||||
theorem get_mk_zero : ∀ {l : List α} (h : 0 < l.length), l.get ⟨0, h⟩ = l.head (length_pos.mp h)
|
||||
| _::_, _ => rfl
|
||||
|
||||
theorem get?_zero (l : List α) : l.get? 0 = l.head? := by cases l <;> rfl
|
||||
|
||||
/--
|
||||
If one has `l.get i` in an expression (with `i : Fin l.length`) and `h : l = l'`,
|
||||
`rw [h]` will give a "motive is not type correct" error, as it cannot rewrite the
|
||||
`i : Fin l.length` to `Fin l'.length` directly. The theorem `get_of_eq` can be used to make
|
||||
such a rewrite, with `rw [get_of_eq h]`.
|
||||
-/
|
||||
theorem get_of_eq {l l' : List α} (h : l = l') (i : Fin l.length) :
|
||||
get l i = get l' ⟨i, h ▸ i.2⟩ := by cases h; rfl
|
||||
|
||||
theorem get!_of_get? [Inhabited α] : ∀ {l : List α} {n}, get? l n = some a → get! l n = a
|
||||
| _a::_, 0, rfl => rfl
|
||||
| _::l, _+1, e => get!_of_get? (l := l) e
|
||||
|
||||
theorem get!_len_le [Inhabited α] : ∀ {l : List α} {n}, length l ≤ n → l.get! n = (default : α)
|
||||
| [], _, _ => rfl
|
||||
| _ :: l, _+1, h => get!_len_le (l := l) <| Nat.le_of_succ_le_succ h
|
||||
|
||||
theorem getElem!_nil [Inhabited α] {n : Nat} : ([] : List α)[n]! = default := rfl
|
||||
|
||||
theorem getElem!_cons_zero [Inhabited α] {l : List α} : (a::l)[0]! = a := by
|
||||
rw [getElem!_pos] <;> simp
|
||||
|
||||
theorem getElem!_cons_succ [Inhabited α] {l : List α} : (a::l)[n+1]! = l[n]! := by
|
||||
by_cases h : n < l.length
|
||||
· rw [getElem!_pos, getElem!_pos] <;> simp_all [Nat.succ_lt_succ_iff]
|
||||
· rw [getElem!_neg, getElem!_neg] <;> simp_all [Nat.succ_lt_succ_iff]
|
||||
|
||||
theorem getElem!_of_getElem? [Inhabited α] : ∀ {l : List α} {n : Nat}, l[n]? = some a → l[n]! = a
|
||||
| _a::_, 0, _ => by
|
||||
rw [getElem!_pos] <;> simp_all
|
||||
| _::l, _+1, e => by
|
||||
simp at e
|
||||
simp_all [getElem!_of_getElem? (l := l) e]
|
||||
|
||||
theorem ext_get {l₁ l₂ : List α} (hl : length l₁ = length l₂)
|
||||
(h : ∀ n h₁ h₂, get l₁ ⟨n, h₁⟩ = get l₂ ⟨n, h₂⟩) : l₁ = l₂ :=
|
||||
ext_getElem hl (by simp_all)
|
||||
|
||||
theorem get_of_mem {a} {l : List α} (h : a ∈ l) : ∃ n, get l n = a := by
|
||||
obtain ⟨n, h, e⟩ := getElem_of_mem h
|
||||
exact ⟨⟨n, h⟩, e⟩
|
||||
|
||||
theorem get?_of_mem {a} {l : List α} (h : a ∈ l) : ∃ n, l.get? n = some a :=
|
||||
let ⟨⟨n, _⟩, e⟩ := get_of_mem h; ⟨n, e ▸ get?_eq_get _⟩
|
||||
|
||||
theorem get_mem : ∀ (l : List α) n, get l n ∈ l
|
||||
| _ :: _, ⟨0, _⟩ => .head ..
|
||||
| _ :: l, ⟨_+1, _⟩ => .tail _ (get_mem l ..)
|
||||
|
||||
theorem mem_of_get? {l : List α} {n a} (e : l.get? n = some a) : a ∈ l :=
|
||||
let ⟨_, e⟩ := get?_eq_some_iff.1 e; e ▸ get_mem ..
|
||||
|
||||
theorem mem_iff_get {a} {l : List α} : a ∈ l ↔ ∃ n, get l n = a :=
|
||||
⟨get_of_mem, fun ⟨_, e⟩ => e ▸ get_mem ..⟩
|
||||
|
||||
theorem mem_iff_get? {a} {l : List α} : a ∈ l ↔ ∃ n, l.get? n = some a := by
|
||||
simp [getElem?_eq_some_iff, Fin.exists_iff, mem_iff_get]
|
||||
|
||||
/-! ### Deprecations -/
|
||||
|
||||
@[deprecated getD_eq_getElem?_getD (since := "2024-06-12")]
|
||||
theorem getD_eq_get? : ∀ l n (a : α), getD l n a = (get? l n).getD a := by simp
|
||||
@[deprecated getElem_singleton (since := "2024-06-12")]
|
||||
theorem get_singleton (a : α) (n : Fin 1) : get [a] n = a := by simp
|
||||
@[deprecated getElem?_concat_length (since := "2024-06-12")]
|
||||
theorem get?_concat_length (l : List α) (a : α) : (l ++ [a]).get? l.length = some a := by simp
|
||||
@[deprecated getElem_set_self (since := "2024-06-12")]
|
||||
theorem get_set_eq {l : List α} {i : Nat} {a : α} (h : i < (l.set i a).length) :
|
||||
(l.set i a).get ⟨i, h⟩ = a := by
|
||||
simp
|
||||
@[deprecated getElem_set_ne (since := "2024-06-12")]
|
||||
theorem get_set_ne {l : List α} {i j : Nat} (h : i ≠ j) {a : α}
|
||||
(hj : j < (l.set i a).length) :
|
||||
(l.set i a).get ⟨j, hj⟩ = l.get ⟨j, by simp at hj; exact hj⟩ := by
|
||||
simp [h]
|
||||
@[deprecated getElem_set (since := "2024-06-12")]
|
||||
theorem get_set {l : List α} {m n} {a : α} (h) :
|
||||
(set l m a).get ⟨n, h⟩ = if m = n then a else l.get ⟨n, length_set .. ▸ h⟩ := by
|
||||
simp [getElem_set]
|
||||
@[deprecated cons_inj_right (since := "2024-06-15")] abbrev cons_inj := @cons_inj_right
|
||||
@[deprecated ne_nil_of_length_eq_add_one (since := "2024-06-16")]
|
||||
abbrev ne_nil_of_length_eq_succ := @ne_nil_of_length_eq_add_one
|
||||
|
||||
@[deprecated "Deprecated without replacement." (since := "2024-07-09")]
|
||||
theorem get_cons_cons_one : (a₁ :: a₂ :: as).get (1 : Fin (as.length + 2)) = a₂ := rfl
|
||||
|
||||
@[deprecated filter_flatten (since := "2024-08-26")]
|
||||
theorem join_map_filter (p : α → Bool) (l : List (List α)) :
|
||||
(l.map (filter p)).flatten = (l.flatten).filter p := by
|
||||
rw [filter_flatten]
|
||||
|
||||
@[deprecated getElem_eq_getElem?_get (since := "2024-09-04")] abbrev getElem_eq_getElem? :=
|
||||
@getElem_eq_getElem?_get
|
||||
@[deprecated flatten_eq_nil_iff (since := "2024-09-05")] abbrev join_eq_nil := @flatten_eq_nil_iff
|
||||
@[deprecated flatten_ne_nil_iff (since := "2024-09-05")] abbrev join_ne_nil := @flatten_ne_nil_iff
|
||||
@[deprecated flatten_eq_cons_iff (since := "2024-09-05")] abbrev join_eq_cons_iff := @flatten_eq_cons_iff
|
||||
@[deprecated flatten_eq_cons_iff (since := "2024-09-05")] abbrev join_eq_cons := @flatten_eq_cons_iff
|
||||
@[deprecated flatten_eq_append_iff (since := "2024-09-05")] abbrev join_eq_append := @flatten_eq_append_iff
|
||||
@[deprecated mem_of_getElem? (since := "2024-09-06")] abbrev getElem?_mem := @mem_of_getElem?
|
||||
@[deprecated mem_of_get? (since := "2024-09-06")] abbrev get?_mem := @mem_of_get?
|
||||
@[deprecated getElem_set_self (since := "2024-09-04")] abbrev getElem_set_eq := @getElem_set_self
|
||||
@[deprecated getElem?_set_self (since := "2024-09-04")] abbrev getElem?_set_eq := @getElem?_set_self
|
||||
@[deprecated set_eq_nil_iff (since := "2024-09-05")] abbrev set_eq_nil := @set_eq_nil_iff
|
||||
|
||||
@[deprecated flatten_nil (since := "2024-10-14")] abbrev join_nil := @flatten_nil
|
||||
@[deprecated flatten_cons (since := "2024-10-14")] abbrev join_cons := @flatten_cons
|
||||
@[deprecated length_flatten (since := "2024-10-14")] abbrev length_join := @length_flatten
|
||||
@[deprecated flatten_singleton (since := "2024-10-14")] abbrev join_singleton := @flatten_singleton
|
||||
@[deprecated mem_flatten (since := "2024-10-14")] abbrev mem_join := @mem_flatten
|
||||
@[deprecated flatten_eq_nil_iff (since := "2024-09-05")] abbrev join_eq_nil := @flatten_eq_nil_iff
|
||||
@[deprecated flatten_eq_nil_iff (since := "2024-10-14")] abbrev join_eq_nil_iff := @flatten_eq_nil_iff
|
||||
@[deprecated flatten_ne_nil_iff (since := "2024-09-05")] abbrev join_ne_nil := @flatten_ne_nil_iff
|
||||
@[deprecated flatten_ne_nil_iff (since := "2024-10-14")] abbrev join_ne_nil_iff := @flatten_ne_nil_iff
|
||||
@[deprecated exists_of_mem_flatten (since := "2024-10-14")] abbrev exists_of_mem_join := @exists_of_mem_flatten
|
||||
@[deprecated mem_flatten_of_mem (since := "2024-10-14")] abbrev mem_join_of_mem := @mem_flatten_of_mem
|
||||
@@ -3469,16 +3481,9 @@ theorem all_eq_not_any_not (l : List α) (p : α → Bool) : l.all p = !l.any (!
|
||||
@[deprecated filter_flatten (since := "2024-10-14")] abbrev filter_join := @filter_flatten
|
||||
@[deprecated flatten_filter_not_isEmpty (since := "2024-10-14")] abbrev join_filter_not_isEmpty := @flatten_filter_not_isEmpty
|
||||
@[deprecated flatten_filter_ne_nil (since := "2024-10-14")] abbrev join_filter_ne_nil := @flatten_filter_ne_nil
|
||||
@[deprecated filter_flatten (since := "2024-08-26")]
|
||||
theorem join_map_filter (p : α → Bool) (l : List (List α)) :
|
||||
(l.map (filter p)).flatten = (l.flatten).filter p := by
|
||||
rw [filter_flatten]
|
||||
@[deprecated flatten_append (since := "2024-10-14")] abbrev join_append := @flatten_append
|
||||
@[deprecated flatten_concat (since := "2024-10-14")] abbrev join_concat := @flatten_concat
|
||||
@[deprecated flatten_flatten (since := "2024-10-14")] abbrev join_join := @flatten_flatten
|
||||
@[deprecated flatten_eq_cons_iff (since := "2024-09-05")] abbrev join_eq_cons_iff := @flatten_eq_cons_iff
|
||||
@[deprecated flatten_eq_cons_iff (since := "2024-09-05")] abbrev join_eq_cons := @flatten_eq_cons_iff
|
||||
@[deprecated flatten_eq_append_iff (since := "2024-09-05")] abbrev join_eq_append := @flatten_eq_append_iff
|
||||
@[deprecated flatten_eq_append_iff (since := "2024-10-14")] abbrev join_eq_append_iff := @flatten_eq_append_iff
|
||||
@[deprecated eq_iff_flatten_eq (since := "2024-10-14")] abbrev eq_iff_join_eq := @eq_iff_flatten_eq
|
||||
@[deprecated flatten_replicate_nil (since := "2024-10-14")] abbrev join_replicate_nil := @flatten_replicate_nil
|
||||
@@ -3513,4 +3518,18 @@ theorem join_map_filter (p : α → Bool) (l : List (List α)) :
|
||||
@[deprecated any_flatMap (since := "2024-10-16")] abbrev any_bind := @any_flatMap
|
||||
@[deprecated all_flatMap (since := "2024-10-16")] abbrev all_bind := @all_flatMap
|
||||
|
||||
@[deprecated get?_eq_none (since := "2024-11-29")] abbrev get?_len_le := @get?_eq_none
|
||||
@[deprecated getElem?_eq_some_iff (since := "2024-11-29")]
|
||||
abbrev getElem?_eq_some := @getElem?_eq_some_iff
|
||||
@[deprecated get?_eq_some_iff (since := "2024-11-29")]
|
||||
abbrev get?_eq_some := @get?_eq_some_iff
|
||||
@[deprecated LawfulGetElem.getElem?_def (since := "2024-11-29")]
|
||||
theorem getElem?_eq (l : List α) (i : Nat) :
|
||||
l[i]? = if h : i < l.length then some l[i] else none :=
|
||||
getElem?_def _ _
|
||||
@[deprecated getElem?_eq_none (since := "2024-11-29")] abbrev getElem?_len_le := @getElem?_eq_none
|
||||
|
||||
|
||||
|
||||
|
||||
end List
|
||||
|
||||
@@ -87,8 +87,8 @@ theorem mapFinIdx_eq_ofFn {as : List α} {f : Fin as.length → α → β} :
|
||||
apply ext_getElem <;> simp
|
||||
|
||||
@[simp] theorem getElem?_mapFinIdx {l : List α} {f : Fin l.length → α → β} {i : Nat} :
|
||||
(l.mapFinIdx f)[i]? = l[i]?.pbind fun x m => f ⟨i, by simp [getElem?_eq_some] at m; exact m.1⟩ x := by
|
||||
simp only [getElem?_eq, length_mapFinIdx, getElem_mapFinIdx]
|
||||
(l.mapFinIdx f)[i]? = l[i]?.pbind fun x m => f ⟨i, by simp [getElem?_eq_some_iff] at m; exact m.1⟩ x := by
|
||||
simp only [getElem?_def, length_mapFinIdx, getElem_mapFinIdx]
|
||||
split <;> simp
|
||||
|
||||
@[simp]
|
||||
@@ -126,7 +126,8 @@ theorem mapFinIdx_singleton {a : α} {f : Fin 1 → α → β} :
|
||||
|
||||
theorem mapFinIdx_eq_enum_map {l : List α} {f : Fin l.length → α → β} :
|
||||
l.mapFinIdx f = l.enum.attach.map
|
||||
fun ⟨⟨i, x⟩, m⟩ => f ⟨i, by rw [mk_mem_enum_iff_getElem?, getElem?_eq_some] at m; exact m.1⟩ x := by
|
||||
fun ⟨⟨i, x⟩, m⟩ =>
|
||||
f ⟨i, by rw [mk_mem_enum_iff_getElem?, getElem?_eq_some_iff] at m; exact m.1⟩ x := by
|
||||
apply ext_getElem <;> simp
|
||||
|
||||
@[simp]
|
||||
@@ -235,7 +236,7 @@ theorem getElem?_mapIdx_go : ∀ {l : List α} {arr : Array β} {i : Nat},
|
||||
(mapIdx.go f l arr)[i]? =
|
||||
if h : i < arr.size then some arr[i] else Option.map (f i) l[i - arr.size]?
|
||||
| [], arr, i => by
|
||||
simp only [mapIdx.go, Array.toListImpl_eq, getElem?_eq, Array.length_toList,
|
||||
simp only [mapIdx.go, Array.toListImpl_eq, getElem?_def, Array.length_toList,
|
||||
Array.getElem_eq_getElem_toList, length_nil, Nat.not_lt_zero, ↓reduceDIte, Option.map_none']
|
||||
| a :: l, arr, i => by
|
||||
rw [mapIdx.go, getElem?_mapIdx_go]
|
||||
|
||||
@@ -15,3 +15,4 @@ import Init.Data.List.Nat.Find
|
||||
import Init.Data.List.Nat.BEq
|
||||
import Init.Data.List.Nat.Modify
|
||||
import Init.Data.List.Nat.InsertIdx
|
||||
import Init.Data.List.Nat.Perm
|
||||
|
||||
54
src/Init/Data/List/Nat/Perm.lean
Normal file
54
src/Init/Data/List/Nat/Perm.lean
Normal file
@@ -0,0 +1,54 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Kim Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.List.Nat.TakeDrop
|
||||
import Init.Data.List.Perm
|
||||
|
||||
namespace List
|
||||
|
||||
/-- Helper lemma for `set_set_perm`-/
|
||||
private theorem set_set_perm' {as : List α} {i j : Nat} (h₁ : i < as.length) (h₂ : i + j < as.length)
|
||||
(hj : 0 < j) :
|
||||
(as.set i as[i + j]).set (i + j) as[i] ~ as := by
|
||||
have : as =
|
||||
as.take i ++ as[i] :: (as.take (i + j)).drop (i + 1) ++ as[i + j] :: as.drop (i + j + 1) := by
|
||||
simp only [getElem_cons_drop, append_assoc, cons_append]
|
||||
rw [← drop_append_of_le_length]
|
||||
· simp
|
||||
· simp; omega
|
||||
conv => lhs; congr; congr; rw [this]
|
||||
conv => rhs; rw [this]
|
||||
rw [set_append_left _ _ (by simp; omega)]
|
||||
rw [set_append_right _ _ (by simp; omega)]
|
||||
rw [set_append_right _ _ (by simp; omega)]
|
||||
simp only [length_append, length_take, length_set, length_cons, length_drop]
|
||||
rw [(show i - min i as.length = 0 by omega)]
|
||||
rw [(show i + j - (min i as.length + (min (i + j) as.length - (i + 1) + 1)) = 0 by omega)]
|
||||
simp only [set_cons_zero]
|
||||
simp only [append_assoc]
|
||||
apply Perm.append_left
|
||||
apply cons_append_cons_perm
|
||||
|
||||
theorem set_set_perm {as : List α} {i j : Nat} (h₁ : i < as.length) (h₂ : j < as.length) :
|
||||
(as.set i as[j]).set j as[i] ~ as := by
|
||||
if h₃ : i = j then
|
||||
simp [h₃]
|
||||
else
|
||||
if h₃ : i < j then
|
||||
let j' := j - i
|
||||
have t : j = i + j' := by omega
|
||||
generalize j' = j' at t
|
||||
subst t
|
||||
exact set_set_perm' _ _ (by omega)
|
||||
else
|
||||
rw [set_comm _ _ _ (by omega)]
|
||||
let i' := i - j
|
||||
have t : i = j + i' := by omega
|
||||
generalize i' = i' at t
|
||||
subst t
|
||||
apply set_set_perm' _ _ (by omega)
|
||||
|
||||
end List
|
||||
@@ -345,7 +345,7 @@ theorem drop_append {l₁ l₂ : List α} (i : Nat) : drop (l₁.length + i) (l
|
||||
rw [drop_append_eq_append_drop, drop_eq_nil_of_le] <;>
|
||||
simp [Nat.add_sub_cancel_left, Nat.le_add_right]
|
||||
|
||||
theorem set_eq_take_append_cons_drop {l : List α} {n : Nat} {a : α} :
|
||||
theorem set_eq_take_append_cons_drop (l : List α) (n : Nat) (a : α) :
|
||||
l.set n a = if n < l.length then l.take n ++ a :: l.drop (n + 1) else l := by
|
||||
split <;> rename_i h
|
||||
· ext1 m
|
||||
|
||||
@@ -39,6 +39,9 @@ protected theorem Perm.symm {l₁ l₂ : List α} (h : l₁ ~ l₂) : l₂ ~ l
|
||||
| swap => exact swap ..
|
||||
| trans _ _ ih₁ ih₂ => exact trans ih₂ ih₁
|
||||
|
||||
instance : Trans (Perm (α := α)) (Perm (α := α)) (Perm (α := α)) where
|
||||
trans h₁ h₂ := Perm.trans h₁ h₂
|
||||
|
||||
theorem perm_comm {l₁ l₂ : List α} : l₁ ~ l₂ ↔ l₂ ~ l₁ := ⟨Perm.symm, Perm.symm⟩
|
||||
|
||||
theorem Perm.swap' (x y : α) {l₁ l₂ : List α} (p : l₁ ~ l₂) : y :: x :: l₁ ~ x :: y :: l₂ :=
|
||||
@@ -102,7 +105,7 @@ theorem perm_append_comm : ∀ {l₁ l₂ : List α}, l₁ ++ l₂ ~ l₂ ++ l
|
||||
| _ :: _, _ => (perm_append_comm.cons _).trans perm_middle.symm
|
||||
|
||||
theorem perm_append_comm_assoc (l₁ l₂ l₃ : List α) :
|
||||
Perm (l₁ ++ (l₂ ++ l₃)) (l₂ ++ (l₁ ++ l₃)) := by
|
||||
(l₁ ++ (l₂ ++ l₃)) ~ (l₂ ++ (l₁ ++ l₃)) := by
|
||||
simpa only [List.append_assoc] using perm_append_comm.append_right _
|
||||
|
||||
theorem concat_perm (l : List α) (a : α) : concat l a ~ a :: l := by simp
|
||||
@@ -133,7 +136,7 @@ theorem Perm.nil_eq {l : List α} (p : [] ~ l) : [] = l := p.symm.eq_nil.symm
|
||||
|
||||
theorem not_perm_nil_cons (x : α) (l : List α) : ¬[] ~ x :: l := (nomatch ·.symm.eq_nil)
|
||||
|
||||
theorem not_perm_cons_nil {l : List α} {a : α} : ¬(Perm (a::l) []) :=
|
||||
theorem not_perm_cons_nil {l : List α} {a : α} : ¬((a::l) ~ []) :=
|
||||
fun h => by simpa using h.length_eq
|
||||
|
||||
theorem Perm.isEmpty_eq {l l' : List α} (h : Perm l l') : l.isEmpty = l'.isEmpty := by
|
||||
@@ -478,6 +481,15 @@ theorem Perm.flatten {l₁ l₂ : List (List α)} (h : l₁ ~ l₂) : l₁.flatt
|
||||
|
||||
@[deprecated Perm.flatten (since := "2024-10-14")] abbrev Perm.join := @Perm.flatten
|
||||
|
||||
theorem cons_append_cons_perm {a b : α} {as bs : List α} :
|
||||
a :: as ++ b :: bs ~ b :: as ++ a :: bs := by
|
||||
suffices [[a], as, [b], bs].flatten ~ [[b], as, [a], bs].flatten by simpa
|
||||
apply Perm.flatten
|
||||
calc
|
||||
[[a], as, [b], bs] ~ [as, [a], [b], bs] := Perm.swap as [a] _
|
||||
_ ~ [as, [b], [a], bs] := Perm.cons _ (Perm.swap [b] [a] _)
|
||||
_ ~ [[b], as, [a], bs] := Perm.swap [b] as _
|
||||
|
||||
theorem Perm.flatMap_right {l₁ l₂ : List α} (f : α → List β) (p : l₁ ~ l₂) : l₁.flatMap f ~ l₂.flatMap f :=
|
||||
(p.map _).flatten
|
||||
|
||||
|
||||
@@ -841,7 +841,7 @@ theorem isPrefix_iff : l₁ <+: l₂ ↔ ∀ i (h : i < l₁.length), l₂[i]? =
|
||||
theorem isPrefix_iff_getElem {l₁ l₂ : List α} :
|
||||
l₁ <+: l₂ ↔ ∃ (h : l₁.length ≤ l₂.length), ∀ x (hx : x < l₁.length),
|
||||
l₁[x] = l₂[x]'(Nat.lt_of_lt_of_le hx h) where
|
||||
mp h := ⟨h.length_le, fun _ _ ↦ h.getElem _⟩
|
||||
mp h := ⟨h.length_le, fun _ h' ↦ h.getElem h'⟩
|
||||
mpr h := by
|
||||
obtain ⟨hl, h⟩ := h
|
||||
induction l₂ generalizing l₁ with
|
||||
|
||||
@@ -65,13 +65,13 @@ theorem lt_length_of_take_ne_self {l : List α} {n} (h : l.take n ≠ l) : n < l
|
||||
theorem getElem_cons_drop : ∀ (l : List α) (i : Nat) (h : i < l.length),
|
||||
l[i] :: drop (i + 1) l = drop i l
|
||||
| _::_, 0, _ => rfl
|
||||
| _::_, i+1, _ => getElem_cons_drop _ i _
|
||||
| _::_, i+1, h => getElem_cons_drop _ i (Nat.add_one_lt_add_one_iff.mp h)
|
||||
|
||||
@[deprecated getElem_cons_drop (since := "2024-06-12")]
|
||||
theorem get_cons_drop (l : List α) (i) : get l i :: drop (i + 1) l = drop i l := by
|
||||
simp
|
||||
|
||||
theorem drop_eq_getElem_cons {n} {l : List α} (h) : drop n l = l[n] :: drop (n + 1) l :=
|
||||
theorem drop_eq_getElem_cons {n} {l : List α} (h : n < l.length) : drop n l = l[n] :: drop (n + 1) l :=
|
||||
(getElem_cons_drop _ n h).symm
|
||||
|
||||
@[deprecated drop_eq_getElem_cons (since := "2024-06-12")]
|
||||
@@ -192,6 +192,24 @@ theorem take_concat_get (l : List α) (i : Nat) (h : i < l.length) :
|
||||
Eq.symm <| (append_left_inj _).1 <| (take_append_drop (i+1) l).trans <| by
|
||||
rw [concat_eq_append, append_assoc, singleton_append, getElem_cons_drop_succ_eq_drop, take_append_drop]
|
||||
|
||||
@[simp] theorem take_append_getElem (l : List α) (i : Nat) (h : i < l.length) :
|
||||
(l.take i) ++ [l[i]] = l.take (i+1) := by
|
||||
simpa using take_concat_get l i h
|
||||
|
||||
@[simp] theorem take_append_getLast (l : List α) (h : l ≠ []) :
|
||||
(l.take (l.length - 1)) ++ [l.getLast h] = l := by
|
||||
rw [getLast_eq_getElem]
|
||||
cases l
|
||||
· contradiction
|
||||
· simp
|
||||
|
||||
@[simp] theorem take_append_getLast? (l : List α) :
|
||||
(l.take (l.length - 1)) ++ l.getLast?.toList = l := by
|
||||
match l with
|
||||
| [] => simp
|
||||
| x :: xs =>
|
||||
simpa using take_append_getLast (x :: xs) (by simp)
|
||||
|
||||
@[deprecated take_succ_cons (since := "2024-07-25")]
|
||||
theorem take_cons_succ : (a::as).take (i+1) = a :: as.take i := rfl
|
||||
|
||||
|
||||
@@ -71,6 +71,9 @@ theorem shiftRight_eq_div_pow (m : Nat) : ∀ n, m >>> n = m / 2 ^ n
|
||||
rw [shiftRight_add, shiftRight_eq_div_pow m k]
|
||||
simp [Nat.div_div_eq_div_mul, ← Nat.pow_succ, shiftRight_succ]
|
||||
|
||||
theorem shiftRight_eq_zero (m n : Nat) (hn : m < 2^n) : m >>> n = 0 := by
|
||||
simp [Nat.shiftRight_eq_div_pow, Nat.div_eq_of_lt hn]
|
||||
|
||||
/-!
|
||||
### testBit
|
||||
We define an operation for testing individual bits in the binary representation
|
||||
|
||||
@@ -39,9 +39,9 @@ protected theorem dvd_add_iff_right {k m n : Nat} (h : k ∣ m) : k ∣ n ↔ k
|
||||
protected theorem dvd_add_iff_left {k m n : Nat} (h : k ∣ n) : k ∣ m ↔ k ∣ m + n := by
|
||||
rw [Nat.add_comm]; exact Nat.dvd_add_iff_right h
|
||||
|
||||
theorem dvd_mod_iff {k m n : Nat} (h: k ∣ n) : k ∣ m % n ↔ k ∣ m :=
|
||||
have := Nat.dvd_add_iff_left <| Nat.dvd_trans h <| Nat.dvd_mul_right n (m / n)
|
||||
by rwa [mod_add_div] at this
|
||||
theorem dvd_mod_iff {k m n : Nat} (h: k ∣ n) : k ∣ m % n ↔ k ∣ m := by
|
||||
have := Nat.dvd_add_iff_left (m := m % n) <| Nat.dvd_trans h <| Nat.dvd_mul_right n (m / n)
|
||||
rwa [mod_add_div] at this
|
||||
|
||||
theorem le_of_dvd {m n : Nat} (h : 0 < n) : m ∣ n → m ≤ n
|
||||
| ⟨k, e⟩ => by
|
||||
@@ -77,7 +77,7 @@ theorem dvd_of_mod_eq_zero {m n : Nat} (H : n % m = 0) : m ∣ n := by
|
||||
theorem dvd_iff_mod_eq_zero {m n : Nat} : m ∣ n ↔ n % m = 0 :=
|
||||
⟨mod_eq_zero_of_dvd, dvd_of_mod_eq_zero⟩
|
||||
|
||||
instance decidable_dvd : @DecidableRel Nat (·∣·) :=
|
||||
instance decidable_dvd : @DecidableRel Nat Nat (·∣·) :=
|
||||
fun _ _ => decidable_of_decidable_of_iff dvd_iff_mod_eq_zero.symm
|
||||
|
||||
theorem emod_pos_of_not_dvd {a b : Nat} (h : ¬ a ∣ b) : 0 < b % a := by
|
||||
|
||||
@@ -5,6 +5,7 @@ Authors: Floris van Doorn, Leonardo de Moura, Kim Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Omega
|
||||
import Init.Data.List.FinRange
|
||||
|
||||
set_option linter.missingDocs true -- keep it documented
|
||||
universe u
|
||||
@@ -137,6 +138,54 @@ theorem allTR_loop_congr {n m : Nat} (w : n = m) (f : (i : Nat) → i < n → Bo
|
||||
omega
|
||||
go n 0 f
|
||||
|
||||
@[simp] theorem fold_zero {α : Type u} (f : (i : Nat) → i < 0 → α → α) (init : α) :
|
||||
fold 0 f init = init := by simp [fold]
|
||||
|
||||
@[simp] theorem fold_succ {α : Type u} (n : Nat) (f : (i : Nat) → i < n + 1 → α → α) (init : α) :
|
||||
fold (n + 1) f init = f n (by omega) (fold n (fun i h => f i (by omega)) init) := by simp [fold]
|
||||
|
||||
theorem fold_eq_finRange_foldl {α : Type u} (n : Nat) (f : (i : Nat) → i < n → α → α) (init : α) :
|
||||
fold n f init = (List.finRange n).foldl (fun acc ⟨i, h⟩ => f i h acc) init := by
|
||||
induction n with
|
||||
| zero => simp
|
||||
| succ n ih =>
|
||||
simp [ih, List.finRange_succ_last, List.foldl_map]
|
||||
|
||||
@[simp] theorem foldRev_zero {α : Type u} (f : (i : Nat) → i < 0 → α → α) (init : α) :
|
||||
foldRev 0 f init = init := by simp [foldRev]
|
||||
|
||||
@[simp] theorem foldRev_succ {α : Type u} (n : Nat) (f : (i : Nat) → i < n + 1 → α → α) (init : α) :
|
||||
foldRev (n + 1) f init = foldRev n (fun i h => f i (by omega)) (f n (by omega) init) := by
|
||||
simp [foldRev]
|
||||
|
||||
theorem foldRev_eq_finRange_foldr {α : Type u} (n : Nat) (f : (i : Nat) → i < n → α → α) (init : α) :
|
||||
foldRev n f init = (List.finRange n).foldr (fun ⟨i, h⟩ acc => f i h acc) init := by
|
||||
induction n generalizing init with
|
||||
| zero => simp
|
||||
| succ n ih => simp [ih, List.finRange_succ_last, List.foldr_map]
|
||||
|
||||
@[simp] theorem any_zero {f : (i : Nat) → i < 0 → Bool} : any 0 f = false := by simp [any]
|
||||
|
||||
@[simp] theorem any_succ {n : Nat} (f : (i : Nat) → i < n + 1 → Bool) :
|
||||
any (n + 1) f = (any n (fun i h => f i (by omega)) || f n (by omega)) := by simp [any]
|
||||
|
||||
theorem any_eq_finRange_any {n : Nat} (f : (i : Nat) → i < n → Bool) :
|
||||
any n f = (List.finRange n).any (fun ⟨i, h⟩ => f i h) := by
|
||||
induction n with
|
||||
| zero => simp
|
||||
| succ n ih => simp [ih, List.finRange_succ_last, List.any_map, Function.comp_def]
|
||||
|
||||
@[simp] theorem all_zero {f : (i : Nat) → i < 0 → Bool} : all 0 f = true := by simp [all]
|
||||
|
||||
@[simp] theorem all_succ {n : Nat} (f : (i : Nat) → i < n + 1 → Bool) :
|
||||
all (n + 1) f = (all n (fun i h => f i (by omega)) && f n (by omega)) := by simp [all]
|
||||
|
||||
theorem all_eq_finRange_all {n : Nat} (f : (i : Nat) → i < n → Bool) :
|
||||
all n f = (List.finRange n).all (fun ⟨i, h⟩ => f i h) := by
|
||||
induction n with
|
||||
| zero => simp
|
||||
| succ n ih => simp [ih, List.finRange_succ_last, List.all_map, Function.comp_def]
|
||||
|
||||
end Nat
|
||||
|
||||
namespace Prod
|
||||
|
||||
@@ -679,6 +679,10 @@ theorem add_mod (a b n : Nat) : (a + b) % n = ((a % n) + (b % n)) % n := by
|
||||
@[simp] theorem mod_mul_mod {a b c : Nat} : (a % c * b) % c = a * b % c := by
|
||||
rw [mul_mod, mod_mod, ← mul_mod]
|
||||
|
||||
theorem mod_eq_sub (x w : Nat) : x % w = x - w * (x / w) := by
|
||||
conv => rhs; congr; rw [← mod_add_div x w]
|
||||
simp
|
||||
|
||||
/-! ### pow -/
|
||||
|
||||
theorem pow_succ' {m n : Nat} : m ^ n.succ = m * m ^ n := by
|
||||
|
||||
@@ -36,3 +36,7 @@ theorem neZero_iff {n : R} : NeZero n ↔ n ≠ 0 :=
|
||||
|
||||
@[simp] theorem neZero_zero_iff_false {α : Type _} [Zero α] : NeZero (0 : α) ↔ False :=
|
||||
⟨fun _ ↦ NeZero.ne (0 : α) rfl, fun h ↦ h.elim⟩
|
||||
|
||||
instance {p : Prop} [Decidable p] {n m : Nat} [NeZero n] [NeZero m] :
|
||||
NeZero (if p then n else m) := by
|
||||
split <;> infer_instance
|
||||
|
||||
@@ -10,3 +10,4 @@ import Init.Data.Option.Instances
|
||||
import Init.Data.Option.Lemmas
|
||||
import Init.Data.Option.Attach
|
||||
import Init.Data.Option.List
|
||||
import Init.Data.Option.Monadic
|
||||
|
||||
@@ -119,10 +119,14 @@ theorem attachWith_map_subtype_val {p : α → Prop} (o : Option α) (H : ∀ a
|
||||
· simp at h
|
||||
· simp [get_some]
|
||||
|
||||
@[simp] theorem toList_attach (o : Option α) :
|
||||
theorem toList_attach (o : Option α) :
|
||||
o.attach.toList = o.toList.attach.map fun ⟨x, h⟩ => ⟨x, by simpa using h⟩ := by
|
||||
cases o <;> simp
|
||||
|
||||
@[simp] theorem attach_toList (o : Option α) :
|
||||
o.toList.attach = (o.attach.map fun ⟨a, h⟩ => ⟨a, by simpa using h⟩).toList := by
|
||||
cases o <;> simp
|
||||
|
||||
theorem attach_map {o : Option α} (f : α → β) :
|
||||
(o.map f).attach = o.attach.map (fun ⟨x, h⟩ => ⟨f x, mem_map_of_mem f h⟩) := by
|
||||
cases o <;> simp
|
||||
|
||||
@@ -96,12 +96,12 @@ This is similar to `<|>`/`orElse`, but it is strict in the second argument. -/
|
||||
| some a, _ => some a
|
||||
| none, b => b
|
||||
|
||||
@[inline] protected def lt (r : α → α → Prop) : Option α → Option α → Prop
|
||||
@[inline] protected def lt (r : α → β → Prop) : Option α → Option β → Prop
|
||||
| none, some _ => True
|
||||
| some x, some y => r x y
|
||||
| _, _ => False
|
||||
|
||||
instance (r : α → α → Prop) [s : DecidableRel r] : DecidableRel (Option.lt r)
|
||||
instance (r : α → β → Prop) [s : DecidableRel r] : DecidableRel (Option.lt r)
|
||||
| none, some _ => isTrue trivial
|
||||
| some x, some y => s x y
|
||||
| some _, none => isFalse not_false
|
||||
|
||||
@@ -70,6 +70,13 @@ satisfy `p`, using the proof to apply `f`.
|
||||
| none, _ => none
|
||||
| some a, H => f a (H a rfl)
|
||||
|
||||
/-- Partial elimination. If `o : Option α` and `f : (a : α) → a ∈ o → β`, then `o.pelim b f` is
|
||||
the same as `o.elim b f` but `f` is passed the proof that `a ∈ o`. -/
|
||||
@[inline] def pelim (o : Option α) (b : β) (f : (a : α) → a ∈ o → β) : β :=
|
||||
match o with
|
||||
| none => b
|
||||
| some a => f a rfl
|
||||
|
||||
/-- Map a monadic function which returns `Unit` over an `Option`. -/
|
||||
@[inline] protected def forM [Pure m] : Option α → (α → m PUnit) → m PUnit
|
||||
| none , _ => pure ⟨⟩
|
||||
|
||||
@@ -629,4 +629,12 @@ theorem pbind_eq_some_iff {o : Option α} {f : (a : α) → a ∈ o → Option
|
||||
· rintro ⟨h, rfl⟩
|
||||
rfl
|
||||
|
||||
/-! ### pelim -/
|
||||
|
||||
@[simp] theorem pelim_none : pelim none b f = b := rfl
|
||||
@[simp] theorem pelim_some : pelim (some a) b f = f a rfl := rfl
|
||||
|
||||
@[simp] theorem pelim_eq_elim : pelim o b (fun a _ => f a) = o.elim b f := by
|
||||
cases o <;> simp
|
||||
|
||||
end Option
|
||||
|
||||
@@ -15,17 +15,25 @@ namespace Option
|
||||
forIn' none b f = pure b := by
|
||||
rfl
|
||||
|
||||
@[simp] theorem forIn'_some [Monad m] (a : α) (b : β) (f : (a' : α) → a' ∈ some a → β → m (ForInStep β)) :
|
||||
forIn' (some a) b f = bind (f a rfl b) (fun | .done r | .yield r => pure r) := by
|
||||
rfl
|
||||
@[simp] theorem forIn'_some [Monad m] [LawfulMonad m] (a : α) (b : β) (f : (a' : α) → a' ∈ some a → β → m (ForInStep β)) :
|
||||
forIn' (some a) b f = bind (f a rfl b) (fun r => pure (ForInStep.value r)) := by
|
||||
simp only [forIn', bind_pure_comp]
|
||||
rw [map_eq_pure_bind]
|
||||
congr
|
||||
funext x
|
||||
split <;> rfl
|
||||
|
||||
@[simp] theorem forIn_none [Monad m] (b : β) (f : α → β → m (ForInStep β)) :
|
||||
forIn none b f = pure b := by
|
||||
rfl
|
||||
|
||||
@[simp] theorem forIn_some [Monad m] (a : α) (b : β) (f : α → β → m (ForInStep β)) :
|
||||
forIn (some a) b f = bind (f a b) (fun | .done r | .yield r => pure r) := by
|
||||
rfl
|
||||
@[simp] theorem forIn_some [Monad m] [LawfulMonad m] (a : α) (b : β) (f : α → β → m (ForInStep β)) :
|
||||
forIn (some a) b f = bind (f a b) (fun r => pure (ForInStep.value r)) := by
|
||||
simp only [forIn, forIn', bind_pure_comp]
|
||||
rw [map_eq_pure_bind]
|
||||
congr
|
||||
funext x
|
||||
split <;> rfl
|
||||
|
||||
@[simp] theorem forIn'_toList [Monad m] (o : Option α) (b : β) (f : (a : α) → a ∈ o.toList → β → m (ForInStep β)) :
|
||||
forIn' o.toList b f = forIn' o b fun a m b => f a (by simpa using m) b := by
|
||||
@@ -35,4 +43,20 @@ namespace Option
|
||||
forIn o.toList b f = forIn o b f := by
|
||||
cases o <;> rfl
|
||||
|
||||
@[simp] theorem foldlM_toList [Monad m] [LawfulMonad m] (o : Option β) (a : α) (f : α → β → m α) :
|
||||
o.toList.foldlM f a = o.elim (pure a) (fun b => f a b) := by
|
||||
cases o <;> simp
|
||||
|
||||
@[simp] theorem foldrM_toList [Monad m] [LawfulMonad m] (o : Option β) (a : α) (f : β → α → m α) :
|
||||
o.toList.foldrM f a = o.elim (pure a) (fun b => f b a) := by
|
||||
cases o <;> simp
|
||||
|
||||
@[simp] theorem foldl_toList (o : Option β) (a : α) (f : α → β → α) :
|
||||
o.toList.foldl f a = o.elim a (fun b => f a b) := by
|
||||
cases o <;> simp
|
||||
|
||||
@[simp] theorem foldr_toList (o : Option β) (a : α) (f : β → α → α) :
|
||||
o.toList.foldr f a = o.elim a (fun b => f b a) := by
|
||||
cases o <;> simp
|
||||
|
||||
end Option
|
||||
|
||||
75
src/Init/Data/Option/Monadic.lean
Normal file
75
src/Init/Data/Option/Monadic.lean
Normal file
@@ -0,0 +1,75 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Kim Morrison
|
||||
-/
|
||||
prelude
|
||||
|
||||
import Init.Data.Option.Attach
|
||||
import Init.Control.Lawful.Basic
|
||||
|
||||
namespace Option
|
||||
|
||||
@[congr] theorem forIn'_congr [Monad m] [LawfulMonad m]{as bs : Option α} (w : as = bs)
|
||||
{b b' : β} (hb : b = b')
|
||||
{f : (a' : α) → a' ∈ as → β → m (ForInStep β)}
|
||||
{g : (a' : α) → a' ∈ bs → β → m (ForInStep β)}
|
||||
(h : ∀ a m b, f a (by simpa [w] using m) b = g a m b) :
|
||||
forIn' as b f = forIn' bs b' g := by
|
||||
cases as <;> cases bs
|
||||
· simp [hb]
|
||||
· simp at w
|
||||
· simp at w
|
||||
· simp only [some.injEq] at w
|
||||
subst w
|
||||
simp [hb, h]
|
||||
|
||||
theorem forIn'_eq_pelim [Monad m] [LawfulMonad m]
|
||||
(o : Option α) (f : (a : α) → a ∈ o → β → m (ForInStep β)) (b : β) :
|
||||
forIn' o b f =
|
||||
o.pelim (pure b) (fun a h => ForInStep.value <$> f a h b) := by
|
||||
cases o <;> simp
|
||||
|
||||
@[simp] theorem forIn'_yield_eq_pelim [Monad m] [LawfulMonad m] (o : Option α)
|
||||
(f : (a : α) → a ∈ o → β → m γ) (g : (a : α) → a ∈ o → β → γ → β) (b : β) :
|
||||
forIn' o b (fun a m b => (fun c => .yield (g a m b c)) <$> f a m b) =
|
||||
o.pelim (pure b) (fun a h => g a h b <$> f a h b) := by
|
||||
cases o <;> simp
|
||||
|
||||
theorem forIn'_pure_yield_eq_pelim [Monad m] [LawfulMonad m]
|
||||
(o : Option α) (f : (a : α) → a ∈ o → β → β) (b : β) :
|
||||
forIn' o b (fun a m b => pure (.yield (f a m b))) =
|
||||
pure (f := m) (o.pelim b (fun a h => f a h b)) := by
|
||||
cases o <;> simp
|
||||
|
||||
@[simp] theorem forIn'_id_yield_eq_pelim
|
||||
(o : Option α) (f : (a : α) → a ∈ o → β → β) (b : β) :
|
||||
forIn' (m := Id) o b (fun a m b => .yield (f a m b)) =
|
||||
o.pelim b (fun a h => f a h b) := by
|
||||
cases o <;> simp
|
||||
|
||||
theorem forIn_eq_elim [Monad m] [LawfulMonad m]
|
||||
(o : Option α) (f : (a : α) → β → m (ForInStep β)) (b : β) :
|
||||
forIn o b f =
|
||||
o.elim (pure b) (fun a => ForInStep.value <$> f a b) := by
|
||||
cases o <;> simp
|
||||
|
||||
@[simp] theorem forIn_yield_eq_elim [Monad m] [LawfulMonad m] (o : Option α)
|
||||
(f : (a : α) → β → m γ) (g : (a : α) → β → γ → β) (b : β) :
|
||||
forIn o b (fun a b => (fun c => .yield (g a b c)) <$> f a b) =
|
||||
o.elim (pure b) (fun a => g a b <$> f a b) := by
|
||||
cases o <;> simp
|
||||
|
||||
theorem forIn_pure_yield_eq_elim [Monad m] [LawfulMonad m]
|
||||
(o : Option α) (f : (a : α) → β → β) (b : β) :
|
||||
forIn o b (fun a b => pure (.yield (f a b))) =
|
||||
pure (f := m) (o.elim b (fun a => f a b)) := by
|
||||
cases o <;> simp
|
||||
|
||||
@[simp] theorem forIn_id_yield_eq_elim
|
||||
(o : Option α) (f : (a : α) → β → β) (b : β) :
|
||||
forIn (m := Id) o b (fun a b => .yield (f a b)) =
|
||||
o.elim b (fun a => f a b) := by
|
||||
cases o <;> simp
|
||||
|
||||
end Option
|
||||
@@ -278,6 +278,16 @@ This function is overridden with a native implementation.
|
||||
@[extern "lean_usize_of_nat"]
|
||||
def USize.ofNat32 (n : @& Nat) (h : n < 4294967296) : USize :=
|
||||
USize.ofNatCore n (Nat.lt_of_lt_of_le h le_usize_size)
|
||||
@[extern "lean_uint8_to_usize"]
|
||||
def UInt8.toUSize (a : UInt8) : USize :=
|
||||
USize.ofNat32 a.toBitVec.toNat (Nat.lt_trans a.toBitVec.isLt (by decide))
|
||||
@[extern "lean_usize_to_uint8"]
|
||||
def USize.toUInt8 (a : USize) : UInt8 := a.toNat.toUInt8
|
||||
@[extern "lean_uint16_to_usize"]
|
||||
def UInt16.toUSize (a : UInt16) : USize :=
|
||||
USize.ofNat32 a.toBitVec.toNat (Nat.lt_trans a.toBitVec.isLt (by decide))
|
||||
@[extern "lean_usize_to_uint16"]
|
||||
def USize.toUInt16 (a : USize) : UInt16 := a.toNat.toUInt16
|
||||
@[extern "lean_uint32_to_usize"]
|
||||
def UInt32.toUSize (a : UInt32) : USize := USize.ofNat32 a.toBitVec.toNat a.toBitVec.isLt
|
||||
@[extern "lean_usize_to_uint32"]
|
||||
|
||||
@@ -1,25 +1,39 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All Rights Reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Markus Himmel
|
||||
Authors: Markus Himmel, Mac Malone
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.UInt.Basic
|
||||
import Init.Data.UInt.Lemmas
|
||||
import Init.Data.Fin.Bitwise
|
||||
import Init.Data.BitVec.Lemmas
|
||||
|
||||
set_option hygiene false in
|
||||
macro "declare_bitwise_uint_theorems" typeName:ident : command =>
|
||||
macro "declare_bitwise_uint_theorems" typeName:ident bits:term:arg : command =>
|
||||
`(
|
||||
namespace $typeName
|
||||
|
||||
@[simp] protected theorem and_toNat (a b : $typeName) : (a &&& b).toNat = a.toNat &&& b.toNat := BitVec.toNat_and ..
|
||||
@[simp] protected theorem toBitVec_and (a b : $typeName) : (a &&& b).toBitVec = a.toBitVec &&& b.toBitVec := rfl
|
||||
@[simp] protected theorem toBitVec_or (a b : $typeName) : (a ||| b).toBitVec = a.toBitVec ||| b.toBitVec := rfl
|
||||
@[simp] protected theorem toBitVec_xor (a b : $typeName) : (a ^^^ b).toBitVec = a.toBitVec ^^^ b.toBitVec := rfl
|
||||
@[simp] protected theorem toBitVec_shiftLeft (a b : $typeName) : (a <<< b).toBitVec = a.toBitVec <<< (b.toBitVec % $bits) := rfl
|
||||
@[simp] protected theorem toBitVec_shiftRight (a b : $typeName) : (a >>> b).toBitVec = a.toBitVec >>> (b.toBitVec % $bits) := rfl
|
||||
|
||||
@[simp] protected theorem toNat_and (a b : $typeName) : (a &&& b).toNat = a.toNat &&& b.toNat := by simp [toNat]
|
||||
@[simp] protected theorem toNat_or (a b : $typeName) : (a ||| b).toNat = a.toNat ||| b.toNat := by simp [toNat]
|
||||
@[simp] protected theorem toNat_xor (a b : $typeName) : (a ^^^ b).toNat = a.toNat ^^^ b.toNat := by simp [toNat]
|
||||
@[simp] protected theorem toNat_shiftLeft (a b : $typeName) : (a <<< b).toNat = a.toNat <<< (b.toNat % $bits) % 2 ^ $bits := by simp [toNat]
|
||||
@[simp] protected theorem toNat_shiftRight (a b : $typeName) : (a >>> b).toNat = a.toNat >>> (b.toNat % $bits) := by simp [toNat]
|
||||
|
||||
open $typeName (toNat_and) in
|
||||
@[deprecated toNat_and (since := "2024-11-28")]
|
||||
protected theorem and_toNat (a b : $typeName) : (a &&& b).toNat = a.toNat &&& b.toNat := BitVec.toNat_and ..
|
||||
|
||||
end $typeName
|
||||
)
|
||||
|
||||
declare_bitwise_uint_theorems UInt8
|
||||
declare_bitwise_uint_theorems UInt16
|
||||
declare_bitwise_uint_theorems UInt32
|
||||
declare_bitwise_uint_theorems UInt64
|
||||
declare_bitwise_uint_theorems USize
|
||||
declare_bitwise_uint_theorems UInt8 8
|
||||
declare_bitwise_uint_theorems UInt16 16
|
||||
declare_bitwise_uint_theorems UInt32 32
|
||||
declare_bitwise_uint_theorems UInt64 64
|
||||
declare_bitwise_uint_theorems USize System.Platform.numBits
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/-
|
||||
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
|
||||
Authors: Leonardo de Moura, François G. Dorais, Mario Carneiro, Mac Malone
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.UInt.Basic
|
||||
@@ -9,129 +9,202 @@ import Init.Data.Fin.Lemmas
|
||||
import Init.Data.BitVec.Lemmas
|
||||
import Init.Data.BitVec.Bitblast
|
||||
|
||||
open Lean in
|
||||
set_option hygiene false in
|
||||
macro "declare_uint_theorems" typeName:ident : command =>
|
||||
`(
|
||||
namespace $typeName
|
||||
macro "declare_uint_theorems" typeName:ident bits:term:arg : command => do
|
||||
let mut cmds ← Syntax.getArgs <$> `(
|
||||
namespace $typeName
|
||||
|
||||
instance : Inhabited $typeName where
|
||||
default := 0
|
||||
theorem zero_def : (0 : $typeName) = ⟨0⟩ := rfl
|
||||
theorem one_def : (1 : $typeName) = ⟨1⟩ := rfl
|
||||
theorem sub_def (a b : $typeName) : a - b = ⟨a.toBitVec - b.toBitVec⟩ := rfl
|
||||
theorem mul_def (a b : $typeName) : a * b = ⟨a.toBitVec * b.toBitVec⟩ := rfl
|
||||
theorem mod_def (a b : $typeName) : a % b = ⟨a.toBitVec % b.toBitVec⟩ := rfl
|
||||
theorem add_def (a b : $typeName) : a + b = ⟨a.toBitVec + b.toBitVec⟩ := rfl
|
||||
|
||||
theorem zero_def : (0 : $typeName) = ⟨0⟩ := rfl
|
||||
theorem one_def : (1 : $typeName) = ⟨1⟩ := rfl
|
||||
theorem sub_def (a b : $typeName) : a - b = ⟨a.toBitVec - b.toBitVec⟩ := rfl
|
||||
theorem mul_def (a b : $typeName) : a * b = ⟨a.toBitVec * b.toBitVec⟩ := rfl
|
||||
theorem mod_def (a b : $typeName) : a % b = ⟨a.toBitVec % b.toBitVec⟩ := rfl
|
||||
theorem add_def (a b : $typeName) : a + b = ⟨a.toBitVec + b.toBitVec⟩ := rfl
|
||||
@[simp] theorem toNat_mk : (mk a).toNat = a.toNat := rfl
|
||||
|
||||
@[simp] theorem mk_toBitVec_eq : ∀ (a : $typeName), mk a.toBitVec = a
|
||||
| ⟨_, _⟩ => rfl
|
||||
@[simp] theorem toNat_ofNat {n : Nat} : (ofNat n).toNat = n % 2 ^ $bits := BitVec.toNat_ofNat ..
|
||||
|
||||
theorem toBitVec_eq_of_lt {a : Nat} : a < size → (ofNat a).toBitVec.toNat = a :=
|
||||
Nat.mod_eq_of_lt
|
||||
@[simp] theorem toNat_ofNatCore {n : Nat} {h : n < size} : (ofNatCore n h).toNat = n := BitVec.toNat_ofNatLt ..
|
||||
|
||||
theorem toNat_ofNat_of_lt {n : Nat} (h : n < size) : (ofNat n).toNat = n := by
|
||||
rw [toNat, toBitVec_eq_of_lt h]
|
||||
@[simp] theorem val_val_eq_toNat (x : $typeName) : x.val.val = x.toNat := rfl
|
||||
|
||||
theorem le_def {a b : $typeName} : a ≤ b ↔ a.toBitVec ≤ b.toBitVec := .rfl
|
||||
theorem toNat_toBitVec_eq_toNat (x : $typeName) : x.toBitVec.toNat = x.toNat := rfl
|
||||
|
||||
theorem lt_def {a b : $typeName} : a < b ↔ a.toBitVec < b.toBitVec := .rfl
|
||||
@[simp] theorem mk_toBitVec_eq : ∀ (a : $typeName), mk a.toBitVec = a
|
||||
| ⟨_, _⟩ => rfl
|
||||
|
||||
@[simp] protected theorem not_le {a b : $typeName} : ¬ a ≤ b ↔ b < a := by simp [le_def, lt_def]
|
||||
theorem toBitVec_eq_of_lt {a : Nat} : a < size → (ofNat a).toBitVec.toNat = a :=
|
||||
Nat.mod_eq_of_lt
|
||||
|
||||
@[simp] protected theorem not_lt {a b : $typeName} : ¬ a < b ↔ b ≤ a := by simp [le_def, lt_def]
|
||||
theorem toNat_ofNat_of_lt {n : Nat} (h : n < size) : (ofNat n).toNat = n := by
|
||||
rw [toNat, toBitVec_eq_of_lt h]
|
||||
|
||||
@[simp] protected theorem le_refl (a : $typeName) : a ≤ a := by simp [le_def]
|
||||
theorem le_def {a b : $typeName} : a ≤ b ↔ a.toBitVec ≤ b.toBitVec := .rfl
|
||||
|
||||
@[simp] protected theorem lt_irrefl (a : $typeName) : ¬ a < a := by simp
|
||||
theorem lt_def {a b : $typeName} : a < b ↔ a.toBitVec < b.toBitVec := .rfl
|
||||
|
||||
protected theorem le_trans {a b c : $typeName} : a ≤ b → b ≤ c → a ≤ c := BitVec.le_trans
|
||||
theorem le_iff_toNat_le {a b : $typeName} : a ≤ b ↔ a.toNat ≤ b.toNat := .rfl
|
||||
|
||||
protected theorem lt_trans {a b c : $typeName} : a < b → b < c → a < c := BitVec.lt_trans
|
||||
theorem lt_iff_toNat_lt {a b : $typeName} : a < b ↔ a.toNat < b.toNat := .rfl
|
||||
|
||||
protected theorem le_total (a b : $typeName) : a ≤ b ∨ b ≤ a := BitVec.le_total ..
|
||||
@[simp] protected theorem not_le {a b : $typeName} : ¬ a ≤ b ↔ b < a := by simp [le_def, lt_def]
|
||||
|
||||
protected theorem lt_asymm {a b : $typeName} : a < b → ¬ b < a := BitVec.lt_asymm
|
||||
@[simp] protected theorem not_lt {a b : $typeName} : ¬ a < b ↔ b ≤ a := by simp [le_def, lt_def]
|
||||
|
||||
protected theorem toBitVec_eq_of_eq {a b : $typeName} (h : a = b) : a.toBitVec = b.toBitVec := h ▸ rfl
|
||||
@[simp] protected theorem le_refl (a : $typeName) : a ≤ a := by simp [le_def]
|
||||
|
||||
protected theorem eq_of_toBitVec_eq {a b : $typeName} (h : a.toBitVec = b.toBitVec) : a = b := by
|
||||
cases a; cases b; simp_all
|
||||
@[simp] protected theorem lt_irrefl (a : $typeName) : ¬ a < a := by simp
|
||||
|
||||
open $typeName (eq_of_toBitVec_eq) in
|
||||
protected theorem eq_of_val_eq {a b : $typeName} (h : a.val = b.val) : a = b := by
|
||||
rcases a with ⟨⟨_⟩⟩; rcases b with ⟨⟨_⟩⟩; simp_all [val]
|
||||
protected theorem le_trans {a b c : $typeName} : a ≤ b → b ≤ c → a ≤ c := BitVec.le_trans
|
||||
|
||||
open $typeName (toBitVec_eq_of_eq) in
|
||||
protected theorem ne_of_toBitVec_ne {a b : $typeName} (h : a.toBitVec ≠ b.toBitVec) : a ≠ b :=
|
||||
fun h' => absurd (toBitVec_eq_of_eq h') h
|
||||
protected theorem lt_trans {a b c : $typeName} : a < b → b < c → a < c := BitVec.lt_trans
|
||||
|
||||
open $typeName (ne_of_toBitVec_ne) in
|
||||
protected theorem ne_of_lt {a b : $typeName} (h : a < b) : a ≠ b := by
|
||||
apply ne_of_toBitVec_ne
|
||||
apply BitVec.ne_of_lt
|
||||
simpa [lt_def] using h
|
||||
protected theorem le_total (a b : $typeName) : a ≤ b ∨ b ≤ a := BitVec.le_total ..
|
||||
|
||||
@[simp] protected theorem toNat_zero : (0 : $typeName).toNat = 0 := Nat.zero_mod _
|
||||
protected theorem lt_asymm {a b : $typeName} : a < b → ¬ b < a := BitVec.lt_asymm
|
||||
|
||||
@[simp] protected theorem toNat_mod (a b : $typeName) : (a % b).toNat = a.toNat % b.toNat := BitVec.toNat_umod ..
|
||||
protected theorem toBitVec_eq_of_eq {a b : $typeName} (h : a = b) : a.toBitVec = b.toBitVec := h ▸ rfl
|
||||
|
||||
@[simp] protected theorem toNat_div (a b : $typeName) : (a / b).toNat = a.toNat / b.toNat := BitVec.toNat_udiv ..
|
||||
protected theorem eq_of_toBitVec_eq {a b : $typeName} (h : a.toBitVec = b.toBitVec) : a = b := by
|
||||
cases a; cases b; simp_all
|
||||
|
||||
@[simp] protected theorem toNat_sub_of_le (a b : $typeName) : b ≤ a → (a - b).toNat = a.toNat - b.toNat := BitVec.toNat_sub_of_le
|
||||
open $typeName (eq_of_toBitVec_eq toBitVec_eq_of_eq) in
|
||||
protected theorem toBitVec_inj {a b : $typeName} : a.toBitVec = b.toBitVec ↔ a = b :=
|
||||
Iff.intro eq_of_toBitVec_eq toBitVec_eq_of_eq
|
||||
|
||||
protected theorem toNat_lt_size (a : $typeName) : a.toNat < size := a.toBitVec.isLt
|
||||
open $typeName (eq_of_toBitVec_eq) in
|
||||
protected theorem eq_of_val_eq {a b : $typeName} (h : a.val = b.val) : a = b := by
|
||||
rcases a with ⟨⟨_⟩⟩; rcases b with ⟨⟨_⟩⟩; simp_all [val]
|
||||
|
||||
open $typeName (toNat_mod toNat_lt_size) in
|
||||
protected theorem toNat_mod_lt {m : Nat} : ∀ (u : $typeName), m > 0 → toNat (u % ofNat m) < m := by
|
||||
intro u h1
|
||||
by_cases h2 : m < size
|
||||
· rw [toNat_mod, toNat_ofNat_of_lt h2]
|
||||
apply Nat.mod_lt _ h1
|
||||
· apply Nat.lt_of_lt_of_le
|
||||
· apply toNat_lt_size
|
||||
· simpa using h2
|
||||
open $typeName (eq_of_val_eq) in
|
||||
protected theorem val_inj {a b : $typeName} : a.val = b.val ↔ a = b :=
|
||||
Iff.intro eq_of_val_eq (congrArg val)
|
||||
|
||||
open $typeName (toNat_mod_lt) in
|
||||
set_option linter.deprecated false in
|
||||
@[deprecated toNat_mod_lt (since := "2024-09-24")]
|
||||
protected theorem modn_lt {m : Nat} : ∀ (u : $typeName), m > 0 → toNat (u % m) < m := by
|
||||
intro u
|
||||
simp only [(· % ·)]
|
||||
simp only [gt_iff_lt, toNat, modn, Fin.modn_val, BitVec.natCast_eq_ofNat, BitVec.toNat_ofNat,
|
||||
Nat.reducePow]
|
||||
rw [Nat.mod_eq_of_lt]
|
||||
· apply Nat.mod_lt
|
||||
· apply Nat.lt_of_le_of_lt
|
||||
· apply Nat.mod_le
|
||||
· apply Fin.is_lt
|
||||
open $typeName (toBitVec_eq_of_eq) in
|
||||
protected theorem ne_of_toBitVec_ne {a b : $typeName} (h : a.toBitVec ≠ b.toBitVec) : a ≠ b :=
|
||||
fun h' => absurd (toBitVec_eq_of_eq h') h
|
||||
|
||||
protected theorem mod_lt (a : $typeName) {b : $typeName} : 0 < b → a % b < b := by
|
||||
simp only [lt_def, mod_def]
|
||||
apply BitVec.umod_lt
|
||||
open $typeName (ne_of_toBitVec_ne) in
|
||||
protected theorem ne_of_lt {a b : $typeName} (h : a < b) : a ≠ b := by
|
||||
apply ne_of_toBitVec_ne
|
||||
apply BitVec.ne_of_lt
|
||||
simpa [lt_def] using h
|
||||
|
||||
protected theorem toNat.inj : ∀ {a b : $typeName}, a.toNat = b.toNat → a = b
|
||||
| ⟨_, _⟩, ⟨_, _⟩, rfl => rfl
|
||||
@[simp] protected theorem toNat_zero : (0 : $typeName).toNat = 0 := Nat.zero_mod _
|
||||
|
||||
@[simp] protected theorem ofNat_one : ofNat 1 = 1 := rfl
|
||||
@[simp] protected theorem toNat_add (a b : $typeName) : (a + b).toNat = (a.toNat + b.toNat) % 2 ^ $bits := BitVec.toNat_add ..
|
||||
|
||||
@[simp]
|
||||
theorem val_ofNat (n : Nat) : val (no_index (OfNat.ofNat n)) = OfNat.ofNat n := rfl
|
||||
protected theorem toNat_sub (a b : $typeName) : (a - b).toNat = (2 ^ $bits - b.toNat + a.toNat) % 2 ^ $bits := BitVec.toNat_sub ..
|
||||
|
||||
@[simp]
|
||||
theorem toBitVec_ofNat (n : Nat) : toBitVec (no_index (OfNat.ofNat n)) = BitVec.ofNat _ n := rfl
|
||||
@[simp] protected theorem toNat_mul (a b : $typeName) : (a * b).toNat = a.toNat * b.toNat % 2 ^ $bits := BitVec.toNat_mul ..
|
||||
|
||||
@[simp]
|
||||
theorem mk_ofNat (n : Nat) : mk (BitVec.ofNat _ n) = OfNat.ofNat n := rfl
|
||||
@[simp] protected theorem toNat_mod (a b : $typeName) : (a % b).toNat = a.toNat % b.toNat := BitVec.toNat_umod ..
|
||||
|
||||
end $typeName
|
||||
)
|
||||
@[simp] protected theorem toNat_div (a b : $typeName) : (a / b).toNat = a.toNat / b.toNat := BitVec.toNat_udiv ..
|
||||
|
||||
declare_uint_theorems UInt8
|
||||
declare_uint_theorems UInt16
|
||||
declare_uint_theorems UInt32
|
||||
declare_uint_theorems UInt64
|
||||
declare_uint_theorems USize
|
||||
@[simp] protected theorem toNat_sub_of_le (a b : $typeName) : b ≤ a → (a - b).toNat = a.toNat - b.toNat := BitVec.toNat_sub_of_le
|
||||
|
||||
protected theorem toNat_lt_size (a : $typeName) : a.toNat < size := a.toBitVec.isLt
|
||||
|
||||
open $typeName (toNat_mod toNat_lt_size) in
|
||||
protected theorem toNat_mod_lt {m : Nat} : ∀ (u : $typeName), m > 0 → toNat (u % ofNat m) < m := by
|
||||
intro u h1
|
||||
by_cases h2 : m < size
|
||||
· rw [toNat_mod, toNat_ofNat_of_lt h2]
|
||||
apply Nat.mod_lt _ h1
|
||||
· apply Nat.lt_of_lt_of_le
|
||||
· apply toNat_lt_size
|
||||
· simpa using h2
|
||||
|
||||
open $typeName (toNat_mod_lt) in
|
||||
set_option linter.deprecated false in
|
||||
@[deprecated toNat_mod_lt (since := "2024-09-24")]
|
||||
protected theorem modn_lt {m : Nat} : ∀ (u : $typeName), m > 0 → toNat (u % m) < m := by
|
||||
intro u
|
||||
simp only [(· % ·)]
|
||||
simp only [gt_iff_lt, toNat, modn, Fin.modn_val, BitVec.natCast_eq_ofNat, BitVec.toNat_ofNat,
|
||||
Nat.reducePow]
|
||||
rw [Nat.mod_eq_of_lt]
|
||||
· apply Nat.mod_lt
|
||||
· apply Nat.lt_of_le_of_lt
|
||||
· apply Nat.mod_le
|
||||
· apply Fin.is_lt
|
||||
|
||||
protected theorem mod_lt (a : $typeName) {b : $typeName} : 0 < b → a % b < b := by
|
||||
simp only [lt_def, mod_def]
|
||||
apply BitVec.umod_lt
|
||||
|
||||
protected theorem toNat.inj : ∀ {a b : $typeName}, a.toNat = b.toNat → a = b
|
||||
| ⟨_, _⟩, ⟨_, _⟩, rfl => rfl
|
||||
|
||||
protected theorem toNat_inj : ∀ {a b : $typeName}, a.toNat = b.toNat ↔ a = b :=
|
||||
Iff.intro toNat.inj (congrArg toNat)
|
||||
|
||||
open $typeName (toNat_inj) in
|
||||
protected theorem le_antisymm_iff {a b : $typeName} : a = b ↔ a ≤ b ∧ b ≤ a :=
|
||||
toNat_inj.symm.trans Nat.le_antisymm_iff
|
||||
|
||||
open $typeName (le_antisymm_iff) in
|
||||
protected theorem le_antisymm {a b : $typeName} (h₁ : a ≤ b) (h₂ : b ≤ a) : a = b :=
|
||||
le_antisymm_iff.2 ⟨h₁, h₂⟩
|
||||
|
||||
@[simp] protected theorem ofNat_one : ofNat 1 = 1 := rfl
|
||||
|
||||
@[simp] protected theorem ofNat_toNat {x : $typeName} : ofNat x.toNat = x := by
|
||||
apply toNat.inj
|
||||
simp [Nat.mod_eq_of_lt x.toNat_lt_size]
|
||||
|
||||
@[simp]
|
||||
theorem val_ofNat (n : Nat) : val (no_index (OfNat.ofNat n)) = OfNat.ofNat n := rfl
|
||||
|
||||
@[simp]
|
||||
theorem toBitVec_ofNat (n : Nat) : toBitVec (no_index (OfNat.ofNat n)) = BitVec.ofNat _ n := rfl
|
||||
|
||||
@[simp]
|
||||
theorem mk_ofNat (n : Nat) : mk (BitVec.ofNat _ n) = OfNat.ofNat n := rfl
|
||||
|
||||
)
|
||||
if let some nbits := bits.raw.isNatLit? then
|
||||
if nbits > 8 then
|
||||
cmds := cmds.push <| ←
|
||||
`(@[simp] theorem toNat_toUInt8 (x : $typeName) : x.toUInt8.toNat = x.toNat % 2 ^ 8 := rfl)
|
||||
if nbits < 16 then
|
||||
cmds := cmds.push <| ←
|
||||
`(@[simp] theorem toNat_toUInt16 (x : $typeName) : x.toUInt16.toNat = x.toNat := rfl)
|
||||
else if nbits > 16 then
|
||||
cmds := cmds.push <| ←
|
||||
`(@[simp] theorem toNat_toUInt16 (x : $typeName) : x.toUInt16.toNat = x.toNat % 2 ^ 16 := rfl)
|
||||
if nbits < 32 then
|
||||
cmds := cmds.push <| ←
|
||||
`(@[simp] theorem toNat_toUInt32 (x : $typeName) : x.toUInt32.toNat = x.toNat := rfl)
|
||||
else if nbits > 32 then
|
||||
cmds := cmds.push <| ←
|
||||
`(@[simp] theorem toNat_toUInt32 (x : $typeName) : x.toUInt32.toNat = x.toNat % 2 ^ 32 := rfl)
|
||||
if nbits ≤ 32 then
|
||||
cmds := cmds.push <| ←
|
||||
`(@[simp] theorem toNat_toUSize (x : $typeName) : x.toUSize.toNat = x.toNat := rfl)
|
||||
else
|
||||
cmds := cmds.push <| ←
|
||||
`(@[simp] theorem toNat_toUSize (x : $typeName) : x.toUSize.toNat = x.toNat % 2 ^ System.Platform.numBits := rfl)
|
||||
if nbits < 64 then
|
||||
cmds := cmds.push <| ←
|
||||
`(@[simp] theorem toNat_toUInt64 (x : $typeName) : x.toUInt64.toNat = x.toNat := rfl)
|
||||
cmds := cmds.push <| ← `(end $typeName)
|
||||
return ⟨mkNullNode cmds⟩
|
||||
|
||||
declare_uint_theorems UInt8 8
|
||||
declare_uint_theorems UInt16 16
|
||||
declare_uint_theorems UInt32 32
|
||||
declare_uint_theorems UInt64 64
|
||||
declare_uint_theorems USize System.Platform.numBits
|
||||
|
||||
@[simp] theorem USize.toNat_ofNat32 {n : Nat} {h : n < 4294967296} : (ofNat32 n h).toNat = n := rfl
|
||||
|
||||
@[simp] theorem USize.toNat_toUInt32 (x : USize) : x.toUInt32.toNat = x.toNat % 2 ^ 32 := rfl
|
||||
|
||||
@[simp] theorem USize.toNat_toUInt64 (x : USize) : x.toUInt64.toNat = x.toNat := rfl
|
||||
|
||||
theorem USize.toNat_ofNat_of_lt_32 {n : Nat} (h : n < 4294967296) : toNat (ofNat n) = n :=
|
||||
toNat_ofNat_of_lt (Nat.lt_of_lt_of_le h le_usize_size)
|
||||
|
||||
@@ -21,6 +21,9 @@ deriving Repr, DecidableEq
|
||||
|
||||
attribute [simp] Vector.size_toArray
|
||||
|
||||
/-- Convert `xs : Array α` to `Vector α xs.size`. -/
|
||||
abbrev Array.toVector (xs : Array α) : Vector α xs.size := .mk xs rfl
|
||||
|
||||
namespace Vector
|
||||
|
||||
/-- Syntax for `Vector α n` -/
|
||||
@@ -44,9 +47,6 @@ def elimAsList {motive : Vector α n → Sort u}
|
||||
(v : Vector α n) → motive v
|
||||
| ⟨⟨a⟩, ha⟩ => mk a ha
|
||||
|
||||
/-- The empty vector. -/
|
||||
@[inline] def empty : Vector α 0 := ⟨.empty, rfl⟩
|
||||
|
||||
/-- Make an empty vector with pre-allocated capacity. -/
|
||||
@[inline] def mkEmpty (capacity : Nat) : Vector α 0 := ⟨.mkEmpty capacity, rfl⟩
|
||||
|
||||
|
||||
426
src/Init/Data/Vector/Lemmas.lean
Normal file
426
src/Init/Data/Vector/Lemmas.lean
Normal file
@@ -0,0 +1,426 @@
|
||||
/-
|
||||
Copyright (c) 2024 Shreyas Srinivas. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Shreyas Srinivas, Francois Dorais
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Vector.Basic
|
||||
|
||||
/-!
|
||||
## Vectors
|
||||
Lemmas about `Vector α n`
|
||||
-/
|
||||
|
||||
namespace Array
|
||||
|
||||
theorem toVector_inj {a b : Array α} (h₁ : a.size = b.size) (h₂ : a.toVector.cast h₁ = b.toVector) : a = b := by
|
||||
ext i ih₁ ih₂
|
||||
· exact h₁
|
||||
· simpa using congrArg (fun a => a[i]) h₂
|
||||
|
||||
end Array
|
||||
|
||||
namespace Vector
|
||||
|
||||
@[simp] theorem getElem_mk {data : Array α} {size : data.size = n} {i : Nat} (h : i < n) :
|
||||
(Vector.mk data size)[i] = data[i] := rfl
|
||||
|
||||
@[simp] theorem getElem_toArray {α n} (xs : Vector α n) (i : Nat) (h : i < xs.toArray.size) :
|
||||
xs.toArray[i] = xs[i]'(by simpa using h) := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[simp] theorem getElem_ofFn {α n} (f : Fin n → α) (i : Nat) (h : i < n) :
|
||||
(Vector.ofFn f)[i] = f ⟨i, by simpa using h⟩ := by
|
||||
simp [ofFn]
|
||||
|
||||
/-- The empty vector maps to the empty vector. -/
|
||||
@[simp]
|
||||
theorem map_empty (f : α → β) : map f #v[] = #v[] := by
|
||||
rw [map, mk.injEq]
|
||||
exact Array.map_empty f
|
||||
|
||||
theorem toArray_inj : ∀ {v w : Vector α n}, v.toArray = w.toArray → v = w
|
||||
| {..}, {..}, rfl => rfl
|
||||
|
||||
/-- A vector of length `0` is the empty vector. -/
|
||||
protected theorem eq_empty (v : Vector α 0) : v = #v[] := by
|
||||
apply Vector.toArray_inj
|
||||
apply Array.eq_empty_of_size_eq_zero v.2
|
||||
|
||||
/--
|
||||
`Vector.ext` is an extensionality theorem.
|
||||
Vectors `a` and `b` are equal to each other if their elements are equal for each valid index.
|
||||
-/
|
||||
@[ext]
|
||||
protected theorem ext {a b : Vector α n} (h : (i : Nat) → (_ : i < n) → a[i] = b[i]) : a = b := by
|
||||
apply Vector.toArray_inj
|
||||
apply Array.ext
|
||||
· rw [a.size_toArray, b.size_toArray]
|
||||
· intro i hi _
|
||||
rw [a.size_toArray] at hi
|
||||
exact h i hi
|
||||
|
||||
@[simp] theorem push_mk {data : Array α} {size : data.size = n} {x : α} :
|
||||
(Vector.mk data size).push x =
|
||||
Vector.mk (data.push x) (by simp [size, Nat.succ_eq_add_one]) := rfl
|
||||
|
||||
@[simp] theorem pop_mk {data : Array α} {size : data.size = n} :
|
||||
(Vector.mk data size).pop = Vector.mk data.pop (by simp [size]) := rfl
|
||||
|
||||
@[simp] theorem getElem_push_last {v : Vector α n} {x : α} : (v.push x)[n] = x := by
|
||||
rcases v with ⟨data, rfl⟩
|
||||
simp
|
||||
|
||||
@[simp] theorem getElem_push_lt {v : Vector α n} {x : α} {i : Nat} (h : i < n) :
|
||||
(v.push x)[i] = v[i] := by
|
||||
rcases v with ⟨data, rfl⟩
|
||||
simp [Array.getElem_push_lt, h]
|
||||
|
||||
@[simp] theorem getElem_pop {v : Vector α n} {i : Nat} (h : i < n - 1) : (v.pop)[i] = v[i] := by
|
||||
rcases v with ⟨data, rfl⟩
|
||||
simp
|
||||
|
||||
/--
|
||||
Variant of `getElem_pop` that will sometimes fire when `getElem_pop` gets stuck because of
|
||||
defeq issues in the implicit size argument.
|
||||
-/
|
||||
@[simp] theorem getElem_pop' (v : Vector α (n + 1)) (i : Nat) (h : i < n + 1 - 1) :
|
||||
@getElem (Vector α n) Nat α (fun _ i => i < n) instGetElemNatLt v.pop i h = v[i] :=
|
||||
getElem_pop h
|
||||
|
||||
@[simp] theorem push_pop_back (v : Vector α (n + 1)) : v.pop.push v.back = v := by
|
||||
ext i
|
||||
by_cases h : i < n
|
||||
· simp [h]
|
||||
· replace h : i = v.size - 1 := by rw [size_toArray]; omega
|
||||
subst h
|
||||
simp [pop, back, back!, ← Array.eq_push_pop_back!_of_size_ne_zero]
|
||||
|
||||
|
||||
/-! ### mk lemmas -/
|
||||
|
||||
theorem toArray_mk (a : Array α) (h : a.size = n) : (Vector.mk a h).toArray = a := rfl
|
||||
|
||||
@[simp] theorem allDiff_mk [BEq α] (a : Array α) (h : a.size = n) :
|
||||
(Vector.mk a h).allDiff = a.allDiff := rfl
|
||||
|
||||
@[simp] theorem mk_append_mk (a b : Array α) (ha : a.size = n) (hb : b.size = m) :
|
||||
Vector.mk a ha ++ Vector.mk b hb = Vector.mk (a ++ b) (by simp [ha, hb]) := rfl
|
||||
|
||||
@[simp] theorem back!_mk [Inhabited α] (a : Array α) (h : a.size = n) :
|
||||
(Vector.mk a h).back! = a.back! := rfl
|
||||
|
||||
@[simp] theorem back?_mk (a : Array α) (h : a.size = n) :
|
||||
(Vector.mk a h).back? = a.back? := rfl
|
||||
|
||||
@[simp] theorem drop_mk (a : Array α) (h : a.size = n) (m) :
|
||||
(Vector.mk a h).drop m = Vector.mk (a.extract m a.size) (by simp [h]) := rfl
|
||||
|
||||
@[simp] theorem eraseIdx_mk (a : Array α) (h : a.size = n) (i) (h') :
|
||||
(Vector.mk a h).eraseIdx i h' = Vector.mk (a.eraseIdx i) (by simp [h]) := rfl
|
||||
|
||||
@[simp] theorem eraseIdx!_mk (a : Array α) (h : a.size = n) (i) (hi : i < n) :
|
||||
(Vector.mk a h).eraseIdx! i = Vector.mk (a.eraseIdx i) (by simp [h, hi]) := by
|
||||
simp [Vector.eraseIdx!, hi]
|
||||
|
||||
@[simp] theorem cast_mk (a : Array α) (h : a.size = n) (h' : n = m) :
|
||||
(Vector.mk a h).cast h' = Vector.mk a (by simp [h, h']) := rfl
|
||||
|
||||
@[simp] theorem extract_mk (a : Array α) (h : a.size = n) (start stop) :
|
||||
(Vector.mk a h).extract start stop = Vector.mk (a.extract start stop) (by simp [h]) := rfl
|
||||
|
||||
@[simp] theorem indexOf?_mk [BEq α] (a : Array α) (h : a.size = n) (x : α) :
|
||||
(Vector.mk a h).indexOf? x = (a.indexOf? x).map (Fin.cast h) := rfl
|
||||
|
||||
@[simp] theorem mk_isEqv_mk (r : α → α → Bool) (a b : Array α) (ha : a.size = n) (hb : b.size = n) :
|
||||
Vector.isEqv (Vector.mk a ha) (Vector.mk b hb) r = Array.isEqv a b r := by
|
||||
simp [Vector.isEqv, Array.isEqv, ha, hb]
|
||||
|
||||
@[simp] theorem mk_isPrefixOf_mk [BEq α] (a b : Array α) (ha : a.size = n) (hb : b.size = m) :
|
||||
(Vector.mk a ha).isPrefixOf (Vector.mk b hb) = a.isPrefixOf b := rfl
|
||||
|
||||
@[simp] theorem map_mk (a : Array α) (h : a.size = n) (f : α → β) :
|
||||
(Vector.mk a h).map f = Vector.mk (a.map f) (by simp [h]) := rfl
|
||||
|
||||
@[simp] theorem reverse_mk (a : Array α) (h : a.size = n) :
|
||||
(Vector.mk a h).reverse = Vector.mk a.reverse (by simp [h]) := rfl
|
||||
|
||||
@[simp] theorem set_mk (a : Array α) (h : a.size = n) (i x w) :
|
||||
(Vector.mk a h).set i x = Vector.mk (a.set i x) (by simp [h]) := rfl
|
||||
|
||||
@[simp] theorem set!_mk (a : Array α) (h : a.size = n) (i x) :
|
||||
(Vector.mk a h).set! i x = Vector.mk (a.set! i x) (by simp [h]) := rfl
|
||||
|
||||
@[simp] theorem setIfInBounds_mk (a : Array α) (h : a.size = n) (i x) :
|
||||
(Vector.mk a h).setIfInBounds i x = Vector.mk (a.setIfInBounds i x) (by simp [h]) := rfl
|
||||
|
||||
@[simp] theorem swap_mk (a : Array α) (h : a.size = n) (i j) (hi hj) :
|
||||
(Vector.mk a h).swap i j = Vector.mk (a.swap i j) (by simp [h]) :=
|
||||
rfl
|
||||
|
||||
@[simp] theorem swapIfInBounds_mk (a : Array α) (h : a.size = n) (i j) :
|
||||
(Vector.mk a h).swapIfInBounds i j = Vector.mk (a.swapIfInBounds i j) (by simp [h]) := rfl
|
||||
|
||||
@[simp] theorem swapAt_mk (a : Array α) (h : a.size = n) (i x) (hi) :
|
||||
(Vector.mk a h).swapAt i x =
|
||||
((a.swapAt i x).fst, Vector.mk (a.swapAt i x).snd (by simp [h])) :=
|
||||
rfl
|
||||
|
||||
@[simp] theorem swapAt!_mk (a : Array α) (h : a.size = n) (i x) : (Vector.mk a h).swapAt! i x =
|
||||
((a.swapAt! i x).fst, Vector.mk (a.swapAt! i x).snd (by simp [h])) := rfl
|
||||
|
||||
@[simp] theorem take_mk (a : Array α) (h : a.size = n) (m) :
|
||||
(Vector.mk a h).take m = Vector.mk (a.take m) (by simp [h]) := rfl
|
||||
|
||||
@[simp] theorem mk_zipWith_mk (f : α → β → γ) (a : Array α) (b : Array β)
|
||||
(ha : a.size = n) (hb : b.size = n) : zipWith (Vector.mk a ha) (Vector.mk b hb) f =
|
||||
Vector.mk (Array.zipWith a b f) (by simp [ha, hb]) := rfl
|
||||
|
||||
/-! ### toArray lemmas -/
|
||||
|
||||
@[simp] theorem toArray_append (a : Vector α m) (b : Vector α n) :
|
||||
(a ++ b).toArray = a.toArray ++ b.toArray := rfl
|
||||
|
||||
@[simp] theorem toArray_drop (a : Vector α n) (m) :
|
||||
(a.drop m).toArray = a.toArray.extract m a.size := rfl
|
||||
|
||||
@[simp] theorem toArray_empty : (#v[] : Vector α 0).toArray = #[] := rfl
|
||||
|
||||
@[simp] theorem toArray_mkEmpty (cap) :
|
||||
(Vector.mkEmpty (α := α) cap).toArray = Array.mkEmpty cap := rfl
|
||||
|
||||
@[simp] theorem toArray_eraseIdx (a : Vector α n) (i) (h) :
|
||||
(a.eraseIdx i h).toArray = a.toArray.eraseIdx i (by simp [h]) := rfl
|
||||
|
||||
@[simp] theorem toArray_eraseIdx! (a : Vector α n) (i) (hi : i < n) :
|
||||
(a.eraseIdx! i).toArray = a.toArray.eraseIdx! i := by
|
||||
cases a; simp_all [Array.eraseIdx!]
|
||||
|
||||
@[simp] theorem toArray_cast (a : Vector α n) (h : n = m) :
|
||||
(a.cast h).toArray = a.toArray := rfl
|
||||
|
||||
@[simp] theorem toArray_extract (a : Vector α n) (start stop) :
|
||||
(a.extract start stop).toArray = a.toArray.extract start stop := rfl
|
||||
|
||||
@[simp] theorem toArray_map (f : α → β) (a : Vector α n) :
|
||||
(a.map f).toArray = a.toArray.map f := rfl
|
||||
|
||||
@[simp] theorem toArray_ofFn (f : Fin n → α) : (Vector.ofFn f).toArray = Array.ofFn f := rfl
|
||||
|
||||
@[simp] theorem toArray_pop (a : Vector α n) : a.pop.toArray = a.toArray.pop := rfl
|
||||
|
||||
@[simp] theorem toArray_push (a : Vector α n) (x) : (a.push x).toArray = a.toArray.push x := rfl
|
||||
|
||||
@[simp] theorem toArray_range : (Vector.range n).toArray = Array.range n := rfl
|
||||
|
||||
@[simp] theorem toArray_reverse (a : Vector α n) : a.reverse.toArray = a.toArray.reverse := rfl
|
||||
|
||||
@[simp] theorem toArray_set (a : Vector α n) (i x h) :
|
||||
(a.set i x).toArray = a.toArray.set i x (by simpa using h):= rfl
|
||||
|
||||
@[simp] theorem toArray_set! (a : Vector α n) (i x) :
|
||||
(a.set! i x).toArray = a.toArray.set! i x := rfl
|
||||
|
||||
@[simp] theorem toArray_setIfInBounds (a : Vector α n) (i x) :
|
||||
(a.setIfInBounds i x).toArray = a.toArray.setIfInBounds i x := rfl
|
||||
|
||||
@[simp] theorem toArray_singleton (x : α) : (Vector.singleton x).toArray = #[x] := rfl
|
||||
|
||||
@[simp] theorem toArray_swap (a : Vector α n) (i j) (hi hj) : (a.swap i j).toArray =
|
||||
a.toArray.swap i j (by simp [hi, hj]) (by simp [hi, hj]) := rfl
|
||||
|
||||
@[simp] theorem toArray_swapIfInBounds (a : Vector α n) (i j) :
|
||||
(a.swapIfInBounds i j).toArray = a.toArray.swapIfInBounds i j := rfl
|
||||
|
||||
@[simp] theorem toArray_swapAt (a : Vector α n) (i x h) :
|
||||
((a.swapAt i x).fst, (a.swapAt i x).snd.toArray) =
|
||||
((a.toArray.swapAt i x (by simpa using h)).fst,
|
||||
(a.toArray.swapAt i x (by simpa using h)).snd) := rfl
|
||||
|
||||
@[simp] theorem toArray_swapAt! (a : Vector α n) (i x) :
|
||||
((a.swapAt! i x).fst, (a.swapAt! i x).snd.toArray) =
|
||||
((a.toArray.swapAt! i x).fst, (a.toArray.swapAt! i x).snd) := rfl
|
||||
|
||||
@[simp] theorem toArray_take (a : Vector α n) (m) : (a.take m).toArray = a.toArray.take m := rfl
|
||||
|
||||
@[simp] theorem toArray_zipWith (f : α → β → γ) (a : Vector α n) (b : Vector β n) :
|
||||
(Vector.zipWith a b f).toArray = Array.zipWith a.toArray b.toArray f := rfl
|
||||
|
||||
/-! ### toList lemmas -/
|
||||
|
||||
theorem length_toList {α n} (xs : Vector α n) : xs.toList.length = n := by simp
|
||||
|
||||
theorem getElem_toList {α n} (xs : Vector α n) (i : Nat) (h : i < xs.toList.length) :
|
||||
xs.toList[i] = xs[i]'(by simpa using h) := by simp
|
||||
|
||||
theorem toList_inj {a b : Vector α n} (h : a.toList = b.toList) : a = b := by
|
||||
rcases a with ⟨⟨a⟩, ha⟩
|
||||
rcases b with ⟨⟨b⟩, hb⟩
|
||||
simpa using h
|
||||
|
||||
/-! ### set -/
|
||||
|
||||
theorem getElem_set (a : Vector α n) (i : Nat) (x : α) (hi : i < n) (j : Nat) (hj : j < n) :
|
||||
(a.set i x hi)[j] = if i = j then x else a[j] := by
|
||||
cases a
|
||||
split <;> simp_all [Array.getElem_set]
|
||||
|
||||
@[simp] theorem getElem_set_eq (a : Vector α n) (i : Nat) (x : α) (hi : i < n) :
|
||||
(a.set i x hi)[i] = x := by simp [getElem_set]
|
||||
|
||||
@[simp] theorem getElem_set_ne (a : Vector α n) (i : Nat) (x : α) (hi : i < n) (j : Nat)
|
||||
(hj : j < n) (h : i ≠ j) : (a.set i x hi)[j] = a[j] := by simp [getElem_set, h]
|
||||
|
||||
/-! ### setIfInBounds -/
|
||||
|
||||
theorem getElem_setIfInBounds (a : Vector α n) (i : Nat) (x : α) (j : Nat)
|
||||
(hj : j < n) : (a.setIfInBounds i x)[j] = if i = j then x else a[j] := by
|
||||
cases a
|
||||
split <;> simp_all [Array.getElem_setIfInBounds]
|
||||
|
||||
@[simp] theorem getElem_setIfInBounds_eq (a : Vector α n) (i : Nat) (x : α) (hj : i < n) :
|
||||
(a.setIfInBounds i x)[i] = x := by simp [getElem_setIfInBounds]
|
||||
|
||||
@[simp] theorem getElem_setIfInBounds_ne (a : Vector α n) (i : Nat) (x : α) (j : Nat)
|
||||
(hj : j < n) (h : i ≠ j) : (a.setIfInBounds i x)[j] = a[j] := by simp [getElem_setIfInBounds, h]
|
||||
|
||||
/-! ### append -/
|
||||
|
||||
theorem getElem_append (a : Vector α n) (b : Vector α m) (i : Nat) (hi : i < n + m) :
|
||||
(a ++ b)[i] = if h : i < n then a[i] else b[i - n] := by
|
||||
rcases a with ⟨a, rfl⟩
|
||||
rcases b with ⟨b, rfl⟩
|
||||
simp [Array.getElem_append, hi]
|
||||
|
||||
theorem getElem_append_left {a : Vector α n} {b : Vector α m} {i : Nat} (hi : i < n) :
|
||||
(a ++ b)[i] = a[i] := by simp [getElem_append, hi]
|
||||
|
||||
theorem getElem_append_right {a : Vector α n} {b : Vector α m} {i : Nat} (h : i < n + m) (hi : n ≤ i) :
|
||||
(a ++ b)[i] = b[i - n] := by
|
||||
rw [getElem_append, dif_neg (by omega)]
|
||||
|
||||
/-! ### cast -/
|
||||
|
||||
@[simp] theorem getElem_cast (a : Vector α n) (h : n = m) (i : Nat) (hi : i < m) :
|
||||
(a.cast h)[i] = a[i] := by
|
||||
cases a
|
||||
simp
|
||||
|
||||
/-! ### extract -/
|
||||
|
||||
@[simp] theorem getElem_extract (a : Vector α n) (start stop) (i : Nat) (hi : i < min stop n - start) :
|
||||
(a.extract start stop)[i] = a[start + i] := by
|
||||
cases a
|
||||
simp
|
||||
|
||||
/-! ### map -/
|
||||
|
||||
@[simp] theorem getElem_map (f : α → β) (a : Vector α n) (i : Nat) (hi : i < n) :
|
||||
(a.map f)[i] = f a[i] := by
|
||||
cases a
|
||||
simp
|
||||
|
||||
/-! ### zipWith -/
|
||||
|
||||
@[simp] theorem getElem_zipWith (f : α → β → γ) (a : Vector α n) (b : Vector β n) (i : Nat)
|
||||
(hi : i < n) : (zipWith a b f)[i] = f a[i] b[i] := by
|
||||
cases a
|
||||
cases b
|
||||
simp
|
||||
|
||||
/-! ### swap -/
|
||||
|
||||
theorem getElem_swap (a : Vector α n) (i j : Nat) {hi hj} (k : Nat) (hk : k < n) :
|
||||
(a.swap i j hi hj)[k] = if k = i then a[j] else if k = j then a[i] else a[k] := by
|
||||
cases a
|
||||
simp_all [Array.getElem_swap]
|
||||
|
||||
@[simp] theorem getElem_swap_right (a : Vector α n) {i j : Nat} {hi hj} :
|
||||
(a.swap i j hi hj)[j]'(by simpa using hj) = a[i] := by
|
||||
simp +contextual [getElem_swap]
|
||||
|
||||
@[simp] theorem getElem_swap_left (a : Vector α n) {i j : Nat} {hi hj} :
|
||||
(a.swap i j hi hj)[i]'(by simpa using hi) = a[j] := by
|
||||
simp [getElem_swap]
|
||||
|
||||
@[simp] theorem getElem_swap_of_ne (a : Vector α n) {i j : Nat} {hi hj} (hp : p < n)
|
||||
(hi' : p ≠ i) (hj' : p ≠ j) : (a.swap i j hi hj)[p] = a[p] := by
|
||||
simp_all [getElem_swap]
|
||||
|
||||
@[simp] theorem swap_swap (a : Vector α n) {i j : Nat} {hi hj} :
|
||||
(a.swap i j hi hj).swap i j hi hj = a := by
|
||||
cases a
|
||||
simp_all [Array.swap_swap]
|
||||
|
||||
theorem swap_comm (a : Vector α n) {i j : Nat} {hi hj} :
|
||||
a.swap i j hi hj = a.swap j i hj hi := by
|
||||
cases a
|
||||
simp only [swap_mk, mk.injEq]
|
||||
rw [Array.swap_comm]
|
||||
|
||||
/-! ### range -/
|
||||
|
||||
@[simp] theorem getElem_range (i : Nat) (hi : i < n) : (Vector.range n)[i] = i := by
|
||||
simp [Vector.range]
|
||||
|
||||
/-! ### take -/
|
||||
|
||||
@[simp] theorem getElem_take (a : Vector α n) (m : Nat) (hi : i < min n m) :
|
||||
(a.take m)[i] = a[i] := by
|
||||
cases a
|
||||
simp
|
||||
|
||||
/-! ### drop -/
|
||||
|
||||
@[simp] theorem getElem_drop (a : Vector α n) (m : Nat) (hi : i < n - m) :
|
||||
(a.drop m)[i] = a[m + i] := by
|
||||
cases a
|
||||
simp
|
||||
|
||||
/-! ### reverse -/
|
||||
|
||||
@[simp] theorem getElem_reverse (a : Vector α n) (i : Nat) (hi : i < n) :
|
||||
(a.reverse)[i] = a[n - 1 - i] := by
|
||||
rcases a with ⟨a, rfl⟩
|
||||
simp
|
||||
|
||||
/-! ### Decidable quantifiers. -/
|
||||
|
||||
theorem forall_zero_iff {P : Vector α 0 → Prop} :
|
||||
(∀ v, P v) ↔ P #v[] := by
|
||||
constructor
|
||||
· intro h
|
||||
apply h
|
||||
· intro h v
|
||||
obtain (rfl : v = #v[]) := (by ext i h; simp at h)
|
||||
apply h
|
||||
|
||||
theorem forall_cons_iff {P : Vector α (n + 1) → Prop} :
|
||||
(∀ v : Vector α (n + 1), P v) ↔ (∀ (x : α) (v : Vector α n), P (v.push x)) := by
|
||||
constructor
|
||||
· intro h _ _
|
||||
apply h
|
||||
· intro h v
|
||||
have w : v = v.pop.push v.back := by simp
|
||||
rw [w]
|
||||
apply h
|
||||
|
||||
instance instDecidableForallVectorZero (P : Vector α 0 → Prop) :
|
||||
∀ [Decidable (P #v[])], Decidable (∀ v, P v)
|
||||
| .isTrue h => .isTrue fun ⟨v, s⟩ => by
|
||||
obtain (rfl : v = .empty) := (by ext i h₁ h₂; exact s; cases h₂)
|
||||
exact h
|
||||
| .isFalse h => .isFalse (fun w => h (w _))
|
||||
|
||||
instance instDecidableForallVectorSucc (P : Vector α (n+1) → Prop)
|
||||
[Decidable (∀ (x : α) (v : Vector α n), P (v.push x))] : Decidable (∀ v, P v) :=
|
||||
decidable_of_iff' (∀ x (v : Vector α n), P (v.push x)) forall_cons_iff
|
||||
|
||||
instance instDecidableExistsVectorZero (P : Vector α 0 → Prop) [Decidable (P #v[])] :
|
||||
Decidable (∃ v, P v) :=
|
||||
decidable_of_iff (¬ ∀ v, ¬ P v) Classical.not_forall_not
|
||||
|
||||
instance instDecidableExistsVectorSucc (P : Vector α (n+1) → Prop)
|
||||
[Decidable (∀ (x : α) (v : Vector α n), ¬ P (v.push x))] : Decidable (∃ v, P v) :=
|
||||
decidable_of_iff (¬ ∀ v, ¬ P v) Classical.not_forall_not
|
||||
@@ -172,6 +172,16 @@ theorem getElem!_neg [GetElem? cont idx elem dom] [LawfulGetElem cont idx elem d
|
||||
simp only [getElem?_def] at h ⊢
|
||||
split <;> simp_all
|
||||
|
||||
@[simp] theorem isNone_getElem? [GetElem? cont idx elem dom] [LawfulGetElem cont idx elem dom]
|
||||
(c : cont) (i : idx) [Decidable (dom c i)] : c[i]?.isNone = ¬dom c i := by
|
||||
simp only [getElem?_def]
|
||||
split <;> simp_all
|
||||
|
||||
@[simp] theorem isSome_getElem? [GetElem? cont idx elem dom] [LawfulGetElem cont idx elem dom]
|
||||
(c : cont) (i : idx) [Decidable (dom c i)] : c[i]?.isSome = dom c i := by
|
||||
simp only [getElem?_def]
|
||||
split <;> simp_all
|
||||
|
||||
namespace Fin
|
||||
|
||||
instance instGetElemFinVal [GetElem cont Nat elem dom] : GetElem cont (Fin n) elem fun xs i => dom xs i where
|
||||
@@ -221,7 +231,7 @@ theorem getElem_cons_drop_succ_eq_drop {as : List α} {i : Nat} (h : i < as.leng
|
||||
as[i] :: as.drop (i+1) = as.drop i :=
|
||||
match as, i with
|
||||
| _::_, 0 => rfl
|
||||
| _::_, i+1 => getElem_cons_drop_succ_eq_drop (i := i) _
|
||||
| _::_, i+1 => getElem_cons_drop_succ_eq_drop (i := i) (Nat.add_one_lt_add_one_iff.mp h)
|
||||
|
||||
@[deprecated getElem_cons_drop_succ_eq_drop (since := "2024-11-05")]
|
||||
abbrev get_drop_eq_drop := @getElem_cons_drop_succ_eq_drop
|
||||
|
||||
@@ -224,7 +224,8 @@ structure Config where
|
||||
-/
|
||||
index : Bool := true
|
||||
/--
|
||||
This option does not have any effect (yet).
|
||||
If `implicitDefEqProofs := true`, `simp` does not create proof terms when the
|
||||
input and output terms are definitionally equal.
|
||||
-/
|
||||
implicitDefEqProofs : Bool := true
|
||||
deriving Inhabited, BEq
|
||||
@@ -249,6 +250,13 @@ def neutralConfig : Simp.Config := {
|
||||
zetaDelta := false
|
||||
}
|
||||
|
||||
structure NormCastConfig extends Simp.Config where
|
||||
zeta := false
|
||||
beta := false
|
||||
eta := false
|
||||
proj := false
|
||||
iota := false
|
||||
|
||||
end Simp
|
||||
|
||||
/-- Configuration for which occurrences that match an expression should be rewritten. -/
|
||||
|
||||
@@ -48,6 +48,10 @@ def tactic : Category := {}
|
||||
For example, `let x ← e` is a `doElem`, and a `do` block consists of a list of `doElem`s. -/
|
||||
def doElem : Category := {}
|
||||
|
||||
/-- `structInstFieldDecl` is the syntax category for value declarations for fields in structure instance notation.
|
||||
For example, the `:= 1` and `| 0 => 0 | n + 1 => n` in `{ x := 1, f | 0 => 0 | n + 1 => n }` are in the `structInstFieldDecl` class. -/
|
||||
def structInstFieldDecl : Category := {}
|
||||
|
||||
/-- `level` is a builtin syntax category for universe levels.
|
||||
This is the `u` in `Sort u`: it can contain `max` and `imax`, addition with
|
||||
constants, and variables. -/
|
||||
|
||||
@@ -32,13 +32,9 @@ theorem get_map {xs : IntList} (h : f 0 = 0) : get (xs.map f) i = f (xs.get i) :
|
||||
cases xs[i]? <;> simp_all
|
||||
|
||||
theorem get_of_length_le {xs : IntList} (h : xs.length ≤ i) : xs.get i = 0 := by
|
||||
rw [get, List.get?_eq_none.mpr h]
|
||||
rw [get, List.get?_eq_none_iff.mpr h]
|
||||
rfl
|
||||
|
||||
-- theorem lt_length_of_get_nonzero {xs : IntList} (h : xs.get i ≠ 0) : i < xs.length := by
|
||||
-- revert h
|
||||
-- simpa using mt get_of_length_le
|
||||
|
||||
/-- Like `List.set`, but right-pad with zeroes as necessary first. -/
|
||||
def set (xs : IntList) (i : Nat) (y : Int) : IntList :=
|
||||
match xs, i with
|
||||
|
||||
@@ -860,8 +860,8 @@ abbrev DecidablePred {α : Sort u} (r : α → Prop) :=
|
||||
(a : α) → Decidable (r a)
|
||||
|
||||
/-- A decidable relation. See `Decidable`. -/
|
||||
abbrev DecidableRel {α : Sort u} (r : α → α → Prop) :=
|
||||
(a b : α) → Decidable (r a b)
|
||||
abbrev DecidableRel {α : Sort u} {β : Sort v} (r : α → β → Prop) :=
|
||||
(a : α) → (b : β) → Decidable (r a b)
|
||||
|
||||
/--
|
||||
Asserts that `α` has decidable equality, that is, `a = b` is decidable
|
||||
|
||||
@@ -959,3 +959,36 @@ syntax "println! " (interpolatedStr(term) <|> term) : term
|
||||
macro_rules
|
||||
| `(println! $msg:interpolatedStr) => `((IO.println (s! $msg) : IO Unit))
|
||||
| `(println! $msg:term) => `((IO.println $msg : IO Unit))
|
||||
|
||||
/--
|
||||
Marks given value and its object graph closure as multi-threaded if currently
|
||||
marked single-threaded. This will make reference counter updates atomic and
|
||||
thus more costly. It can still be useful to do eagerly when the value will be
|
||||
shared between threads later anyway and there is available time budget to mark
|
||||
it now. -/
|
||||
@[extern "lean_runtime_mark_multi_threaded"]
|
||||
def Runtime.markMultiThreaded (a : α) : BaseIO α := return a
|
||||
|
||||
/--
|
||||
Marks given value and its object graph closure as persistent. This will remove
|
||||
reference counter updates but prevent the closure from being deallocated until
|
||||
the end of the process! It can still be useful to do eagerly when the value
|
||||
will be marked persistent later anyway and there is available time budget to
|
||||
mark it now or it would be unnecessarily marked multi-threaded in between.
|
||||
|
||||
This function is only safe to use on objects (in the full closure) which are
|
||||
not used concurrently or which are already persistent.
|
||||
-/
|
||||
@[extern "lean_runtime_mark_persistent"]
|
||||
unsafe def Runtime.markPersistent (a : α) : BaseIO α := return a
|
||||
|
||||
set_option linter.unusedVariables false in
|
||||
/--
|
||||
Discards the passed owned reference. This leads to `a` any any object reachable from it never being
|
||||
freed. This can be a useful optimization for eliding deallocation time of big object graphs that are
|
||||
kept alive close to the end of the process anyway (in which case calling `Runtime.markPersistent`
|
||||
would be similarly costly to deallocation). It is still considered a safe operation as it cannot
|
||||
lead to undefined behavior.
|
||||
-/
|
||||
@[extern "lean_runtime_forget"]
|
||||
def Runtime.forget (a : α) : BaseIO Unit := return
|
||||
|
||||
@@ -7,6 +7,9 @@ prelude
|
||||
import Init.System.IO
|
||||
import Init.Control.StateRef
|
||||
|
||||
|
||||
set_option linter.deprecated false
|
||||
|
||||
namespace IO
|
||||
|
||||
private opaque BaseMutexImpl : NonemptyType.{0}
|
||||
@@ -16,12 +19,13 @@ Mutual exclusion primitive (a lock).
|
||||
|
||||
If you want to guard shared state, use `Mutex α` instead.
|
||||
-/
|
||||
@[deprecated "Use Std.BaseMutex from Std.Sync.Mutex instead" (since := "2024-12-02")]
|
||||
def BaseMutex : Type := BaseMutexImpl.type
|
||||
|
||||
instance : Nonempty BaseMutex := BaseMutexImpl.property
|
||||
|
||||
/-- Creates a new `BaseMutex`. -/
|
||||
@[extern "lean_io_basemutex_new"]
|
||||
@[extern "lean_io_basemutex_new", deprecated "Use Std.BaseMutex.new from Std.Sync.Mutex instead" (since := "2024-12-02")]
|
||||
opaque BaseMutex.new : BaseIO BaseMutex
|
||||
|
||||
/--
|
||||
@@ -30,7 +34,7 @@ Locks a `BaseMutex`. Waits until no other thread has locked the mutex.
|
||||
The current thread must not have already locked the mutex.
|
||||
Reentrant locking is undefined behavior (inherited from the C++ implementation).
|
||||
-/
|
||||
@[extern "lean_io_basemutex_lock"]
|
||||
@[extern "lean_io_basemutex_lock", deprecated "Use Std.BaseMutex.lock from Std.Sync.Mutex instead" (since := "2024-12-02")]
|
||||
opaque BaseMutex.lock (mutex : @& BaseMutex) : BaseIO Unit
|
||||
|
||||
/--
|
||||
@@ -39,33 +43,35 @@ Unlocks a `BaseMutex`.
|
||||
The current thread must have already locked the mutex.
|
||||
Unlocking an unlocked mutex is undefined behavior (inherited from the C++ implementation).
|
||||
-/
|
||||
@[extern "lean_io_basemutex_unlock"]
|
||||
@[extern "lean_io_basemutex_unlock", deprecated "Use Std.BaseMutex.unlock from Std.Sync.Mutex instead" (since := "2024-12-02")]
|
||||
opaque BaseMutex.unlock (mutex : @& BaseMutex) : BaseIO Unit
|
||||
|
||||
private opaque CondvarImpl : NonemptyType.{0}
|
||||
|
||||
/-- Condition variable. -/
|
||||
@[deprecated "Use Std.Condvar from Std.Sync.Mutex instead" (since := "2024-12-02")]
|
||||
def Condvar : Type := CondvarImpl.type
|
||||
|
||||
instance : Nonempty Condvar := CondvarImpl.property
|
||||
|
||||
/-- Creates a new condition variable. -/
|
||||
@[extern "lean_io_condvar_new"]
|
||||
@[extern "lean_io_condvar_new", deprecated "Use Std.Condvar.new from Std.Sync.Mutex instead" (since := "2024-12-02")]
|
||||
opaque Condvar.new : BaseIO Condvar
|
||||
|
||||
/-- Waits until another thread calls `notifyOne` or `notifyAll`. -/
|
||||
@[extern "lean_io_condvar_wait"]
|
||||
@[extern "lean_io_condvar_wait", deprecated "Use Std.Condvar.wait from Std.Sync.Mutex instead" (since := "2024-12-02")]
|
||||
opaque Condvar.wait (condvar : @& Condvar) (mutex : @& BaseMutex) : BaseIO Unit
|
||||
|
||||
/-- Wakes up a single other thread executing `wait`. -/
|
||||
@[extern "lean_io_condvar_notify_one"]
|
||||
@[extern "lean_io_condvar_notify_one", deprecated "Use Std.Condvar.notifyOne from Std.Sync.Mutex instead" (since := "2024-12-02")]
|
||||
opaque Condvar.notifyOne (condvar : @& Condvar) : BaseIO Unit
|
||||
|
||||
/-- Wakes up all other threads executing `wait`. -/
|
||||
@[extern "lean_io_condvar_notify_all"]
|
||||
@[extern "lean_io_condvar_notify_all", deprecated "Use Std.Condvar.notifyAll from Std.Sync.Mutex instead" (since := "2024-12-02")]
|
||||
opaque Condvar.notifyAll (condvar : @& Condvar) : BaseIO Unit
|
||||
|
||||
/-- Waits on the condition variable until the predicate is true. -/
|
||||
@[deprecated "Use Std.Condvar.waitUntil from Std.Sync.Mutex instead" (since := "2024-12-02")]
|
||||
def Condvar.waitUntil [Monad m] [MonadLift BaseIO m]
|
||||
(condvar : Condvar) (mutex : BaseMutex) (pred : m Bool) : m Unit := do
|
||||
while !(← pred) do
|
||||
@@ -78,6 +84,7 @@ The type `Mutex α` is similar to `IO.Ref α`,
|
||||
except that concurrent accesses are guarded by a mutex
|
||||
instead of atomic pointer operations and busy-waiting.
|
||||
-/
|
||||
@[deprecated "Use Std.Mutex from Std.Sync.Mutex instead" (since := "2024-12-02")]
|
||||
structure Mutex (α : Type) where private mk ::
|
||||
private ref : IO.Ref α
|
||||
mutex : BaseMutex
|
||||
@@ -86,6 +93,7 @@ structure Mutex (α : Type) where private mk ::
|
||||
instance : CoeOut (Mutex α) BaseMutex where coe := Mutex.mutex
|
||||
|
||||
/-- Creates a new mutex. -/
|
||||
@[deprecated "Use Std.Mutex.new from Std.Sync.Mutex instead" (since := "2024-12-02")]
|
||||
def Mutex.new (a : α) : BaseIO (Mutex α) :=
|
||||
return { ref := ← mkRef a, mutex := ← BaseMutex.new }
|
||||
|
||||
@@ -94,9 +102,11 @@ def Mutex.new (a : α) : BaseIO (Mutex α) :=
|
||||
with outside monad `m`.
|
||||
The action has access to the state `α` of the mutex (via `get` and `set`).
|
||||
-/
|
||||
@[deprecated "Use Std.AtomicT from Std.Sync.Mutex instead" (since := "2024-12-02")]
|
||||
abbrev AtomicT := StateRefT' IO.RealWorld
|
||||
|
||||
/-- `mutex.atomically k` runs `k` with access to the mutex's state while locking the mutex. -/
|
||||
@[deprecated "Use Std.Mutex.atomically from Std.Sync.Mutex instead" (since := "2024-12-02")]
|
||||
def Mutex.atomically [Monad m] [MonadLiftT BaseIO m] [MonadFinally m]
|
||||
(mutex : Mutex α) (k : AtomicT α m β) : m β := do
|
||||
try
|
||||
@@ -110,6 +120,7 @@ def Mutex.atomically [Monad m] [MonadLiftT BaseIO m] [MonadFinally m]
|
||||
waiting on `condvar` until `pred` returns true.
|
||||
Both `k` and `pred` have access to the mutex's state.
|
||||
-/
|
||||
@[deprecated "Use Std.Mutex.atomicallyOnce from Std.Sync.Mutex instead" (since := "2024-12-02")]
|
||||
def Mutex.atomicallyOnce [Monad m] [MonadLiftT BaseIO m] [MonadFinally m]
|
||||
(mutex : Mutex α) (condvar : Condvar)
|
||||
(pred : AtomicT α m Bool) (k : AtomicT α m β) : m β :=
|
||||
|
||||
@@ -23,5 +23,14 @@ def isEmscripten : Bool := getIsEmscripten ()
|
||||
/-- The LLVM target triple of the current platform. Empty if missing at Lean compile time. -/
|
||||
def target : String := getTarget ()
|
||||
|
||||
theorem numBits_pos : 0 < numBits := by
|
||||
cases numBits_eq <;> next h => simp [h]
|
||||
|
||||
theorem le_numBits : 32 ≤ numBits := by
|
||||
cases numBits_eq <;> next h => simp [h]
|
||||
|
||||
theorem numBits_le : numBits ≤ 64 := by
|
||||
cases numBits_eq <;> next h => simp [h]
|
||||
|
||||
end Platform
|
||||
end System
|
||||
|
||||
@@ -1309,7 +1309,7 @@ macro "bv_omega" : tactic => `(tactic| (try simp only [bv_toNat] at *) <;> omega
|
||||
syntax (name := acNf0) "ac_nf0" (location)? : tactic
|
||||
|
||||
/-- Implementation of `norm_cast` (the full `norm_cast` calls `trivial` afterwards). -/
|
||||
syntax (name := normCast0) "norm_cast0" (location)? : tactic
|
||||
syntax (name := normCast0) "norm_cast0" optConfig (location)? : tactic
|
||||
|
||||
/-- `assumption_mod_cast` is a variant of `assumption` that solves the goal
|
||||
using a hypothesis. Unlike `assumption`, it first pre-processes the goal and
|
||||
@@ -1318,7 +1318,7 @@ in more situations.
|
||||
|
||||
Concretely, it runs `norm_cast` on the goal. For each local hypothesis `h`, it also
|
||||
normalizes `h` with `norm_cast` and tries to use that to close the goal. -/
|
||||
macro "assumption_mod_cast" : tactic => `(tactic| norm_cast0 at * <;> assumption)
|
||||
macro "assumption_mod_cast" cfg:optConfig : tactic => `(tactic| norm_cast0 $cfg at * <;> assumption)
|
||||
|
||||
/--
|
||||
The `norm_cast` family of tactics is used to normalize certain coercions (*casts*) in expressions.
|
||||
@@ -1355,26 +1355,9 @@ their operation, to make them more flexible about the expressions they accept
|
||||
|
||||
See also `push_cast`, which moves casts inwards rather than lifting them outwards.
|
||||
-/
|
||||
macro "norm_cast" loc:(location)? : tactic =>
|
||||
`(tactic| norm_cast0 $[$loc]? <;> try trivial)
|
||||
macro "norm_cast" cfg:optConfig loc:(location)? : tactic =>
|
||||
`(tactic| norm_cast0 $cfg $[$loc]? <;> try trivial)
|
||||
|
||||
/--
|
||||
`ac_nf` normalizes equalities up to application of an associative and commutative operator.
|
||||
- `ac_nf` normalizes all hypotheses and the goal target of the goal.
|
||||
- `ac_nf at l` normalizes at location(s) `l`, where `l` is either `*` or a
|
||||
list of hypotheses in the local context. In the latter case, a turnstile `⊢` or `|-`
|
||||
can also be used, to signify the target of the goal.
|
||||
```
|
||||
instance : Associative (α := Nat) (.+.) := ⟨Nat.add_assoc⟩
|
||||
instance : Commutative (α := Nat) (.+.) := ⟨Nat.add_comm⟩
|
||||
|
||||
example (a b c d : Nat) : a + b + c + d = d + (b + c) + a := by
|
||||
ac_nf
|
||||
-- goal: a + (b + (c + d)) = a + (b + (c + d))
|
||||
```
|
||||
-/
|
||||
macro "ac_nf" loc:(location)? : tactic =>
|
||||
`(tactic| ac_nf0 $[$loc]? <;> try trivial)
|
||||
|
||||
/--
|
||||
`push_cast` rewrites the goal to move certain coercions (*casts*) inward, toward the leaf nodes.
|
||||
@@ -1417,6 +1400,24 @@ syntax (name := pushCast) "push_cast" optConfig (discharger)? (&" only")?
|
||||
-/
|
||||
syntax (name := normCastAddElim) "norm_cast_add_elim" ident : command
|
||||
|
||||
/--
|
||||
`ac_nf` normalizes equalities up to application of an associative and commutative operator.
|
||||
- `ac_nf` normalizes all hypotheses and the goal target of the goal.
|
||||
- `ac_nf at l` normalizes at location(s) `l`, where `l` is either `*` or a
|
||||
list of hypotheses in the local context. In the latter case, a turnstile `⊢` or `|-`
|
||||
can also be used, to signify the target of the goal.
|
||||
```
|
||||
instance : Associative (α := Nat) (.+.) := ⟨Nat.add_assoc⟩
|
||||
instance : Commutative (α := Nat) (.+.) := ⟨Nat.add_comm⟩
|
||||
|
||||
example (a b c d : Nat) : a + b + c + d = d + (b + c) + a := by
|
||||
ac_nf
|
||||
-- goal: a + (b + (c + d)) = a + (b + (c + d))
|
||||
```
|
||||
-/
|
||||
macro "ac_nf" loc:(location)? : tactic =>
|
||||
`(tactic| ac_nf0 $[$loc]? <;> try trivial)
|
||||
|
||||
/--
|
||||
* `symm` applies to a goal whose target has the form `t ~ u` where `~` is a symmetric relation,
|
||||
that is, a relation which has a symmetry lemma tagged with the attribute [symm].
|
||||
|
||||
@@ -79,21 +79,3 @@ def withPtrEq {α : Type u} (a b : α) (k : Unit → Bool) (h : a = b → k () =
|
||||
|
||||
@[implemented_by withPtrAddrUnsafe]
|
||||
def withPtrAddr {α : Type u} {β : Type v} (a : α) (k : USize → β) (h : ∀ u₁ u₂, k u₁ = k u₂) : β := k 0
|
||||
|
||||
/--
|
||||
Marks given value and its object graph closure as multi-threaded if currently
|
||||
marked single-threaded. This will make reference counter updates atomic and
|
||||
thus more costly. It can still be useful to do eagerly when the value will be
|
||||
shared between threads later anyway and there is available time budget to mark
|
||||
it now. -/
|
||||
@[extern "lean_runtime_mark_multi_threaded"]
|
||||
def Runtime.markMultiThreaded (a : α) : α := a
|
||||
|
||||
/--
|
||||
Marks given value and its object graph closure as persistent. This will remove
|
||||
reference counter updates but prevent the closure from being deallocated until
|
||||
the end of the process! It can still be useful to do eagerly when the value
|
||||
will be marked persistent later anyway and there is available time budget to
|
||||
mark it now or it would be unnecessarily marked multi-threaded in between. -/
|
||||
@[extern "lean_runtime_mark_persistent"]
|
||||
def Runtime.markPersistent (a : α) : α := a
|
||||
|
||||
@@ -593,6 +593,14 @@ where
|
||||
let minor ← visit minor
|
||||
mkOverApplication minor args arity
|
||||
|
||||
visitHEqRec (e : Expr) : M Arg :=
|
||||
let arity := 7
|
||||
etaIfUnderApplied e arity do
|
||||
let args := e.getAppArgs
|
||||
let minor := if e.isAppOf ``HEq.rec || e.isAppOf ``HEq.ndrec then args[3]! else args[6]!
|
||||
let minor ← visit minor
|
||||
mkOverApplication minor args arity
|
||||
|
||||
visitFalseRec (e : Expr) : M Arg :=
|
||||
let arity := 2
|
||||
etaIfUnderApplied e arity do
|
||||
@@ -669,6 +677,8 @@ where
|
||||
visitCtor 3 e
|
||||
else if declName == ``Eq.casesOn || declName == ``Eq.rec || declName == ``Eq.ndrec then
|
||||
visitEqRec e
|
||||
else if declName == ``HEq.casesOn || declName == ``HEq.rec || declName == ``HEq.ndrec then
|
||||
visitHEqRec e
|
||||
else if declName == ``And.rec || declName == ``Iff.rec then
|
||||
visitAndIffRecCore e (minorPos := 3)
|
||||
else if declName == ``And.casesOn || declName == ``Iff.casesOn then
|
||||
|
||||
@@ -31,6 +31,19 @@ register_builtin_option maxHeartbeats : Nat := {
|
||||
descr := "maximum amount of heartbeats per command. A heartbeat is number of (small) memory allocations (in thousands), 0 means no limit"
|
||||
}
|
||||
|
||||
register_builtin_option Elab.async : Bool := {
|
||||
defValue := false
|
||||
descr := "perform elaboration using multiple threads where possible\
|
||||
\n\
|
||||
\nThis option defaults to `false` but (when not explicitly set) is overridden to `true` in \
|
||||
`Lean.Language.Lean.process` as used by the cmdline driver and language server. \
|
||||
Metaprogramming users driving elaboration directly via e.g. \
|
||||
`Lean.Elab.Command.elabCommandTopLevel` can opt into asynchronous elaboration by setting \
|
||||
this option but then are responsible for processing messages and other data not only in the \
|
||||
resulting command state but also from async tasks in `Lean.Command.Context.snap?` and \
|
||||
`Lean.Command.State.snapshotTasks`."
|
||||
}
|
||||
|
||||
/--
|
||||
If the `diagnostics` option is not already set, gives a message explaining this option.
|
||||
Begins with a `\n`, so an error message can look like `m!"some error occurred{useDiagnosticMsg}"`.
|
||||
@@ -351,9 +364,7 @@ Returns the current log and then resets its messages while adjusting `MessageLog
|
||||
for incremental reporting during elaboration of a single command.
|
||||
-/
|
||||
def getAndEmptyMessageLog : CoreM MessageLog :=
|
||||
modifyGet fun s => (s.messages, { s with
|
||||
messages.unreported := {}
|
||||
messages.hadErrors := s.messages.hasErrors })
|
||||
modifyGet fun s => (s.messages, { s with messages := s.messages.markAllReported })
|
||||
|
||||
instance : MonadLog CoreM where
|
||||
getRef := getRef
|
||||
@@ -412,7 +423,7 @@ def wrapAsyncAsSnapshot (act : Unit → CoreM Unit) (desc : String := by exact d
|
||||
IO.FS.withIsolatedStreams (isolateStderr := stderrAsMessages.get (← getOptions)) do
|
||||
let tid ← IO.getTID
|
||||
-- reset trace state and message log so as not to report them twice
|
||||
modify ({ · with messages := {}, traceState := { tid } })
|
||||
modify fun st => { st with messages := st.messages.markAllReported, traceState := { tid } }
|
||||
try
|
||||
withTraceNode `Elab.async (fun _ => return desc) do
|
||||
act ()
|
||||
|
||||
@@ -35,7 +35,7 @@ theorem RArray.get_ofFn {n : Nat} (f : Fin n → α) (h : 0 < n) (i : Fin n) :
|
||||
go 0 n h (Nat.le_refl _) (Nat.zero_le _) i.2
|
||||
where
|
||||
go lb ub h1 h2 (h3 : lb ≤ i.val) (h3 : i.val < ub) : (ofFn.go f lb ub h1 h2).get i = f i := by
|
||||
induction lb, ub, h1, h2 using RArray.ofFn.go.induct (f := f) (n := n)
|
||||
induction lb, ub, h1, h2 using RArray.ofFn.go.induct (n := n)
|
||||
case case1 =>
|
||||
simp [ofFn.go, RArray.get_eq_getImpl, RArray.getImpl]
|
||||
congr
|
||||
@@ -53,7 +53,7 @@ theorem RArray.size_ofFn {n : Nat} (f : Fin n → α) (h : 0 < n) :
|
||||
go 0 n h (Nat.le_refl _)
|
||||
where
|
||||
go lb ub h1 h2 : (ofFn.go f lb ub h1 h2).size = ub - lb := by
|
||||
induction lb, ub, h1, h2 using RArray.ofFn.go.induct (f := f) (n := n)
|
||||
induction lb, ub, h1, h2 using RArray.ofFn.go.induct (n := n)
|
||||
case case1 => simp [ofFn.go, size]; omega
|
||||
case case2 ih1 ih2 hiu => rw [ofFn.go]; simp [size, *]; omega
|
||||
|
||||
|
||||
@@ -488,6 +488,9 @@ where
|
||||
let mut lines : Array MessageData := #[]
|
||||
let decls ← getOptionDecls
|
||||
for (name, val) in opts do
|
||||
-- `#guard_msgs` sets this option internally, we don't want it to end up in its output
|
||||
if name == `Elab.async then
|
||||
continue
|
||||
let (isSet, isUnknown) :=
|
||||
match decls.find? name with
|
||||
| some decl => (decl.defValue != val, false)
|
||||
|
||||
@@ -101,7 +101,7 @@ structure Context where
|
||||
(mutual) defs and contained tactics, in which case the `DynamicSnapshot` is a
|
||||
`HeadersParsedSnapshot`.
|
||||
|
||||
Definitely resolved in `Language.Lean.process.doElab`.
|
||||
Definitely resolved in `Lean.Elab.Command.elabCommandTopLevel`.
|
||||
|
||||
Invariant: if the bundle's `old?` is set, the context and state at the beginning of current and
|
||||
old elaboration are identical.
|
||||
@@ -287,7 +287,9 @@ def runLinters (stx : Syntax) : CommandElabM Unit := do
|
||||
| Exception.internal _ _ =>
|
||||
logException ex
|
||||
finally
|
||||
modify fun s => { savedState with messages := s.messages }
|
||||
-- TODO: it would be good to preserve even more state (#4363) but preserving info
|
||||
-- trees currently breaks from linters adding context-less info nodes
|
||||
modify fun s => { savedState with messages := s.messages, traceState := s.traceState }
|
||||
|
||||
/--
|
||||
Catches and logs exceptions occurring in `x`. Unlike `try catch` in `CommandElabM`, this function
|
||||
@@ -311,7 +313,7 @@ def wrapAsyncAsSnapshot (act : Unit → CommandElabM Unit)
|
||||
IO.FS.withIsolatedStreams (isolateStderr := Core.stderrAsMessages.get (← getOptions)) do
|
||||
let tid ← IO.getTID
|
||||
-- reset trace state and message log so as not to report them twice
|
||||
modify ({ · with messages := {}, traceState := { tid } })
|
||||
modify fun st => { st with messages := st.messages.markAllReported, traceState := { tid } }
|
||||
try
|
||||
withTraceNode `Elab.async (fun _ => return desc) do
|
||||
act ()
|
||||
@@ -344,6 +346,17 @@ def wrapAsyncAsSnapshot (act : Unit → CommandElabM Unit)
|
||||
def logSnapshotTask (task : Language.SnapshotTask Language.SnapshotTree) : CommandElabM Unit :=
|
||||
modify fun s => { s with snapshotTasks := s.snapshotTasks.push task }
|
||||
|
||||
def runLintersAsync (stx : Syntax) : CommandElabM Unit := do
|
||||
if !Elab.async.get (← getOptions) then
|
||||
withoutModifyingEnv do
|
||||
runLinters stx
|
||||
return
|
||||
|
||||
-- We only start one task for all linters for now as most linters are fast and we simply want
|
||||
-- to unblock elaboration of the next command
|
||||
let lintAct ← wrapAsyncAsSnapshot fun _ => runLinters stx
|
||||
logSnapshotTask { range? := none, task := (← BaseIO.asTask lintAct) }
|
||||
|
||||
protected def getCurrMacroScope : CommandElabM Nat := do pure (← read).currMacroScope
|
||||
protected def getMainModule : CommandElabM Name := do pure (← getEnv).mainModule
|
||||
|
||||
@@ -547,8 +560,13 @@ def elabCommandTopLevel (stx : Syntax) : CommandElabM Unit := withRef stx do pro
|
||||
-- rather than engineer a general solution.
|
||||
unless (stx.find? (·.isOfKind ``Lean.guardMsgsCmd)).isSome do
|
||||
withLogging do
|
||||
runLinters stx
|
||||
runLintersAsync stx
|
||||
finally
|
||||
-- Make sure `snap?` is definitely resolved; we do not use it for reporting as `#guard_msgs` may
|
||||
-- be the caller of this function and add new messages and info trees
|
||||
if let some snap := (← read).snap? then
|
||||
snap.new.resolve default
|
||||
|
||||
-- note the order: first process current messages & info trees, then add back old messages & trees,
|
||||
-- then convert new traces to messages
|
||||
let mut msgs := (← get).messages
|
||||
|
||||
@@ -169,6 +169,8 @@ def runFrontend
|
||||
IO.FS.writeFile ⟨out⟩ <| Json.compress <| toJson profile
|
||||
|
||||
let hasErrors := snaps.getAll.any (·.diagnostics.msgLog.hasErrors)
|
||||
-- no point in freeing the snapshot graph and all referenced data this close to process exit
|
||||
Runtime.forget snaps
|
||||
pure (cmdState.env, !hasErrors)
|
||||
|
||||
|
||||
|
||||
@@ -140,9 +140,15 @@ def MessageOrdering.apply (mode : MessageOrdering) (msgs : List String) : List S
|
||||
|>.trim |> removeTrailingWhitespaceMarker
|
||||
let (whitespace, ordering, specFn) ← parseGuardMsgsSpec spec?
|
||||
let initMsgs ← modifyGet fun st => (st.messages, { st with messages := {} })
|
||||
-- The `#guard_msgs` command is special-cased in `elabCommandTopLevel` to ensure linters only run once.
|
||||
elabCommandTopLevel cmd
|
||||
let msgs := (← get).messages
|
||||
-- do not forward snapshot as we don't want messages assigned to it to leak outside
|
||||
withReader ({ · with snap? := none }) do
|
||||
-- The `#guard_msgs` command is special-cased in `elabCommandTopLevel` to ensure linters only run once.
|
||||
elabCommandTopLevel cmd
|
||||
-- collect sync and async messages
|
||||
let msgs := (← get).messages ++
|
||||
(← get).snapshotTasks.foldl (· ++ ·.get.getAll.foldl (· ++ ·.diagnostics.msgLog) {}) {}
|
||||
-- clear async messages as we don't want them to leak outside
|
||||
modify ({ · with snapshotTasks := #[] })
|
||||
let mut toCheck : MessageLog := .empty
|
||||
let mut toPassthrough : MessageLog := .empty
|
||||
for msg in msgs.toList do
|
||||
|
||||
@@ -282,52 +282,36 @@ private partial def withFunLocalDecls {α} (headers : Array DefViewElabHeader) (
|
||||
k fvars
|
||||
loop 0 #[]
|
||||
|
||||
private def expandWhereStructInst : Macro
|
||||
| whereStx@`(Parser.Command.whereStructInst|where%$whereTk $[$decls:letDecl];* $[$whereDecls?:whereDecls]?) => do
|
||||
let letIdDecls ← decls.mapM fun stx => match stx with
|
||||
| `(letDecl|$_decl:letPatDecl) => Macro.throwErrorAt stx "patterns are not allowed here"
|
||||
| `(letDecl|$decl:letEqnsDecl) => expandLetEqnsDecl decl (useExplicit := false)
|
||||
| `(letDecl|$decl:letIdDecl) => pure decl
|
||||
| _ => Macro.throwUnsupported
|
||||
let structInstFields ← letIdDecls.mapM fun
|
||||
| stx@`(letIdDecl|$id:ident $binders* $[: $ty?]? := $val) => withRef stx do
|
||||
let mut val := val
|
||||
if let some ty := ty? then
|
||||
val ← `(($val : $ty))
|
||||
-- HACK: this produces invalid syntax, but the fun elaborator supports letIdBinders as well
|
||||
have : Coe (TSyntax ``letIdBinder) (TSyntax ``funBinder) := ⟨(⟨·⟩)⟩
|
||||
val ← if binders.size > 0 then `(fun $binders* => $val) else pure val
|
||||
`(structInstField|$id:ident := $val)
|
||||
| stx@`(letIdDecl|_ $_* $[: $_]? := $_) => Macro.throwErrorAt stx "'_' is not allowed here"
|
||||
| _ => Macro.throwUnsupported
|
||||
private def expandWhereStructInst : Macro := fun whereStx => do
|
||||
let `(Parser.Command.whereStructInst| where%$whereTk $[$structInstFields];* $[$whereDecls?:whereDecls]?) := whereStx
|
||||
| Macro.throwUnsupported
|
||||
|
||||
let startOfStructureTkInfo : SourceInfo :=
|
||||
match whereTk.getPos? with
|
||||
| some pos => .synthetic pos ⟨pos.byteIdx + 1⟩ true
|
||||
| none => .none
|
||||
-- Position the closing `}` at the end of the trailing whitespace of `where $[$_:letDecl];*`.
|
||||
-- We need an accurate range of the generated structure instance in the generated `TermInfo`
|
||||
-- so that we can determine the expected type in structure field completion.
|
||||
let structureStxTailInfo :=
|
||||
whereStx[1].getTailInfo?
|
||||
<|> whereStx[0].getTailInfo?
|
||||
let endOfStructureTkInfo : SourceInfo :=
|
||||
match structureStxTailInfo with
|
||||
| some (SourceInfo.original _ _ trailing _) =>
|
||||
let tokenPos := trailing.str.prev trailing.stopPos
|
||||
let tokenEndPos := trailing.stopPos
|
||||
.synthetic tokenPos tokenEndPos true
|
||||
| _ => .none
|
||||
let startOfStructureTkInfo : SourceInfo :=
|
||||
match whereTk.getPos? with
|
||||
| some pos => .synthetic pos ⟨pos.byteIdx + 1⟩ true
|
||||
| none => .none
|
||||
-- Position the closing `}` at the end of the trailing whitespace of `where $[$_:letDecl];*`.
|
||||
-- We need an accurate range of the generated structure instance in the generated `TermInfo`
|
||||
-- so that we can determine the expected type in structure field completion.
|
||||
let structureStxTailInfo :=
|
||||
whereStx[1].getTailInfo?
|
||||
<|> whereStx[0].getTailInfo?
|
||||
let endOfStructureTkInfo : SourceInfo :=
|
||||
match structureStxTailInfo with
|
||||
| some (SourceInfo.original _ _ trailing _) =>
|
||||
let tokenPos := trailing.str.prev trailing.stopPos
|
||||
let tokenEndPos := trailing.stopPos
|
||||
.synthetic tokenPos tokenEndPos true
|
||||
| _ => .none
|
||||
|
||||
let body ← `(structInst| { $structInstFields,* })
|
||||
let body := body.raw.setInfo <|
|
||||
match startOfStructureTkInfo.getPos?, endOfStructureTkInfo.getTailPos? with
|
||||
| some startPos, some endPos => .synthetic startPos endPos true
|
||||
| _, _ => .none
|
||||
match whereDecls? with
|
||||
| some whereDecls => expandWhereDecls whereDecls body
|
||||
| none => return body
|
||||
| _ => Macro.throwUnsupported
|
||||
let body ← `(structInst| { $structInstFields,* })
|
||||
let body := body.raw.setInfo <|
|
||||
match startOfStructureTkInfo.getPos?, endOfStructureTkInfo.getTailPos? with
|
||||
| some startPos, some endPos => .synthetic startPos endPos true
|
||||
| _, _ => .none
|
||||
match whereDecls? with
|
||||
| some whereDecls => expandWhereDecls whereDecls body
|
||||
| none => return body
|
||||
|
||||
/-
|
||||
Recall that
|
||||
@@ -415,6 +399,20 @@ register_builtin_option linter.unusedSectionVars : Bool := {
|
||||
descr := "enable the 'unused section variables in theorem body' linter"
|
||||
}
|
||||
|
||||
register_builtin_option debug.proofAsSorry : Bool := {
|
||||
defValue := false
|
||||
group := "debug"
|
||||
descr := "replace the bodies (proofs) of theorems with `sorry`"
|
||||
}
|
||||
|
||||
/-- Returns true if `k` is a theorem, option `debug.proofAsSorry` is set to true, and the environment contains the axiom `sorryAx`. -/
|
||||
private def useProofAsSorry (k : DefKind) : CoreM Bool := do
|
||||
if k.isTheorem then
|
||||
if debug.proofAsSorry.get (← getOptions) then
|
||||
if (← getEnv).contains ``sorryAx then
|
||||
return true
|
||||
return false
|
||||
|
||||
private def elabFunValues (headers : Array DefViewElabHeader) (vars : Array Expr) (sc : Command.Scope) : TermElabM (Array Expr) :=
|
||||
headers.mapM fun header => do
|
||||
let mut reusableResult? := none
|
||||
@@ -436,7 +434,9 @@ private def elabFunValues (headers : Array DefViewElabHeader) (vars : Array Expr
|
||||
for h : i in [0:header.binderIds.size] do
|
||||
-- skip auto-bound prefix in `xs`
|
||||
addLocalVarInfo header.binderIds[i] xs[header.numParams - header.binderIds.size + i]!
|
||||
let val ← withReader ({ · with tacSnap? := header.tacSnap? }) do
|
||||
let val ← if (← useProofAsSorry header.kind) then
|
||||
mkSorry type false
|
||||
else withReader ({ · with tacSnap? := header.tacSnap? }) do
|
||||
-- Store instantiated body in info tree for the benefit of the unused variables linter
|
||||
-- and other metaprograms that may want to inspect it without paying for the instantiation
|
||||
-- again
|
||||
|
||||
@@ -265,7 +265,7 @@ partial def collect (stx : Syntax) : M Syntax := withRef stx <| withFreshMacroSc
|
||||
| `(Parser.Term.structInstField| $lval:structInstLVal := $val) => do
|
||||
let newVal ← collect val
|
||||
`(Parser.Term.structInstField| $lval:structInstLVal := $newVal)
|
||||
| _ => throwInvalidPattern -- `structInstFieldAbbrev` should be expanded at this point
|
||||
| _ => throwInvalidPattern -- `structInstField` should be expanded at this point
|
||||
`({ $[$srcs?,* with]? $fields,* $[..%$ell?]? $[: $ty?]? })
|
||||
| _ => throwInvalidPattern
|
||||
|
||||
|
||||
@@ -31,13 +31,32 @@ open Meta
|
||||
open TSyntax.Compat
|
||||
|
||||
/-!
|
||||
Recall that structure instances are of the form:
|
||||
```
|
||||
"{" >> optional (atomic (sepBy1 termParser ", " >> " with "))
|
||||
>> manyIndent (group ((structInstFieldAbbrev <|> structInstField) >> optional ", "))
|
||||
Recall that structure instances are (after removing parsing and pretty printing hints):
|
||||
|
||||
```lean
|
||||
def structInst := leading_parser
|
||||
"{ " >> optional (sepBy1 termParser ", " >> " with ")
|
||||
>> structInstFields (sepByIndent structInstField ", " (allowTrailingSep := true))
|
||||
>> optEllipsis
|
||||
>> optional (" : " >> termParser)
|
||||
>> " }"
|
||||
>> optional (" : " >> termParser) >> " }"
|
||||
|
||||
def structInstField := leading_parser
|
||||
structInstLVal >> optional (many structInstFieldBinder >> optType >> structInstFieldDecl)
|
||||
|
||||
@[builtin_structInstFieldDecl_parser]
|
||||
def structInstFieldDef := leading_parser
|
||||
" := " >> termParser
|
||||
|
||||
@[builtin_structInstFieldDecl_parser]
|
||||
def structInstFieldEqns := leading_parser
|
||||
matchAlts
|
||||
|
||||
def structInstWhereBody := leading_parser
|
||||
structInstFields (sepByIndent structInstField "; " (allowTrailingSep := true))
|
||||
|
||||
@[builtin_structInstFieldDecl_parser]
|
||||
def structInstFieldWhere := leading_parser
|
||||
"where" >> structInstWhereBody
|
||||
```
|
||||
-/
|
||||
|
||||
@@ -54,22 +73,57 @@ Structure instance notation makes use of the expected type.
|
||||
let stxNew := stx.setArg 4 mkNullNode
|
||||
`(($stxNew : $expected))
|
||||
|
||||
def mkStructInstField (lval : TSyntax ``Parser.Term.structInstLVal) (binders : TSyntaxArray ``Parser.Term.structInstFieldBinder)
|
||||
(type? : Option Term) (val : Term) : MacroM (TSyntax ``Parser.Term.structInstField) := do
|
||||
let mut val := val
|
||||
if let some type := type? then
|
||||
val ← `(($val : $type))
|
||||
if !binders.isEmpty then
|
||||
-- HACK: this produces invalid syntax, but the fun elaborator supports structInstFieldBinder as well
|
||||
val ← `(fun $binders* => $val)
|
||||
`(Parser.Term.structInstField| $lval := $val)
|
||||
|
||||
/--
|
||||
Expands field abbreviation notation.
|
||||
Example: `{ x, y := 0 }` expands to `{ x := x, y := 0 }`.
|
||||
Takes an arbitrary `structInstField` and expands it to be a `structInstFieldDef` without any binders or type ascription.
|
||||
-/
|
||||
@[builtin_macro Lean.Parser.Term.structInst] def expandStructInstFieldAbbrev : Macro
|
||||
| `({ $[$srcs,* with]? $fields,* $[..%$ell]? $[: $ty]? }) =>
|
||||
if fields.getElems.raw.any (·.getKind == ``Lean.Parser.Term.structInstFieldAbbrev) then do
|
||||
let fieldsNew ← fields.getElems.mapM fun
|
||||
| `(Parser.Term.structInstFieldAbbrev| $id:ident) =>
|
||||
`(Parser.Term.structInstField| $id:ident := $id:ident)
|
||||
| field => return field
|
||||
`({ $[$srcs,* with]? $fieldsNew,* $[..%$ell]? $[: $ty]? })
|
||||
else
|
||||
Macro.throwUnsupported
|
||||
private def expandStructInstField (stx : Syntax) : MacroM (Option Syntax) := withRef stx do
|
||||
match stx with
|
||||
| `(Parser.Term.structInstField| $_:structInstLVal := $_) =>
|
||||
-- Already expanded.
|
||||
return none
|
||||
| `(Parser.Term.structInstField| $lval:structInstLVal $[$binders]* $[: $ty?]? $decl:structInstFieldDecl) =>
|
||||
match decl with
|
||||
| `(Parser.Term.structInstFieldDef| := $val) =>
|
||||
mkStructInstField lval binders ty? val
|
||||
| `(Parser.Term.structInstFieldEqns| $alts:matchAlts) =>
|
||||
let val ← expandMatchAltsIntoMatch stx alts (useExplicit := false)
|
||||
mkStructInstField lval binders ty? val
|
||||
| _ => Macro.throwUnsupported
|
||||
| `(Parser.Term.structInstField| $lval:structInstLVal) =>
|
||||
-- Abbreviation
|
||||
match lval with
|
||||
| `(Parser.Term.structInstLVal| $id:ident) =>
|
||||
mkStructInstField lval #[] none id
|
||||
| _ =>
|
||||
Macro.throwErrorAt lval "unsupported structure instance field abbreviation, expecting identifier"
|
||||
| _ => Macro.throwUnsupported
|
||||
|
||||
/--
|
||||
Expands fields.
|
||||
* Abbrevations. Example: `{ x }` expands to `{ x := x }`.
|
||||
* Equations. Example: `{ f | 0 => 0 | n + 1 => n }` expands to `{ f := fun x => match x with | 0 => 0 | n + 1 => n }`.
|
||||
* Binders and types. Example: `{ f n : Nat := n + 1 }` expands to `{ f := fun n => (n + 1 : Nat) }`.
|
||||
-/
|
||||
@[builtin_macro Lean.Parser.Term.structInst] def expandStructInstFields : Macro | stx => do
|
||||
let structInstFields := stx[2]
|
||||
let fields := structInstFields[0].getSepArgs
|
||||
let fields? ← fields.mapM expandStructInstField
|
||||
if fields?.all (·.isNone) then
|
||||
Macro.throwUnsupported
|
||||
let fields := fields?.zipWith fields Option.getD
|
||||
let structInstFields := structInstFields.setArg 0 <| Syntax.mkSep fields (mkAtomFrom stx ", ")
|
||||
return stx.setArg 2 structInstFields
|
||||
|
||||
/--
|
||||
If `stx` is of the form `{ s₁, ..., sₙ with ... }` and `sᵢ` is not a local variable,
|
||||
expands into `let __src := sᵢ; { ..., __src, ... with ... }`.
|
||||
@@ -187,12 +241,13 @@ def structInstArrayRef := leading_parser "[" >> termParser >>"]"
|
||||
-/
|
||||
private def isModifyOp? (stx : Syntax) : TermElabM (Option Syntax) := do
|
||||
let s? ← stx[2][0].getSepArgs.foldlM (init := none) fun s? arg => do
|
||||
/- arg is of the form `structInstFieldAbbrev <|> structInstField` -/
|
||||
if arg.getKind == ``Lean.Parser.Term.structInstField then
|
||||
/- Remark: the syntax for `structInstField` is
|
||||
/- arg is of the form `structInstField`. It should be macro expanded at this point, but we make sure it's the case. -/
|
||||
if arg[1][2].getKind == ``Lean.Parser.Term.structInstFieldDef then
|
||||
/- Remark: the syntax for `structInstField` after macro expansion is
|
||||
```
|
||||
def structInstLVal := leading_parser (ident <|> numLit <|> structInstArrayRef) >> many (group ("." >> (ident <|> numLit)) <|> structInstArrayRef)
|
||||
def structInstField := leading_parser structInstLVal >> " := " >> termParser
|
||||
def structInstFieldDef := leading_parser
|
||||
structInstLVal >> group (null >> null >> group (" := " >> termParser))
|
||||
```
|
||||
-/
|
||||
let lval := arg[0]
|
||||
@@ -235,7 +290,7 @@ private def elabModifyOp (stx modifyOp : Syntax) (sources : Array ExplicitSource
|
||||
withMacroExpansion stx stxNew <| elabTerm stxNew expectedType?
|
||||
let rest := modifyOp[0][1]
|
||||
if rest.isNone then
|
||||
cont modifyOp[2]
|
||||
cont modifyOp[1][2][1]
|
||||
else
|
||||
let s ← `(s)
|
||||
let valFirst := rest[0]
|
||||
@@ -388,7 +443,7 @@ Converts a `FieldLHS` back into syntax. This assumes the `ref` fields have the c
|
||||
|
||||
Recall that `structInstField` elements have the form
|
||||
```lean
|
||||
def structInstField := leading_parser structInstLVal >> " := " >> termParser
|
||||
def structInstField := leading_parser structInstLVal >> group (null >> null >> group (" := " >> termParser))
|
||||
def structInstLVal := leading_parser (ident <|> numLit <|> structInstArrayRef) >> many (("." >> (ident <|> numLit)) <|> structInstArrayRef)
|
||||
def structInstArrayRef := leading_parser "[" >> termParser >>"]"
|
||||
```
|
||||
@@ -412,9 +467,9 @@ Converts a `Field StructInstView` back into syntax. Used to construct synthetic
|
||||
private def Field.toSyntax : Field → Syntax
|
||||
| field =>
|
||||
let stx := field.ref
|
||||
let stx := stx.setArg 2 field.val.toSyntax
|
||||
let stx := stx.setArg 1 <| stx[1].setArg 2 <| stx[1][2].setArg 1 field.val.toSyntax
|
||||
match field.lhs with
|
||||
| first::rest => stx.setArg 0 <| mkNullNode #[first.toSyntax true, mkNullNode <| rest.toArray.map (FieldLHS.toSyntax false) ]
|
||||
| first::rest => stx.setArg 0 <| mkNode ``Parser.Term.structInstLVal #[first.toSyntax true, mkNullNode <| rest.toArray.map (FieldLHS.toSyntax false) ]
|
||||
| _ => unreachable!
|
||||
|
||||
/-- Creates a view of a field left-hand side. -/
|
||||
@@ -428,7 +483,7 @@ private def toFieldLHS (stx : Syntax) : MacroM FieldLHS :=
|
||||
return FieldLHS.fieldName stx stx.getId.eraseMacroScopes
|
||||
else match stx.isFieldIdx? with
|
||||
| some idx => return FieldLHS.fieldIndex stx idx
|
||||
| none => Macro.throwError "unexpected structure syntax"
|
||||
| none => Macro.throwErrorAt stx "unexpected structure syntax"
|
||||
|
||||
/--
|
||||
Creates a structure instance view from structure instance notation
|
||||
@@ -436,21 +491,21 @@ and the computed structure name (from `Lean.Elab.Term.StructInst.getStructName`)
|
||||
and structure source view (from `Lean.Elab.Term.StructInst.getStructSources`).
|
||||
-/
|
||||
private def mkStructView (stx : Syntax) (structName : Name) (sources : SourcesView) : MacroM StructInstView := do
|
||||
/- Recall that `stx` is of the form
|
||||
```
|
||||
leading_parser "{" >> optional (atomic (sepBy1 termParser ", " >> " with "))
|
||||
>> structInstFields (sepByIndent (structInstFieldAbbrev <|> structInstField) ...)
|
||||
>> optional ".."
|
||||
>> optional (" : " >> termParser)
|
||||
>> " }"
|
||||
```
|
||||
|
||||
This method assumes that `structInstFieldAbbrev` had already been expanded.
|
||||
/-
|
||||
Recall that `stx` is of the form
|
||||
```
|
||||
leading_parser "{" >> optional (atomic (sepBy1 termParser ", " >> " with "))
|
||||
>> structInstFields (sepByIndent structInstField ...)
|
||||
>> optional ".."
|
||||
>> optional (" : " >> termParser)
|
||||
>> " }"
|
||||
```
|
||||
This method assumes that `structInstField` had already been expanded by the macro `expandStructInstFields`.
|
||||
-/
|
||||
let fields ← stx[2][0].getSepArgs.toList.mapM fun fieldStx => do
|
||||
let val := fieldStx[2]
|
||||
let first ← toFieldLHS fieldStx[0][0]
|
||||
let rest ← fieldStx[0][1].getArgs.toList.mapM toFieldLHS
|
||||
let `(Parser.Term.structInstField| $lval:structInstLVal := $val) := fieldStx | Macro.throwUnsupported
|
||||
let first ← toFieldLHS lval.raw[0]
|
||||
let rest ← lval.raw[1].getArgs.toList.mapM toFieldLHS
|
||||
return { ref := fieldStx, lhs := first :: rest, val := FieldVal.term val : Field }
|
||||
return { ref := stx, structName, params := #[], fields, sources }
|
||||
|
||||
@@ -596,7 +651,7 @@ mutual
|
||||
let updateSource (structStx : Syntax) : TermElabM Syntax := do
|
||||
let sourcesNew ← s.sources.explicit.filterMapM fun source => mkProjStx? source.stx source.structName fieldName
|
||||
let explicitSourceStx := if sourcesNew.isEmpty then mkNullNode else mkSourcesWithSyntax sourcesNew
|
||||
let implicitSourceStx := s.sources.implicit.getD mkNullNode
|
||||
let implicitSourceStx := s.sources.implicit.getD (mkNode ``Parser.Term.optEllipsis #[mkNullNode])
|
||||
return (structStx.setArg 1 explicitSourceStx).setArg 3 implicitSourceStx
|
||||
let valStx := s.ref -- construct substructure syntax using s.ref as template
|
||||
let valStx := valStx.setArg 4 mkNullNode -- erase optional expected type
|
||||
|
||||
@@ -8,6 +8,7 @@ import Std.Data.HashMap
|
||||
import Std.Tactic.BVDecide.Bitblast.BVExpr.Basic
|
||||
import Lean.Meta.AppBuilder
|
||||
import Lean.ToExpr
|
||||
import Lean.Data.RArray
|
||||
|
||||
/-!
|
||||
This module contains the implementation of the reflection monad, used by all other components of this
|
||||
@@ -138,9 +139,11 @@ structure State where
|
||||
-/
|
||||
atoms : Std.HashMap Expr Atom := {}
|
||||
/--
|
||||
A cache for `atomsAssignment`.
|
||||
A cache for `atomsAssignment`. We maintain the invariant that this value is only used if
|
||||
`atoms` is non empty. The reason for not using an `Option` is that it would pollute a lot of code
|
||||
with error handling that is never hit as this invariant is enforced before all of this code.
|
||||
-/
|
||||
atomsAssignmentCache : Expr := mkConst ``List.nil [.zero]
|
||||
atomsAssignmentCache : Expr := mkConst `illegal
|
||||
|
||||
/--
|
||||
The reflection monad, used to track `BitVec` variables that we see as we traverse the context.
|
||||
@@ -157,9 +160,9 @@ structure ReifiedBVExpr where
|
||||
-/
|
||||
bvExpr : BVExpr width
|
||||
/--
|
||||
A proof that `bvExpr.eval atomsAssignment = originalBVExpr`.
|
||||
A proof that `bvExpr.eval atomsAssignment = originalBVExpr`, none if it holds by `rfl`.
|
||||
-/
|
||||
evalsAtAtoms : M Expr
|
||||
evalsAtAtoms : M (Option Expr)
|
||||
/--
|
||||
A cache for `toExpr bvExpr`.
|
||||
-/
|
||||
@@ -174,9 +177,9 @@ structure ReifiedBVPred where
|
||||
-/
|
||||
bvPred : BVPred
|
||||
/--
|
||||
A proof that `bvPred.eval atomsAssignment = originalBVPredExpr`.
|
||||
A proof that `bvPred.eval atomsAssignment = originalBVPredExpr`, none if it holds by `rfl`.
|
||||
-/
|
||||
evalsAtAtoms : M Expr
|
||||
evalsAtAtoms : M (Option Expr)
|
||||
/--
|
||||
A cache for `toExpr bvPred`
|
||||
-/
|
||||
@@ -191,9 +194,9 @@ structure ReifiedBVLogical where
|
||||
-/
|
||||
bvExpr : BVLogicalExpr
|
||||
/--
|
||||
A proof that `bvExpr.eval atomsAssignment = originalBVLogicalExpr`.
|
||||
A proof that `bvExpr.eval atomsAssignment = originalBVLogicalExpr`, none if it holds by `rfl`.
|
||||
-/
|
||||
evalsAtAtoms : M Expr
|
||||
evalsAtAtoms : M (Option Expr)
|
||||
/--
|
||||
A cache for `toExpr bvExpr`
|
||||
-/
|
||||
@@ -228,9 +231,9 @@ def run (m : M α) : MetaM α :=
|
||||
/--
|
||||
Retrieve the atoms as pairs of their width and expression.
|
||||
-/
|
||||
def atoms : M (List (Nat × Expr)) := do
|
||||
def atoms : M (Array (Nat × Expr)) := do
|
||||
let sortedAtoms := (← getThe State).atoms.toArray.qsort (·.2.atomNumber < ·.2.atomNumber)
|
||||
return sortedAtoms.map (fun (expr, {width, ..}) => (width, expr)) |>.toList
|
||||
return sortedAtoms.map (fun (expr, {width, ..}) => (width, expr))
|
||||
|
||||
/--
|
||||
Retrieve a `BitVec.Assignment` representing the atoms we found so far.
|
||||
@@ -257,11 +260,37 @@ def lookup (e : Expr) (width : Nat) (synthetic : Bool) : M Nat := do
|
||||
where
|
||||
updateAtomsAssignment : M Unit := do
|
||||
let as ← atoms
|
||||
let packed :=
|
||||
as.map (fun (width, expr) => mkApp2 (mkConst ``BVExpr.PackedBitVec.mk) (toExpr width) expr)
|
||||
let packedType := mkConst ``BVExpr.PackedBitVec
|
||||
let newAtomsAssignment ← mkListLit packedType packed
|
||||
modify fun s => { s with atomsAssignmentCache := newAtomsAssignment }
|
||||
if h : 0 < as.size then
|
||||
let ras := Lean.RArray.ofArray as h
|
||||
let packedType := mkConst ``BVExpr.PackedBitVec
|
||||
let pack := fun (width, expr) => mkApp2 (mkConst ``BVExpr.PackedBitVec.mk) (toExpr width) expr
|
||||
let newAtomsAssignment := ras.toExpr packedType pack
|
||||
modify fun s => { s with atomsAssignmentCache := newAtomsAssignment }
|
||||
else
|
||||
throwError "updateAtomsAssignment should only be called when there is an atom"
|
||||
|
||||
@[specialize]
|
||||
def simplifyBinaryProof' (mkFRefl : Expr → Expr) (fst : Expr) (fproof : Option Expr)
|
||||
(mkSRefl : Expr → Expr) (snd : Expr) (sproof : Option Expr) : Option (Expr × Expr) := do
|
||||
match fproof, sproof with
|
||||
| some fproof, some sproof => some (fproof, sproof)
|
||||
| some fproof, none => some (fproof, mkSRefl snd)
|
||||
| none, some sproof => some (mkFRefl fst, sproof)
|
||||
| none, none => none
|
||||
|
||||
@[specialize]
|
||||
def simplifyBinaryProof (mkRefl : Expr → Expr) (fst : Expr) (fproof : Option Expr) (snd : Expr)
|
||||
(sproof : Option Expr) : Option (Expr × Expr) := do
|
||||
simplifyBinaryProof' mkRefl fst fproof mkRefl snd sproof
|
||||
|
||||
@[specialize]
|
||||
def simplifyTernaryProof (mkRefl : Expr → Expr) (fst : Expr) (fproof : Option Expr) (snd : Expr)
|
||||
(sproof : Option Expr) (thd : Expr) (tproof : Option Expr) : Option (Expr × Expr × Expr) := do
|
||||
match fproof, simplifyBinaryProof mkRefl snd sproof thd tproof with
|
||||
| some fproof, some stproof => some (fproof, stproof)
|
||||
| some fproof, none => some (fproof, mkRefl snd, mkRefl thd)
|
||||
| none, some stproof => some (mkRefl fst, stproof)
|
||||
| none, none => none
|
||||
|
||||
end M
|
||||
|
||||
|
||||
@@ -37,9 +37,8 @@ Register `e` as an atom of `width` that might potentially be `synthetic`.
|
||||
def mkAtom (e : Expr) (width : Nat) (synthetic : Bool) : M ReifiedBVExpr := do
|
||||
let ident ← M.lookup e width synthetic
|
||||
let expr := mkApp2 (mkConst ``BVExpr.var) (toExpr width) (toExpr ident)
|
||||
let proof := do
|
||||
let evalExpr ← mkEvalExpr width expr
|
||||
return mkBVRefl width evalExpr
|
||||
-- This is safe because this proof always holds definitionally.
|
||||
let proof := pure none
|
||||
return ⟨width, .var ident, proof, expr⟩
|
||||
|
||||
/--
|
||||
@@ -70,9 +69,8 @@ Build a reified version of the constant `val`.
|
||||
def mkBVConst (val : BitVec w) : M ReifiedBVExpr := do
|
||||
let bvExpr : BVExpr w := .const val
|
||||
let expr := mkApp2 (mkConst ``BVExpr.const) (toExpr w) (toExpr val)
|
||||
let proof := do
|
||||
let evalExpr ← ReifiedBVExpr.mkEvalExpr w expr
|
||||
return ReifiedBVExpr.mkBVRefl w evalExpr
|
||||
-- This is safe because this proof always holds definitionally.
|
||||
let proof := pure none
|
||||
return ⟨w, bvExpr, proof, expr⟩
|
||||
|
||||
end ReifiedBVExpr
|
||||
|
||||
@@ -49,7 +49,8 @@ Build a reified version of the constant `val`.
|
||||
def mkBoolConst (val : Bool) : M ReifiedBVLogical := do
|
||||
let boolExpr := .const val
|
||||
let expr := mkApp2 (mkConst ``BoolExpr.const) (mkConst ``BVPred) (toExpr val)
|
||||
let proof := pure <| ReifiedBVLogical.mkRefl (toExpr val)
|
||||
-- This is safe because this proof always holds definitionally.
|
||||
let proof := pure none
|
||||
return ⟨boolExpr, proof, expr⟩
|
||||
|
||||
/--
|
||||
@@ -71,8 +72,13 @@ def mkGate (lhs rhs : ReifiedBVLogical) (lhsExpr rhsExpr : Expr) (gate : Gate) :
|
||||
let proof := do
|
||||
let lhsEvalExpr ← ReifiedBVLogical.mkEvalExpr lhs.expr
|
||||
let rhsEvalExpr ← ReifiedBVLogical.mkEvalExpr rhs.expr
|
||||
let lhsProof ← lhs.evalsAtAtoms
|
||||
let rhsProof ← rhs.evalsAtAtoms
|
||||
let lhsProof? ← lhs.evalsAtAtoms
|
||||
let rhsProof? ← rhs.evalsAtAtoms
|
||||
let some (lhsProof, rhsProof) :=
|
||||
M.simplifyBinaryProof
|
||||
ReifiedBVLogical.mkRefl
|
||||
lhsEvalExpr lhsProof?
|
||||
rhsEvalExpr rhsProof? | return none
|
||||
return mkApp6
|
||||
(mkConst congrThm)
|
||||
lhsExpr rhsExpr
|
||||
@@ -95,8 +101,9 @@ def mkNot (sub : ReifiedBVLogical) (subExpr : Expr) : M ReifiedBVLogical := do
|
||||
let boolExpr := .not sub.bvExpr
|
||||
let expr := mkApp2 (mkConst ``BoolExpr.not) (mkConst ``BVPred) sub.expr
|
||||
let proof := do
|
||||
-- This is safe as `not_congr` holds definitionally if the arguments are defeq.
|
||||
let some subProof ← sub.evalsAtAtoms | return none
|
||||
let subEvalExpr ← ReifiedBVLogical.mkEvalExpr sub.expr
|
||||
let subProof ← sub.evalsAtAtoms
|
||||
return mkApp3 (mkConst ``Std.Tactic.BVDecide.Reflect.Bool.not_congr) subExpr subEvalExpr subProof
|
||||
return ⟨boolExpr, proof, expr⟩
|
||||
|
||||
@@ -119,9 +126,15 @@ def mkIte (discr lhs rhs : ReifiedBVLogical) (discrExpr lhsExpr rhsExpr : Expr)
|
||||
let discrEvalExpr ← ReifiedBVLogical.mkEvalExpr discr.expr
|
||||
let lhsEvalExpr ← ReifiedBVLogical.mkEvalExpr lhs.expr
|
||||
let rhsEvalExpr ← ReifiedBVLogical.mkEvalExpr rhs.expr
|
||||
let discrProof ← discr.evalsAtAtoms
|
||||
let lhsProof ← lhs.evalsAtAtoms
|
||||
let rhsProof ← rhs.evalsAtAtoms
|
||||
let discrProof? ← discr.evalsAtAtoms
|
||||
let lhsProof? ← lhs.evalsAtAtoms
|
||||
let rhsProof? ← rhs.evalsAtAtoms
|
||||
let some (discrProof, lhsProof, rhsProof) :=
|
||||
M.simplifyTernaryProof
|
||||
ReifiedBVLogical.mkRefl
|
||||
discrEvalExpr discrProof?
|
||||
lhsEvalExpr lhsProof?
|
||||
rhsEvalExpr rhsProof? | return none
|
||||
return mkApp9
|
||||
(mkConst ``Std.Tactic.BVDecide.Reflect.Bool.ite_congr)
|
||||
discrExpr lhsExpr rhsExpr
|
||||
|
||||
@@ -35,8 +35,10 @@ def boolAtom (t : Expr) : M (Option ReifiedBVPred) := do
|
||||
let bvExpr : BVPred := .getLsbD atom.bvExpr 0
|
||||
let expr := mkApp3 (mkConst ``BVPred.getLsbD) (toExpr 1) atom.expr (toExpr 0)
|
||||
let proof := do
|
||||
-- ofBool_congr does not hold definitionally, if this ever becomes an issue we need to find
|
||||
-- a more clever encoding for boolean atoms
|
||||
let atomEval ← ReifiedBVExpr.mkEvalExpr atom.width atom.expr
|
||||
let atomProof ← atom.evalsAtAtoms
|
||||
let atomProof := (← atom.evalsAtAtoms).getD (ReifiedBVExpr.mkBVRefl atom.width atomEval)
|
||||
return mkApp3
|
||||
(mkConst ``Std.Tactic.BVDecide.Reflect.BitVec.ofBool_congr)
|
||||
t
|
||||
@@ -63,9 +65,14 @@ def mkBinPred (lhs rhs : ReifiedBVExpr) (lhsExpr rhsExpr : Expr) (pred : BVBinPr
|
||||
rhs.expr
|
||||
let proof := do
|
||||
let lhsEval ← ReifiedBVExpr.mkEvalExpr lhs.width lhs.expr
|
||||
let lhsProof ← lhs.evalsAtAtoms
|
||||
let rhsEval ← ReifiedBVExpr.mkEvalExpr rhs.width rhs.expr
|
||||
let rhsProof ← rhs.evalsAtAtoms
|
||||
let lhsProof? ← lhs.evalsAtAtoms
|
||||
let rhsProof? ← rhs.evalsAtAtoms
|
||||
let some (lhsProof, rhsProof) :=
|
||||
M.simplifyBinaryProof
|
||||
(ReifiedBVExpr.mkBVRefl lhs.width)
|
||||
lhsEval lhsProof?
|
||||
rhsEval rhsProof? | return none
|
||||
return mkApp7
|
||||
(mkConst congrThm)
|
||||
(toExpr lhs.width)
|
||||
@@ -90,8 +97,9 @@ def mkGetLsbD (sub : ReifiedBVExpr) (subExpr : Expr) (idx : Nat) : M ReifiedBVPr
|
||||
let idxExpr := toExpr idx
|
||||
let expr := mkApp3 (mkConst ``BVPred.getLsbD) (toExpr sub.width) sub.expr idxExpr
|
||||
let proof := do
|
||||
-- This is safe as `getLsbD_congr` holds definitionally if the arguments are defeq.
|
||||
let some subProof ← sub.evalsAtAtoms | return none
|
||||
let subEval ← ReifiedBVExpr.mkEvalExpr sub.width sub.expr
|
||||
let subProof ← sub.evalsAtAtoms
|
||||
return mkApp5
|
||||
(mkConst ``Std.Tactic.BVDecide.Reflect.BitVec.getLsbD_congr)
|
||||
idxExpr
|
||||
|
||||
@@ -62,7 +62,7 @@ where
|
||||
|
||||
let proof := do
|
||||
let evalExpr ← ReifiedBVLogical.mkEvalExpr imp.expr
|
||||
let congrProof ← imp.evalsAtAtoms
|
||||
let congrProof := (← imp.evalsAtAtoms).getD (ReifiedBVLogical.mkRefl evalExpr)
|
||||
let lemmaProof := mkApp4 (mkConst lemmaName) (toExpr lhs.width) discrExpr lhsExpr rhsExpr
|
||||
|
||||
let trueExpr := mkConst ``Bool.true
|
||||
|
||||
@@ -112,7 +112,8 @@ where
|
||||
inner.expr
|
||||
let proof := do
|
||||
let innerEval ← ReifiedBVExpr.mkEvalExpr inner.width inner.expr
|
||||
let innerProof ← inner.evalsAtAtoms
|
||||
-- This is safe as `zeroExtend_congr` holds definitionally if the arguments are defeq.
|
||||
let some innerProof ← inner.evalsAtAtoms | return none
|
||||
return mkApp5 (mkConst ``Std.Tactic.BVDecide.Reflect.BitVec.zeroExtend_congr)
|
||||
newWidthExpr
|
||||
(toExpr inner.width)
|
||||
@@ -132,7 +133,8 @@ where
|
||||
inner.expr
|
||||
let proof := do
|
||||
let innerEval ← ReifiedBVExpr.mkEvalExpr inner.width inner.expr
|
||||
let innerProof ← inner.evalsAtAtoms
|
||||
-- This is safe as `zeroExtend_congr` holds definitionally if the arguments are defeq.
|
||||
let some innerProof ← inner.evalsAtAtoms | return none
|
||||
return mkApp5 (mkConst ``Std.Tactic.BVDecide.Reflect.BitVec.signExtend_congr)
|
||||
newWidthExpr
|
||||
(toExpr inner.width)
|
||||
@@ -150,9 +152,13 @@ where
|
||||
lhs.expr rhs.expr
|
||||
let proof := do
|
||||
let lhsEval ← ReifiedBVExpr.mkEvalExpr lhs.width lhs.expr
|
||||
let lhsProof ← lhs.evalsAtAtoms
|
||||
let rhsProof ← rhs.evalsAtAtoms
|
||||
let rhsEval ← ReifiedBVExpr.mkEvalExpr rhs.width rhs.expr
|
||||
let lhsProof? ← lhs.evalsAtAtoms
|
||||
let rhsProof? ← rhs.evalsAtAtoms
|
||||
let some (lhsProof, rhsProof) :=
|
||||
M.simplifyBinaryProof'
|
||||
(ReifiedBVExpr.mkBVRefl lhs.width) lhsEval lhsProof?
|
||||
(ReifiedBVExpr.mkBVRefl rhs.width) rhsEval rhsProof? | return none
|
||||
return mkApp8 (mkConst ``Std.Tactic.BVDecide.Reflect.BitVec.append_congr)
|
||||
(toExpr lhs.width) (toExpr rhs.width)
|
||||
lhsExpr lhsEval
|
||||
@@ -169,7 +175,8 @@ where
|
||||
inner.expr
|
||||
let proof := do
|
||||
let innerEval ← ReifiedBVExpr.mkEvalExpr inner.width inner.expr
|
||||
let innerProof ← inner.evalsAtAtoms
|
||||
-- This is safe as `zeroExtend_congr` holds definitionally if the arguments are defeq.
|
||||
let some innerProof ← inner.evalsAtAtoms | return none
|
||||
return mkApp5 (mkConst ``Std.Tactic.BVDecide.Reflect.BitVec.replicate_congr)
|
||||
(toExpr n)
|
||||
(toExpr inner.width)
|
||||
@@ -189,7 +196,8 @@ where
|
||||
inner.expr
|
||||
let proof := do
|
||||
let innerEval ← ReifiedBVExpr.mkEvalExpr inner.width inner.expr
|
||||
let innerProof ← inner.evalsAtAtoms
|
||||
-- This is safe as `zeroExtend_congr` holds definitionally if the arguments are defeq.
|
||||
let some innerProof ← inner.evalsAtAtoms | return none
|
||||
return mkApp6 (mkConst ``Std.Tactic.BVDecide.Reflect.BitVec.extract_congr)
|
||||
startExpr
|
||||
lenExpr
|
||||
@@ -301,11 +309,16 @@ where
|
||||
return none
|
||||
|
||||
binaryCongrProof (lhs rhs : ReifiedBVExpr) (lhsExpr rhsExpr : Expr) (congrThm : Expr) :
|
||||
M Expr := do
|
||||
M (Option Expr) := do
|
||||
let lhsEval ← ReifiedBVExpr.mkEvalExpr lhs.width lhs.expr
|
||||
let lhsProof ← lhs.evalsAtAtoms
|
||||
let rhsProof ← rhs.evalsAtAtoms
|
||||
let rhsEval ← ReifiedBVExpr.mkEvalExpr rhs.width rhs.expr
|
||||
let lhsProof? ← lhs.evalsAtAtoms
|
||||
let rhsProof? ← rhs.evalsAtAtoms
|
||||
let some (lhsProof, rhsProof) :=
|
||||
M.simplifyBinaryProof
|
||||
(ReifiedBVExpr.mkBVRefl lhs.width)
|
||||
lhsEval lhsProof?
|
||||
rhsEval rhsProof? | return none
|
||||
return mkApp6 congrThm lhsExpr rhsExpr lhsEval rhsEval lhsProof rhsProof
|
||||
|
||||
unaryReflection (innerExpr : Expr) (op : BVUnOp) (congrThm : Name) :
|
||||
@@ -316,9 +329,9 @@ where
|
||||
let proof := unaryCongrProof inner innerExpr (mkConst congrThm)
|
||||
return some ⟨inner.width, bvExpr, proof, expr⟩
|
||||
|
||||
unaryCongrProof (inner : ReifiedBVExpr) (innerExpr : Expr) (congrProof : Expr) : M Expr := do
|
||||
unaryCongrProof (inner : ReifiedBVExpr) (innerExpr : Expr) (congrProof : Expr) : M (Option Expr) := do
|
||||
let innerEval ← ReifiedBVExpr.mkEvalExpr inner.width inner.expr
|
||||
let innerProof ← inner.evalsAtAtoms
|
||||
let some innerProof ← inner.evalsAtAtoms | return none
|
||||
return mkApp4 congrProof (toExpr inner.width) innerExpr innerEval innerProof
|
||||
|
||||
goBvLit (x : Expr) : M (Option ReifiedBVExpr) := do
|
||||
|
||||
@@ -37,7 +37,7 @@ partial def of (h : Expr) : LemmaM (Option SatAtBVLogical) := do
|
||||
let proof := do
|
||||
let evalLogic ← ReifiedBVLogical.mkEvalExpr bvLogical.expr
|
||||
-- this is evalLogic = lhsExpr
|
||||
let evalProof ← bvLogical.evalsAtAtoms
|
||||
let evalProof := (← bvLogical.evalsAtAtoms).getD (ReifiedBVLogical.mkRefl evalLogic)
|
||||
-- h is lhsExpr = true
|
||||
-- we prove evalLogic = true by evalLogic = lhsExpr = true
|
||||
return ReifiedBVLogical.mkTrans evalLogic lhsExpr (mkConst ``Bool.true) evalProof h
|
||||
@@ -61,13 +61,16 @@ def and (x y : SatAtBVLogical) : SatAtBVLogical where
|
||||
|
||||
/-- Given a proof that `x.expr.Unsat`, produce a proof of `False`. -/
|
||||
def proveFalse (x : SatAtBVLogical) (h : Expr) : M Expr := do
|
||||
let atomsList ← M.atomsAssignment
|
||||
let evalExpr := mkApp2 (mkConst ``BVLogicalExpr.eval) atomsList x.expr
|
||||
return mkApp3
|
||||
(mkConst ``Std.Tactic.BVDecide.Reflect.Bool.false_of_eq_true_of_eq_false)
|
||||
evalExpr
|
||||
(← x.satAtAtoms)
|
||||
(.app h atomsList)
|
||||
if (← get).atoms.isEmpty then
|
||||
throwError "Unable to identify any relevant atoms."
|
||||
else
|
||||
let atomsList ← M.atomsAssignment
|
||||
let evalExpr := mkApp2 (mkConst ``BVLogicalExpr.eval) atomsList x.expr
|
||||
return mkApp3
|
||||
(mkConst ``Std.Tactic.BVDecide.Reflect.Bool.false_of_eq_true_of_eq_false)
|
||||
evalExpr
|
||||
(← x.satAtAtoms)
|
||||
(.app h atomsList)
|
||||
|
||||
|
||||
end SatAtBVLogical
|
||||
|
||||
@@ -168,20 +168,16 @@ def numeralToCoe (e : Expr) : MetaM Simp.Result := do
|
||||
let some pr ← proveEqUsingDown e newE | failure
|
||||
return pr
|
||||
|
||||
declare_config_elab elabNormCastConfig NormCastConfig
|
||||
|
||||
/--
|
||||
The core simplification routine of `normCast`.
|
||||
-/
|
||||
def derive (e : Expr) : MetaM Simp.Result := do
|
||||
def derive (e : Expr) (config : NormCastConfig := {}) : MetaM Simp.Result := do
|
||||
withTraceNode `Tactic.norm_cast (fun _ => return m!"{e}") do
|
||||
let e ← instantiateMVars e
|
||||
|
||||
let config : Simp.Config := {
|
||||
zeta := false
|
||||
beta := false
|
||||
eta := false
|
||||
proj := false
|
||||
iota := false
|
||||
}
|
||||
let config := config.toConfig
|
||||
let congrTheorems ← Meta.getSimpCongrTheorems
|
||||
|
||||
let r : Simp.Result := { expr := e }
|
||||
@@ -193,13 +189,13 @@ def derive (e : Expr) : MetaM Simp.Result := do
|
||||
-- step 1: pre-processing of numerals
|
||||
let r ← withTrace "pre-processing numerals" do
|
||||
let post e := return Simp.Step.done (← try numeralToCoe e catch _ => pure {expr := e})
|
||||
let ctx ← Simp.mkContext (config := config) (congrTheorems := congrTheorems)
|
||||
let ctx ← Simp.mkContext config (congrTheorems := congrTheorems)
|
||||
r.mkEqTrans (← Simp.main r.expr ctx (methods := { post })).1
|
||||
|
||||
-- step 2: casts are moved upwards and eliminated
|
||||
let r ← withTrace "moving upward, splitting and eliminating" do
|
||||
let post := upwardAndElim (← normCastExt.up.getTheorems)
|
||||
let ctx ← Simp.mkContext (config := config) (congrTheorems := congrTheorems)
|
||||
let ctx ← Simp.mkContext config (congrTheorems := congrTheorems)
|
||||
r.mkEqTrans (← Simp.main r.expr ctx (methods := { post })).1
|
||||
|
||||
let simprocs ← ({} : Simp.SimprocsArray).add `reduceCtorEq false
|
||||
@@ -234,32 +230,33 @@ open Term
|
||||
| _ => throwUnsupportedSyntax
|
||||
|
||||
/-- Implementation of the `norm_cast` tactic when operating on the main goal. -/
|
||||
def normCastTarget : TacticM Unit :=
|
||||
def normCastTarget (cfg : NormCastConfig) : TacticM Unit :=
|
||||
liftMetaTactic1 fun goal => do
|
||||
let tgt ← instantiateMVars (← goal.getType)
|
||||
let prf ← derive tgt
|
||||
let prf ← derive tgt cfg
|
||||
applySimpResultToTarget goal tgt prf
|
||||
|
||||
/-- Implementation of the `norm_cast` tactic when operating on a hypothesis. -/
|
||||
def normCastHyp (fvarId : FVarId) : TacticM Unit :=
|
||||
def normCastHyp (cfg : NormCastConfig) (fvarId : FVarId) : TacticM Unit :=
|
||||
liftMetaTactic1 fun goal => do
|
||||
let hyp ← instantiateMVars (← fvarId.getDecl).type
|
||||
let prf ← derive hyp
|
||||
let prf ← derive hyp cfg
|
||||
return (← applySimpResultToLocalDecl goal fvarId prf false).map (·.snd)
|
||||
|
||||
@[builtin_tactic normCast0]
|
||||
def evalNormCast0 : Tactic := fun stx => do
|
||||
match stx with
|
||||
| `(tactic| norm_cast0 $[$loc?]?) =>
|
||||
| `(tactic| norm_cast0 $cfg $[$loc?]?) =>
|
||||
let loc := if let some loc := loc? then expandLocation loc else Location.targets #[] true
|
||||
let cfg ← elabNormCastConfig cfg
|
||||
withMainContext do
|
||||
match loc with
|
||||
| Location.targets hyps target =>
|
||||
if target then normCastTarget
|
||||
(← getFVarIds hyps).forM normCastHyp
|
||||
if target then (normCastTarget cfg)
|
||||
(← getFVarIds hyps).forM (normCastHyp cfg)
|
||||
| Location.wildcard =>
|
||||
normCastTarget
|
||||
(← (← getMainGoal).getNondepPropHyps).forM normCastHyp
|
||||
normCastTarget cfg
|
||||
(← (← getMainGoal).getNondepPropHyps).forM (normCastHyp cfg)
|
||||
| _ => throwUnsupportedSyntax
|
||||
|
||||
@[builtin_tactic Lean.Parser.Tactic.Conv.normCast]
|
||||
|
||||
@@ -228,6 +228,7 @@ partial def asLinearComboImpl (e : Expr) : OmegaM (LinearCombo × OmegaM Expr ×
|
||||
| .app (.app (.app (.app (.const ``Prod.mk [u, v]) _) _) x) y =>
|
||||
rewrite e (mkApp4 (.const ``Prod.snd_mk [u, v]) α x β y)
|
||||
| _ => mkAtomLinearCombo e
|
||||
| (``Int.negSucc, #[n]) => rewrite e (mkApp (.const ``Int.negSucc_eq []) n)
|
||||
| _ => mkAtomLinearCombo e
|
||||
where
|
||||
/--
|
||||
|
||||
@@ -897,13 +897,18 @@ def finalizeImport (s : ImportState) (imports : Array Import) (opts : Options) (
|
||||
initialized constant. We have seen significant savings in `open Mathlib`
|
||||
timings, where we have both a big environment and interpreted environment
|
||||
extensions, from this. There is no significant extra cost to calling
|
||||
`markPersistent` multiple times like this. -/
|
||||
env := Runtime.markPersistent env
|
||||
`markPersistent` multiple times like this.
|
||||
|
||||
Safety: There are no concurrent accesses to `env` at this point. -/
|
||||
env ← unsafe Runtime.markPersistent env
|
||||
env ← finalizePersistentExtensions env s.moduleData opts
|
||||
if leakEnv then
|
||||
/- Ensure the final environment including environment extension states is
|
||||
marked persistent as documented. -/
|
||||
env := Runtime.markPersistent env
|
||||
marked persistent as documented.
|
||||
|
||||
Safety: There are no concurrent accesses to `env` at this point, assuming
|
||||
extensions' `addImportFn`s did not spawn any unbound tasks. -/
|
||||
env ← unsafe Runtime.markPersistent env
|
||||
pure env
|
||||
|
||||
@[export lean_import_modules]
|
||||
|
||||
@@ -1366,7 +1366,11 @@ See also `Lean.Expr.instantiateRange`, which instantiates with the "backwards" i
|
||||
@[extern "lean_expr_instantiate_rev_range"]
|
||||
opaque instantiateRevRange (e : @& Expr) (beginIdx endIdx : @& Nat) (subst : @& Array Expr) : Expr
|
||||
|
||||
/-- Replace free (or meta) variables `xs` with loose bound variables. -/
|
||||
/-- Replace free (or meta) variables `xs` with loose bound variables,
|
||||
with `xs` ordered from outermost to innermost de Bruijn index.
|
||||
|
||||
For example, `e := f x y` with `xs := #[x, y]` goes to `f #1 #0`,
|
||||
whereas `e := f x y` with `xs := #[y, x]` goes to `f #0 #1`. -/
|
||||
@[extern "lean_expr_abstract"]
|
||||
opaque abstract (e : @& Expr) (xs : @& Array Expr) : Expr
|
||||
|
||||
|
||||
@@ -46,17 +46,33 @@ delete the space after private, it becomes a syntactically correct structure wit
|
||||
privateaxiom! So clearly, because of uses of atomic in the grammar, an edit can affect a command
|
||||
syntax tree even across multiple tokens.
|
||||
|
||||
Now, what we do today, and have done since Lean 3, is to always reparse the last command completely
|
||||
preceding the edit location. If its syntax tree is unchanged, we preserve its data and reprocess all
|
||||
following commands only, otherwise we reprocess it fully as well. This seems to have worked well so
|
||||
far but it does seem a bit arbitrary given that even if it works for our current grammar, it can
|
||||
certainly be extended in ways that break the assumption.
|
||||
What we did in Lean 3 was to always reparse the last command completely preceding the edit location.
|
||||
If its syntax tree is unchanged, we preserve its data and reprocess all following commands only,
|
||||
otherwise we reprocess it fully as well. This worked well but did seem a bit arbitrary given that
|
||||
even if it works for a grammar at some point, it can certainly be extended in ways that break the
|
||||
assumption.
|
||||
|
||||
With grammar changes in Lean 4, we found that the following example indeed breaks this assumption:
|
||||
```
|
||||
structure Signature where
|
||||
/-- a docstring -/
|
||||
Sort : Type
|
||||
--^ insert: "s"
|
||||
```
|
||||
As the keyword `Sort` is not a valid start of a structure field and the parser backtracks across the
|
||||
docstring in that case, this is parsed as the complete command `structure Signature where` followed
|
||||
by the partial command `/-- a docstring -/ <missing>`. If we insert an `s` after the `t`, the last
|
||||
command completely preceding the edit location is the partial command containing the docstring. Thus
|
||||
we need to go up two commands to ensure we reparse the `structure` command as well. This kind of
|
||||
nested docstring is the only part of the grammar to our knowledge that requires going up at least
|
||||
two commands; as we never backtrack across more than one docstring, going up two commands should
|
||||
also be sufficient.
|
||||
|
||||
Finally, a more actually principled and generic solution would be to invalidate a syntax tree when
|
||||
the parser has reached the edit location during parsing. If it did not, surely the edit cannot have
|
||||
an effect on the syntax tree in question. Sadly such a "high-water mark" parser position does not
|
||||
exist currently and likely it could at best be approximated by e.g. "furthest `tokenFn` parse". Thus
|
||||
we remain at "go two commands up" at this point.
|
||||
we remain at "go up two commands" at this point.
|
||||
-/
|
||||
|
||||
/-!
|
||||
@@ -231,7 +247,7 @@ structure SetupImportsResult where
|
||||
/-- Performance option used by cmdline driver. -/
|
||||
register_builtin_option internal.cmdlineSnapshots : Bool := {
|
||||
defValue := false
|
||||
descr := "mark persistent and reduce information stored in snapshots to the minimum necessary \
|
||||
descr := "reduce information stored in snapshots to the minimum necessary \
|
||||
for the cmdline driver: diagnostics per command and final full snapshot"
|
||||
}
|
||||
|
||||
@@ -340,11 +356,12 @@ where
|
||||
if let some old := old? then
|
||||
if let some oldSuccess := old.result? then
|
||||
if let some (some processed) ← old.processedResult.get? then
|
||||
-- ...and the edit location is after the next command (see note [Incremental Parsing])...
|
||||
-- ...and the edit is after the second-next command (see note [Incremental Parsing])...
|
||||
if let some nextCom ← processed.firstCmdSnap.get? then
|
||||
if (← isBeforeEditPos nextCom.parserState.pos) then
|
||||
-- ...go immediately to next snapshot
|
||||
return (← unchanged old old.stx oldSuccess.parserState)
|
||||
if let some nextNextCom ← processed.firstCmdSnap.get? then
|
||||
if (← isBeforeEditPos nextNextCom.parserState.pos) then
|
||||
-- ...go immediately to next snapshot
|
||||
return (← unchanged old old.stx oldSuccess.parserState)
|
||||
|
||||
withHeaderExceptions ({ · with
|
||||
ictx, stx := .missing, result? := none, cancelTk? := none }) do
|
||||
@@ -416,6 +433,8 @@ where
|
||||
}
|
||||
-- now that imports have been loaded, check options again
|
||||
let opts ← reparseOptions setup.opts
|
||||
-- default to async elaboration; see also `Elab.async` docs
|
||||
let opts := Elab.async.setIfNotSet opts true
|
||||
let cmdState := Elab.Command.mkState headerEnv msgLog opts
|
||||
let cmdState := { cmdState with
|
||||
infoState := {
|
||||
@@ -437,11 +456,6 @@ where
|
||||
traceState
|
||||
}
|
||||
let prom ← IO.Promise.new
|
||||
-- The speedup of these `markPersistent`s is negligible but they help in making unexpected
|
||||
-- `inc_ref_cold`s more visible
|
||||
let parserState := Runtime.markPersistent parserState
|
||||
let cmdState := Runtime.markPersistent cmdState
|
||||
let ctx := Runtime.markPersistent ctx
|
||||
parseCmd none parserState cmdState prom (sync := true) ctx
|
||||
return {
|
||||
diagnostics
|
||||
@@ -473,11 +487,12 @@ where
|
||||
prom.resolve <| { old with nextCmdSnap? := some { range? := none, task := newProm.result } }
|
||||
else prom.resolve old -- terminal command, we're done!
|
||||
|
||||
-- fast path, do not even start new task for this snapshot
|
||||
-- fast path, do not even start new task for this snapshot (see [Incremental Parsing])
|
||||
if let some old := old? then
|
||||
if let some nextCom ← old.nextCmdSnap?.bindM (·.get?) then
|
||||
if (← isBeforeEditPos nextCom.parserState.pos) then
|
||||
return (← unchanged old old.parserState)
|
||||
if let some nextNextCom ← nextCom.nextCmdSnap?.bindM (·.get?) then
|
||||
if (← isBeforeEditPos nextNextCom.parserState.pos) then
|
||||
return (← unchanged old old.parserState)
|
||||
|
||||
let beginPos := parserState.pos
|
||||
let scope := cmdState.scopes.head!
|
||||
@@ -626,22 +641,21 @@ where
|
||||
pos := ctx.fileMap.toPosition beginPos
|
||||
data := output
|
||||
}
|
||||
let cmdState := { cmdState with messages }
|
||||
let cmdState : Command.State := { cmdState with messages }
|
||||
let mut reportedCmdState := cmdState
|
||||
-- definitely resolve eventually
|
||||
snap.new.resolve <| .ofTyped { diagnostics := .empty : SnapshotLeaf }
|
||||
|
||||
let mut infoTree := cmdState.infoState.trees[0]!
|
||||
let infoTree : InfoTree := cmdState.infoState.trees[0]!
|
||||
let cmdline := internal.cmdlineSnapshots.get scope.opts && !Parser.isTerminalCommand stx
|
||||
if cmdline then
|
||||
infoTree := Runtime.markPersistent infoTree
|
||||
-- discard all metadata apart from the environment; see `internal.cmdlineSnapshots`
|
||||
reportedCmdState := { env := reportedCmdState.env, maxRecDepth := 0 }
|
||||
finishedPromise.resolve {
|
||||
diagnostics := (← Snapshot.Diagnostics.ofMessageLog cmdState.messages)
|
||||
infoTree? := infoTree
|
||||
traces := cmdState.traceState
|
||||
cmdState := if cmdline then {
|
||||
env := Runtime.markPersistent cmdState.env
|
||||
maxRecDepth := 0
|
||||
} else cmdState
|
||||
cmdState := reportedCmdState
|
||||
}
|
||||
-- The reported `cmdState` in the snapshot may be minimized as seen above, so we return the full
|
||||
-- state here for further processing on the same thread
|
||||
|
||||
@@ -51,8 +51,8 @@ def checkDeprecated [Monad m] [MonadEnv m] [MonadLog m] [AddMessageContext m] [M
|
||||
if getLinterValue linter.deprecated (← getOptions) then
|
||||
let some attr := deprecatedAttr.getParam? (← getEnv) declName | pure ()
|
||||
logWarning <| .tagged ``deprecatedAttr <|
|
||||
s!"`{declName}` has been deprecated" ++ match attr.text? with
|
||||
m!"`{.ofConstName declName true}` has been deprecated" ++ match attr.text? with
|
||||
| some text => s!": {text}"
|
||||
| none => match attr.newName? with
|
||||
| some newName => s!": use `{newName}` instead"
|
||||
| some newName => m!": use `{.ofConstName newName true}` instead"
|
||||
| none => ""
|
||||
|
||||
@@ -248,7 +248,7 @@ builtin_initialize addBuiltinUnusedVariablesIgnoreFn (fun _ stack opts =>
|
||||
!getLinterUnusedVariablesFunArgs opts &&
|
||||
stack.matches [`null, none, `null, ``Lean.Parser.Term.letIdDecl, none] &&
|
||||
(stack.get? 3 |>.any fun (_, pos) => pos == 1) &&
|
||||
(stack.get? 5 |>.any fun (stx, _) => !stx.isOfKind ``Lean.Parser.Command.whereStructField))
|
||||
(stack.get? 5 |>.any fun (stx, _) => !stx.isOfKind ``Lean.Parser.Term.structInstField))
|
||||
|
||||
/--
|
||||
Function argument in declaration signature (when `linter.unusedVariables.funArgs` is false)
|
||||
|
||||
@@ -5,14 +5,40 @@ Authors: Mac Malone
|
||||
-/
|
||||
prelude
|
||||
import Init.System.IO
|
||||
|
||||
namespace Lean
|
||||
|
||||
/--
|
||||
Dynamically loads a shared library so that its symbols can be used by
|
||||
the Lean interpreter (e.g., for interpreting `@[extern]` declarations).
|
||||
Equivalent to passing `--load-dynlib=lib` to `lean`.
|
||||
Equivalent to passing `--load-dynlib=path` to `lean`.
|
||||
|
||||
Note that Lean never unloads libraries.
|
||||
**Lean never unloads libraries.** Attempting to load a library that defines
|
||||
symbols shared with a previously loaded library (including itself) will error.
|
||||
If multiple libraries share common symbols, those symbols should be linked
|
||||
and loaded as separate libraries.
|
||||
-/
|
||||
@[extern "lean_load_dynlib"]
|
||||
opaque loadDynlib (path : @& System.FilePath) : IO Unit
|
||||
|
||||
/--
|
||||
Loads a Lean plugin and runs its initializers.
|
||||
|
||||
A Lean plugin is a shared library built from a Lean module.
|
||||
This means it has an `initialize_<module-name>` symbol that runs the
|
||||
module's initializers (including its imports' initializers). Initializers
|
||||
are declared with the `initialize` or `builtin_initialize` commands.
|
||||
|
||||
This is similar to passing `--plugin=path` to `lean`.
|
||||
Lean environment initializers, such as definitions calling
|
||||
`registerEnvExtension`, also require `Lean.initializing` to be `true`.
|
||||
To enable them, use `loadPlugin` within a `withImporting` block. This will
|
||||
set `Lean.initializing` (but not `IO.initializing`).
|
||||
|
||||
**Lean never unloads plugins.** Attempting to load a plugin that defines
|
||||
symbols shared with a previously loaded plugin (including itself) will error.
|
||||
If multiple plugins share common symbols (e.g., imports), those symbols
|
||||
should be linked and loaded separately.
|
||||
-/
|
||||
@[extern "lean_load_plugin"]
|
||||
opaque loadPlugin (path : @& System.FilePath) : IO Unit
|
||||
|
||||
@@ -441,6 +441,10 @@ instance : Append MessageLog :=
|
||||
def hasErrors (log : MessageLog) : Bool :=
|
||||
log.hadErrors || log.unreported.any (·.severity matches .error)
|
||||
|
||||
/-- Clears unreported messages while preserving `hasErrors`. -/
|
||||
def markAllReported (log : MessageLog) : MessageLog :=
|
||||
{ log with unreported := {}, hadErrors := log.hasErrors }
|
||||
|
||||
def errorsToWarnings (log : MessageLog) : MessageLog :=
|
||||
{ unreported := log.unreported.map (fun m => match m.severity with | MessageSeverity.error => { m with severity := MessageSeverity.warning } | _ => m) }
|
||||
|
||||
|
||||
@@ -229,7 +229,7 @@ structure ParamInfo where
|
||||
hasFwdDeps : Bool := false
|
||||
/-- `backDeps` contains the backwards dependencies. That is, the (0-indexed) position of previous parameters that this one depends on. -/
|
||||
backDeps : Array Nat := #[]
|
||||
/-- `isProp` is true if the parameter is always a proposition. -/
|
||||
/-- `isProp` is true if the parameter type is always a proposition. -/
|
||||
isProp : Bool := false
|
||||
/--
|
||||
`isDecInst` is true if the parameter's type is of the form `Decidable ...`.
|
||||
|
||||
@@ -35,11 +35,27 @@ def isConstructorApp? (e : Expr) : MetaM (Option ConstructorVal) := do
|
||||
|
||||
/--
|
||||
Similar to `isConstructorApp?`, but uses `whnf`.
|
||||
It also uses `isOffset?` for `Nat`.
|
||||
|
||||
See also `Lean.Meta.constructorApp'?`.
|
||||
-/
|
||||
def isConstructorApp'? (e : Expr) : MetaM (Option ConstructorVal) := do
|
||||
if let some r ← isConstructorApp? e then
|
||||
if let some (_, k) ← isOffset? e then
|
||||
if k = 0 then
|
||||
return none
|
||||
else
|
||||
let .ctorInfo val ← getConstInfo ``Nat.succ | return none
|
||||
return some val
|
||||
else if let some r ← isConstructorApp? e then
|
||||
return r
|
||||
isConstructorApp? (← whnf e)
|
||||
else try
|
||||
/-
|
||||
We added the `try` block here because `whnf` fails at terms `n ^ m`
|
||||
when `m` is a big numeral, and `n` is a numeral. This is a little bit hackish.
|
||||
-/
|
||||
isConstructorApp? (← whnf e)
|
||||
catch _ =>
|
||||
return none
|
||||
|
||||
/--
|
||||
Returns `true`, if `e` is constructor application of builtin literal defeq to
|
||||
@@ -70,7 +86,9 @@ def constructorApp? (e : Expr) : MetaM (Option (ConstructorVal × Array Expr)) :
|
||||
|
||||
/--
|
||||
Similar to `constructorApp?`, but on failure it puts `e` in WHNF and tries again.
|
||||
It also `isOffset?`
|
||||
It also uses `isOffset?` for `Nat`.
|
||||
|
||||
See also `Lean.Meta.isConstructorApp'?`.
|
||||
-/
|
||||
def constructorApp'? (e : Expr) : MetaM (Option (ConstructorVal × Array Expr)) := do
|
||||
if let some (e, k) ← isOffset? e then
|
||||
|
||||
@@ -62,13 +62,13 @@ def getStringValue? (e : Expr) : (Option String) :=
|
||||
| .lit (.strVal s) => some s
|
||||
| _ => none
|
||||
|
||||
/-- Return `some ⟨n, v⟩` if `e` is af `OfNat.ofNat` application encoding a `Fin n` with value `v` -/
|
||||
/-- Return `some ⟨n, v⟩` if `e` is an `OfNat.ofNat` application encoding a `Fin n` with value `v` -/
|
||||
def getFinValue? (e : Expr) : MetaM (Option ((n : Nat) × Fin n)) := OptionT.run do
|
||||
let (v, type) ← getOfNatValue? e ``Fin
|
||||
let n ← getNatValue? (← whnfD type.appArg!)
|
||||
match n with
|
||||
| 0 => failure
|
||||
| m+1 => return ⟨m+1, Fin.ofNat v⟩
|
||||
| m+1 => return ⟨m+1, Fin.ofNat' _ v⟩
|
||||
|
||||
/--
|
||||
Return `some ⟨n, v⟩` if `e` is:
|
||||
|
||||
@@ -719,13 +719,11 @@ def deriveUnaryInduction (name : Name) : MetaM Name := do
|
||||
let e' ← abstractIndependentMVars mvars (← motive.fvarId!.getDecl).index e'
|
||||
let e' ← mkLambdaFVars #[motive] e'
|
||||
|
||||
-- We could pass (usedOnly := true) below, and get nicer induction principles that
|
||||
-- do not mention odd unused parameters.
|
||||
-- But the downside is that automatic instantiation of the principle (e.g. in a tactic
|
||||
-- that derives them from an function application in the goal) is harder, as
|
||||
-- one would have to infer or keep track of which parameters to pass.
|
||||
-- So for now lets just keep them around.
|
||||
let e' ← mkLambdaFVars (binderInfoForMVars := .default) fixedParams e'
|
||||
-- We used to pass (usedOnly := false) below in the hope that the types of the
|
||||
-- induction principle match the type of the function better.
|
||||
-- But this leads to avoidable parameters that make functional induction strictly less
|
||||
-- useful (e.g. when the unsued parameter mentions bound variables in the users' goal)
|
||||
let e' ← mkLambdaFVars (binderInfoForMVars := .default) (usedOnly := true) fixedParams e'
|
||||
instantiateMVars e'
|
||||
| _ =>
|
||||
if funBody.isAppOf ``WellFounded.fix then
|
||||
@@ -1062,13 +1060,11 @@ def deriveInductionStructural (names : Array Name) (numFixed : Nat) : MetaM Unit
|
||||
let e' ← abstractIndependentMVars mvars (← motives.back!.fvarId!.getDecl).index e'
|
||||
let e' ← mkLambdaFVars motives e'
|
||||
|
||||
-- We could pass (usedOnly := true) below, and get nicer induction principles that
|
||||
-- do not mention odd unused parameters.
|
||||
-- But the downside is that automatic instantiation of the principle (e.g. in a tactic
|
||||
-- that derives them from an function application in the goal) is harder, as
|
||||
-- one would have to infer or keep track of which parameters to pass.
|
||||
-- So for now lets just keep them around.
|
||||
let e' ← mkLambdaFVars (binderInfoForMVars := .default) xs e'
|
||||
-- We used to pass (usedOnly := false) below in the hope that the types of the
|
||||
-- induction principle match the type of the function better.
|
||||
-- But this leads to avoidable parameters that make functional induction strictly less
|
||||
-- useful (e.g. when the unsued parameter mentions bound variables in the users' goal)
|
||||
let e' ← mkLambdaFVars (binderInfoForMVars := .default) (usedOnly := true) xs e'
|
||||
let e' ← instantiateMVars e'
|
||||
trace[Meta.FunInd] "complete body of mutual induction principle:{indentExpr e'}"
|
||||
pure e'
|
||||
|
||||
@@ -20,6 +20,18 @@ def fromExpr? (e : Expr) : SimpM (Option Value) := do
|
||||
let some ⟨n, value⟩ ← getFinValue? e | return none
|
||||
return some { n, value }
|
||||
|
||||
@[inline] def reduceOp (declName : Name) (arity : Nat) (f : Nat → Nat) (op : {n : Nat} → Fin n → Fin (f n)) (e : Expr) : SimpM DStep := do
|
||||
unless e.isAppOfArity declName arity do return .continue
|
||||
let some v ← fromExpr? e.appArg! | return .continue
|
||||
let v' := op v.value
|
||||
return .done <| toExpr v'
|
||||
|
||||
@[inline] def reduceNatOp (declName : Name) (arity : Nat) (f : Nat → Nat) (op : (n : Nat) → Fin (f n)) (e : Expr) : SimpM DStep := do
|
||||
unless e.isAppOfArity declName arity do return .continue
|
||||
let some v ← getNatValue? e.appArg! | return .continue
|
||||
let v' := op v
|
||||
return .done <| toExpr v'
|
||||
|
||||
@[inline] def reduceBin (declName : Name) (arity : Nat) (op : {n : Nat} → Fin n → Fin n → Fin n) (e : Expr) : SimpM DStep := do
|
||||
unless e.isAppOfArity declName arity do return .continue
|
||||
let some v₁ ← fromExpr? e.appFn!.appArg! | return .continue
|
||||
@@ -47,12 +59,23 @@ The following code assumes users did not override the `Fin n` instances for the
|
||||
If they do, they must disable the following `simprocs`.
|
||||
-/
|
||||
|
||||
builtin_dsimproc [simp, seval] reduceSucc (Fin.succ _) := reduceOp ``Fin.succ 2 (· + 1) Fin.succ
|
||||
builtin_dsimproc [simp, seval] reduceRev (Fin.rev _) := reduceOp ``Fin.rev 2 (·) Fin.rev
|
||||
builtin_dsimproc [simp, seval] reduceLast (Fin.last _) := reduceNatOp ``Fin.last 1 (· + 1) Fin.last
|
||||
|
||||
builtin_dsimproc [simp, seval] reduceAdd ((_ + _ : Fin _)) := reduceBin ``HAdd.hAdd 6 (· + ·)
|
||||
builtin_dsimproc [simp, seval] reduceMul ((_ * _ : Fin _)) := reduceBin ``HMul.hMul 6 (· * ·)
|
||||
builtin_dsimproc [simp, seval] reduceSub ((_ - _ : Fin _)) := reduceBin ``HSub.hSub 6 (· - ·)
|
||||
builtin_dsimproc [simp, seval] reduceDiv ((_ / _ : Fin _)) := reduceBin ``HDiv.hDiv 6 (· / ·)
|
||||
builtin_dsimproc [simp, seval] reduceMod ((_ % _ : Fin _)) := reduceBin ``HMod.hMod 6 (· % ·)
|
||||
|
||||
builtin_dsimproc [simp, seval] reduceAnd ((_ &&& _ : Fin _)) := reduceBin ``HAnd.hAnd 6 (· &&& ·)
|
||||
builtin_dsimproc [simp, seval] reduceOr ((_ ||| _ : Fin _)) := reduceBin ``HOr.hOr 6 (· ||| ·)
|
||||
builtin_dsimproc [simp, seval] reduceXor ((_ ^^^ _ : Fin _)) := reduceBin ``HXor.hXor 6 (· ^^^ ·)
|
||||
|
||||
builtin_dsimproc [simp, seval] reduceShiftLeft ((_ <<< _ : Fin _)) := reduceBin ``HShiftLeft.hShiftLeft 6 (· <<< ·)
|
||||
builtin_dsimproc [simp, seval] reduceShiftRight ((_ >>> _ : Fin _)) := reduceBin ``HShiftRight.hShiftRight 6 (· >>> ·)
|
||||
|
||||
builtin_simproc [simp, seval] reduceLT (( _ : Fin _) < _) := reduceBinPred ``LT.lt 4 (. < .)
|
||||
builtin_simproc [simp, seval] reduceLE (( _ : Fin _) ≤ _) := reduceBinPred ``LE.le 4 (. ≤ .)
|
||||
builtin_simproc [simp, seval] reduceGT (( _ : Fin _) > _) := reduceBinPred ``GT.gt 4 (. > .)
|
||||
@@ -83,4 +106,70 @@ builtin_dsimproc [simp, seval] reduceFinMk (Fin.mk _ _) := fun e => do
|
||||
else
|
||||
return .continue
|
||||
|
||||
builtin_dsimproc [simp, seval] reduceOfNat' (Fin.ofNat' _ _) := fun e => do
|
||||
unless e.isAppOfArity ``Fin.ofNat' 3 do return .continue
|
||||
let some (n + 1) ← getNatValue? e.appFn!.appFn!.appArg! | return .continue
|
||||
let some k ← getNatValue? e.appArg! | return .continue
|
||||
return .done <| toExpr (Fin.ofNat' (n + 1) k)
|
||||
|
||||
builtin_dsimproc [simp, seval] reduceCastSucc (Fin.castSucc _) := fun e => do
|
||||
unless e.isAppOfArity ``Fin.castSucc 2 do return .continue
|
||||
let some k ← fromExpr? e.appArg! | return .continue
|
||||
return .done <| toExpr (castSucc k.value)
|
||||
|
||||
builtin_dsimproc [simp, seval] reduceCastAdd (Fin.castAdd _ _) := fun e => do
|
||||
unless e.isAppOfArity ``Fin.castAdd 3 do return .continue
|
||||
let some m ← getNatValue? e.appFn!.appArg! | return .continue
|
||||
let some k ← fromExpr? e.appArg! | return .continue
|
||||
return .done <| toExpr (castAdd m k.value)
|
||||
|
||||
builtin_dsimproc [simp, seval] reduceAddNat (Fin.addNat _ _) := fun e => do
|
||||
unless e.isAppOfArity ``Fin.addNat 3 do return .continue
|
||||
let some k ← fromExpr? e.appFn!.appArg! | return .continue
|
||||
let some m ← getNatValue? e.appArg! | return .continue
|
||||
return .done <| toExpr (addNat k.value m)
|
||||
|
||||
builtin_dsimproc [simp, seval] reduceNatAdd (Fin.natAdd _ _) := fun e => do
|
||||
unless e.isAppOfArity ``Fin.natAdd 3 do return .continue
|
||||
let some m ← getNatValue? e.appFn!.appArg! | return .continue
|
||||
let some k ← fromExpr? e.appArg! | return .continue
|
||||
return .done <| toExpr (natAdd m k.value)
|
||||
|
||||
builtin_dsimproc [simp, seval] reduceCastLT (Fin.castLT _ _) := fun e => do
|
||||
unless e.isAppOfArity ``Fin.castLT 4 do return .continue
|
||||
let some n ← getNatValue? e.appFn!.appFn!.appFn!.appArg! | return .continue
|
||||
let some i ← fromExpr? e.appFn!.appArg! | return .continue
|
||||
if h : i.value < n then
|
||||
return .done <| toExpr (castLT i.value h)
|
||||
else
|
||||
return .continue
|
||||
|
||||
builtin_dsimproc [simp, seval] reduceCastLE (Fin.castLE _ _) := fun e => do
|
||||
unless e.isAppOfArity ``Fin.castLE 4 do return .continue
|
||||
let some m ← getNatValue? e.appFn!.appFn!.appArg! | return .continue
|
||||
let some i ← fromExpr? e.appArg! | return .continue
|
||||
if h : i.n ≤ m then
|
||||
return .done <| toExpr (castLE h i.value)
|
||||
else
|
||||
return .continue
|
||||
|
||||
-- No simproc is needed for `Fin.cast`, as for explicit numbers `Fin.cast_refl` will apply.
|
||||
|
||||
builtin_dsimproc [simp, seval] reduceSubNat (Fin.subNat _ _ _) := fun e => do
|
||||
unless e.isAppOfArity ``Fin.subNat 4 do return .continue
|
||||
let some m ← getNatValue? e.appFn!.appFn!.appArg! | return .continue
|
||||
let some i ← fromExpr? e.appFn!.appArg! | return .continue
|
||||
if h : m ≤ i.value then
|
||||
return .done <| toExpr (subNat m (i.value.cast (by omega : i.n = (i.n - m) + m)) h)
|
||||
else
|
||||
return .continue
|
||||
|
||||
builtin_dsimproc [simp, seval] reducePred (Fin.pred _ _) := fun e => do
|
||||
unless e.isAppOfArity ``Fin.pred 3 do return .continue
|
||||
let some ⟨(_ + 1), i⟩ ← fromExpr? e.appFn!.appArg! | return .continue
|
||||
if h : i ≠ 0 then
|
||||
return .done <| toExpr (pred i h)
|
||||
else
|
||||
return .continue
|
||||
|
||||
end Fin
|
||||
|
||||
@@ -71,8 +71,8 @@ builtin_dsimproc [simp, seval] reduceMul ((_ * _ : Int)) := reduceBin ``HMul.hMu
|
||||
builtin_dsimproc [simp, seval] reduceSub ((_ - _ : Int)) := reduceBin ``HSub.hSub 6 (· - ·)
|
||||
builtin_dsimproc [simp, seval] reduceDiv ((_ / _ : Int)) := reduceBin ``HDiv.hDiv 6 (· / ·)
|
||||
builtin_dsimproc [simp, seval] reduceMod ((_ % _ : Int)) := reduceBin ``HMod.hMod 6 (· % ·)
|
||||
builtin_dsimproc [simp, seval] reduceTDiv (tdiv _ _) := reduceBin ``Int.div 2 Int.tdiv
|
||||
builtin_dsimproc [simp, seval] reduceTMod (tmod _ _) := reduceBin ``Int.mod 2 Int.tmod
|
||||
builtin_dsimproc [simp, seval] reduceTDiv (tdiv _ _) := reduceBin ``Int.tdiv 2 Int.tdiv
|
||||
builtin_dsimproc [simp, seval] reduceTMod (tmod _ _) := reduceBin ``Int.tmod 2 Int.tmod
|
||||
builtin_dsimproc [simp, seval] reduceFDiv (fdiv _ _) := reduceBin ``Int.fdiv 2 Int.fdiv
|
||||
builtin_dsimproc [simp, seval] reduceFMod (fmod _ _) := reduceBin ``Int.fmod 2 Int.fmod
|
||||
builtin_dsimproc [simp, seval] reduceBdiv (bdiv _ _) := reduceBinIntNatOp ``bdiv bdiv
|
||||
|
||||
@@ -84,8 +84,22 @@ declare_uint_simprocs UInt8
|
||||
declare_uint_simprocs UInt16
|
||||
declare_uint_simprocs UInt32
|
||||
declare_uint_simprocs UInt64
|
||||
|
||||
/-
|
||||
We disabled the simprocs for USize since the result of most operations depend on an opaque value: `System.Platform.numBits`.
|
||||
We could reduce some cases using the fact that this opaque value is `32` or `64`, but it is unclear whether it would be useful in practice.
|
||||
We do not use the normal simprocs for `USize` since the result of most operations depend on an opaque value: `System.Platform.numBits`.
|
||||
However, we do reduce natural literals using the fact this opaque value is at least `32`.
|
||||
-/
|
||||
-- declare_uint_simprocs USize
|
||||
namespace USize
|
||||
|
||||
def fromExpr (e : Expr) : SimpM (Option USize) := do
|
||||
let some (n, _) ← getOfNatValue? e ``USize | return none
|
||||
return USize.ofNat n
|
||||
|
||||
builtin_simproc [simp, seval] reduceToNat (USize.toNat _) := fun e => do
|
||||
let_expr USize.toNat e ← e | return .continue
|
||||
let some (n, _) ← getOfNatValue? e ``USize | return .continue
|
||||
unless n < UInt32.size do return .continue
|
||||
let e := toExpr n
|
||||
let p ← mkDecideProof (← mkLT e (mkNatLit UInt32.size))
|
||||
let p := mkApp2 (mkConst ``USize.toNat_ofNat_of_lt_32) e p
|
||||
return .done { expr := e, proof? := p }
|
||||
|
||||
@@ -108,13 +108,19 @@ where
|
||||
trace[Meta.Tactic.simp.discharge] "{← ppOrigin thmId}, failed to synthesize instance{indentExpr type}"
|
||||
return false
|
||||
|
||||
private def useImplicitDefEqProof (thm : SimpTheorem) : SimpM Bool := do
|
||||
if thm.rfl then
|
||||
return (← getConfig).implicitDefEqProofs
|
||||
else
|
||||
return false
|
||||
|
||||
private def tryTheoremCore (lhs : Expr) (xs : Array Expr) (bis : Array BinderInfo) (val : Expr) (type : Expr) (e : Expr) (thm : SimpTheorem) (numExtraArgs : Nat) : SimpM (Option Result) := do
|
||||
recordTriedSimpTheorem thm.origin
|
||||
let rec go (e : Expr) : SimpM (Option Result) := do
|
||||
if (← isDefEq lhs e) then
|
||||
unless (← synthesizeArgs thm.origin bis xs) do
|
||||
return none
|
||||
let proof? ← if thm.rfl then
|
||||
let proof? ← if (← useImplicitDefEqProof thm) then
|
||||
pure none
|
||||
else
|
||||
let proof ← instantiateMVars (mkAppN val xs)
|
||||
|
||||
@@ -269,7 +269,7 @@ def mkDiscrGenErrorMsg (e : Expr) : MessageData :=
|
||||
def throwDiscrGenError (e : Expr) : MetaM α :=
|
||||
throwError (mkDiscrGenErrorMsg e)
|
||||
|
||||
def splitMatch (mvarId : MVarId) (e : Expr) : MetaM (List MVarId) := do
|
||||
def splitMatch (mvarId : MVarId) (e : Expr) : MetaM (List MVarId) := mvarId.withContext do
|
||||
let some app ← matchMatcherApp? e | throwError "internal error in `split` tactic: match application expected{indentExpr e}\nthis error typically occurs when the `split` tactic internal functions have been used in a new meta-program"
|
||||
let matchEqns ← Match.getEquationsFor app.matcherName
|
||||
let mvarIds ← applyMatchSplitter mvarId app.matcherName app.matcherLevels app.params app.discrs
|
||||
@@ -278,43 +278,14 @@ def splitMatch (mvarId : MVarId) (e : Expr) : MetaM (List MVarId) := do
|
||||
return (i+1, mvarId::mvarIds)
|
||||
return mvarIds.reverse
|
||||
|
||||
/-- Return an `if-then-else` or `match-expr` to split. -/
|
||||
partial def findSplit? (env : Environment) (e : Expr) (splitIte := true) (exceptionSet : ExprSet := {}) : Option Expr :=
|
||||
go e
|
||||
where
|
||||
go (e : Expr) : Option Expr :=
|
||||
if let some target := e.find? isCandidate then
|
||||
if e.isIte || e.isDIte then
|
||||
let cond := target.getArg! 1 5
|
||||
-- Try to find a nested `if` in `cond`
|
||||
go cond |>.getD target
|
||||
else
|
||||
some target
|
||||
else
|
||||
none
|
||||
|
||||
isCandidate (e : Expr) : Bool := Id.run do
|
||||
if exceptionSet.contains e then
|
||||
false
|
||||
else if splitIte && (e.isIte || e.isDIte) then
|
||||
!(e.getArg! 1 5).hasLooseBVars
|
||||
else if let some info := isMatcherAppCore? env e then
|
||||
let args := e.getAppArgs
|
||||
for i in [info.getFirstDiscrPos : info.getFirstDiscrPos + info.numDiscrs] do
|
||||
if args[i]!.hasLooseBVars then
|
||||
return false
|
||||
return true
|
||||
else
|
||||
false
|
||||
|
||||
end Split
|
||||
|
||||
open Split
|
||||
|
||||
partial def splitTarget? (mvarId : MVarId) (splitIte := true) : MetaM (Option (List MVarId)) := commitWhenSome? do
|
||||
partial def splitTarget? (mvarId : MVarId) (splitIte := true) : MetaM (Option (List MVarId)) := commitWhenSome? do mvarId.withContext do
|
||||
let target ← instantiateMVars (← mvarId.getType)
|
||||
let rec go (badCases : ExprSet) : MetaM (Option (List MVarId)) := do
|
||||
if let some e := findSplit? (← getEnv) target splitIte badCases then
|
||||
if let some e ← findSplit? target (if splitIte then .both else .match) badCases then
|
||||
if e.isIte || e.isDIte then
|
||||
return (← splitIfTarget? mvarId).map fun (s₁, s₂) => [s₁.mvarId, s₂.mvarId]
|
||||
else
|
||||
@@ -333,7 +304,7 @@ partial def splitTarget? (mvarId : MVarId) (splitIte := true) : MetaM (Option (L
|
||||
|
||||
def splitLocalDecl? (mvarId : MVarId) (fvarId : FVarId) : MetaM (Option (List MVarId)) := commitWhenSome? do
|
||||
mvarId.withContext do
|
||||
if let some e := findSplit? (← getEnv) (← instantiateMVars (← inferType (mkFVar fvarId))) then
|
||||
if let some e ← findSplit? (← instantiateMVars (← inferType (mkFVar fvarId))) then
|
||||
if e.isIte || e.isDIte then
|
||||
return (← splitIfLocalDecl? mvarId fvarId).map fun (mvarId₁, mvarId₂) => [mvarId₁, mvarId₂]
|
||||
else
|
||||
|
||||
@@ -8,6 +8,124 @@ import Lean.Meta.Tactic.Cases
|
||||
import Lean.Meta.Tactic.Simp.Main
|
||||
|
||||
namespace Lean.Meta
|
||||
|
||||
inductive SplitKind where
|
||||
| ite | match | both
|
||||
|
||||
def SplitKind.considerIte : SplitKind → Bool
|
||||
| .ite | .both => true
|
||||
| _ => false
|
||||
|
||||
def SplitKind.considerMatch : SplitKind → Bool
|
||||
| .match | .both => true
|
||||
| _ => false
|
||||
|
||||
namespace FindSplitImpl
|
||||
|
||||
structure Context where
|
||||
exceptionSet : ExprSet := {}
|
||||
kind : SplitKind := .both
|
||||
|
||||
unsafe abbrev FindM := ReaderT Context $ StateT (PtrSet Expr) MetaM
|
||||
|
||||
/--
|
||||
Checks whether `e` is a candidate for `split`.
|
||||
Returns `some e'` if a prefix is a candidate.
|
||||
Example: suppose `e` is `(if b then f else g) x`, then
|
||||
the result is `some e'` where `e'` is the subterm `(if b then f else g)`
|
||||
-/
|
||||
private def isCandidate? (env : Environment) (ctx : Context) (e : Expr) : Option Expr := Id.run do
|
||||
let ret (e : Expr) : Option Expr :=
|
||||
if ctx.exceptionSet.contains e then none else some e
|
||||
if ctx.kind.considerIte then
|
||||
if e.isAppOf ``ite || e.isAppOf ``dite then
|
||||
let numArgs := e.getAppNumArgs
|
||||
if numArgs >= 5 && !(e.getArg! 1 5).hasLooseBVars then
|
||||
return ret (e.getBoundedAppFn (numArgs - 5))
|
||||
if ctx.kind.considerMatch then
|
||||
if let some info := isMatcherAppCore? env e then
|
||||
let args := e.getAppArgs
|
||||
for i in [info.getFirstDiscrPos : info.getFirstDiscrPos + info.numDiscrs] do
|
||||
if args[i]!.hasLooseBVars then
|
||||
return none
|
||||
return ret (e.getBoundedAppFn (args.size - info.arity))
|
||||
return none
|
||||
|
||||
@[inline] unsafe def checkVisited (e : Expr) : OptionT FindM Unit := do
|
||||
if (← get).contains e then
|
||||
failure
|
||||
modify fun s => s.insert e
|
||||
|
||||
unsafe def visit (e : Expr) : OptionT FindM Expr := do
|
||||
checkVisited e
|
||||
if let some e := isCandidate? (← getEnv) (← read) e then
|
||||
return e
|
||||
else
|
||||
-- We do not look for split candidates in proofs.
|
||||
unless e.hasLooseBVars do
|
||||
if (← isProof e) then
|
||||
failure
|
||||
match e with
|
||||
| .lam _ _ b _ | .proj _ _ b -- We do not look for split candidates in the binder of lambdas.
|
||||
| .mdata _ b => visit b
|
||||
| .forallE _ d b _ => visit d <|> visit b -- We want to look for candidates at `A → B`
|
||||
| .letE _ _ v b _ => visit v <|> visit b
|
||||
| .app .. => visitApp? e
|
||||
| _ => failure
|
||||
where
|
||||
visitApp? (e : Expr) : FindM (Option Expr) :=
|
||||
e.withApp fun f args => do
|
||||
-- See comment at `Canonicalizer.lean` regarding the case where
|
||||
-- `f` has loose bound variables.
|
||||
let info ← if f.hasLooseBVars then
|
||||
pure {}
|
||||
else
|
||||
getFunInfo f
|
||||
for u : i in [0:args.size] do
|
||||
let arg := args[i]
|
||||
if h : i < info.paramInfo.size then
|
||||
let info := info.paramInfo[i]
|
||||
unless info.isProp do
|
||||
if info.isExplicit then
|
||||
let some found ← visit arg | pure ()
|
||||
return found
|
||||
else
|
||||
let some found ← visit arg | pure ()
|
||||
return found
|
||||
visit f
|
||||
|
||||
end FindSplitImpl
|
||||
|
||||
/-- Return an `if-then-else` or `match-expr` to split. -/
|
||||
partial def findSplit? (e : Expr) (kind : SplitKind := .both) (exceptionSet : ExprSet := {}) : MetaM (Option Expr) := do
|
||||
go (← instantiateMVars e)
|
||||
where
|
||||
go (e : Expr) : MetaM (Option Expr) := do
|
||||
if let some target ← find? e then
|
||||
if target.isIte || target.isDIte then
|
||||
let cond := target.getArg! 1 5
|
||||
-- Try to find a nested `if` in `cond`
|
||||
return (← go cond).getD target
|
||||
else
|
||||
return some target
|
||||
else
|
||||
return none
|
||||
|
||||
find? (e : Expr) : MetaM (Option Expr) := do
|
||||
let some candidate ← unsafe FindSplitImpl.visit e { kind, exceptionSet } |>.run' mkPtrSet
|
||||
| return none
|
||||
trace[split.debug] "candidate:{indentExpr candidate}"
|
||||
return some candidate
|
||||
|
||||
/-- Return the condition and decidable instance of an `if` expression to case split. -/
|
||||
private partial def findIfToSplit? (e : Expr) : MetaM (Option (Expr × Expr)) := do
|
||||
if let some iteApp ← findSplit? e .ite then
|
||||
let cond := iteApp.getArg! 1 5
|
||||
let dec := iteApp.getArg! 2 5
|
||||
return (cond, dec)
|
||||
else
|
||||
return none
|
||||
|
||||
namespace SplitIf
|
||||
|
||||
/--
|
||||
@@ -62,19 +180,9 @@ private def discharge? (numIndices : Nat) (useDecide : Bool) : Simp.Discharge :=
|
||||
def mkDischarge? (useDecide := false) : MetaM Simp.Discharge :=
|
||||
return discharge? (← getLCtx).numIndices useDecide
|
||||
|
||||
/-- Return the condition and decidable instance of an `if` expression to case split. -/
|
||||
private partial def findIfToSplit? (e : Expr) : Option (Expr × Expr) :=
|
||||
if let some iteApp := e.find? fun e => (e.isIte || e.isDIte) && !(e.getArg! 1 5).hasLooseBVars then
|
||||
let cond := iteApp.getArg! 1 5
|
||||
let dec := iteApp.getArg! 2 5
|
||||
-- Try to find a nested `if` in `cond`
|
||||
findIfToSplit? cond |>.getD (cond, dec)
|
||||
else
|
||||
none
|
||||
|
||||
def splitIfAt? (mvarId : MVarId) (e : Expr) (hName? : Option Name) : MetaM (Option (ByCasesSubgoal × ByCasesSubgoal)) := do
|
||||
def splitIfAt? (mvarId : MVarId) (e : Expr) (hName? : Option Name) : MetaM (Option (ByCasesSubgoal × ByCasesSubgoal)) := mvarId.withContext do
|
||||
let e ← instantiateMVars e
|
||||
if let some (cond, decInst) := findIfToSplit? e then
|
||||
if let some (cond, decInst) ← findIfToSplit? e then
|
||||
let hName ← match hName? with
|
||||
| none => mkFreshUserName `h
|
||||
| some hName => pure hName
|
||||
@@ -106,6 +214,7 @@ def splitIfTarget? (mvarId : MVarId) (hName? : Option Name := none) : MetaM (Opt
|
||||
let mvarId₁ ← simpIfTarget s₁.mvarId
|
||||
let mvarId₂ ← simpIfTarget s₂.mvarId
|
||||
if s₁.mvarId == mvarId₁ && s₂.mvarId == mvarId₂ then
|
||||
trace[split.failure] "`split` tactic failed to simplify target using new hypotheses Goals:\n{mvarId₁}\n{mvarId₂}"
|
||||
return none
|
||||
else
|
||||
return some ({ s₁ with mvarId := mvarId₁ }, { s₂ with mvarId := mvarId₂ })
|
||||
@@ -118,6 +227,7 @@ def splitIfLocalDecl? (mvarId : MVarId) (fvarId : FVarId) (hName? : Option Name
|
||||
let mvarId₁ ← simpIfLocalDecl s₁.mvarId fvarId
|
||||
let mvarId₂ ← simpIfLocalDecl s₂.mvarId fvarId
|
||||
if s₁.mvarId == mvarId₁ && s₂.mvarId == mvarId₂ then
|
||||
trace[split.failure] "`split` tactic failed to simplify target using new hypotheses Goals:\n{mvarId₁}\n{mvarId₂}"
|
||||
return none
|
||||
else
|
||||
return some (mvarId₁, mvarId₂)
|
||||
|
||||
@@ -134,10 +134,8 @@ def declValSimple := leading_parser
|
||||
" :=" >> ppHardLineUnlessUngrouped >> declBody >> Termination.suffix >> optional Term.whereDecls
|
||||
def declValEqns := leading_parser
|
||||
Term.matchAltsWhereDecls
|
||||
def whereStructField := leading_parser
|
||||
Term.letDecl
|
||||
def whereStructInst := leading_parser
|
||||
ppIndent ppSpace >> "where" >> Term.structInstFields (sepByIndent (ppGroup whereStructField) "; " (allowTrailingSep := true)) >>
|
||||
ppIndent ppSpace >> "where" >> Term.structInstFields (sepByIndent Term.structInstField "; " (allowTrailingSep := true)) >>
|
||||
optional Term.whereDecls
|
||||
/-- `declVal` matches the right-hand side of a declaration, one of:
|
||||
* `:= expr` (a "simple declaration")
|
||||
|
||||
@@ -269,38 +269,6 @@ an optional `x :`, then a term `ty`, then `from val` or `by tac`. -/
|
||||
@[builtin_term_parser] def «suffices» := leading_parser:leadPrec
|
||||
withPosition ("suffices " >> sufficesDecl) >> optSemicolon termParser
|
||||
@[builtin_term_parser] def «show» := leading_parser:leadPrec "show " >> termParser >> ppSpace >> showRhs
|
||||
def structInstArrayRef := leading_parser
|
||||
"[" >> withoutPosition termParser >> "]"
|
||||
def structInstLVal := leading_parser
|
||||
(ident <|> fieldIdx <|> structInstArrayRef) >>
|
||||
many (group ("." >> (ident <|> fieldIdx)) <|> structInstArrayRef)
|
||||
def structInstField := ppGroup $ leading_parser
|
||||
structInstLVal >> " := " >> termParser
|
||||
def structInstFieldAbbrev := leading_parser
|
||||
-- `x` is an abbreviation for `x := x`
|
||||
atomic (ident >> notFollowedBy ("." <|> ":=" <|> symbol "[") "invalid field abbreviation")
|
||||
def optEllipsis := leading_parser
|
||||
optional " .."
|
||||
/-
|
||||
Tags the structure instance field syntax with a `Lean.Parser.Term.structInstFields` syntax node.
|
||||
This node is used to enable structure instance field completion in the whitespace
|
||||
of a structure instance notation.
|
||||
-/
|
||||
def structInstFields (p : Parser) : Parser := node `Lean.Parser.Term.structInstFields p
|
||||
/--
|
||||
Structure instance. `{ x := e, ... }` assigns `e` to field `x`, which may be
|
||||
inherited. If `e` is itself a variable called `x`, it can be elided:
|
||||
`fun y => { x := 1, y }`.
|
||||
A *structure update* of an existing value can be given via `with`:
|
||||
`{ point with x := 1 }`.
|
||||
The structure type can be specified if not inferable:
|
||||
`{ x := 1, y := 2 : Point }`.
|
||||
-/
|
||||
@[builtin_term_parser] def structInst := leading_parser
|
||||
"{ " >> withoutPosition (optional (atomic (sepBy1 termParser ", " >> " with "))
|
||||
>> structInstFields (sepByIndent (structInstFieldAbbrev <|> structInstField) ", " (allowTrailingSep := true))
|
||||
>> optEllipsis
|
||||
>> optional (" : " >> termParser)) >> " }"
|
||||
def typeSpec := leading_parser " : " >> termParser
|
||||
def optType : Parser := optional typeSpec
|
||||
/--
|
||||
@@ -488,6 +456,56 @@ e.g. because it has no constructors.
|
||||
|
||||
@[builtin_term_parser] def «nofun» := leading_parser "nofun"
|
||||
|
||||
/-
|
||||
Syntax category for structure instance notation fields.
|
||||
Does not initialize `registerBuiltinDynamicParserAttribute` since this category is not meant to be user-extensible.
|
||||
-/
|
||||
builtin_initialize
|
||||
registerBuiltinParserAttribute `builtin_structInstFieldDecl_parser ``Category.structInstFieldDecl
|
||||
@[inline] def structInstFieldDeclParser (rbp : Nat := 0) : Parser :=
|
||||
categoryParser `structInstFieldDecl rbp
|
||||
def optEllipsis := leading_parser
|
||||
optional " .."
|
||||
def structInstArrayRef := leading_parser
|
||||
"[" >> withoutPosition termParser >> "]"
|
||||
def structInstLVal := leading_parser
|
||||
(ident <|> fieldIdx <|> structInstArrayRef) >>
|
||||
many (group ("." >> (ident <|> fieldIdx)) <|> structInstArrayRef)
|
||||
def structInstFieldBinder :=
|
||||
withAntiquot (mkAntiquot "structInstFieldBinder" decl_name% (isPseudoKind := true)) <|
|
||||
binderIdent <|> bracketedBinder
|
||||
def optTypeForStructInst : Parser := optional (atomic (typeSpec >> notFollowedBy "}" "}"))
|
||||
/- `x` is an abbreviation for `x := x` -/
|
||||
def structInstField := ppGroup <| leading_parser
|
||||
structInstLVal >> optional (many (checkColGt >> structInstFieldBinder) >> optTypeForStructInst >> ppDedent structInstFieldDeclParser)
|
||||
/-
|
||||
Tags the structure instance field syntax with a `Lean.Parser.Term.structInstFields` syntax node.
|
||||
This node is used to enable structure instance field completion in the whitespace
|
||||
of a structure instance notation.
|
||||
-/
|
||||
def structInstFields (p : Parser) : Parser := node `Lean.Parser.Term.structInstFields p
|
||||
/--
|
||||
Structure instance. `{ x := e, ... }` assigns `e` to field `x`, which may be
|
||||
inherited. If `e` is itself a variable called `x`, it can be elided:
|
||||
`fun y => { x := 1, y }`.
|
||||
A *structure update* of an existing value can be given via `with`:
|
||||
`{ point with x := 1 }`.
|
||||
The structure type can be specified if not inferable:
|
||||
`{ x := 1, y := 2 : Point }`.
|
||||
-/
|
||||
@[builtin_term_parser] def structInst := leading_parser
|
||||
"{ " >> withoutPosition (optional (atomic (sepBy1 termParser ", " >> " with "))
|
||||
>> structInstFields (sepByIndent structInstField ", " (allowTrailingSep := true))
|
||||
>> optEllipsis
|
||||
>> optional (" : " >> termParser)) >> " }"
|
||||
|
||||
@[builtin_structInstFieldDecl_parser]
|
||||
def structInstFieldDef := leading_parser
|
||||
" := " >> termParser
|
||||
@[builtin_structInstFieldDecl_parser]
|
||||
def structInstFieldEqns := leading_parser
|
||||
matchAlts
|
||||
|
||||
def funImplicitBinder := withAntiquot (mkAntiquot "implicitBinder" ``implicitBinder) <|
|
||||
atomic (lookahead ("{" >> many1 binderIdent >> (symbol " : " <|> "}"))) >> implicitBinder
|
||||
def funStrictImplicitBinder :=
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user