mirror of
https://github.com/leanprover/lean4.git
synced 2026-03-18 02:44:12 +00:00
Compare commits
106 Commits
rel_coe
...
CheckAssig
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
03bae7b676 | ||
|
|
a993934839 | ||
|
|
aa3c87b2c7 | ||
|
|
869e42b7c3 | ||
|
|
bdbadbd74b | ||
|
|
3120c3d8f8 | ||
|
|
e1cbae26cc | ||
|
|
9009c1ac91 | ||
|
|
5c61ad38be | ||
|
|
44985dc9a6 | ||
|
|
3dfa7812f9 | ||
|
|
2dd6b2b9c8 | ||
|
|
6d0b00885e | ||
|
|
75c0373c1a | ||
|
|
b37df8e31a | ||
|
|
da9c68a37a | ||
|
|
6fce7b82bc | ||
|
|
f220efc5ba | ||
|
|
613dbf1637 | ||
|
|
8e68c5d44e | ||
|
|
9ce15fb0c6 | ||
|
|
0dc317c73c | ||
|
|
44366382d3 | ||
|
|
095c7b2bfc | ||
|
|
c4e4248487 | ||
|
|
9ef996259b | ||
|
|
30fa18816c | ||
|
|
94fd406c04 | ||
|
|
3411935e53 | ||
|
|
b518091bd4 | ||
|
|
a58a09056f | ||
|
|
c45a6a93f9 | ||
|
|
f917f811c8 | ||
|
|
3c687df6d5 | ||
|
|
45475d6434 | ||
|
|
c6feffa2bd | ||
|
|
b54a9ec9b9 | ||
|
|
68bb92a35a | ||
|
|
dcdbb9b411 | ||
|
|
dd22447afd | ||
|
|
f0b0c60e0f | ||
|
|
9305049f1e | ||
|
|
852ee1683f | ||
|
|
4c9db2fab8 | ||
|
|
70c1e5690d | ||
|
|
5d84aebeb9 | ||
|
|
7e5d1103c2 | ||
|
|
2d9cbdb450 | ||
|
|
fcdecacc4f | ||
|
|
c9c2c8720a | ||
|
|
703658391e | ||
|
|
8898c8eaa9 | ||
|
|
2d89693b71 | ||
|
|
c3655b626e | ||
|
|
644a12744b | ||
|
|
92b271ee64 | ||
|
|
24f550fd6f | ||
|
|
cee84286e6 | ||
|
|
75781b46f5 | ||
|
|
ea97aac83b | ||
|
|
b1ebe7b484 | ||
|
|
07013da720 | ||
|
|
2bc87298d9 | ||
|
|
390a9a63a2 | ||
|
|
6d4ec153ad | ||
|
|
bf304769e0 | ||
|
|
7488b27b0d | ||
|
|
33d24c3bca | ||
|
|
f71a1fb4ae | ||
|
|
01ec8c5e14 | ||
|
|
d975e4302e | ||
|
|
74715a0f9c | ||
|
|
d540ba787a | ||
|
|
b33d08078d | ||
|
|
e9025bdf79 | ||
|
|
5651a11ac8 | ||
|
|
481894e95d | ||
|
|
7213583c8d | ||
|
|
6a473e67aa | ||
|
|
e5d44f4033 | ||
|
|
c78bb62c51 | ||
|
|
e620cf3c80 | ||
|
|
edecf3d4ba | ||
|
|
1c73983dcf | ||
|
|
4b7b69c20a | ||
|
|
87d361d9b6 | ||
|
|
a3ae75f847 | ||
|
|
0a8d1bf808 | ||
|
|
a58da122b9 | ||
|
|
3b1af163eb | ||
|
|
0e823710e3 | ||
|
|
c38d271283 | ||
|
|
4dbd20343f | ||
|
|
0203cb091d | ||
|
|
f6ce866e39 | ||
|
|
95549f17da | ||
|
|
15c6ac2076 | ||
|
|
4aa74d9c0b | ||
|
|
efbecf272d | ||
|
|
78146190e5 | ||
|
|
b4db495f98 | ||
|
|
9f47e08ecc | ||
|
|
728980443f | ||
|
|
ca945be133 | ||
|
|
f2573dc51e | ||
|
|
51f01d8c8a |
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -176,7 +176,7 @@ jobs:
|
||||
"check-level": 2,
|
||||
"CMAKE_PRESET": "debug",
|
||||
// exclude seriously slow tests
|
||||
"CTEST_OPTIONS": "-E 'interactivetest|leanpkgtest|laketest|benchtest'"
|
||||
"CTEST_OPTIONS": "-E 'interactivetest|leanpkgtest|laketest|benchtest|bv_bitblast_stress'"
|
||||
},
|
||||
// TODO: suddenly started failing in CI
|
||||
/*{
|
||||
|
||||
6
.github/workflows/restart-on-label.yml
vendored
6
.github/workflows/restart-on-label.yml
vendored
@@ -14,8 +14,9 @@ jobs:
|
||||
# (unfortunately cannot search by PR number, only base branch,
|
||||
# and that is't even unique given PRs from forks, but the risk
|
||||
# of confusion is low and the danger is mild)
|
||||
run_id=$(gh run list -e pull_request -b "$head_ref" --workflow 'CI' --limit 1 \
|
||||
--limit 1 --json databaseId --jq '.[0].databaseId')
|
||||
echo "Trying to find a run with branch $head_ref and commit $head_sha"
|
||||
run_id="$(gh run list -e pull_request -b "$head_ref" -c "$head_sha" \
|
||||
--workflow 'CI' --limit 1 --json databaseId --jq '.[0].databaseId')"
|
||||
echo "Run id: ${run_id}"
|
||||
gh run view "$run_id"
|
||||
echo "Cancelling (just in case)"
|
||||
@@ -29,5 +30,6 @@ jobs:
|
||||
shell: bash
|
||||
env:
|
||||
head_ref: ${{ github.head_ref }}
|
||||
head_sha: ${{ github.event.pull_request.head.sha }}
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
GH_REPO: ${{ github.repository }}
|
||||
|
||||
@@ -30,6 +30,35 @@ if(NOT (DEFINED STAGE0_CMAKE_EXECUTABLE_SUFFIX))
|
||||
set(STAGE0_CMAKE_EXECUTABLE_SUFFIX "${CMAKE_EXECUTABLE_SUFFIX}")
|
||||
endif()
|
||||
|
||||
# Don't do anything with cadical on wasm
|
||||
if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
|
||||
# On CI Linux, we source cadical from Nix instead; see flake.nix
|
||||
find_program(CADICAL cadical)
|
||||
if(NOT CADICAL)
|
||||
set(CADICAL_CXX c++)
|
||||
find_program(CCACHE ccache)
|
||||
if(CCACHE)
|
||||
set(CADICAL_CXX "${CCACHE} ${CADICAL_CXX}")
|
||||
endif()
|
||||
# missing stdio locking API on Windows
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
|
||||
string(APPEND CADICAL_CXXFLAGS " -DNUNLOCKED")
|
||||
endif()
|
||||
ExternalProject_add(cadical
|
||||
PREFIX cadical
|
||||
GIT_REPOSITORY https://github.com/arminbiere/cadical
|
||||
GIT_TAG rel-1.9.5
|
||||
CONFIGURE_COMMAND ""
|
||||
# https://github.com/arminbiere/cadical/blob/master/BUILD.md#manual-build
|
||||
BUILD_COMMAND $(MAKE) -f ${CMAKE_SOURCE_DIR}/src/cadical.mk CMAKE_EXECUTABLE_SUFFIX=${CMAKE_EXECUTABLE_SUFFIX} CXX=${CADICAL_CXX} CXXFLAGS=${CADICAL_CXXFLAGS}
|
||||
BUILD_IN_SOURCE ON
|
||||
INSTALL_COMMAND "")
|
||||
set(CADICAL ${CMAKE_BINARY_DIR}/cadical/cadical${CMAKE_EXECUTABLE_SUFFIX} CACHE FILEPATH "path to cadical binary" FORCE)
|
||||
set(EXTRA_DEPENDS "cadical")
|
||||
endif()
|
||||
list(APPEND CL_ARGS -DCADICAL=${CADICAL})
|
||||
endif()
|
||||
|
||||
ExternalProject_add(stage0
|
||||
SOURCE_DIR "${LEAN_SOURCE_DIR}/stage0"
|
||||
SOURCE_SUBDIR src
|
||||
|
||||
@@ -43,3 +43,5 @@
|
||||
/src/Init/Guard.lean @digama0
|
||||
/src/Lean/Server/CodeActions/ @digama0
|
||||
/src/Std/ @TwoFX
|
||||
/src/Std/Tactic/BVDecide/ @hargoniX
|
||||
/src/Lean/Elab/Tactic/BVDecide/ @hargoniX
|
||||
|
||||
30
LICENSES
30
LICENSES
@@ -1341,3 +1341,33 @@ whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
||||
==============================================================================
|
||||
CaDiCaL is under the MIT License:
|
||||
==============================================================================
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016-2021 Armin Biere, Johannes Kepler University Linz, Austria
|
||||
Copyright (c) 2020-2021 Mathias Fleury, Johannes Kepler University Linz, Austria
|
||||
Copyright (c) 2020-2021 Nils Froleyks, Johannes Kepler University Linz, Austria
|
||||
Copyright (c) 2022-2024 Katalin Fazekas, Vienna University of Technology, Austria
|
||||
Copyright (c) 2021-2024 Armin Biere, University of Freiburg, Germany
|
||||
Copyright (c) 2021-2024 Mathias Fleury, University of Freiburg, Germany
|
||||
Copyright (c) 2023-2024 Florian Pollitt, University of Freiburg, Germany
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -4,15 +4,18 @@ open Lean Widget
|
||||
/-!
|
||||
# The user-widgets system
|
||||
|
||||
Proving and programming are inherently interactive tasks. Lots of mathematical objects and data
|
||||
structures are visual in nature. *User widgets* let you associate custom interactive UIs with
|
||||
sections of a Lean document. User widgets are rendered in the Lean infoview.
|
||||
Proving and programming are inherently interactive tasks.
|
||||
Lots of mathematical objects and data structures are visual in nature.
|
||||
*User widgets* let you associate custom interactive UIs
|
||||
with sections of a Lean document.
|
||||
User widgets are rendered in the Lean infoview.
|
||||
|
||||

|
||||
|
||||
## Trying it out
|
||||
|
||||
To try it out, simply type in the following code and place your cursor over the `#widget` command.
|
||||
To try it out, type in the following code and place your cursor over the `#widget` command.
|
||||
You can also [view this manual entry in the online editor](https://live.lean-lang.org/#url=https%3A%2F%2Fraw.githubusercontent.com%2Fleanprover%2Flean4%2Fmaster%2Fdoc%2Fexamples%2Fwidgets.lean).
|
||||
-/
|
||||
|
||||
@[widget_module]
|
||||
@@ -21,38 +24,37 @@ def helloWidget : Widget.Module where
|
||||
import * as React from 'react';
|
||||
export default function(props) {
|
||||
const name = props.name || 'world'
|
||||
return React.createElement('p', {}, name + '!')
|
||||
return React.createElement('p', {}, 'Hello ' + name + '!')
|
||||
}"
|
||||
|
||||
#widget helloWidget
|
||||
|
||||
/-!
|
||||
If you want to dive into a full sample right away, check out
|
||||
[`RubiksCube`](https://github.com/leanprover/lean4-samples/blob/main/RubiksCube/).
|
||||
[`Rubiks`](https://github.com/leanprover-community/ProofWidgets4/blob/main/ProofWidgets/Demos/Rubiks.lean).
|
||||
This sample uses higher-level widget components from the ProofWidgets library.
|
||||
|
||||
Below, we'll explain the system piece by piece.
|
||||
|
||||
⚠️ WARNING: All of the user widget APIs are **unstable** and subject to breaking changes.
|
||||
|
||||
## Widget sources and instances
|
||||
## Widget modules and instances
|
||||
|
||||
A *widget source* is a valid JavaScript [ESModule](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules)
|
||||
which exports a [React component](https://reactjs.org/docs/components-and-props.html). To access
|
||||
React, the module must use `import * as React from 'react'`. Our first example of a widget source
|
||||
is of course the value of `helloWidget.javascript`.
|
||||
A [widget module](https://leanprover-community.github.io/mathlib4_docs/Lean/Widget/UserWidget.html#Lean.Widget.Module)
|
||||
is a valid JavaScript [ESModule](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules)
|
||||
that can execute in the Lean infoview.
|
||||
Most widget modules export a [React component](https://reactjs.org/docs/components-and-props.html)
|
||||
as the piece of user interface to be rendered.
|
||||
To access React, the module can use `import * as React from 'react'`.
|
||||
Our first example of a widget module is `helloWidget` above.
|
||||
Widget modules must be registered with the `@[widget_module]` attribute.
|
||||
|
||||
We can register a widget source with the `@[widget]` attribute, giving it a friendlier name
|
||||
in the `name` field. This is bundled together in a `UserWidgetDefinition`.
|
||||
|
||||
A *widget instance* is then the identifier of a `UserWidgetDefinition` (so `` `helloWidget ``,
|
||||
not `"Hello"`) associated with a range of positions in the Lean source code. Widget instances
|
||||
are stored in the *infotree* in the same manner as other information about the source file
|
||||
such as the type of every expression. In our example, the `#widget` command stores a widget instance
|
||||
with the entire line as its range. We can think of a widget instance as an instruction for the
|
||||
infoview: "when the user places their cursor here, please render the following widget".
|
||||
|
||||
Every widget instance also contains a `props : Json` value. This value is passed as an argument
|
||||
to the React component. In our first invocation of `#widget`, we set it to `.null`. Try out what
|
||||
happens when you type in:
|
||||
A [widget instance](https://leanprover-community.github.io/mathlib4_docs/Lean/Widget/Types.html#Lean.Widget.WidgetInstance)
|
||||
is then the identifier of a widget module (e.g. `` `helloWidget ``)
|
||||
bundled with a value for its props.
|
||||
This value is passed as the argument to the React component.
|
||||
In our first invocation of `#widget`, we set it to `.null`.
|
||||
Try out what happens when you type in:
|
||||
-/
|
||||
|
||||
structure HelloWidgetProps where
|
||||
@@ -62,21 +64,37 @@ structure HelloWidgetProps where
|
||||
#widget helloWidget with { name? := "<your name here>" : HelloWidgetProps }
|
||||
|
||||
/-!
|
||||
💡 NOTE: The RPC system presented below does not depend on JavaScript. However the primary use case
|
||||
is the web-based infoview in VSCode.
|
||||
Under the hood, widget instances are associated with a range of positions in the source file.
|
||||
Widget instances are stored in the *infotree*
|
||||
in the same manner as other information about the source file
|
||||
such as the type of every expression.
|
||||
In our example, the `#widget` command stores a widget instance
|
||||
with the entire line as its range.
|
||||
One can think of the infotree entry as an instruction for the infoview:
|
||||
"when the user places their cursor here, please render the following widget".
|
||||
-/
|
||||
|
||||
/-!
|
||||
## Querying the Lean server
|
||||
|
||||
Besides enabling us to create cool client-side visualizations, user widgets come with the ability
|
||||
to communicate with the Lean server. Thanks to this, they have the same metaprogramming capabilities
|
||||
as custom elaborators or the tactic framework. To see this in action, let's implement a `#check`
|
||||
command as a web input form. This example assumes some familiarity with React.
|
||||
💡 NOTE: The RPC system presented below does not depend on JavaScript.
|
||||
However, the primary use case is the web-based infoview in VSCode.
|
||||
|
||||
The first thing we'll need is to create an *RPC method*. Meaning "Remote Procedure Call", this
|
||||
is basically a Lean function callable from widget code (possibly remotely over the internet).
|
||||
Besides enabling us to create cool client-side visualizations,
|
||||
user widgets have the ability to communicate with the Lean server.
|
||||
Thanks to this, they have the same metaprogramming capabilities
|
||||
as custom elaborators or the tactic framework.
|
||||
To see this in action, let's implement a `#check` command as a web input form.
|
||||
This example assumes some familiarity with React.
|
||||
|
||||
The first thing we'll need is to create an *RPC method*.
|
||||
Meaning "Remote Procedure Call",this is a Lean function callable from widget code
|
||||
(possibly remotely over the internet).
|
||||
Our method will take in the `name : Name` of a constant in the environment and return its type.
|
||||
By convention, we represent the input data as a `structure`. Since it will be sent over from JavaScript,
|
||||
we need `FromJson` and `ToJson`. We'll see below why the position field is needed.
|
||||
By convention, we represent the input data as a `structure`.
|
||||
Since it will be sent over from JavaScript,
|
||||
we need `FromJson` and `ToJson` instnace.
|
||||
We'll see why the position field is needed later.
|
||||
-/
|
||||
|
||||
structure GetTypeParams where
|
||||
@@ -87,25 +105,33 @@ structure GetTypeParams where
|
||||
deriving FromJson, ToJson
|
||||
|
||||
/-!
|
||||
After its arguments, we define the `getType` method. Every RPC method executes in the `RequestM`
|
||||
monad and must return a `RequestTask α` where `α` is its "actual" return type. The `Task` is so
|
||||
that requests can be handled concurrently. A first guess for `α` might be `Expr`. However,
|
||||
expressions in general can be large objects which depend on an `Environment` and `LocalContext`.
|
||||
Thus we cannot directly serialize an `Expr` and send it to the widget. Instead, there are two
|
||||
options:
|
||||
- One is to send a *reference* which points to an object residing on the server. From JavaScript's
|
||||
point of view, references are entirely opaque, but they can be sent back to other RPC methods for
|
||||
further processing.
|
||||
- Two is to pretty-print the expression and send its textual representation called `CodeWithInfos`.
|
||||
This representation contains extra data which the infoview uses for interactivity. We take this
|
||||
strategy here.
|
||||
After its argument structure, we define the `getType` method.
|
||||
RPCs method execute in the `RequestM` monad and must return a `RequestTask α`
|
||||
where `α` is the "actual" return type.
|
||||
The `Task` is so that requests can be handled concurrently.
|
||||
As a first guess, we'd use `Expr` as `α`.
|
||||
However, expressions in general can be large objects
|
||||
which depend on an `Environment` and `LocalContext`.
|
||||
Thus we cannot directly serialize an `Expr` and send it to JavaScript.
|
||||
Instead, there are two options:
|
||||
|
||||
RPC methods execute in the context of a file, but not any particular `Environment` so they don't
|
||||
know about the available `def`initions and `theorem`s. Thus, we need to pass in a position at which
|
||||
we want to use the local `Environment`. This is why we store it in `GetTypeParams`. The `withWaitFindSnapAtPos`
|
||||
method launches a concurrent computation whose job is to find such an `Environment` and a bit
|
||||
more information for us, in the form of a `snap : Snapshot`. With this in hand, we can call
|
||||
`MetaM` procedures to find out the type of `name` and pretty-print it.
|
||||
- One is to send a *reference* which points to an object residing on the server.
|
||||
From JavaScript's point of view, references are entirely opaque,
|
||||
but they can be sent back to other RPC methods for further processing.
|
||||
- The other is to pretty-print the expression and send its textual representation called `CodeWithInfos`.
|
||||
This representation contains extra data which the infoview uses for interactivity.
|
||||
We take this strategy here.
|
||||
|
||||
RPC methods execute in the context of a file,
|
||||
but not of any particular `Environment`,
|
||||
so they don't know about the available `def`initions and `theorem`s.
|
||||
Thus, we need to pass in a position at which we want to use the local `Environment`.
|
||||
This is why we store it in `GetTypeParams`.
|
||||
The `withWaitFindSnapAtPos` method launches a concurrent computation
|
||||
whose job is to find such an `Environment` for us,
|
||||
in the form of a `snap : Snapshot`.
|
||||
With this in hand, we can call `MetaM` procedures
|
||||
to find out the type of `name` and pretty-print it.
|
||||
-/
|
||||
|
||||
open Server RequestM in
|
||||
@@ -121,18 +147,22 @@ def getType (params : GetTypeParams) : RequestM (RequestTask CodeWithInfos) :=
|
||||
/-!
|
||||
## Using infoview components
|
||||
|
||||
Now that we have all we need on the server side, let's write the widget source. By importing
|
||||
`@leanprover/infoview`, widgets can render UI components used to implement the infoview itself.
|
||||
For example, the `<InteractiveCode>` component displays expressions with `term : type` tooltips
|
||||
as seen in the goal view. We will use it to implement our custom `#check` display.
|
||||
Now that we have all we need on the server side, let's write the widget module.
|
||||
By importing `@leanprover/infoview`, widgets can render UI components used to implement the infoview itself.
|
||||
For example, the `<InteractiveCode>` component displays expressions
|
||||
with `term : type` tooltips as seen in the goal view.
|
||||
We will use it to implement our custom `#check` display.
|
||||
|
||||
⚠️ WARNING: Like the other widget APIs, the infoview JS API is **unstable** and subject to breaking changes.
|
||||
|
||||
The code below demonstrates useful parts of the API. To make RPC method calls, we use the `RpcContext`.
|
||||
The `useAsync` helper packs the results of a call into an `AsyncState` structure which indicates
|
||||
whether the call has resolved successfully, has returned an error, or is still in-flight. Based
|
||||
on this we either display an `InteractiveCode` with the type, `mapRpcError` the error in order
|
||||
to turn it into a readable message, or show a `Loading..` message, respectively.
|
||||
The code below demonstrates useful parts of the API.
|
||||
To make RPC method calls, we invoke the `useRpcSession` hook.
|
||||
The `useAsync` helper packs the results of an RPC call into an `AsyncState` structure
|
||||
which indicates whether the call has resolved successfully,
|
||||
has returned an error, or is still in-flight.
|
||||
Based on this we either display an `InteractiveCode` component with the result,
|
||||
`mapRpcError` the error in order to turn it into a readable message,
|
||||
or show a `Loading..` message, respectively.
|
||||
-/
|
||||
|
||||
@[widget_module]
|
||||
@@ -140,10 +170,10 @@ def checkWidget : Widget.Module where
|
||||
javascript := "
|
||||
import * as React from 'react';
|
||||
const e = React.createElement;
|
||||
import { RpcContext, InteractiveCode, useAsync, mapRpcError } from '@leanprover/infoview';
|
||||
import { useRpcSession, InteractiveCode, useAsync, mapRpcError } from '@leanprover/infoview';
|
||||
|
||||
export default function(props) {
|
||||
const rs = React.useContext(RpcContext)
|
||||
const rs = useRpcSession()
|
||||
const [name, setName] = React.useState('getType')
|
||||
|
||||
const st = useAsync(() =>
|
||||
@@ -159,7 +189,7 @@ export default function(props) {
|
||||
"
|
||||
|
||||
/-!
|
||||
Finally we can try out the widget.
|
||||
We can now try out the widget.
|
||||
-/
|
||||
|
||||
#widget checkWidget
|
||||
@@ -169,30 +199,31 @@ Finally we can try out the widget.
|
||||
|
||||
## Building widget sources
|
||||
|
||||
While typing JavaScript inline is fine for a simple example, for real developments we want to use
|
||||
packages from NPM, a proper build system, and JSX. Thus, most actual widget sources are built with
|
||||
Lake and NPM. They consist of multiple files and may import libraries which don't work as ESModules
|
||||
by default. On the other hand a widget source must be a single, self-contained ESModule in the form
|
||||
of a string. Readers familiar with web development may already have guessed that to obtain such a
|
||||
string, we need a *bundler*. Two popular choices are [`rollup.js`](https://rollupjs.org/guide/en/)
|
||||
and [`esbuild`](https://esbuild.github.io/). If we go with `rollup.js`, to make a widget work with
|
||||
the infoview we need to:
|
||||
While typing JavaScript inline is fine for a simple example,
|
||||
for real developments we want to use packages from NPM, a proper build system, and JSX.
|
||||
Thus, most actual widget sources are built with Lake and NPM.
|
||||
They consist of multiple files and may import libraries which don't work as ESModules by default.
|
||||
On the other hand a widget module must be a single, self-contained ESModule in the form of a string.
|
||||
Readers familiar with web development may already have guessed that to obtain such a string, we need a *bundler*.
|
||||
Two popular choices are [`rollup.js`](https://rollupjs.org/guide/en/)
|
||||
and [`esbuild`](https://esbuild.github.io/).
|
||||
If we go with `rollup.js`, to make a widget work with the infoview we need to:
|
||||
- Set [`output.format`](https://rollupjs.org/guide/en/#outputformat) to `'es'`.
|
||||
- [Externalize](https://rollupjs.org/guide/en/#external) `react`, `react-dom`, `@leanprover/infoview`.
|
||||
These libraries are already loaded by the infoview so they should not be bundled.
|
||||
|
||||
In the RubiksCube sample, we provide a working `rollup.js` build configuration in
|
||||
[rollup.config.js](https://github.com/leanprover/lean4-samples/blob/main/RubiksCube/widget/rollup.config.js).
|
||||
ProofWidgets provides a working `rollup.js` build configuration in
|
||||
[rollup.config.js](https://github.com/leanprover-community/ProofWidgets4/blob/main/widget/rollup.config.js).
|
||||
|
||||
## Inserting text
|
||||
|
||||
We can also instruct the editor to insert text, copy text to the clipboard, or
|
||||
reveal a certain location in the document.
|
||||
To do this, use the `React.useContext(EditorContext)` React context.
|
||||
This will return an `EditorConnection` whose `api` field contains a number of methods to
|
||||
interact with the text editor.
|
||||
Besides making RPC calls, widgets can instruct the editor to carry out certain actions.
|
||||
We can insert text, copy text to the clipboard, or highlight a certain location in the document.
|
||||
To do this, use the `EditorContext` React context.
|
||||
This will return an `EditorConnection`
|
||||
whose `api` field contains a number of methods that interact with the editor.
|
||||
|
||||
You can see the full API for this [here](https://github.com/leanprover/vscode-lean4/blob/master/lean4-infoview-api/src/infoviewApi.ts#L52)
|
||||
The full API can be viewed [here](https://github.com/leanprover/vscode-lean4/blob/master/lean4-infoview-api/src/infoviewApi.ts#L52).
|
||||
-/
|
||||
|
||||
@[widget_module]
|
||||
@@ -212,6 +243,4 @@ export default function(props) {
|
||||
}
|
||||
"
|
||||
|
||||
/-! Finally, we can try this out: -/
|
||||
|
||||
#widget insertTextWidget
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
@@ -5,11 +5,11 @@ See [Setup](./setup.md) for supported platforms and other ways to set up Lean 4.
|
||||
|
||||
1. Install [VS Code](https://code.visualstudio.com/).
|
||||
|
||||
1. Launch VS Code and install the `lean4` extension by clicking on the "Extensions" sidebar entry and searching for "lean4".
|
||||
1. Launch VS Code and install the `Lean 4` extension by clicking on the 'Extensions' sidebar entry and searching for 'Lean 4'.
|
||||
|
||||

|
||||
|
||||
1. Open the Lean 4 setup guide by creating a new text file using "File > New Text File" (`Ctrl+N` / `Cmd+N`), clicking on the ∀-symbol in the top right and selecting "Documentation… > Docs: Show Setup Guide".
|
||||
1. Open the Lean 4 setup guide by creating a new text file using 'File > New Text File' (`Ctrl+N` / `Cmd+N`), clicking on the ∀-symbol in the top right and selecting 'Documentation… > Docs: Show Setup Guide'.
|
||||
|
||||

|
||||
|
||||
|
||||
17
flake.lock
generated
17
flake.lock
generated
@@ -34,6 +34,22 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-cadical": {
|
||||
"locked": {
|
||||
"lastModified": 1722221733,
|
||||
"narHash": "sha256-sga9SrrPb+pQJxG1ttJfMPheZvDOxApFfwXCFO0H9xw=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "12bf09802d77264e441f48e25459c10c93eada2e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "12bf09802d77264e441f48e25459c10c93eada2e",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-old": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
@@ -55,6 +71,7 @@
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"nixpkgs-cadical": "nixpkgs-cadical",
|
||||
"nixpkgs-old": "nixpkgs-old"
|
||||
}
|
||||
},
|
||||
|
||||
11
flake.nix
11
flake.nix
@@ -5,6 +5,8 @@
|
||||
# old nixpkgs used for portable release with older glibc (2.27)
|
||||
inputs.nixpkgs-old.url = "github:NixOS/nixpkgs/nixos-19.03";
|
||||
inputs.nixpkgs-old.flake = false;
|
||||
# for cadical 1.9.5; sync with CMakeLists.txt
|
||||
inputs.nixpkgs-cadical.url = "github:NixOS/nixpkgs/12bf09802d77264e441f48e25459c10c93eada2e";
|
||||
inputs.flake-utils.url = "github:numtide/flake-utils";
|
||||
|
||||
outputs = { self, nixpkgs, nixpkgs-old, flake-utils, ... }@inputs: flake-utils.lib.eachDefaultSystem (system:
|
||||
@@ -14,6 +16,11 @@
|
||||
pkgsDist-old = import nixpkgs-old { inherit system; };
|
||||
# An old nixpkgs for creating releases with an old glibc
|
||||
pkgsDist-old-aarch = import nixpkgs-old { localSystem.config = "aarch64-unknown-linux-gnu"; };
|
||||
pkgsCadical = import inputs.nixpkgs-cadical { inherit system; };
|
||||
cadical = if pkgs.stdenv.isLinux then
|
||||
# use statically-linked cadical on Linux to avoid glibc versioning troubles
|
||||
pkgsCadical.pkgsStatic.cadical.overrideAttrs { doCheck = false; }
|
||||
else pkgsCadical.cadical;
|
||||
|
||||
lean-packages = pkgs.callPackage (./nix/packages.nix) { src = ./.; };
|
||||
|
||||
@@ -21,11 +28,9 @@
|
||||
stdenv = pkgs.overrideCC pkgs.stdenv lean-packages.llvmPackages.clang;
|
||||
} ({
|
||||
buildInputs = with pkgs; [
|
||||
cmake gmp libuv ccache
|
||||
cmake gmp libuv ccache cadical
|
||||
lean-packages.llvmPackages.llvm # llvm-symbolizer for asan/lsan
|
||||
gdb
|
||||
# TODO: only add when proven to not affect the flakification
|
||||
#pkgs.python3
|
||||
tree # for CI
|
||||
];
|
||||
# https://github.com/NixOS/nixpkgs/issues/60919
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{ src, debug ? false, stage0debug ? false, extraCMakeFlags ? [],
|
||||
stdenv, lib, cmake, gmp, libuv, git, gnumake, bash, buildLeanPackage, writeShellScriptBin, runCommand, symlinkJoin, lndir, perl, gnused, darwin, llvmPackages, linkFarmFromDrvs,
|
||||
stdenv, lib, cmake, gmp, libuv, cadical, git, gnumake, bash, buildLeanPackage, writeShellScriptBin, runCommand, symlinkJoin, lndir, perl, gnused, darwin, llvmPackages, linkFarmFromDrvs,
|
||||
... } @ args:
|
||||
with builtins;
|
||||
lib.warn "The Nix-based build is deprecated" rec {
|
||||
@@ -17,7 +17,7 @@ lib.warn "The Nix-based build is deprecated" rec {
|
||||
'';
|
||||
} // args // {
|
||||
src = args.realSrc or (sourceByRegex args.src [ "[a-z].*" "CMakeLists\.txt" ]);
|
||||
cmakeFlags = (args.cmakeFlags or [ "-DSTAGE=1" "-DPREV_STAGE=./faux-prev-stage" "-DUSE_GITHASH=OFF" ]) ++ (args.extraCMakeFlags or extraCMakeFlags) ++ lib.optional (args.debug or debug) [ "-DCMAKE_BUILD_TYPE=Debug" ];
|
||||
cmakeFlags = (args.cmakeFlags or [ "-DSTAGE=1" "-DPREV_STAGE=./faux-prev-stage" "-DUSE_GITHASH=OFF" "-DCADICAL=${cadical}/bin/cadical" ]) ++ (args.extraCMakeFlags or extraCMakeFlags) ++ lib.optional (args.debug or debug) [ "-DCMAKE_BUILD_TYPE=Debug" ];
|
||||
preConfigure = args.preConfigure or "" + ''
|
||||
# ignore absence of submodule
|
||||
sed -i 's!lake/Lake.lean!!' CMakeLists.txt
|
||||
@@ -158,7 +158,7 @@ lib.warn "The Nix-based build is deprecated" rec {
|
||||
test = buildCMake {
|
||||
name = "lean-test-${desc}";
|
||||
realSrc = lib.sourceByRegex src [ "src.*" "tests.*" ];
|
||||
buildInputs = [ gmp libuv perl git ];
|
||||
buildInputs = [ gmp libuv perl git cadical ];
|
||||
preConfigure = ''
|
||||
cd src
|
||||
'';
|
||||
|
||||
17
releases_drafts/new-variable.md
Normal file
17
releases_drafts/new-variable.md
Normal file
@@ -0,0 +1,17 @@
|
||||
**breaking change**
|
||||
|
||||
The effect of the `variable` command on proofs of `theorem`s has been changed. Whether such section variables are accessible in the proof now depends only on the theorem signature and other top-level commands, not on the proof itself.
|
||||
This change ensures that
|
||||
* the statement of a theorem is independent of its proof. In other words, changes in the proof cannot change the theorem statement.
|
||||
* tactics such as `induction` cannot accidentally include a section variable.
|
||||
* the proof can be elaborated in parallel to subsequent declarations in a future version of Lean.
|
||||
|
||||
The effect of `variable`s on the theorem header as well as on other kinds of declarations is unchanged.
|
||||
|
||||
Specifically, section variables are included if they
|
||||
* are directly referenced by the theorem header,
|
||||
* are included via the new `include` command in the current section and not subsequently mentioned in an `omit` statement,
|
||||
* are directly referenced by any variable included by these rules, OR
|
||||
* are instance-implicit variables that reference only variables included by these rules.
|
||||
|
||||
For porting, a new option `deprecated.oldSectionVars` is included to locally switch back to the old behavior.
|
||||
@@ -535,6 +535,12 @@ else()
|
||||
OUTPUT_NAME leancpp)
|
||||
endif()
|
||||
|
||||
if((${STAGE} GREATER 0) AND 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)
|
||||
endif()
|
||||
|
||||
# MSYS2 bash usually handles Windows paths relatively well, but not when putting them in the PATH
|
||||
string(REGEX REPLACE "^([a-zA-Z]):" "/\\1" LEAN_BIN "${CMAKE_BINARY_DIR}/bin")
|
||||
|
||||
@@ -633,6 +639,10 @@ 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)
|
||||
install(PROGRAMS "${CADICAL}" DESTINATION bin)
|
||||
endif()
|
||||
|
||||
add_custom_target(clean-stdlib
|
||||
COMMAND rm -rf "${CMAKE_BINARY_DIR}/lib" || true)
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ theorem apply_ite (f : α → β) (P : Prop) [Decidable P] (x y : α) :
|
||||
-- We don't mark this as `simp` as it is already handled by `ite_eq_right_iff`.
|
||||
theorem ite_some_none_eq_none [Decidable P] :
|
||||
(if P then some x else none) = none ↔ ¬ P := by
|
||||
simp only [ite_eq_right_iff]
|
||||
simp only [ite_eq_right_iff, reduceCtorEq]
|
||||
rfl
|
||||
|
||||
@[simp] theorem ite_some_none_eq_some [Decidable P] :
|
||||
|
||||
@@ -313,12 +313,6 @@ instance optionCoe {α : Type u} : Coe α (Option α) where
|
||||
instance subtypeCoe {α : Sort u} {p : α → Prop} : CoeOut (Subtype p) α where
|
||||
coe v := v.val
|
||||
|
||||
instance boolPredToPred : Coe (α → Bool) (α → Prop) where
|
||||
coe r := fun a => Eq (r a) true
|
||||
|
||||
instance boolRelToRel : Coe (α → α → Bool) (α → α → Prop) where
|
||||
coe r := fun a b => Eq (r a b) true
|
||||
|
||||
/-! # Coe bridge -/
|
||||
|
||||
/--
|
||||
|
||||
@@ -36,6 +36,17 @@ and `flip (·<·)` is the greater-than relation.
|
||||
|
||||
theorem Function.comp_def {α β δ} (f : β → δ) (g : α → β) : f ∘ g = fun x => f (g x) := rfl
|
||||
|
||||
@[simp] theorem Function.const_comp {f : α → β} {c : γ} :
|
||||
(Function.const β c ∘ f) = Function.const α c := by
|
||||
rfl
|
||||
@[simp] theorem Function.comp_const {f : β → γ} {b : β} :
|
||||
(f ∘ Function.const α b) = Function.const α (f b) := by
|
||||
rfl
|
||||
@[simp] theorem Function.true_comp {f : α → β} : ((fun _ => true) ∘ f) = fun _ => true := by
|
||||
rfl
|
||||
@[simp] theorem Function.false_comp {f : α → β} : ((fun _ => false) ∘ f) = fun _ => false := by
|
||||
rfl
|
||||
|
||||
attribute [simp] namedPattern
|
||||
|
||||
/--
|
||||
@@ -1553,7 +1564,7 @@ so you should consider the simpler versions if they apply:
|
||||
* `Quot.recOnSubsingleton`, when the target type is a `Subsingleton`
|
||||
* `Quot.hrecOn`, which uses `HEq (f a) (f b)` instead of a `sound p ▸ f a = f b` assummption
|
||||
-/
|
||||
protected abbrev rec
|
||||
@[elab_as_elim] protected abbrev rec
|
||||
(f : (a : α) → motive (Quot.mk r a))
|
||||
(h : (a b : α) → (p : r a b) → Eq.ndrec (f a) (sound p) = f b)
|
||||
(q : Quot r) : motive q :=
|
||||
@@ -1639,7 +1650,7 @@ protected theorem ind {α : Sort u} {s : Setoid α} {motive : Quotient s → Pro
|
||||
|
||||
/--
|
||||
The analogue of `Quot.liftOn`: if `f : α → β` respects the equivalence relation `≈`,
|
||||
then it lifts to a function on `Quotient s` such that `lift (mk a) f h = f a`.
|
||||
then it lifts to a function on `Quotient s` such that `liftOn (mk a) f h = f a`.
|
||||
-/
|
||||
protected abbrev liftOn {α : Sort u} {β : Sort v} {s : Setoid α} (q : Quotient s) (f : α → β) (c : (a b : α) → a ≈ b → f a = f b) : β :=
|
||||
Quot.liftOn q f c
|
||||
|
||||
@@ -37,3 +37,5 @@ import Init.Data.Cast
|
||||
import Init.Data.Sum
|
||||
import Init.Data.BEq
|
||||
import Init.Data.Subtype
|
||||
import Init.Data.ULift
|
||||
import Init.Data.PLift
|
||||
|
||||
@@ -260,7 +260,7 @@ theorem Context.evalList_sort (ctx : Context α) (h : ContextInformation.isComm
|
||||
simp [ContextInformation.isComm, Option.isSome] at h
|
||||
match h₂ : ctx.comm with
|
||||
| none =>
|
||||
simp only [h₂] at h
|
||||
simp [h₂] at h
|
||||
| some val =>
|
||||
simp [h₂] at h
|
||||
exact val.down
|
||||
|
||||
@@ -13,11 +13,11 @@ namespace Array
|
||||
/-- `a ∈ as` is a predicate which asserts that `a` is in the array `as`. -/
|
||||
-- NB: This is defined as a structure rather than a plain def so that a lemma
|
||||
-- like `sizeOf_lt_of_mem` will not apply with no actual arrays around.
|
||||
structure Mem (a : α) (as : Array α) : Prop where
|
||||
structure Mem (as : Array α) (a : α) : Prop where
|
||||
val : a ∈ as.data
|
||||
|
||||
instance : Membership α (Array α) where
|
||||
mem a as := Mem a as
|
||||
mem := Mem
|
||||
|
||||
theorem sizeOf_lt_of_mem [SizeOf α] {as : Array α} (h : a ∈ as) : sizeOf a < sizeOf as := by
|
||||
cases as with | _ as =>
|
||||
|
||||
@@ -152,9 +152,6 @@ theorem getLsb_ofNat (n : Nat) (x : Nat) (i : Nat) :
|
||||
getLsb (BitVec.ofNat n x) i = (i < n && x.testBit i) := by
|
||||
simp [getLsb, BitVec.ofNat, Fin.val_ofNat']
|
||||
|
||||
@[simp, deprecated toNat_ofNat (since := "2024-02-22")]
|
||||
theorem toNat_zero (n : Nat) : (0#n).toNat = 0 := by trivial
|
||||
|
||||
@[simp] theorem getLsb_zero : (0#w).getLsb i = false := by simp [getLsb]
|
||||
|
||||
@[simp] theorem getMsb_zero : (0#w).getMsb i = false := by simp [getMsb]
|
||||
@@ -303,6 +300,17 @@ theorem toInt_ofNat {n : Nat} (x : Nat) :
|
||||
@[simp] theorem ofInt_natCast (w n : Nat) :
|
||||
BitVec.ofInt w (n : Int) = BitVec.ofNat w n := rfl
|
||||
|
||||
@[simp] theorem ofInt_ofNat (w n : Nat) :
|
||||
BitVec.ofInt w (no_index (OfNat.ofNat n)) = BitVec.ofNat w (OfNat.ofNat n) := rfl
|
||||
|
||||
theorem toInt_neg_iff {w : Nat} {x : BitVec w} :
|
||||
BitVec.toInt x < 0 ↔ 2 ^ w ≤ 2 * x.toNat := by
|
||||
simp [toInt_eq_toNat_cond]; omega
|
||||
|
||||
theorem toInt_pos_iff {w : Nat} {x : BitVec w} :
|
||||
0 ≤ BitVec.toInt x ↔ 2 * x.toNat < 2 ^ w := by
|
||||
simp [toInt_eq_toNat_cond]; omega
|
||||
|
||||
/-! ### zeroExtend and truncate -/
|
||||
|
||||
theorem truncate_eq_zeroExtend {v : Nat} {x : BitVec w} :
|
||||
@@ -413,11 +421,9 @@ theorem msb_truncate (x : BitVec w) : (x.truncate (k + 1)).msb = x.getLsb k := b
|
||||
(x.truncate l).truncate k = x.truncate k :=
|
||||
zeroExtend_zeroExtend_of_le x h
|
||||
|
||||
/--Truncating by the bitwidth has no effect. -/
|
||||
@[simp]
|
||||
theorem truncate_eq_self {x : BitVec w} : x.truncate w = x := by
|
||||
ext i
|
||||
simp [getLsb_zeroExtend]
|
||||
/-- Truncating by the bitwidth has no effect. -/
|
||||
-- This doesn't need to be a `@[simp]` lemma, as `zeroExtend_eq` applies.
|
||||
theorem truncate_eq_self {x : BitVec w} : x.truncate w = x := zeroExtend_eq _
|
||||
|
||||
@[simp] theorem truncate_cast {h : w = v} : (cast h x).truncate k = x.truncate k := by
|
||||
apply eq_of_getLsb_eq
|
||||
@@ -776,7 +782,6 @@ theorem shiftLeft_shiftLeft {w : Nat} (x : BitVec w) (n m : Nat) :
|
||||
@[simp]
|
||||
theorem shiftLeft_eq' {x : BitVec w₁} {y : BitVec w₂} : x <<< y = x <<< y.toNat := by rfl
|
||||
|
||||
@[simp]
|
||||
theorem shiftLeft_zero' {x : BitVec w₁} : x <<< 0#w₂ = x := by simp
|
||||
|
||||
theorem shiftLeft_shiftLeft' {x : BitVec w₁} {y : BitVec w₂} {z : BitVec w₃} :
|
||||
@@ -986,7 +991,7 @@ theorem signExtend_eq_not_zeroExtend_not_of_msb_false {x : BitVec w} {v : Nat} (
|
||||
ext i
|
||||
by_cases hv : i < v
|
||||
· simp only [signExtend, getLsb, getLsb_zeroExtend, hv, decide_True, Bool.true_and, toNat_ofInt,
|
||||
BitVec.toInt_eq_msb_cond, hmsb, ↓reduceIte]
|
||||
BitVec.toInt_eq_msb_cond, hmsb, ↓reduceIte, reduceCtorEq]
|
||||
rw [Int.ofNat_mod_ofNat, Int.toNat_ofNat, Nat.testBit_mod_two_pow]
|
||||
simp [BitVec.testBit_toNat]
|
||||
· simp only [getLsb_zeroExtend, hv, decide_False, Bool.false_and]
|
||||
@@ -1022,6 +1027,18 @@ theorem signExtend_eq_not_zeroExtend_not_of_msb_true {x : BitVec w} {v : Nat} (h
|
||||
· rw [signExtend_eq_not_zeroExtend_not_of_msb_true hmsb]
|
||||
by_cases (i < v) <;> by_cases (i < w) <;> simp_all <;> omega
|
||||
|
||||
/-- Sign extending to a width smaller than the starting width is a truncation. -/
|
||||
theorem signExtend_eq_truncate_of_lt (x : BitVec w) {v : Nat} (hv : v ≤ w):
|
||||
x.signExtend v = x.truncate v := by
|
||||
ext i
|
||||
simp only [getLsb_signExtend, Fin.is_lt, decide_True, Bool.true_and, getLsb_zeroExtend,
|
||||
ite_eq_left_iff, Nat.not_lt]
|
||||
omega
|
||||
|
||||
/-- Sign extending to the same bitwidth is a no op. -/
|
||||
theorem signExtend_eq (x : BitVec w) : x.signExtend w = x := by
|
||||
rw [signExtend_eq_truncate_of_lt _ (Nat.le_refl _), truncate_eq]
|
||||
|
||||
/-! ### append -/
|
||||
|
||||
theorem append_def (x : BitVec v) (y : BitVec w) :
|
||||
@@ -1449,20 +1466,6 @@ protected theorem lt_of_le_ne (x y : BitVec n) (h1 : x <= y) (h2 : ¬ x = y) : x
|
||||
simp
|
||||
exact Nat.lt_of_le_of_ne
|
||||
|
||||
/-! ### intMax -/
|
||||
|
||||
/-- The bitvector of width `w` that has the largest value when interpreted as an integer. -/
|
||||
def intMax (w : Nat) : BitVec w := BitVec.ofNat w (2^w - 1)
|
||||
|
||||
theorem getLsb_intMax_eq (w : Nat) : (intMax w).getLsb i = decide (i < w) := by
|
||||
simp [intMax, getLsb]
|
||||
|
||||
theorem toNat_intMax_eq : (intMax w).toNat = 2^w - 1 := by
|
||||
have h : 2^w - 1 < 2^w := by
|
||||
have pos : 2^w > 0 := Nat.pow_pos (by decide)
|
||||
omega
|
||||
simp [intMax, Nat.shiftLeft_eq, Nat.one_mul, natCast_eq_ofNat, toNat_ofNat, Nat.mod_eq_of_lt h]
|
||||
|
||||
/-! ### ofBoolList -/
|
||||
|
||||
@[simp] theorem getMsb_ofBoolListBE : (ofBoolListBE bs).getMsb i = bs.getD i false := by
|
||||
@@ -1786,4 +1789,54 @@ theorem getLsb_replicate {n w : Nat} (x : BitVec w) :
|
||||
simp only [show ¬i < w * n by omega, decide_False, cond_false, hi, Bool.false_and]
|
||||
apply BitVec.getLsb_ge (x := x) (i := i - w * n) (ge := by omega)
|
||||
|
||||
/-! ### intMin -/
|
||||
|
||||
/-- The bitvector of width `w` that has the smallest value when interpreted as an integer. -/
|
||||
abbrev intMin (w : Nat) := twoPow w (w - 1)
|
||||
|
||||
theorem getLsb_intMin (w : Nat) : (intMin w).getLsb i = decide (i + 1 = w) := by
|
||||
simp only [getLsb_twoPow, boolToPropSimps]
|
||||
omega
|
||||
|
||||
@[simp, bv_toNat]
|
||||
theorem toNat_intMin : (intMin w).toNat = 2 ^ (w - 1) % 2 ^ w := by
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem neg_intMin {w : Nat} : -intMin w = intMin w := by
|
||||
by_cases h : 0 < w
|
||||
· simp [bv_toNat, h]
|
||||
· simp only [Nat.not_lt, Nat.le_zero_eq] at h
|
||||
simp [bv_toNat, h]
|
||||
|
||||
/-! ### intMax -/
|
||||
|
||||
/-- The bitvector of width `w` that has the largest value when interpreted as an integer. -/
|
||||
abbrev intMax (w : Nat) := (twoPow w (w - 1)) - 1
|
||||
|
||||
@[simp, bv_toNat]
|
||||
theorem toNat_intMax : (intMax w).toNat = 2 ^ (w - 1) - 1 := by
|
||||
simp only [intMax]
|
||||
by_cases h : w = 0
|
||||
· simp [h]
|
||||
· have h' : 0 < w := by omega
|
||||
rw [toNat_sub, toNat_twoPow, ← Nat.sub_add_comm (by simpa [h'] using Nat.one_le_two_pow),
|
||||
Nat.add_sub_assoc (by simpa [h'] using Nat.one_le_two_pow),
|
||||
Nat.two_pow_pred_mod_two_pow h', ofNat_eq_ofNat, toNat_ofNat, Nat.one_mod_two_pow h',
|
||||
Nat.add_mod_left, Nat.mod_eq_of_lt]
|
||||
have := Nat.two_pow_pred_lt_two_pow h'
|
||||
have := Nat.two_pow_pos w
|
||||
omega
|
||||
|
||||
@[simp]
|
||||
theorem getLsb_intMax (w : Nat) : (intMax w).getLsb i = decide (i + 1 < w) := by
|
||||
rw [← testBit_toNat, toNat_intMax, Nat.testBit_two_pow_sub_one, decide_eq_decide]
|
||||
omega
|
||||
|
||||
@[simp] theorem intMax_add_one {w : Nat} : intMax w + 1#w = intMin w := by
|
||||
simp only [toNat_eq, toNat_intMax, toNat_add, toNat_intMin, toNat_ofNat, Nat.add_mod_mod]
|
||||
by_cases h : w = 0
|
||||
· simp [h]
|
||||
· rw [Nat.sub_add_cancel (Nat.two_pow_pos (w - 1)), Nat.two_pow_pred_mod_two_pow (by omega)]
|
||||
|
||||
end BitVec
|
||||
|
||||
@@ -55,6 +55,12 @@ theorem eq_iff_iff {a b : Bool} : a = b ↔ (a ↔ b) := by cases b <;> simp
|
||||
theorem decide_true_eq {b : Bool} [Decidable (true = b)] : decide (true = b) = b := by cases b <;> simp
|
||||
theorem decide_false_eq {b : Bool} [Decidable (false = b)] : decide (false = b) = !b := by cases b <;> simp
|
||||
|
||||
-- These lemmas assist with confluence.
|
||||
@[simp] theorem eq_false_imp_eq_true_iff :
|
||||
∀(a b : Bool), ((a = false → b = true) ↔ (b = false → a = true)) = True := by decide
|
||||
@[simp] theorem eq_true_imp_eq_false_iff :
|
||||
∀(a b : Bool), ((a = true → b = false) ↔ (b = true → a = false)) = True := by decide
|
||||
|
||||
/-! ### and -/
|
||||
|
||||
@[simp] theorem and_self_left : ∀(a b : Bool), (a && (a && b)) = (a && b) := by decide
|
||||
@@ -91,6 +97,11 @@ Needed for confluence of term `(a && b) ↔ a` which reduces to `(a && b) = a` v
|
||||
@[simp] theorem iff_self_and : ∀(a b : Bool), (a = (a && b)) ↔ (a → b) := by decide
|
||||
@[simp] theorem iff_and_self : ∀(a b : Bool), (b = (a && b)) ↔ (b → a) := by decide
|
||||
|
||||
@[simp] theorem not_and_iff_left_iff_imp : ∀ (a b : Bool), ((!a && b) = a) ↔ !a ∧ !b := by decide
|
||||
@[simp] theorem and_not_iff_right_iff_imp : ∀ (a b : Bool), ((a && !b) = b) ↔ !a ∧ !b := by decide
|
||||
@[simp] theorem iff_not_self_and : ∀ (a b : Bool), (a = (!a && b)) ↔ !a ∧ !b := by decide
|
||||
@[simp] theorem iff_and_not_self : ∀ (a b : Bool), (b = (a && !b)) ↔ !a ∧ !b := by decide
|
||||
|
||||
/-! ### or -/
|
||||
|
||||
@[simp] theorem or_self_left : ∀(a b : Bool), (a || (a || b)) = (a || b) := by decide
|
||||
@@ -120,6 +131,11 @@ Needed for confluence of term `(a || b) ↔ a` which reduces to `(a || b) = a` v
|
||||
@[simp] theorem iff_self_or : ∀(a b : Bool), (a = (a || b)) ↔ (b → a) := by decide
|
||||
@[simp] theorem iff_or_self : ∀(a b : Bool), (b = (a || b)) ↔ (a → b) := by decide
|
||||
|
||||
@[simp] theorem not_or_iff_left_iff_imp : ∀ (a b : Bool), ((!a || b) = a) ↔ a ∧ b := by decide
|
||||
@[simp] theorem or_not_iff_right_iff_imp : ∀ (a b : Bool), ((a || !b) = b) ↔ a ∧ b := by decide
|
||||
@[simp] theorem iff_not_self_or : ∀ (a b : Bool), (a = (!a || b)) ↔ a ∧ b := by decide
|
||||
@[simp] theorem iff_or_not_self : ∀ (a b : Bool), (b = (a || !b)) ↔ a ∧ b := by decide
|
||||
|
||||
theorem or_comm : ∀ (x y : Bool), (x || y) = (y || x) := by decide
|
||||
instance : Std.Commutative (· || ·) := ⟨or_comm⟩
|
||||
|
||||
@@ -134,7 +150,7 @@ theorem and_or_distrib_right : ∀ (x y z : Bool), ((x || y) && z) = (x && z ||
|
||||
theorem or_and_distrib_left : ∀ (x y z : Bool), (x || y && z) = ((x || y) && (x || z)) := by decide
|
||||
theorem or_and_distrib_right : ∀ (x y z : Bool), (x && y || z) = ((x || z) && (y || z)) := by decide
|
||||
|
||||
theorem and_xor_distrib_left : ∀ (x y z : Bool), (x && xor y z) = xor (x && y) (x && z) := by decide
|
||||
theorem and_xor_distrib_left : ∀ (x y z : Bool), (x && xor y z) = xor (x && y) (x && z) := by decide
|
||||
theorem and_xor_distrib_right : ∀ (x y z : Bool), (xor x y && z) = xor (x && z) (y && z) := by decide
|
||||
|
||||
/-- De Morgan's law for boolean and -/
|
||||
@@ -202,8 +218,11 @@ instance : Std.LawfulIdentity (· != ·) false where
|
||||
@[simp] theorem not_beq_self : ∀ (x : Bool), ((!x) == x) = false := by decide
|
||||
@[simp] theorem beq_not_self : ∀ (x : Bool), (x == !x) = false := by decide
|
||||
|
||||
@[simp] theorem not_bne_self : ∀ (x : Bool), ((!x) != x) = true := by decide
|
||||
@[simp] theorem bne_not_self : ∀ (x : Bool), (x != !x) = true := by decide
|
||||
@[simp] theorem not_bne : ∀ (a b : Bool), ((!a) != b) = !(a != b) := by decide
|
||||
@[simp] theorem bne_not : ∀ (a b : Bool), (a != !b) = !(a != b) := by decide
|
||||
|
||||
theorem not_bne_self : ∀ (x : Bool), ((!x) != x) = true := by decide
|
||||
theorem bne_not_self : ∀ (x : Bool), (x != !x) = true := by decide
|
||||
|
||||
/-
|
||||
Added for equivalence with `Bool.not_beq_self` and needed for confluence
|
||||
@@ -235,8 +254,10 @@ theorem beq_eq_decide_eq [BEq α] [LawfulBEq α] [DecidableEq α] (a b : α) :
|
||||
· simp [ne_of_beq_false h]
|
||||
· simp [eq_of_beq h]
|
||||
|
||||
@[simp] theorem not_eq_not : ∀ {a b : Bool}, ¬a = !b ↔ a = b := by decide
|
||||
theorem eq_not : ∀ (a b : Bool), (a = (!b)) ↔ (a ≠ b) := by decide
|
||||
theorem not_eq : ∀ (a b : Bool), ((!a) = b) ↔ (a ≠ b) := by decide
|
||||
|
||||
@[simp] theorem not_eq_not : ∀ {a b : Bool}, ¬a = !b ↔ a = b := by decide
|
||||
@[simp] theorem not_not_eq : ∀ {a b : Bool}, ¬(!a) = b ↔ a = b := by decide
|
||||
|
||||
@[simp] theorem coe_iff_coe : ∀(a b : Bool), (a ↔ b) ↔ a = b := by decide
|
||||
@@ -360,9 +381,6 @@ def toNat (b : Bool) : Nat := cond b 1 0
|
||||
theorem toNat_le (c : Bool) : c.toNat ≤ 1 := by
|
||||
cases c <;> trivial
|
||||
|
||||
@[deprecated toNat_le (since := "2024-02-23")]
|
||||
abbrev toNat_le_one := toNat_le
|
||||
|
||||
theorem toNat_lt (b : Bool) : b.toNat < 2 :=
|
||||
Nat.lt_succ_of_le (toNat_le _)
|
||||
|
||||
@@ -427,16 +445,18 @@ theorem not_ite_eq_false_eq_true (p : Prop) [h : Decidable p] (b c : Bool) :
|
||||
cases h with | _ p => simp [p]
|
||||
|
||||
/-
|
||||
Added for confluence between `if_true_left` and `ite_false_same` on
|
||||
`if b = true then True else b = true`
|
||||
It would be nice to have this for confluence between `if_true_left` and `ite_false_same` on
|
||||
`if b = true then True else b = true`.
|
||||
However the discrimination tree key is just `→`, so this is tried too often.
|
||||
-/
|
||||
@[simp] theorem eq_false_imp_eq_true : ∀(b:Bool), (b = false → b = true) ↔ (b = true) := by decide
|
||||
theorem eq_false_imp_eq_true : ∀(b:Bool), (b = false → b = true) ↔ (b = true) := by decide
|
||||
|
||||
/-
|
||||
Added for confluence between `if_true_left` and `ite_false_same` on
|
||||
`if b = false then True else b = false`
|
||||
It would be nice to have this for confluence between `if_true_left` and `ite_false_same` on
|
||||
`if b = false then True else b = false`.
|
||||
However the discrimination tree key is just `→`, so this is tried too often.
|
||||
-/
|
||||
@[simp] theorem eq_true_imp_eq_false : ∀(b:Bool), (b = true → b = false) ↔ (b = false) := by decide
|
||||
theorem eq_true_imp_eq_false : ∀(b:Bool), (b = true → b = false) ↔ (b = false) := by decide
|
||||
|
||||
/-! ### forall -/
|
||||
|
||||
@@ -509,6 +529,10 @@ protected theorem cond_false {α : Type u} {a b : α} : cond false a b = b := co
|
||||
@[simp] theorem cond_true_right : ∀(c t : Bool), cond c t true = (!c || t) := by decide
|
||||
@[simp] theorem cond_false_right : ∀(c t : Bool), cond c t false = ( c && t) := by decide
|
||||
|
||||
-- These restore confluence between the above lemmas and `cond_not`.
|
||||
@[simp] theorem cond_true_not_same : ∀ (c b : Bool), cond c (!c) b = (!c && b) := by decide
|
||||
@[simp] theorem cond_false_not_same : ∀ (c b : Bool), cond c b (!c) = (!c || b) := by decide
|
||||
|
||||
@[simp] theorem cond_true_same : ∀(c b : Bool), cond c c b = (c || b) := by decide
|
||||
@[simp] theorem cond_false_same : ∀(c b : Bool), cond c b c = (c && b) := by decide
|
||||
|
||||
@@ -522,7 +546,7 @@ theorem apply_cond (f : α → β) {b : Bool} {a a' : α} :
|
||||
f (bif b then a else a') = bif b then f a else f a' := by
|
||||
cases b <;> simp
|
||||
|
||||
/-# decidability -/
|
||||
/-! # decidability -/
|
||||
|
||||
protected theorem decide_coe (b : Bool) [Decidable (b = true)] : decide (b = true) = b := decide_eq_true
|
||||
|
||||
@@ -538,6 +562,21 @@ protected theorem decide_coe (b : Bool) [Decidable (b = true)] : decide (b = tru
|
||||
decide (p ↔ q) = (decide p == decide q) := by
|
||||
cases dp with | _ p => simp [p]
|
||||
|
||||
@[boolToPropSimps]
|
||||
theorem and_eq_decide (p q : Prop) [dpq : Decidable (p ∧ q)] [dp : Decidable p] [dq : Decidable q] :
|
||||
(p && q) = decide (p ∧ q) := by
|
||||
cases dp with | _ p => simp [p]
|
||||
|
||||
@[boolToPropSimps]
|
||||
theorem or_eq_decide (p q : Prop) [dpq : Decidable (p ∨ q)] [dp : Decidable p] [dq : Decidable q] :
|
||||
(p || q) = decide (p ∨ q) := by
|
||||
cases dp with | _ p => simp [p]
|
||||
|
||||
@[boolToPropSimps]
|
||||
theorem decide_beq_decide (p q : Prop) [dpq : Decidable (p ↔ q)] [dp : Decidable p] [dq : Decidable q] :
|
||||
(decide p == decide q) = decide (p ↔ q) := by
|
||||
cases dp with | _ p => simp [p]
|
||||
|
||||
end Bool
|
||||
|
||||
export Bool (cond_eq_if)
|
||||
@@ -549,3 +588,19 @@ export Bool (cond_eq_if)
|
||||
|
||||
@[simp] theorem true_eq_decide_iff {p : Prop} [h : Decidable p] : true = decide p ↔ p := by
|
||||
cases h with | _ q => simp [q]
|
||||
|
||||
/-! ### coercions -/
|
||||
|
||||
/--
|
||||
This should not be turned on globally as an instance because it degrades performance in Mathlib,
|
||||
but may be used locally.
|
||||
-/
|
||||
def boolPredToPred : Coe (α → Bool) (α → Prop) where
|
||||
coe r := fun a => Eq (r a) true
|
||||
|
||||
/--
|
||||
This should not be turned on globally as an instance because it degrades performance in Mathlib,
|
||||
but may be used locally.
|
||||
-/
|
||||
def boolRelToRel : Coe (α → α → Bool) (α → α → Prop) where
|
||||
coe r := fun a b => Eq (r a b) true
|
||||
|
||||
@@ -275,6 +275,22 @@ def atEnd : Iterator → Bool
|
||||
def hasNext : Iterator → Bool
|
||||
| ⟨arr, i⟩ => i < arr.size
|
||||
|
||||
/-- The byte at the current position. --/
|
||||
@[inline]
|
||||
def curr' (it : Iterator) (h : it.hasNext) : UInt8 :=
|
||||
match it with
|
||||
| ⟨arr, i⟩ =>
|
||||
have : i < arr.size := by
|
||||
simp only [hasNext, decide_eq_true_eq] at h
|
||||
assumption
|
||||
arr[i]
|
||||
|
||||
/-- Moves the iterator's position forward by one byte. --/
|
||||
@[inline]
|
||||
def next' (it : Iterator) (_h : it.hasNext) : Iterator :=
|
||||
match it with
|
||||
| ⟨arr, i⟩ => ⟨arr, i + 1⟩
|
||||
|
||||
/-- True if the position is not zero. -/
|
||||
@[inline]
|
||||
def hasPrev : Iterator → Bool
|
||||
|
||||
@@ -63,27 +63,27 @@ instance : Inhabited Char where
|
||||
default := 'A'
|
||||
|
||||
/-- Is the character a space (U+0020) a tab (U+0009), a carriage return (U+000D) or a newline (U+000A)? -/
|
||||
def isWhitespace (c : Char) : Bool :=
|
||||
@[inline] def isWhitespace (c : Char) : Bool :=
|
||||
c = ' ' || c = '\t' || c = '\r' || c = '\n'
|
||||
|
||||
/-- Is the character in `ABCDEFGHIJKLMNOPQRSTUVWXYZ`? -/
|
||||
def isUpper (c : Char) : Bool :=
|
||||
@[inline] def isUpper (c : Char) : Bool :=
|
||||
c.val ≥ 65 && c.val ≤ 90
|
||||
|
||||
/-- Is the character in `abcdefghijklmnopqrstuvwxyz`? -/
|
||||
def isLower (c : Char) : Bool :=
|
||||
@[inline] def isLower (c : Char) : Bool :=
|
||||
c.val ≥ 97 && c.val ≤ 122
|
||||
|
||||
/-- Is the character in `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz`? -/
|
||||
def isAlpha (c : Char) : Bool :=
|
||||
@[inline] def isAlpha (c : Char) : Bool :=
|
||||
c.isUpper || c.isLower
|
||||
|
||||
/-- Is the character in `0123456789`? -/
|
||||
def isDigit (c : Char) : Bool :=
|
||||
@[inline] def isDigit (c : Char) : Bool :=
|
||||
c.val ≥ 48 && c.val ≤ 57
|
||||
|
||||
/-- Is the character in `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789`? -/
|
||||
def isAlphanum (c : Char) : Bool :=
|
||||
@[inline] def isAlphanum (c : Char) : Bool :=
|
||||
c.isAlpha || c.isDigit
|
||||
|
||||
/-- Convert an upper case character to its lower case character.
|
||||
|
||||
@@ -11,9 +11,6 @@ import Init.ByCases
|
||||
import Init.Conv
|
||||
import Init.Omega
|
||||
|
||||
-- Remove after the next stage0 update
|
||||
set_option allowUnsafeReducibility true
|
||||
|
||||
namespace Fin
|
||||
|
||||
/-- If you actually have an element of `Fin n`, then the `n` is always positive -/
|
||||
@@ -57,9 +54,6 @@ theorem mk_val (i : Fin n) : (⟨i, i.isLt⟩ : Fin n) = i := Fin.eta ..
|
||||
@[simp] theorem val_ofNat' (a : Nat) (is_pos : n > 0) :
|
||||
(Fin.ofNat' a is_pos).val = a % n := rfl
|
||||
|
||||
@[deprecated ofNat'_zero_val (since := "2024-02-22")]
|
||||
theorem ofNat'_zero_val : (Fin.ofNat' 0 h).val = 0 := Nat.zero_mod _
|
||||
|
||||
@[simp] theorem mod_val (a b : Fin n) : (a % b).val = a.val % b.val :=
|
||||
rfl
|
||||
|
||||
@@ -141,6 +135,12 @@ theorem eq_zero_or_eq_succ {n : Nat} : ∀ i : Fin (n + 1), i = 0 ∨ ∃ j : Fi
|
||||
theorem eq_succ_of_ne_zero {n : Nat} {i : Fin (n + 1)} (hi : i ≠ 0) : ∃ j : Fin n, i = j.succ :=
|
||||
(eq_zero_or_eq_succ i).resolve_left hi
|
||||
|
||||
protected theorem le_antisymm_iff {x y : Fin n} : x = y ↔ x ≤ y ∧ y ≤ x :=
|
||||
Fin.ext_iff.trans Nat.le_antisymm_iff
|
||||
|
||||
protected theorem le_antisymm {x y : Fin n} (h1 : x ≤ y) (h2 : y ≤ x) : x = y :=
|
||||
Fin.le_antisymm_iff.2 ⟨h1, h2⟩
|
||||
|
||||
@[simp] theorem val_rev (i : Fin n) : rev i = n - (i + 1) := rfl
|
||||
|
||||
@[simp] theorem rev_rev (i : Fin n) : rev (rev i) = i := Fin.ext <| by
|
||||
|
||||
@@ -10,5 +10,6 @@ import Init.Data.Int.DivMod
|
||||
import Init.Data.Int.DivModLemmas
|
||||
import Init.Data.Int.Gcd
|
||||
import Init.Data.Int.Lemmas
|
||||
import Init.Data.Int.LemmasAux
|
||||
import Init.Data.Int.Order
|
||||
import Init.Data.Int.Pow
|
||||
|
||||
@@ -14,9 +14,6 @@ import Init.RCases
|
||||
# Lemmas about integer division needed to bootstrap `omega`.
|
||||
-/
|
||||
|
||||
-- Remove after the next stage0 update
|
||||
set_option allowUnsafeReducibility true
|
||||
|
||||
open Nat (succ)
|
||||
|
||||
namespace Int
|
||||
@@ -57,7 +54,7 @@ protected theorem dvd_mul_right (a b : Int) : a ∣ a * b := ⟨_, rfl⟩
|
||||
|
||||
protected theorem dvd_mul_left (a b : Int) : b ∣ a * b := ⟨_, Int.mul_comm ..⟩
|
||||
|
||||
protected theorem neg_dvd {a b : Int} : -a ∣ b ↔ a ∣ b := by
|
||||
@[simp] protected theorem neg_dvd {a b : Int} : -a ∣ b ↔ a ∣ b := by
|
||||
constructor <;> exact fun ⟨k, e⟩ =>
|
||||
⟨-k, by simp [e, Int.neg_mul, Int.mul_neg, Int.neg_neg]⟩
|
||||
|
||||
@@ -357,6 +354,7 @@ theorem add_ediv_of_dvd_left {a b c : Int} (H : c ∣ a) : (a + b) / c = a / c +
|
||||
@[simp] theorem mul_ediv_cancel_left (b : Int) (H : a ≠ 0) : (a * b) / a = b :=
|
||||
Int.mul_comm .. ▸ Int.mul_ediv_cancel _ H
|
||||
|
||||
|
||||
theorem div_nonneg_iff_of_pos {a b : Int} (h : 0 < b) : a / b ≥ 0 ↔ a ≥ 0 := by
|
||||
rw [Int.div_def]
|
||||
match b, h with
|
||||
@@ -454,6 +452,12 @@ theorem lt_mul_ediv_self_add {x k : Int} (h : 0 < k) : x < k * (x / k) + k :=
|
||||
@[simp] theorem add_mul_emod_self_left (a b c : Int) : (a + b * c) % b = a % b := by
|
||||
rw [Int.mul_comm, Int.add_mul_emod_self]
|
||||
|
||||
@[simp] theorem add_neg_mul_emod_self {a b c : Int} : (a + -(b * c)) % c = a % c := by
|
||||
rw [Int.neg_mul_eq_neg_mul, add_mul_emod_self]
|
||||
|
||||
@[simp] theorem add_neg_mul_emod_self_left {a b c : Int} : (a + -(b * c)) % b = a % b := by
|
||||
rw [Int.neg_mul_eq_mul_neg, add_mul_emod_self_left]
|
||||
|
||||
@[simp] theorem add_emod_self {a b : Int} : (a + b) % b = a % b := by
|
||||
have := add_mul_emod_self_left a b 1; rwa [Int.mul_one] at this
|
||||
|
||||
@@ -498,9 +502,12 @@ theorem mul_emod (a b n : Int) : (a * b) % n = (a % n) * (b % n) % n := by
|
||||
Int.mul_assoc, Int.mul_assoc, ← Int.mul_add n _ _, add_mul_emod_self_left,
|
||||
← Int.mul_assoc, add_mul_emod_self]
|
||||
|
||||
@[local simp] theorem emod_self {a : Int} : a % a = 0 := by
|
||||
@[simp] theorem emod_self {a : Int} : a % a = 0 := by
|
||||
have := mul_emod_left 1 a; rwa [Int.one_mul] at this
|
||||
|
||||
@[simp] theorem neg_emod_self (a : Int) : -a % a = 0 := by
|
||||
rw [neg_emod, Int.sub_self, zero_emod]
|
||||
|
||||
@[simp] theorem emod_emod_of_dvd (n : Int) {m k : Int}
|
||||
(h : m ∣ k) : (n % k) % m = n % m := by
|
||||
conv => rhs; rw [← emod_add_ediv n k]
|
||||
@@ -596,6 +603,14 @@ theorem emod_eq_zero_of_dvd : ∀ {a b : Int}, a ∣ b → b % a = 0
|
||||
theorem dvd_iff_emod_eq_zero (a b : Int) : a ∣ b ↔ b % a = 0 :=
|
||||
⟨emod_eq_zero_of_dvd, dvd_of_emod_eq_zero⟩
|
||||
|
||||
@[simp] theorem neg_mul_emod_left (a b : Int) : -(a * b) % b = 0 := by
|
||||
rw [← dvd_iff_emod_eq_zero, Int.dvd_neg]
|
||||
exact Int.dvd_mul_left a b
|
||||
|
||||
@[simp] theorem neg_mul_emod_right (a b : Int) : -(a * b) % a = 0 := by
|
||||
rw [← dvd_iff_emod_eq_zero, Int.dvd_neg]
|
||||
exact Int.dvd_mul_right a b
|
||||
|
||||
instance decidableDvd : DecidableRel (α := Int) (· ∣ ·) := fun _ _ =>
|
||||
decidable_of_decidable_of_iff (dvd_iff_emod_eq_zero ..).symm
|
||||
|
||||
@@ -620,6 +635,12 @@ theorem neg_ediv_of_dvd : ∀ {a b : Int}, b ∣ a → (-a) / b = -(a / b)
|
||||
· simp [bz]
|
||||
· rw [Int.neg_mul_eq_mul_neg, Int.mul_ediv_cancel_left _ bz, Int.mul_ediv_cancel_left _ bz]
|
||||
|
||||
@[simp] theorem neg_mul_ediv_cancel (a b : Int) (h : b ≠ 0) : -(a * b) / b = -a := by
|
||||
rw [neg_ediv_of_dvd (Int.dvd_mul_left a b), mul_ediv_cancel _ h]
|
||||
|
||||
@[simp] theorem neg_mul_ediv_cancel_left (a b : Int) (h : a ≠ 0) : -(a * b) / a = -b := by
|
||||
rw [neg_ediv_of_dvd (Int.dvd_mul_right a b), mul_ediv_cancel_left _ h]
|
||||
|
||||
theorem sub_ediv_of_dvd (a : Int) {b c : Int}
|
||||
(hcb : c ∣ b) : (a - b) / c = a / c - b / c := by
|
||||
rw [Int.sub_eq_add_neg, Int.sub_eq_add_neg, Int.add_ediv_of_dvd_right (Int.dvd_neg.2 hcb)]
|
||||
@@ -635,13 +656,22 @@ theorem sub_ediv_of_dvd (a : Int) {b c : Int}
|
||||
@[simp] protected theorem ediv_self {a : Int} (H : a ≠ 0) : a / a = 1 := by
|
||||
have := Int.mul_ediv_cancel 1 H; rwa [Int.one_mul] at this
|
||||
|
||||
@[simp] protected theorem neg_ediv_self (a : Int) (h : a ≠ 0) : (-a) / a = -1 := by
|
||||
rw [neg_ediv_of_dvd (Int.dvd_refl a), Int.ediv_self h]
|
||||
|
||||
@[simp]
|
||||
theorem emod_sub_cancel (x y : Int): (x - y)%y = x%y := by
|
||||
theorem emod_sub_cancel (x y : Int): (x - y) % y = x % y := by
|
||||
by_cases h : y = 0
|
||||
· simp [h]
|
||||
· simp only [Int.emod_def, Int.sub_ediv_of_dvd, Int.dvd_refl, Int.ediv_self h, Int.mul_sub]
|
||||
simp [Int.mul_one, Int.sub_sub, Int.add_comm y]
|
||||
|
||||
@[simp] theorem add_neg_emod_self (a b : Int) : (a + -b) % b = a % b := by
|
||||
rw [← Int.sub_eq_add_neg, emod_sub_cancel]
|
||||
|
||||
@[simp] theorem neg_add_emod_self (a b : Int) : (-a + b) % a = b % a := by
|
||||
rw [Int.add_comm, add_neg_emod_self]
|
||||
|
||||
/-- If `a % b = c` then `b` divides `a - c`. -/
|
||||
theorem dvd_sub_of_emod_eq {a b c : Int} (h : a % b = c) : b ∣ a - c := by
|
||||
have hx : (a % b) % b = c % b := by
|
||||
@@ -891,6 +921,14 @@ theorem mod_eq_zero_of_dvd : ∀ {a b : Int}, a ∣ b → mod b a = 0
|
||||
theorem dvd_iff_mod_eq_zero (a b : Int) : a ∣ b ↔ mod b a = 0 :=
|
||||
⟨mod_eq_zero_of_dvd, dvd_of_mod_eq_zero⟩
|
||||
|
||||
@[simp] theorem neg_mul_mod_right (a b : Int) : (-(a * b)).mod a = 0 := by
|
||||
rw [← dvd_iff_mod_eq_zero, Int.dvd_neg]
|
||||
exact Int.dvd_mul_right a b
|
||||
|
||||
@[simp] theorem neg_mul_mod_left (a b : Int) : (-(a * b)).mod b = 0 := by
|
||||
rw [← dvd_iff_mod_eq_zero, Int.dvd_neg]
|
||||
exact Int.dvd_mul_left a b
|
||||
|
||||
protected theorem div_mul_cancel {a b : Int} (H : b ∣ a) : a.div b * b = a :=
|
||||
div_mul_cancel_of_mod_eq_zero (mod_eq_zero_of_dvd H)
|
||||
|
||||
@@ -903,6 +941,10 @@ protected theorem eq_mul_of_div_eq_right {a b c : Int}
|
||||
@[simp] theorem mod_self {a : Int} : a.mod a = 0 := by
|
||||
have := mul_mod_left 1 a; rwa [Int.one_mul] at this
|
||||
|
||||
@[simp] theorem neg_mod_self (a : Int) : (-a).mod a = 0 := by
|
||||
rw [← dvd_iff_mod_eq_zero, Int.dvd_neg]
|
||||
exact Int.dvd_refl a
|
||||
|
||||
theorem lt_div_add_one_mul_self (a : Int) {b : Int} (H : 0 < b) : a < (a.div b + 1) * b := by
|
||||
rw [Int.add_mul, Int.one_mul, Int.mul_comm]
|
||||
exact Int.lt_add_of_sub_left_lt <| Int.mod_def .. ▸ mod_lt_of_pos _ H
|
||||
@@ -1091,8 +1133,7 @@ theorem bmod_mul_bmod : Int.bmod (Int.bmod x n * y) n = Int.bmod (x * y) n := by
|
||||
next p =>
|
||||
simp
|
||||
next p =>
|
||||
rw [Int.sub_mul, Int.sub_eq_add_neg, ← Int.mul_neg]
|
||||
simp
|
||||
rw [Int.sub_mul, Int.sub_eq_add_neg, ← Int.mul_neg, bmod_add_mul_cancel, emod_mul_bmod_congr]
|
||||
|
||||
@[simp] theorem mul_bmod_bmod : Int.bmod (x * Int.bmod y n) n = Int.bmod (x * y) n := by
|
||||
rw [Int.mul_comm x, bmod_mul_bmod, Int.mul_comm x]
|
||||
|
||||
@@ -7,6 +7,7 @@ prelude
|
||||
import Init.Data.Int.Basic
|
||||
import Init.Conv
|
||||
import Init.NotationExtra
|
||||
import Init.PropLemmas
|
||||
|
||||
namespace Int
|
||||
|
||||
@@ -288,7 +289,7 @@ protected theorem neg_sub (a b : Int) : -(a - b) = b - a := by
|
||||
protected theorem sub_sub_self (a b : Int) : a - (a - b) = b := by
|
||||
simp [Int.sub_eq_add_neg, ← Int.add_assoc]
|
||||
|
||||
protected theorem sub_neg (a b : Int) : a - -b = a + b := by simp [Int.sub_eq_add_neg]
|
||||
@[simp] protected theorem sub_neg (a b : Int) : a - -b = a + b := by simp [Int.sub_eq_add_neg]
|
||||
|
||||
@[simp] protected theorem sub_add_cancel (a b : Int) : a - b + b = a :=
|
||||
Int.neg_add_cancel_right a b
|
||||
@@ -444,10 +445,10 @@ protected theorem neg_mul_eq_neg_mul (a b : Int) : -(a * b) = -a * b :=
|
||||
protected theorem neg_mul_eq_mul_neg (a b : Int) : -(a * b) = a * -b :=
|
||||
Int.neg_eq_of_add_eq_zero <| by rw [← Int.mul_add, Int.add_right_neg, Int.mul_zero]
|
||||
|
||||
@[local simp] protected theorem neg_mul (a b : Int) : -a * b = -(a * b) :=
|
||||
@[simp] protected theorem neg_mul (a b : Int) : -a * b = -(a * b) :=
|
||||
(Int.neg_mul_eq_neg_mul a b).symm
|
||||
|
||||
@[local simp] protected theorem mul_neg (a b : Int) : a * -b = -(a * b) :=
|
||||
@[simp] protected theorem mul_neg (a b : Int) : a * -b = -(a * b) :=
|
||||
(Int.neg_mul_eq_mul_neg a b).symm
|
||||
|
||||
protected theorem neg_mul_neg (a b : Int) : -a * -b = a * b := by simp
|
||||
@@ -486,6 +487,9 @@ protected theorem mul_eq_zero {a b : Int} : a * b = 0 ↔ a = 0 ∨ b = 0 := by
|
||||
protected theorem mul_ne_zero {a b : Int} (a0 : a ≠ 0) (b0 : b ≠ 0) : a * b ≠ 0 :=
|
||||
Or.rec a0 b0 ∘ Int.mul_eq_zero.mp
|
||||
|
||||
@[simp] protected theorem mul_ne_zero_iff (a b : Int) : a * b ≠ 0 ↔ a ≠ 0 ∧ b ≠ 0 := by
|
||||
rw [ne_eq, Int.mul_eq_zero, not_or, ne_eq]
|
||||
|
||||
protected theorem eq_of_mul_eq_mul_right {a b c : Int} (ha : a ≠ 0) (h : b * a = c * a) : b = c :=
|
||||
have : (b - c) * a = 0 := by rwa [Int.sub_mul, Int.sub_eq_zero]
|
||||
Int.sub_eq_zero.1 <| (Int.mul_eq_zero.mp this).resolve_right ha
|
||||
|
||||
40
src/Init/Data/Int/LemmasAux.lean
Normal file
40
src/Init/Data/Int/LemmasAux.lean
Normal file
@@ -0,0 +1,40 @@
|
||||
/-
|
||||
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.Int.Order
|
||||
import Init.Omega
|
||||
|
||||
|
||||
/-!
|
||||
# Further lemmas about `Int` relying on `omega` automation.
|
||||
-/
|
||||
|
||||
namespace Int
|
||||
|
||||
@[simp] theorem toNat_sub' (a : Int) (b : Nat) : a.toNat - b = (a - b).toNat := by
|
||||
simp only [Int.toNat]
|
||||
split <;> rename_i x a
|
||||
· simp only [Int.ofNat_eq_coe]
|
||||
split <;> rename_i y b h
|
||||
· simp at h
|
||||
omega
|
||||
· simp [Int.negSucc_eq] at h
|
||||
omega
|
||||
· simp only [Nat.zero_sub]
|
||||
split <;> rename_i y b h
|
||||
· simp [Int.negSucc_eq] at h
|
||||
omega
|
||||
· rfl
|
||||
|
||||
@[simp] theorem toNat_sub_max_self (a : Int) : (a - max a 0).toNat = 0 := by
|
||||
simp [toNat]
|
||||
split <;> simp_all <;> omega
|
||||
|
||||
@[simp] theorem toNat_sub_self_max (a : Int) : (a - max 0 a).toNat = 0 := by
|
||||
simp [toNat]
|
||||
split <;> simp_all <;> omega
|
||||
|
||||
end Int
|
||||
@@ -240,9 +240,24 @@ theorem le_natAbs {a : Int} : a ≤ natAbs a :=
|
||||
theorem negSucc_lt_zero (n : Nat) : -[n+1] < 0 :=
|
||||
Int.not_le.1 fun h => let ⟨_, h⟩ := eq_ofNat_of_zero_le h; nomatch h
|
||||
|
||||
theorem negSucc_le_zero (n : Nat) : -[n+1] ≤ 0 :=
|
||||
Int.le_of_lt (negSucc_lt_zero n)
|
||||
|
||||
@[simp] theorem negSucc_not_nonneg (n : Nat) : 0 ≤ -[n+1] ↔ False := by
|
||||
simp only [Int.not_le, iff_false]; exact Int.negSucc_lt_zero n
|
||||
|
||||
@[simp] theorem ofNat_max_zero (n : Nat) : (max (n : Int) 0) = n := by
|
||||
rw [Int.max_eq_left (ofNat_zero_le n)]
|
||||
|
||||
@[simp] theorem zero_max_ofNat (n : Nat) : (max 0 (n : Int)) = n := by
|
||||
rw [Int.max_eq_right (ofNat_zero_le n)]
|
||||
|
||||
@[simp] theorem negSucc_max_zero (n : Nat) : (max (Int.negSucc n) 0) = 0 := by
|
||||
rw [Int.max_eq_right (negSucc_le_zero _)]
|
||||
|
||||
@[simp] theorem zero_max_negSucc (n : Nat) : (max 0 (Int.negSucc n)) = 0 := by
|
||||
rw [Int.max_eq_left (negSucc_le_zero _)]
|
||||
|
||||
protected theorem add_le_add_left {a b : Int} (h : a ≤ b) (c : Int) : c + a ≤ c + b :=
|
||||
let ⟨n, hn⟩ := le.dest h; le.intro n <| by rw [Int.add_assoc, hn]
|
||||
|
||||
@@ -470,8 +485,16 @@ theorem toNat_eq_max : ∀ a : Int, (toNat a : Int) = max a 0
|
||||
|
||||
@[simp] theorem toNat_ofNat (n : Nat) : toNat ↑n = n := rfl
|
||||
|
||||
@[simp] theorem toNat_negSucc (n : Nat) : (Int.negSucc n).toNat = 0 := by
|
||||
simp [toNat]
|
||||
|
||||
@[simp] theorem toNat_ofNat_add_one {n : Nat} : ((n : Int) + 1).toNat = n + 1 := rfl
|
||||
|
||||
@[simp] theorem ofNat_toNat (a : Int) : (a.toNat : Int) = max a 0 := by
|
||||
match a with
|
||||
| Int.ofNat n => simp
|
||||
| Int.negSucc n => simp
|
||||
|
||||
theorem self_le_toNat (a : Int) : a ≤ toNat a := by rw [toNat_eq_max]; apply Int.le_max_left
|
||||
|
||||
@[simp] theorem le_toNat {n : Nat} {z : Int} (h : 0 ≤ z) : n ≤ z.toNat ↔ (n : Int) ≤ z := by
|
||||
@@ -1006,7 +1029,7 @@ theorem natAbs_mul_self : ∀ {a : Int}, ↑(natAbs a * natAbs a) = a * a
|
||||
theorem eq_nat_or_neg (a : Int) : ∃ n : Nat, a = n ∨ a = -↑n := ⟨_, natAbs_eq a⟩
|
||||
|
||||
theorem natAbs_mul_natAbs_eq {a b : Int} {c : Nat}
|
||||
(h : a * b = (c : Int)) : a.natAbs * b.natAbs = c := by rw [← natAbs_mul, h, natAbs]
|
||||
(h : a * b = (c : Int)) : a.natAbs * b.natAbs = c := by rw [← natAbs_mul, h, natAbs.eq_def]
|
||||
|
||||
@[simp] theorem natAbs_mul_self' (a : Int) : (natAbs a * natAbs a : Int) = a * a := by
|
||||
rw [← Int.ofNat_mul, natAbs_mul_self]
|
||||
|
||||
@@ -22,3 +22,4 @@ import Init.Data.List.Sublist
|
||||
import Init.Data.List.TakeDrop
|
||||
import Init.Data.List.Zip
|
||||
import Init.Data.List.Perm
|
||||
import Init.Data.List.Sort
|
||||
|
||||
@@ -73,6 +73,13 @@ theorem pmap_map {p : β → Prop} (g : ∀ b, p b → γ) (f : α → β) (l H)
|
||||
· rfl
|
||||
· simp only [*, pmap, map]
|
||||
|
||||
@[simp] theorem attach_cons (x : α) (xs : List α) :
|
||||
(x :: xs).attach = ⟨x, mem_cons_self x xs⟩ :: xs.attach.map fun ⟨y, h⟩ => ⟨y, mem_cons_of_mem x h⟩ := by
|
||||
simp only [attach, attachWith, pmap, map_pmap, cons.injEq, true_and]
|
||||
apply pmap_congr
|
||||
intros a _ m' _
|
||||
rfl
|
||||
|
||||
theorem pmap_eq_map_attach {p : α → Prop} (f : ∀ a, p a → β) (l H) :
|
||||
pmap f l H = l.attach.map fun x => f x.1 (H _ x.2) := by
|
||||
rw [attach, attachWith, map_pmap]; exact pmap_congr l fun _ _ _ _ => rfl
|
||||
@@ -86,7 +93,7 @@ theorem attach_map_val (l : List α) (f : α → β) : (l.attach.map fun i => f
|
||||
|
||||
@[simp]
|
||||
theorem attach_map_subtype_val (l : List α) : l.attach.map Subtype.val = l :=
|
||||
(attach_map_coe _ _).trans l.map_id
|
||||
(attach_map_coe _ _).trans (List.map_id _)
|
||||
|
||||
theorem countP_attach (l : List α) (p : α → Bool) : l.attach.countP (fun a : {x // x ∈ l} => p a) = l.countP p := by
|
||||
simp only [← Function.comp_apply (g := Subtype.val), ← countP_map, attach_map_subtype_val]
|
||||
@@ -121,23 +128,14 @@ theorem length_attach (L : List α) : L.attach.length = L.length :=
|
||||
theorem pmap_eq_nil {p : α → Prop} {f : ∀ a, p a → β} {l H} : pmap f l H = [] ↔ l = [] := by
|
||||
rw [← length_eq_zero, length_pmap, length_eq_zero]
|
||||
|
||||
theorem pmap_ne_nil {P : α → Prop} (f : (a : α) → P a → β) (xs : List α)
|
||||
(H : ∀ (a : α), a ∈ xs → P a) : xs.pmap f H ≠ [] ↔ xs ≠ [] := by
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem attach_eq_nil (l : List α) : l.attach = [] ↔ l = [] :=
|
||||
pmap_eq_nil
|
||||
|
||||
theorem getLast_pmap (p : α → Prop) (f : ∀ a, p a → β) (l : List α)
|
||||
(hl₁ : ∀ a ∈ l, p a) (hl₂ : l ≠ []) :
|
||||
(l.pmap f hl₁).getLast (mt List.pmap_eq_nil.1 hl₂) =
|
||||
f (l.getLast hl₂) (hl₁ _ (List.getLast_mem hl₂)) := by
|
||||
induction l with
|
||||
| nil => apply (hl₂ rfl).elim
|
||||
| cons l_hd l_tl l_ih =>
|
||||
by_cases hl_tl : l_tl = []
|
||||
· simp [hl_tl]
|
||||
· simp only [pmap]
|
||||
rw [getLast_cons, l_ih _ hl_tl]
|
||||
simp only [getLast_cons hl_tl]
|
||||
|
||||
theorem getElem?_pmap {p : α → Prop} (f : ∀ a, p a → β) {l : List α} (h : ∀ a ∈ l, p a) (n : Nat) :
|
||||
(pmap f l h)[n]? = Option.pmap f l[n]? fun x H => h x (getElem?_mem H) := by
|
||||
induction l generalizing n with
|
||||
@@ -181,7 +179,22 @@ theorem get_pmap {p : α → Prop} (f : ∀ a, p a → β) {l : List α} (h :
|
||||
simp only [get_eq_getElem]
|
||||
simp [getElem_pmap]
|
||||
|
||||
theorem pmap_append {p : ι → Prop} (f : ∀ a : ι, p a → α) (l₁ l₂ : List ι)
|
||||
@[simp] theorem head?_pmap {P : α → Prop} (f : (a : α) → P a → β) (xs : List α)
|
||||
(H : ∀ (a : α), a ∈ xs → P a) : (xs.pmap f H).head? = xs.attach.head?.map fun ⟨a, m⟩ => f a (H a m) := by
|
||||
induction xs with
|
||||
| nil => simp
|
||||
| cons x xs ih =>
|
||||
simp at ih
|
||||
simp [head?_pmap, ih]
|
||||
|
||||
@[simp] theorem head_pmap {P : α → Prop} (f : (a : α) → P a → β) (xs : List α)
|
||||
(H : ∀ (a : α), a ∈ xs → P a) (h : xs.pmap f H ≠ []) :
|
||||
(xs.pmap f H).head h = f (xs.head (by simpa using h)) (H _ (head_mem _)) := by
|
||||
induction xs with
|
||||
| nil => simp at h
|
||||
| cons x xs ih => simp [head_pmap, ih]
|
||||
|
||||
@[simp] theorem pmap_append {p : ι → Prop} (f : ∀ a : ι, p a → α) (l₁ l₂ : List ι)
|
||||
(h : ∀ a ∈ l₁ ++ l₂, p a) :
|
||||
(l₁ ++ l₂).pmap f h =
|
||||
(l₁.pmap f fun a ha => h a (mem_append_left l₂ ha)) ++
|
||||
@@ -197,3 +210,63 @@ theorem pmap_append' {p : α → Prop} (f : ∀ a : α, p a → β) (l₁ l₂ :
|
||||
((l₁ ++ l₂).pmap f fun a ha => (List.mem_append.1 ha).elim (h₁ a) (h₂ a)) =
|
||||
l₁.pmap f h₁ ++ l₂.pmap f h₂ :=
|
||||
pmap_append f l₁ l₂ _
|
||||
|
||||
@[simp] theorem pmap_reverse {P : α → Prop} (f : (a : α) → P a → β) (xs : List α)
|
||||
(H : ∀ (a : α), a ∈ xs.reverse → P a) : xs.reverse.pmap f H = (xs.pmap f (fun a h => H a (by simpa using h))).reverse := by
|
||||
induction xs <;> simp_all
|
||||
|
||||
theorem reverse_pmap {P : α → Prop} (f : (a : α) → P a → β) (xs : List α)
|
||||
(H : ∀ (a : α), a ∈ xs → P a) : (xs.pmap f H).reverse = xs.reverse.pmap f (fun a h => H a (by simpa using h)) := by
|
||||
rw [pmap_reverse]
|
||||
|
||||
@[simp] theorem attach_append (xs ys : List α) :
|
||||
(xs ++ ys).attach = xs.attach.map (fun ⟨x, h⟩ => ⟨x, mem_append_of_mem_left ys h⟩) ++
|
||||
ys.attach.map fun ⟨x, h⟩ => ⟨x, mem_append_of_mem_right xs h⟩ := by
|
||||
simp only [attach, attachWith, pmap, map_pmap, pmap_append]
|
||||
congr 1 <;>
|
||||
exact pmap_congr _ fun _ _ _ _ => rfl
|
||||
|
||||
@[simp] theorem attach_reverse (xs : List α) : xs.reverse.attach = xs.attach.reverse.map fun ⟨x, h⟩ => ⟨x, by simpa using h⟩ := by
|
||||
simp only [attach, attachWith, reverse_pmap, map_pmap]
|
||||
apply pmap_congr
|
||||
intros
|
||||
rfl
|
||||
|
||||
theorem reverse_attach (xs : List α) : xs.attach.reverse = xs.reverse.attach.map fun ⟨x, h⟩ => ⟨x, by simpa using h⟩ := by
|
||||
simp only [attach, attachWith, reverse_pmap, map_pmap]
|
||||
apply pmap_congr
|
||||
intros
|
||||
rfl
|
||||
|
||||
|
||||
theorem getLast?_attach {xs : List α} :
|
||||
xs.attach.getLast? = match h : xs.getLast? with | none => none | some a => some ⟨a, mem_of_getLast?_eq_some h⟩ := by
|
||||
rw [getLast?_eq_head?_reverse, reverse_attach, head?_map]
|
||||
split <;> rename_i h
|
||||
· simp only [getLast?_eq_none_iff] at h
|
||||
subst h
|
||||
simp
|
||||
· obtain ⟨ys, rfl⟩ := getLast?_eq_some_iff.mp h
|
||||
simp
|
||||
|
||||
@[simp] theorem getLast?_pmap {P : α → Prop} (f : (a : α) → P a → β) (xs : List α)
|
||||
(H : ∀ (a : α), a ∈ xs → P a) : (xs.pmap f H).getLast? = xs.attach.getLast?.map fun ⟨a, m⟩ => f a (H a m) := by
|
||||
simp only [getLast?_eq_head?_reverse]
|
||||
rw [reverse_pmap, reverse_attach, head?_map, pmap_eq_map_attach, head?_map]
|
||||
simp only [Option.map_map]
|
||||
congr
|
||||
|
||||
@[simp] theorem getLast_pmap {P : α → Prop} (f : (a : α) → P a → β) (xs : List α)
|
||||
(H : ∀ (a : α), a ∈ xs → P a) (h : xs.pmap f H ≠ []) :
|
||||
(xs.pmap f H).getLast h = f (xs.getLast (by simpa using h)) (H _ (getLast_mem _)) := by
|
||||
simp only [getLast_eq_iff_getLast_eq_some, getLast?_pmap, Option.map_eq_some', Subtype.exists]
|
||||
refine ⟨xs.getLast (by simpa using h), by simp, ?_⟩
|
||||
simp only [getLast?_attach, and_true]
|
||||
split <;> rename_i h'
|
||||
· simp only [getLast?_eq_none_iff] at h'
|
||||
subst h'
|
||||
simp at h
|
||||
· symm
|
||||
simpa [getLast_eq_iff_getLast_eq_some]
|
||||
|
||||
end List
|
||||
|
||||
@@ -96,7 +96,7 @@ namespace List
|
||||
|
||||
/-! ### concat -/
|
||||
|
||||
@[simp high] theorem length_concat (as : List α) (a : α) : (concat as a).length = as.length + 1 := by
|
||||
theorem length_concat (as : List α) (a : α) : (concat as a).length = as.length + 1 := by
|
||||
induction as with
|
||||
| nil => rfl
|
||||
| cons _ xs ih => simp [concat, ih]
|
||||
@@ -278,8 +278,9 @@ def getLastD : (as : List α) → (fallback : α) → α
|
||||
| [], a₀ => a₀
|
||||
| a::as, _ => getLast (a::as) (fun h => List.noConfusion h)
|
||||
|
||||
@[simp] theorem getLastD_nil (a) : @getLastD α [] a = a := rfl
|
||||
@[simp] theorem getLastD_cons (a b l) : @getLastD α (b::l) a = getLastD l b := by cases l <;> rfl
|
||||
-- These aren't `simp` lemmas since we always simplify `getLastD` in terms of `getLast?`.
|
||||
theorem getLastD_nil (a) : @getLastD α [] a = a := rfl
|
||||
theorem getLastD_cons (a b l) : @getLastD α (b::l) a = getLastD l b := by cases l <;> rfl
|
||||
|
||||
/-! ## Head and tail -/
|
||||
|
||||
@@ -688,7 +689,7 @@ inductive Mem (a : α) : List α → Prop
|
||||
| tail (b : α) {as : List α} : Mem a as → Mem a (b::as)
|
||||
|
||||
instance : Membership α (List α) where
|
||||
mem := Mem
|
||||
mem l a := Mem a l
|
||||
|
||||
theorem mem_of_elem_eq_true [BEq α] [LawfulBEq α] {a : α} {as : List α} : elem a as = true → a ∈ as := by
|
||||
match as with
|
||||
@@ -962,6 +963,26 @@ def IsInfix (l₁ : List α) (l₂ : List α) : Prop := Exists fun s => Exists f
|
||||
|
||||
@[inherit_doc] infixl:50 " <:+: " => IsInfix
|
||||
|
||||
/-! ### splitAt -/
|
||||
|
||||
/--
|
||||
Split a list at an index.
|
||||
```
|
||||
splitAt 2 [a, b, c] = ([a, b], [c])
|
||||
```
|
||||
-/
|
||||
def splitAt (n : Nat) (l : List α) : List α × List α := go l n [] where
|
||||
/--
|
||||
Auxiliary for `splitAt`:
|
||||
`splitAt.go l xs n acc = (acc.reverse ++ take n xs, drop n xs)` if `n < xs.length`,
|
||||
and `(l, [])` otherwise.
|
||||
-/
|
||||
go : List α → Nat → List α → List α × List α
|
||||
| [], _, _ => (l, []) -- This branch ensures the pointer equality of the result with the input
|
||||
-- without any runtime branching cost.
|
||||
| x :: xs, n+1, acc => go xs n (x :: acc)
|
||||
| xs, _, acc => (acc.reverse, xs)
|
||||
|
||||
/-! ### rotateLeft -/
|
||||
|
||||
/--
|
||||
|
||||
@@ -222,7 +222,7 @@ theorem append_cancel_right {as bs cs : List α} (h : as ++ bs = cs ++ bs) : as
|
||||
next => apply append_cancel_right
|
||||
next => intro h; simp [h]
|
||||
|
||||
@[simp] theorem sizeOf_get [SizeOf α] (as : List α) (i : Fin as.length) : sizeOf (as.get i) < sizeOf as := by
|
||||
theorem sizeOf_get [SizeOf α] (as : List α) (i : Fin as.length) : sizeOf (as.get i) < sizeOf as := by
|
||||
match as, i with
|
||||
| a::as, ⟨0, _⟩ => simp_arith [get]
|
||||
| a::as, ⟨i+1, h⟩ =>
|
||||
|
||||
@@ -47,11 +47,11 @@ theorem length_eq_countP_add_countP (l) : length l = countP p l + countP (fun a
|
||||
if h : p x then
|
||||
rw [countP_cons_of_pos _ _ h, countP_cons_of_neg _ _ _, length, ih]
|
||||
· rw [Nat.add_assoc, Nat.add_comm _ 1, Nat.add_assoc]
|
||||
· simp only [h, not_true_eq_false, decide_False, not_false_eq_true]
|
||||
· simp [h]
|
||||
else
|
||||
rw [countP_cons_of_pos (fun a => ¬p a) _ _, countP_cons_of_neg _ _ h, length, ih]
|
||||
· rfl
|
||||
· simp only [h, not_false_eq_true, decide_True]
|
||||
· simp [h]
|
||||
|
||||
theorem countP_eq_length_filter (l) : countP p l = length (filter p l) := by
|
||||
induction l with
|
||||
@@ -234,7 +234,7 @@ theorem count_erase (a b : α) :
|
||||
rw [if_pos hc_beq, hc, count_cons, Nat.add_sub_cancel]
|
||||
else
|
||||
have hc_beq := beq_false_of_ne hc
|
||||
simp only [hc_beq, if_false, count_cons, count_cons, count_erase a b l]
|
||||
simp only [hc_beq, if_false, count_cons, count_cons, count_erase a b l, reduceCtorEq]
|
||||
if ha : b = a then
|
||||
rw [ha, eq_comm] at hc
|
||||
rw [if_pos ((beq_iff_eq _ _).2 ha), if_neg (by simpa using Ne.symm hc), Nat.add_zero, Nat.add_zero]
|
||||
|
||||
@@ -33,6 +33,25 @@ theorem eraseP_of_forall_not {l : List α} (h : ∀ a, a ∈ l → ¬p a) : l.er
|
||||
| nil => rfl
|
||||
| cons _ _ ih => simp [h _ (.head ..), ih (forall_mem_cons.1 h).2]
|
||||
|
||||
@[simp] theorem eraseP_eq_nil (xs : List α) (p : α → Bool) : xs.eraseP p = [] ↔ xs = [] ∨ ∃ x, p x ∧ xs = [x] := by
|
||||
induction xs with
|
||||
| nil => simp
|
||||
| cons x xs ih =>
|
||||
simp only [eraseP_cons, cond_eq_if]
|
||||
split <;> rename_i h
|
||||
· simp only [reduceCtorEq, cons.injEq, false_or]
|
||||
constructor
|
||||
· rintro rfl
|
||||
simpa
|
||||
· rintro ⟨_, _, rfl, rfl⟩
|
||||
rfl
|
||||
· simp only [reduceCtorEq, cons.injEq, false_or, false_iff, not_exists, not_and]
|
||||
rintro x h' rfl
|
||||
simp_all
|
||||
|
||||
theorem eraseP_ne_nil (xs : List α) (p : α → Bool) : xs.eraseP p ≠ [] ↔ xs ≠ [] ∧ ∀ x, p x → xs ≠ [x] := by
|
||||
simp
|
||||
|
||||
theorem exists_of_eraseP : ∀ {l : List α} {a} (al : a ∈ l) (pa : p a),
|
||||
∃ a l₁ l₂, (∀ b ∈ l₁, ¬p b) ∧ p a ∧ l = l₁ ++ a :: l₂ ∧ l.eraseP p = l₁ ++ l₂
|
||||
| b :: l, a, al, pa =>
|
||||
@@ -159,6 +178,14 @@ theorem eraseP_append (l₁ l₂ : List α) :
|
||||
rw [eraseP_append_right _]
|
||||
simp_all
|
||||
|
||||
theorem eraseP_replicate (n : Nat) (a : α) (p : α → Bool) :
|
||||
(replicate n a).eraseP p = if p a then replicate (n - 1) a else replicate n a := by
|
||||
induction n with
|
||||
| zero => simp
|
||||
| succ n ih =>
|
||||
simp only [replicate_succ, eraseP_cons]
|
||||
split <;> simp [*]
|
||||
|
||||
protected theorem IsPrefix.eraseP (h : l₁ <+: l₂) : l₁.eraseP p <+: l₂.eraseP p := by
|
||||
rw [IsPrefix] at h
|
||||
obtain ⟨t, rfl⟩ := h
|
||||
@@ -213,8 +240,11 @@ theorem eraseP_eq_iff {p} {l : List α} :
|
||||
(replicate n a).eraseP p = replicate n a := by
|
||||
rw [eraseP_of_forall_not (by simp_all)]
|
||||
|
||||
theorem Pairwise.eraseP (q) : Pairwise p l → Pairwise p (l.eraseP q) :=
|
||||
Pairwise.sublist <| eraseP_sublist _
|
||||
|
||||
theorem Nodup.eraseP (p) : Nodup l → Nodup (l.eraseP p) :=
|
||||
Nodup.sublist <| eraseP_sublist _
|
||||
Pairwise.eraseP p
|
||||
|
||||
theorem eraseP_comm {l : List α} (h : ∀ a ∈ l, ¬ p a ∨ ¬ q a) :
|
||||
(l.eraseP p).eraseP q = (l.eraseP q).eraseP p := by
|
||||
@@ -230,6 +260,12 @@ theorem eraseP_comm {l : List α} (h : ∀ a ∈ l, ¬ p a ∨ ¬ q a) :
|
||||
· simp [h₁, h₂, ih (fun b m => h b (mem_cons_of_mem _ m))]
|
||||
· simp [h₁, h₂, ih (fun b m => h b (mem_cons_of_mem _ m))]
|
||||
|
||||
theorem head_eraseP_mem (xs : List α) (p : α → Bool) (h) : (xs.eraseP p).head h ∈ xs :=
|
||||
(eraseP_sublist xs).head_mem h
|
||||
|
||||
theorem getLast_eraseP_mem (xs : List α) (p : α → Bool) (h) : (xs.eraseP p).getLast h ∈ xs :=
|
||||
(eraseP_sublist xs).getLast_mem h
|
||||
|
||||
/-! ### erase -/
|
||||
section erase
|
||||
variable [BEq α]
|
||||
@@ -258,6 +294,16 @@ theorem erase_eq_eraseP [LawfulBEq α] (a : α) : ∀ l : List α, l.erase a =
|
||||
| b :: l => by
|
||||
if h : a = b then simp [h] else simp [h, Ne.symm h, erase_eq_eraseP a l]
|
||||
|
||||
@[simp] theorem erase_eq_nil [LawfulBEq α] (xs : List α) (a : α) :
|
||||
xs.erase a = [] ↔ xs = [] ∨ xs = [a] := by
|
||||
rw [erase_eq_eraseP]
|
||||
simp
|
||||
|
||||
theorem erase_ne_nil [LawfulBEq α] (xs : List α) (a : α) :
|
||||
xs.erase a ≠ [] ↔ xs ≠ [] ∧ xs ≠ [a] := by
|
||||
rw [erase_eq_eraseP]
|
||||
simp
|
||||
|
||||
theorem exists_erase_eq [LawfulBEq α] {a : α} {l : List α} (h : a ∈ l) :
|
||||
∃ l₁ l₂, a ∉ l₁ ∧ l = l₁ ++ a :: l₂ ∧ l.erase a = l₁ ++ l₂ := by
|
||||
let ⟨_, l₁, l₂, h₁, e, h₂, h₃⟩ := exists_of_eraseP h (beq_self_eq_true _)
|
||||
@@ -294,7 +340,7 @@ theorem mem_of_mem_erase {a b : α} {l : List α} (h : a ∈ l.erase b) : a ∈
|
||||
|
||||
@[simp] theorem erase_eq_self_iff [LawfulBEq α] {l : List α} : l.erase a = l ↔ a ∉ l := by
|
||||
rw [erase_eq_eraseP', eraseP_eq_self_iff]
|
||||
simp
|
||||
simp [forall_mem_ne']
|
||||
|
||||
theorem erase_filter [LawfulBEq α] (f : α → Bool) (l : List α) :
|
||||
(filter f l).erase a = filter f (l.erase a) := by
|
||||
@@ -327,6 +373,11 @@ theorem erase_append [LawfulBEq α] {a : α} {l₁ l₂ : List α} :
|
||||
(l₁ ++ l₂).erase a = if a ∈ l₁ then l₁.erase a ++ l₂ else l₁ ++ l₂.erase a := by
|
||||
simp [erase_eq_eraseP, eraseP_append]
|
||||
|
||||
theorem erase_replicate [LawfulBEq α] (n : Nat) (a b : α) :
|
||||
(replicate n a).erase b = if b == a then replicate (n - 1) a else replicate n a := by
|
||||
rw [erase_eq_eraseP]
|
||||
simp [eraseP_replicate]
|
||||
|
||||
theorem erase_comm [LawfulBEq α] (a b : α) (l : List α) :
|
||||
(l.erase a).erase b = (l.erase b).erase a := by
|
||||
if ab : a == b then rw [eq_of_beq ab] else ?_
|
||||
@@ -366,6 +417,9 @@ theorem erase_eq_iff [LawfulBEq α] {a : α} {l : List α} :
|
||||
rw [erase_of_not_mem]
|
||||
simp_all
|
||||
|
||||
theorem Pairwise.erase [LawfulBEq α] {l : List α} (a) : Pairwise p l → Pairwise p (l.erase a) :=
|
||||
Pairwise.sublist <| erase_sublist _ _
|
||||
|
||||
theorem Nodup.erase_eq_filter [LawfulBEq α] {l} (d : Nodup l) (a : α) : l.erase a = l.filter (· != a) := by
|
||||
induction d with
|
||||
| nil => rfl
|
||||
@@ -386,7 +440,13 @@ theorem Nodup.not_mem_erase [LawfulBEq α] {a : α} (h : Nodup l) : a ∉ l.eras
|
||||
simpa using ((Nodup.mem_erase_iff h).mp H).left
|
||||
|
||||
theorem Nodup.erase [LawfulBEq α] (a : α) : Nodup l → Nodup (l.erase a) :=
|
||||
Nodup.sublist <| erase_sublist _ _
|
||||
Pairwise.erase a
|
||||
|
||||
theorem head_erase_mem (xs : List α) (a : α) (h) : (xs.erase a).head h ∈ xs :=
|
||||
(erase_sublist a xs).head_mem h
|
||||
|
||||
theorem getLast_erase_mem (xs : List α) (a : α) (h) : (xs.erase a).getLast h ∈ xs :=
|
||||
(erase_sublist a xs).getLast_mem h
|
||||
|
||||
end erase
|
||||
|
||||
@@ -408,11 +468,26 @@ theorem eraseIdx_eq_take_drop_succ :
|
||||
| a::l, 0 => by simp
|
||||
| a::l, i + 1 => by simp [eraseIdx_eq_take_drop_succ l i]
|
||||
|
||||
@[simp] theorem eraseIdx_eq_nil {l : List α} {i : Nat} : eraseIdx l i = [] ↔ l = [] ∨ (length l = 1 ∧ i = 0) := by
|
||||
match l, i with
|
||||
| [], _
|
||||
| a::l, 0
|
||||
| a::l, i + 1 => simp [Nat.succ_inj']
|
||||
|
||||
theorem eraseIdx_ne_nil {l : List α} {i : Nat} : eraseIdx l i ≠ [] ↔ 2 ≤ l.length ∨ (l.length = 1 ∧ i ≠ 0) := by
|
||||
match l with
|
||||
| []
|
||||
| [a]
|
||||
| a::b::l => simp [Nat.succ_inj']
|
||||
|
||||
theorem eraseIdx_sublist : ∀ (l : List α) (k : Nat), eraseIdx l k <+ l
|
||||
| [], _ => by simp
|
||||
| a::l, 0 => by simp
|
||||
| a::l, k + 1 => by simp [eraseIdx_sublist l k]
|
||||
|
||||
theorem mem_of_mem_eraseIdx {l : List α} {i : Nat} {a : α} (h : a ∈ l.eraseIdx i) : a ∈ l :=
|
||||
(eraseIdx_sublist _ _).mem h
|
||||
|
||||
theorem eraseIdx_subset (l : List α) (k : Nat) : eraseIdx l k ⊆ l := (eraseIdx_sublist l k).subset
|
||||
|
||||
@[simp]
|
||||
@@ -442,6 +517,23 @@ theorem eraseIdx_append_of_length_le {l : List α} {k : Nat} (hk : length l ≤
|
||||
| zero => simp_all
|
||||
| succ k => simp_all [eraseIdx_cons_succ, Nat.succ_sub_succ]
|
||||
|
||||
theorem eraseIdx_replicate {n : Nat} {a : α} {k : Nat} :
|
||||
(replicate n a).eraseIdx k = if k < n then replicate (n - 1) a else replicate n a := by
|
||||
split <;> rename_i h
|
||||
· rw [eq_replicate, length_eraseIdx (by simpa using h)]
|
||||
simp only [length_replicate, true_and]
|
||||
intro b m
|
||||
replace m := mem_of_mem_eraseIdx m
|
||||
simp only [mem_replicate] at m
|
||||
exact m.2
|
||||
· rw [eraseIdx_of_length_le (by simpa using h)]
|
||||
|
||||
theorem Pairwise.eraseIdx {l : List α} (k) : Pairwise p l → Pairwise p (l.eraseIdx k) :=
|
||||
Pairwise.sublist <| eraseIdx_sublist _ _
|
||||
|
||||
theorem Nodup.eraseIdx {l : List α} (k) : Nodup l → Nodup (l.eraseIdx k) :=
|
||||
Pairwise.eraseIdx k
|
||||
|
||||
protected theorem IsPrefix.eraseIdx {l l' : List α} (h : l <+: l') (k : Nat) :
|
||||
eraseIdx l k <+: eraseIdx l' k := by
|
||||
rcases h with ⟨t, rfl⟩
|
||||
|
||||
@@ -10,92 +10,13 @@ import Init.Data.List.Sublist
|
||||
import Init.Data.List.Range
|
||||
|
||||
/-!
|
||||
# Lemmas about `List.find?`, `List.findSome?`, `List.findIdx`, `List.findIdx?`, and `List.indexOf`.
|
||||
# Lemmas about `List.findSome?`, `List.find?`, `List.findIdx`, `List.findIdx?`, and `List.indexOf`.
|
||||
-/
|
||||
|
||||
namespace List
|
||||
|
||||
open Nat
|
||||
|
||||
/-! ### find? -/
|
||||
|
||||
@[simp] theorem find?_cons_of_pos (l) (h : p a) : find? p (a :: l) = some a := by
|
||||
simp [find?, h]
|
||||
|
||||
@[simp] theorem find?_cons_of_neg (l) (h : ¬p a) : find? p (a :: l) = find? p l := by
|
||||
simp [find?, h]
|
||||
|
||||
@[simp] theorem find?_eq_none : find? p l = none ↔ ∀ x ∈ l, ¬ p x := by
|
||||
induction l <;> simp [find?_cons]; split <;> simp [*]
|
||||
|
||||
theorem find?_some : ∀ {l}, find? p l = some a → p a
|
||||
| b :: l, H => by
|
||||
by_cases h : p b <;> simp [find?, h] at H
|
||||
· exact H ▸ h
|
||||
· exact find?_some H
|
||||
|
||||
@[simp] theorem mem_of_find?_eq_some : ∀ {l}, find? p l = some a → a ∈ l
|
||||
| b :: l, H => by
|
||||
by_cases h : p b <;> simp [find?, h] at H
|
||||
· exact H ▸ .head _
|
||||
· exact .tail _ (mem_of_find?_eq_some H)
|
||||
|
||||
@[simp] theorem find?_map (f : β → α) (l : List β) : find? p (l.map f) = (l.find? (p ∘ f)).map f := by
|
||||
induction l with
|
||||
| nil => simp
|
||||
| cons x xs ih =>
|
||||
simp only [map_cons, find?]
|
||||
by_cases h : p (f x) <;> simp [h, ih]
|
||||
|
||||
theorem find?_append {l₁ l₂ : List α} : (l₁ ++ l₂).find? p = (l₁.find? p).or (l₂.find? p) := by
|
||||
induction l₁ with
|
||||
| nil => simp
|
||||
| cons x xs ih =>
|
||||
simp only [cons_append, find?]
|
||||
by_cases h : p x <;> simp [h, ih]
|
||||
|
||||
theorem find?_replicate : find? p (replicate n a) = if n = 0 then none else if p a then some a else none := by
|
||||
cases n
|
||||
· simp
|
||||
· by_cases p a <;> simp_all [replicate_succ]
|
||||
|
||||
@[simp] theorem find?_replicate_of_length_pos (h : 0 < n) : find? p (replicate n a) = if p a then some a else none := by
|
||||
simp [find?_replicate, Nat.ne_of_gt h]
|
||||
|
||||
@[simp] theorem find?_replicate_of_pos (h : p a) : find? p (replicate n a) = if n = 0 then none else some a := by
|
||||
simp [find?_replicate, h]
|
||||
|
||||
@[simp] theorem find?_replicate_of_neg (h : ¬ p a) : find? p (replicate n a) = none := by
|
||||
simp [find?_replicate, h]
|
||||
|
||||
theorem Sublist.find?_isSome {l₁ l₂ : List α} (h : l₁ <+ l₂) : (l₁.find? p).isSome → (l₂.find? p).isSome := by
|
||||
induction h with
|
||||
| slnil => simp
|
||||
| cons a h ih
|
||||
| cons₂ a h ih =>
|
||||
simp only [find?]
|
||||
split <;> simp_all
|
||||
|
||||
theorem Sublist.find?_eq_none {l₁ l₂ : List α} (h : l₁ <+ l₂) : l₂.find? p = none → l₁.find? p = none := by
|
||||
simp only [List.find?_eq_none, Bool.not_eq_true]
|
||||
exact fun w x m => w x (Sublist.mem m h)
|
||||
|
||||
theorem IsPrefix.find?_eq_some {l₁ l₂ : List α} {p : α → Bool} (h : l₁ <+: l₂) :
|
||||
List.find? p l₁ = some b → List.find? p l₂ = some b := by
|
||||
rw [IsPrefix] at h
|
||||
obtain ⟨t, rfl⟩ := h
|
||||
simp (config := {contextual := true}) [find?_append]
|
||||
|
||||
theorem IsPrefix.find?_eq_none {l₁ l₂ : List α} {p : α → Bool} (h : l₁ <+: l₂) :
|
||||
List.find? p l₂ = none → List.find? p l₁ = none :=
|
||||
h.sublist.find?_eq_none
|
||||
theorem IsSuffix.find?_eq_none {l₁ l₂ : List α} {p : α → Bool} (h : l₁ <:+ l₂) :
|
||||
List.find? p l₂ = none → List.find? p l₁ = none :=
|
||||
h.sublist.find?_eq_none
|
||||
theorem IsInfix.find?_eq_none {l₁ l₂ : List α} {p : α → Bool} (h : l₁ <:+: l₂) :
|
||||
List.find? p l₂ = none → List.find? p l₁ = none :=
|
||||
h.sublist.find?_eq_none
|
||||
|
||||
/-! ### findSome? -/
|
||||
|
||||
@[simp] theorem findSome?_cons_of_isSome (l) (h : (f a).isSome) : findSome? f (a :: l) = f a := by
|
||||
@@ -117,11 +38,50 @@ theorem exists_of_findSome?_eq_some {l : List α} {f : α → Option β} (w : l.
|
||||
@[simp] theorem findSome?_eq_none : findSome? p l = none ↔ ∀ x ∈ l, p x = none := by
|
||||
induction l <;> simp [findSome?_cons]; split <;> simp [*]
|
||||
|
||||
@[simp] theorem findSome?_isSome_iff (f : α → Option β) (l : List α) :
|
||||
(l.findSome? f).isSome ↔ ∃ x, x ∈ l ∧ (f x).isSome := by
|
||||
induction l with
|
||||
| nil => simp
|
||||
| cons x xs ih =>
|
||||
simp only [findSome?_cons]
|
||||
split <;> simp_all
|
||||
|
||||
@[simp] theorem findSome?_guard (l : List α) : findSome? (Option.guard fun x => p x) l = find? p l := by
|
||||
induction l with
|
||||
| nil => simp
|
||||
| cons x xs ih =>
|
||||
simp [guard, findSome?, find?]
|
||||
split <;> rename_i h
|
||||
· simp only [Option.guard_eq_some] at h
|
||||
obtain ⟨rfl, h⟩ := h
|
||||
simp [h]
|
||||
· simp only [Option.guard_eq_none] at h
|
||||
simp [ih, h]
|
||||
|
||||
@[simp] theorem filterMap_head? (f : α → Option β) (l : List α) : (l.filterMap f).head? = l.findSome? f := by
|
||||
induction l with
|
||||
| nil => simp
|
||||
| cons x xs ih =>
|
||||
simp only [filterMap_cons, findSome?_cons]
|
||||
split <;> simp [*]
|
||||
|
||||
@[simp] theorem filterMap_head (f : α → Option β) (l : List α) (h) :
|
||||
(l.filterMap f).head h = (l.findSome? f).get (by simp_all [Option.isSome_iff_ne_none]) := by
|
||||
simp [head_eq_iff_head?_eq_some]
|
||||
|
||||
@[simp] theorem filterMap_getLast? (f : α → Option β) (l : List α) : (l.filterMap f).getLast? = l.reverse.findSome? f := by
|
||||
rw [getLast?_eq_head?_reverse]
|
||||
simp [← filterMap_reverse]
|
||||
|
||||
@[simp] theorem filterMap_getLast (f : α → Option β) (l : List α) (h) :
|
||||
(l.filterMap f).getLast h = (l.reverse.findSome? f).get (by simp_all [Option.isSome_iff_ne_none]) := by
|
||||
simp [getLast_eq_iff_getLast_eq_some]
|
||||
|
||||
@[simp] theorem map_findSome? (f : α → Option β) (g : β → γ) (l : List α) :
|
||||
(l.findSome? f).map g = l.findSome? (Option.map g ∘ f) := by
|
||||
induction l <;> simp [findSome?_cons]; split <;> simp [*]
|
||||
|
||||
@[simp] theorem findSome?_map (f : β → γ) (l : List β) : findSome? p (l.map f) = l.findSome? (p ∘ f) := by
|
||||
theorem findSome?_map (f : β → γ) (l : List β) : findSome? p (l.map f) = l.findSome? (p ∘ f) := by
|
||||
induction l with
|
||||
| nil => simp
|
||||
| cons x xs ih =>
|
||||
@@ -146,10 +106,10 @@ theorem findSome?_replicate : findSome? f (replicate n a) = if n = 0 then none e
|
||||
simp [findSome?_replicate, Nat.ne_of_gt h]
|
||||
|
||||
-- Argument is unused, but used to decide whether `simp` should unfold.
|
||||
@[simp] theorem find?_replicate_of_isSome (_ : (f a).isSome) : findSome? f (replicate n a) = if n = 0 then none else f a := by
|
||||
@[simp] theorem findSome?_replicate_of_isSome (_ : (f a).isSome) : findSome? f (replicate n a) = if n = 0 then none else f a := by
|
||||
simp [findSome?_replicate]
|
||||
|
||||
@[simp] theorem find?_replicate_of_isNone (h : (f a).isNone) : findSome? f (replicate n a) = none := by
|
||||
@[simp] theorem findSome?_replicate_of_isNone (h : (f a).isNone) : findSome? f (replicate n a) = none := by
|
||||
rw [Option.isNone_iff_eq_none] at h
|
||||
simp [findSome?_replicate, h]
|
||||
|
||||
@@ -160,7 +120,9 @@ theorem Sublist.findSome?_isSome {l₁ l₂ : List α} (h : l₁ <+ l₂) :
|
||||
| cons a h ih
|
||||
| cons₂ a h ih =>
|
||||
simp only [findSome?]
|
||||
split <;> simp_all
|
||||
split
|
||||
· simp_all
|
||||
· exact ih
|
||||
|
||||
theorem Sublist.findSome?_eq_none {l₁ l₂ : List α} (h : l₁ <+ l₂) :
|
||||
l₂.findSome? f = none → l₁.findSome? f = none := by
|
||||
@@ -183,6 +145,265 @@ theorem IsInfix.findSome?_eq_none {l₁ l₂ : List α} {f : α → Option β} (
|
||||
List.findSome? f l₂ = none → List.findSome? f l₁ = none :=
|
||||
h.sublist.findSome?_eq_none
|
||||
|
||||
/-! ### find? -/
|
||||
|
||||
@[simp] theorem find?_singleton (a : α) (p : α → Bool) : [a].find? p = if p a then some a else none := by
|
||||
simp only [find?]
|
||||
split <;> simp_all
|
||||
|
||||
@[simp] theorem find?_cons_of_pos (l) (h : p a) : find? p (a :: l) = some a := by
|
||||
simp [find?, h]
|
||||
|
||||
@[simp] theorem find?_cons_of_neg (l) (h : ¬p a) : find? p (a :: l) = find? p l := by
|
||||
simp [find?, h]
|
||||
|
||||
@[simp] theorem find?_eq_none : find? p l = none ↔ ∀ x ∈ l, ¬ p x := by
|
||||
induction l <;> simp [find?_cons]; split <;> simp [*]
|
||||
|
||||
theorem find?_eq_some : xs.find? p = some b ↔ p b ∧ ∃ as bs, xs = as ++ b :: bs ∧ ∀ a ∈ as, !p a := by
|
||||
induction xs with
|
||||
| nil => simp
|
||||
| cons x xs ih =>
|
||||
simp only [find?_cons, exists_and_right]
|
||||
split <;> rename_i h
|
||||
· simp only [Option.some.injEq]
|
||||
constructor
|
||||
· rintro rfl
|
||||
exact ⟨h, [], ⟨xs, rfl⟩, by simp⟩
|
||||
· rintro ⟨-, ⟨as, ⟨⟨bs, h₁⟩, h₂⟩⟩⟩
|
||||
cases as with
|
||||
| nil => simp_all
|
||||
| cons a as =>
|
||||
specialize h₂ a (mem_cons_self _ _)
|
||||
simp only [cons_append] at h₁
|
||||
obtain ⟨rfl, -⟩ := h₁
|
||||
simp_all
|
||||
· simp only [ih, Bool.not_eq_true', exists_and_right, and_congr_right_iff]
|
||||
intro pb
|
||||
constructor
|
||||
· rintro ⟨as, ⟨⟨bs, rfl⟩, h₁⟩⟩
|
||||
refine ⟨x :: as, ⟨⟨bs, rfl⟩, ?_⟩⟩
|
||||
intro a m
|
||||
simp at m
|
||||
obtain (rfl|m) := m
|
||||
· exact h
|
||||
· exact h₁ a m
|
||||
· rintro ⟨as, ⟨bs, h₁⟩, h₂⟩
|
||||
cases as with
|
||||
| nil => simp_all
|
||||
| cons a as =>
|
||||
refine ⟨as, ⟨⟨bs, ?_⟩, fun a m => h₂ a (mem_cons_of_mem _ m)⟩⟩
|
||||
cases h₁
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem find?_cons_eq_some : (a :: xs).find? p = some b ↔ (p a ∧ a = b) ∨ (!p a ∧ xs.find? p = some b) := by
|
||||
rw [find?_cons]
|
||||
split <;> simp_all
|
||||
|
||||
@[simp] theorem find?_isSome (xs : List α) (p : α → Bool) : (xs.find? p).isSome ↔ ∃ x, x ∈ xs ∧ p x := by
|
||||
induction xs with
|
||||
| nil => simp
|
||||
| cons x xs ih =>
|
||||
simp only [find?_cons, mem_cons, exists_eq_or_imp]
|
||||
split <;> simp_all
|
||||
|
||||
theorem find?_some : ∀ {l}, find? p l = some a → p a
|
||||
| b :: l, H => by
|
||||
by_cases h : p b <;> simp [find?, h] at H
|
||||
· exact H ▸ h
|
||||
· exact find?_some H
|
||||
|
||||
theorem mem_of_find?_eq_some : ∀ {l}, find? p l = some a → a ∈ l
|
||||
| b :: l, H => by
|
||||
by_cases h : p b <;> simp [find?, h] at H
|
||||
· exact H ▸ .head _
|
||||
· exact .tail _ (mem_of_find?_eq_some H)
|
||||
|
||||
@[simp] theorem get_find?_mem (xs : List α) (p : α → Bool) (h) : (xs.find? p).get h ∈ xs := by
|
||||
induction xs with
|
||||
| nil => simp at h
|
||||
| cons x xs ih =>
|
||||
simp only [find?_cons]
|
||||
by_cases h : p x
|
||||
· simp [h]
|
||||
· simp only [h]
|
||||
right
|
||||
apply ih
|
||||
|
||||
@[simp] theorem find?_filter (xs : List α) (p : α → Bool) (q : α → Bool) :
|
||||
(xs.filter p).find? q = xs.find? (fun a => p a ∧ q a) := by
|
||||
induction xs with
|
||||
| nil => simp
|
||||
| cons x xs ih =>
|
||||
simp only [filter_cons]
|
||||
split <;>
|
||||
· simp only [find?_cons]
|
||||
split <;> simp_all
|
||||
|
||||
@[simp] theorem filter_head? (p : α → Bool) (l : List α) : (l.filter p).head? = l.find? p := by
|
||||
rw [← filterMap_eq_filter, filterMap_head?, findSome?_guard]
|
||||
|
||||
@[simp] theorem filter_head (p : α → Bool) (l : List α) (h) :
|
||||
(l.filter p).head h = (l.find? p).get (by simp_all [Option.isSome_iff_ne_none]) := by
|
||||
simp [head_eq_iff_head?_eq_some]
|
||||
|
||||
@[simp] theorem filter_getLast? (p : α → Bool) (l : List α) : (l.filter p).getLast? = l.reverse.find? p := by
|
||||
rw [getLast?_eq_head?_reverse]
|
||||
simp [← filter_reverse]
|
||||
|
||||
@[simp] theorem filter_getLast (p : α → Bool) (l : List α) (h) :
|
||||
(l.filter p).getLast h = (l.reverse.find? p).get (by simp_all [Option.isSome_iff_ne_none]) := by
|
||||
simp [getLast_eq_iff_getLast_eq_some]
|
||||
|
||||
@[simp] theorem find?_filterMap (xs : List α) (f : α → Option β) (p : β → Bool) :
|
||||
(xs.filterMap f).find? p = (xs.find? (fun a => (f a).any p)).bind f := by
|
||||
induction xs with
|
||||
| nil => simp
|
||||
| cons x xs ih =>
|
||||
simp only [filterMap_cons]
|
||||
split <;>
|
||||
· simp only [find?_cons]
|
||||
split <;> simp_all
|
||||
|
||||
@[simp] theorem find?_map (f : β → α) (l : List β) : find? p (l.map f) = (l.find? (p ∘ f)).map f := by
|
||||
induction l with
|
||||
| nil => simp
|
||||
| cons x xs ih =>
|
||||
simp only [map_cons, find?]
|
||||
by_cases h : p (f x) <;> simp [h, ih]
|
||||
|
||||
@[simp] theorem find?_append {l₁ l₂ : List α} : (l₁ ++ l₂).find? p = (l₁.find? p).or (l₂.find? p) := by
|
||||
induction l₁ with
|
||||
| nil => simp
|
||||
| cons x xs ih =>
|
||||
simp only [cons_append, find?]
|
||||
by_cases h : p x <;> simp [h, ih]
|
||||
|
||||
@[simp] theorem find?_join (xs : List (List α)) (p : α → Bool) :
|
||||
xs.join.find? p = xs.findSome? (·.find? p) := by
|
||||
induction xs with
|
||||
| nil => simp
|
||||
| cons x xs ih =>
|
||||
simp only [join_cons, find?_append, findSome?_cons, ih]
|
||||
split <;> simp [*]
|
||||
|
||||
theorem find?_join_eq_none (xs : List (List α)) (p : α → Bool) :
|
||||
xs.join.find? p = none ↔ ∀ ys ∈ xs, ∀ x ∈ ys, !p x := by
|
||||
simp
|
||||
|
||||
/--
|
||||
If `find? p` returns `some a` from `xs.join`, then `p a` holds, and
|
||||
some list in `xs` contains `a`, and no earlier element of that list satisfies `p`.
|
||||
Moreover, no earlier list in `xs` has an element satisfying `p`.
|
||||
-/
|
||||
theorem find?_join_eq_some (xs : List (List α)) (p : α → Bool) (a : α) :
|
||||
xs.join.find? p = some a ↔
|
||||
p a ∧ ∃ as ys zs bs, xs = as ++ (ys ++ a :: zs) :: bs ∧
|
||||
(∀ a ∈ as, ∀ x ∈ a, !p x) ∧ (∀ x ∈ ys, !p x) := by
|
||||
rw [find?_eq_some]
|
||||
constructor
|
||||
· rintro ⟨h, ⟨ys, zs, h₁, h₂⟩⟩
|
||||
refine ⟨h, ?_⟩
|
||||
rw [join_eq_append] at h₁
|
||||
obtain (⟨as, bs, rfl, rfl, h₁⟩ | ⟨as, bs, c, cs, ds, rfl, rfl, h₁⟩) := h₁
|
||||
· replace h₁ := h₁.symm
|
||||
rw [join_eq_cons] at h₁
|
||||
obtain ⟨bs, cs, ds, rfl, h₁, rfl⟩ := h₁
|
||||
refine ⟨as ++ bs, [], cs, ds, by simp, ?_⟩
|
||||
simp
|
||||
rintro a (ma | mb) x m
|
||||
· simpa using h₂ x (by simpa using ⟨a, ma, m⟩)
|
||||
· specialize h₁ _ mb
|
||||
simp_all
|
||||
· simp [h₁]
|
||||
refine ⟨as, bs, ?_⟩
|
||||
refine ⟨?_, ?_, ?_⟩
|
||||
· simp_all
|
||||
· intro l ml a m
|
||||
simpa using h₂ a (by simpa using .inl ⟨l, ml, m⟩)
|
||||
· intro x m
|
||||
simpa using h₂ x (by simpa using .inr m)
|
||||
· rintro ⟨h, ⟨as, ys, zs, bs, rfl, h₁, h₂⟩⟩
|
||||
refine ⟨h, as.join ++ ys, zs ++ bs.join, by simp, ?_⟩
|
||||
intro a m
|
||||
simp at m
|
||||
obtain ⟨l, ml, m⟩ | m := m
|
||||
· exact h₁ l ml a m
|
||||
· exact h₂ a m
|
||||
|
||||
@[simp] theorem find?_bind (xs : List α) (f : α → List β) (p : β → Bool) :
|
||||
(xs.bind f).find? p = xs.findSome? (fun x => (f x).find? p) := by
|
||||
simp [bind_def, findSome?_map]; rfl
|
||||
|
||||
theorem find?_bind_eq_none (xs : List α) (f : α → List β) (p : β → Bool) :
|
||||
(xs.bind f).find? p = none ↔ ∀ x ∈ xs, ∀ y ∈ f x, !p y := by
|
||||
simp
|
||||
|
||||
theorem find?_replicate : find? p (replicate n a) = if n = 0 then none else if p a then some a else none := by
|
||||
cases n
|
||||
· simp
|
||||
· by_cases p a <;> simp_all [replicate_succ]
|
||||
|
||||
@[simp] theorem find?_replicate_of_length_pos (h : 0 < n) : find? p (replicate n a) = if p a then some a else none := by
|
||||
simp [find?_replicate, Nat.ne_of_gt h]
|
||||
|
||||
@[simp] theorem find?_replicate_of_pos (h : p a) : find? p (replicate n a) = if n = 0 then none else some a := by
|
||||
simp [find?_replicate, h]
|
||||
|
||||
@[simp] theorem find?_replicate_of_neg (h : ¬ p a) : find? p (replicate n a) = none := by
|
||||
simp [find?_replicate, h]
|
||||
|
||||
-- This isn't a `@[simp]` lemma since there is already a lemma for `l.find? p = none` for any `l`.
|
||||
theorem find?_replicate_eq_none (n : Nat) (a : α) (p : α → Bool) :
|
||||
(replicate n a).find? p = none ↔ n = 0 ∨ !p a := by
|
||||
simp [Classical.or_iff_not_imp_left]
|
||||
|
||||
@[simp] theorem find?_replicate_eq_some (n : Nat) (a b : α) (p : α → Bool) :
|
||||
(replicate n a).find? p = some b ↔ n ≠ 0 ∧ p a ∧ a = b := by
|
||||
cases n <;> simp
|
||||
|
||||
@[simp] theorem get_find?_replicate (n : Nat) (a : α) (p : α → Bool) (h) : ((replicate n a).find? p).get h = a := by
|
||||
cases n with
|
||||
| zero => simp at h
|
||||
| succ n => simp
|
||||
|
||||
theorem Sublist.find?_isSome {l₁ l₂ : List α} (h : l₁ <+ l₂) : (l₁.find? p).isSome → (l₂.find? p).isSome := by
|
||||
induction h with
|
||||
| slnil => simp
|
||||
| cons a h ih
|
||||
| cons₂ a h ih =>
|
||||
simp only [find?]
|
||||
split
|
||||
· simp
|
||||
· simpa using ih
|
||||
|
||||
theorem Sublist.find?_eq_none {l₁ l₂ : List α} (h : l₁ <+ l₂) : l₂.find? p = none → l₁.find? p = none := by
|
||||
simp only [List.find?_eq_none, Bool.not_eq_true]
|
||||
exact fun w x m => w x (Sublist.mem m h)
|
||||
|
||||
theorem IsPrefix.find?_eq_some {l₁ l₂ : List α} {p : α → Bool} (h : l₁ <+: l₂) :
|
||||
List.find? p l₁ = some b → List.find? p l₂ = some b := by
|
||||
rw [IsPrefix] at h
|
||||
obtain ⟨t, rfl⟩ := h
|
||||
simp (config := {contextual := true}) [find?_append]
|
||||
|
||||
theorem IsPrefix.find?_eq_none {l₁ l₂ : List α} {p : α → Bool} (h : l₁ <+: l₂) :
|
||||
List.find? p l₂ = none → List.find? p l₁ = none :=
|
||||
h.sublist.find?_eq_none
|
||||
theorem IsSuffix.find?_eq_none {l₁ l₂ : List α} {p : α → Bool} (h : l₁ <:+ l₂) :
|
||||
List.find? p l₂ = none → List.find? p l₁ = none :=
|
||||
h.sublist.find?_eq_none
|
||||
theorem IsInfix.find?_eq_none {l₁ l₂ : List α} {p : α → Bool} (h : l₁ <:+: l₂) :
|
||||
List.find? p l₂ = none → List.find? p l₁ = none :=
|
||||
h.sublist.find?_eq_none
|
||||
|
||||
theorem find?_pmap {P : α → Prop} (f : (a : α) → P a → β) (xs : List α)
|
||||
(H : ∀ (a : α), a ∈ xs → P a) (p : β → Bool) :
|
||||
(xs.pmap f H).find? p = (xs.attach.find? (fun ⟨a, m⟩ => p (f a (H a m)))).map fun ⟨a, m⟩ => f a (H a m) := by
|
||||
simp only [pmap_eq_map_attach, find?_map]
|
||||
rfl
|
||||
|
||||
/-! ### findIdx -/
|
||||
|
||||
theorem findIdx_cons (p : α → Bool) (b : α) (l : List α) :
|
||||
@@ -518,8 +739,8 @@ theorem findIdx?_eq_enum_findSome? {xs : List α} {p : α → Bool} :
|
||||
simp only [findIdx?_cons, Nat.zero_add, findIdx?_succ, enum]
|
||||
split
|
||||
· simp_all
|
||||
· simp_all only [enumFrom_cons, ite_false, Option.isNone_none, findSome?_cons_of_isNone]
|
||||
simp [Function.comp_def, ← map_fst_add_enum_eq_enumFrom]
|
||||
· simp_all only [enumFrom_cons, ite_false, Option.isNone_none, findSome?_cons_of_isNone, reduceCtorEq]
|
||||
simp [Function.comp_def, ← map_fst_add_enum_eq_enumFrom, findSome?_map]
|
||||
|
||||
theorem Sublist.findIdx?_isSome {l₁ l₂ : List α} (h : l₁ <+ l₂) :
|
||||
(l₁.findIdx? p).isSome → (l₂.findIdx? p).isSome := by
|
||||
|
||||
@@ -79,6 +79,11 @@ open Nat
|
||||
|
||||
/-! ## Preliminaries -/
|
||||
|
||||
/-! ### nil -/
|
||||
|
||||
@[simp] theorem nil_eq {α} (xs : List α) : [] = xs ↔ xs = [] := by
|
||||
cases xs <;> simp
|
||||
|
||||
/-! ### cons -/
|
||||
|
||||
theorem cons_ne_nil (a : α) (l : List α) : a :: l ≠ [] := nofun
|
||||
@@ -86,6 +91,10 @@ 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
|
||||
@@ -260,6 +269,14 @@ 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? {α} (xs : List α) (i : Nat) (h : i < xs.length) :
|
||||
(some xs[i] = xs[i]?) ↔ True := by
|
||||
simp [h]
|
||||
|
||||
@[simp] theorem getElem?_eq_some_getElem {α} (xs : List α) (i : Nat) (h : i < xs.length) :
|
||||
(xs[i]? = some xs[i]) ↔ True := by
|
||||
simp [h]
|
||||
|
||||
theorem getElem_eq_iff {l : List α} {n : Nat} {h : n < l.length} : l[n] = x ↔ l[n]? = some x := by
|
||||
simp only [getElem?_eq_some]
|
||||
exact ⟨fun w => ⟨h, w⟩, fun h => h.2⟩
|
||||
@@ -276,6 +293,9 @@ theorem getElem?_cons_zero {l : List α} : (a::l)[0]? = some a := by simp
|
||||
simp only [← get?_eq_getElem?]
|
||||
rfl
|
||||
|
||||
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
|
||||
@@ -344,6 +364,11 @@ theorem get?_concat_length (l : List α) (a : α) : (l ++ [a]).get? l.length = s
|
||||
|
||||
theorem mem_cons_self (a : α) (l : List α) : a ∈ a :: l := .head ..
|
||||
|
||||
theorem mem_concat_self (xs : List α) (a : α) : a ∈ xs ++ [a] :=
|
||||
mem_append_of_mem_right xs (mem_cons_self a _)
|
||||
|
||||
theorem mem_append_cons_self : a ∈ xs ++ a :: ys := mem_append_of_mem_right _ (mem_cons_self _ _)
|
||||
|
||||
theorem mem_cons_of_mem (y : α) {a : α} {l : List α} : a ∈ l → a ∈ y :: l := .tail _
|
||||
|
||||
theorem exists_mem_of_ne_nil (l : List α) (h : l ≠ []) : ∃ x, x ∈ l :=
|
||||
@@ -363,27 +388,21 @@ theorem forall_mem_cons {p : α → Prop} {a : α} {l : List α} :
|
||||
⟨fun H => ⟨H _ (.head ..), fun _ h => H _ (.tail _ h)⟩,
|
||||
fun ⟨H₁, H₂⟩ _ => fun | .head .. => H₁ | .tail _ h => H₂ _ h⟩
|
||||
|
||||
@[simp]
|
||||
theorem forall_mem_ne {a : α} {l : List α} : (∀ a' : α, a' ∈ l → ¬a = a') ↔ a ∉ l :=
|
||||
⟨fun h m => h _ m rfl, fun h _ m e => h (e.symm ▸ m)⟩
|
||||
|
||||
@[simp]
|
||||
theorem forall_mem_ne' {a : α} {l : List α} : (∀ a' : α, a' ∈ l → ¬a' = a) ↔ a ∉ l :=
|
||||
⟨fun h m => h _ m rfl, fun h _ m e => h (e.symm ▸ m)⟩
|
||||
|
||||
@[simp]
|
||||
theorem any_beq [BEq α] [LawfulBEq α] {l : List α} : (l.any fun x => a == x) ↔ a ∈ l := by
|
||||
induction l <;> simp_all
|
||||
|
||||
@[simp]
|
||||
theorem any_beq' [BEq α] [LawfulBEq α] {l : List α} : (l.any fun x => x == a) ↔ a ∈ l := by
|
||||
induction l <;> simp_all [eq_comm (a := a)]
|
||||
|
||||
@[simp]
|
||||
theorem all_bne [BEq α] [LawfulBEq α] {l : List α} : (l.all fun x => a != x) ↔ a ∉ l := by
|
||||
induction l <;> simp_all
|
||||
|
||||
@[simp]
|
||||
theorem all_bne' [BEq α] [LawfulBEq α] {l : List α} : (l.all fun x => x != a) ↔ a ∉ l := by
|
||||
induction l <;> simp_all [eq_comm (a := a)]
|
||||
|
||||
@@ -507,7 +526,7 @@ theorem isEmpty_iff_length_eq_zero {l : List α} : l.isEmpty ↔ l.length = 0 :=
|
||||
@[simp] theorem isEmpty_eq_true {l : List α} : l.isEmpty ↔ l = [] := by
|
||||
cases l <;> simp
|
||||
|
||||
@[simp] theorem isEmpty_eq_false {l : List α} : ¬ l.isEmpty ↔ l ≠ [] := by
|
||||
@[simp] theorem isEmpty_eq_false {l : List α} : l.isEmpty = false ↔ l ≠ [] := by
|
||||
cases l <;> simp
|
||||
|
||||
/-! ### any / all -/
|
||||
@@ -546,8 +565,7 @@ theorem get_set_eq {l : List α} {i : Nat} {a : α} (h : i < (l.set i a).length)
|
||||
(l.set i a)[i]? = some a := by
|
||||
simp_all [getElem?_eq_some]
|
||||
|
||||
@[simp]
|
||||
theorem getElem?_set_eq' {l : List α} {i : Nat} {a : α} : (set l i a)[i]? = (fun _ => a) <$> l[i]? := by
|
||||
theorem getElem?_set_eq' {l : List α} {i : Nat} {a : α} : (set l i a)[i]? = Function.const _ a <$> l[i]? := by
|
||||
by_cases h : i < l.length
|
||||
· simp [getElem?_set_eq h, getElem?_eq_getElem h]
|
||||
· simp only [Nat.not_lt] at h
|
||||
@@ -604,7 +622,7 @@ theorem getElem?_set {l : List α} {i j : Nat} {a : α} :
|
||||
theorem getElem?_set' {l : List α} {i j : Nat} {a : α} :
|
||||
(set l i a)[j]? = if i = j then (fun _ => a) <$> l[j]? else l[j]? := by
|
||||
by_cases i = j
|
||||
· simp only [getElem?_set_eq', Option.map_eq_map, ↓reduceIte, *]
|
||||
· simp only [getElem?_set_eq', Option.map_eq_map, ↓reduceIte, *]; rfl
|
||||
· simp only [ne_eq, not_false_eq_true, getElem?_set_ne, ↓reduceIte, *]
|
||||
|
||||
theorem set_eq_of_length_le {l : List α} {n : Nat} (h : l.length ≤ n) {a : α} :
|
||||
@@ -619,7 +637,7 @@ theorem set_eq_of_length_le {l : List α} {n : Nat} (h : l.length ≤ n) {a : α
|
||||
exact Nat.succ_le_succ_iff.mp h
|
||||
|
||||
@[simp] theorem set_eq_nil (l : List α) (n : Nat) (a : α) : l.set n a = [] ↔ l = [] := by
|
||||
cases l <;> cases n <;> simp only [set]
|
||||
cases l <;> cases n <;> simp [set]
|
||||
|
||||
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
|
||||
@@ -714,9 +732,14 @@ theorem foldr_eq_foldrM (f : α → β → β) (b) (l : List α) :
|
||||
|
||||
/-! ### foldl and foldr -/
|
||||
|
||||
@[simp] theorem foldr_self_append (l : List α) : l.foldr cons l' = l ++ l' := by
|
||||
@[simp] theorem foldr_cons_eq_append (l : List α) : l.foldr cons l' = l ++ l' := by
|
||||
induction l <;> simp [*]
|
||||
|
||||
@[deprecated foldr_cons_eq_append (since := "2024-08-22")] abbrev foldr_self_append := @foldr_cons_eq_append
|
||||
|
||||
@[simp] theorem foldl_flip_cons_eq_append (l : List α) : l.foldl (fun x y => y :: x) l' = l.reverse ++ l' := by
|
||||
induction l generalizing l' <;> simp [*]
|
||||
|
||||
theorem foldr_self (l : List α) : l.foldr cons [] = l := by simp
|
||||
|
||||
theorem foldl_map (f : β₁ → β₂) (g : α → β₂ → α) (l : List β₁) (init : α) :
|
||||
@@ -835,7 +858,7 @@ theorem getLast_eq_getLastD (a l h) : @getLast α (a::l) h = getLastD l a := by
|
||||
theorem getLast!_cons [Inhabited α] : @getLast! α _ (a::l) = getLastD l a := by
|
||||
simp [getLast!, getLast_eq_getLastD]
|
||||
|
||||
theorem getLast_mem : ∀ {l : List α} (h : l ≠ []), getLast l h ∈ l
|
||||
@[simp] theorem getLast_mem : ∀ {l : List α} (h : l ≠ []), getLast l h ∈ l
|
||||
| [], h => absurd rfl h
|
||||
| [_], _ => .head ..
|
||||
| _::a::l, _ => .tail _ <| getLast_mem (cons_ne_nil a l)
|
||||
@@ -855,11 +878,14 @@ theorem get_cons_length (x : α) (xs : List α) (n : Nat) (h : n = xs.length) :
|
||||
|
||||
/-! ### getLast? -/
|
||||
|
||||
theorem getLast?_cons : @getLast? α (a::l) = getLastD l a := by
|
||||
simp only [getLast?, getLast_eq_getLastD]
|
||||
|
||||
@[simp] theorem getLast?_singleton (a : α) : getLast? [a] = a := rfl
|
||||
|
||||
theorem getLast?_cons {a : α} : (a::l).getLast? = l.getLast?.getD a := by
|
||||
cases l <;> simp [getLast?, getLast]
|
||||
|
||||
@[simp] theorem getLast?_cons_cons : (a :: b :: l).getLast? = (b :: l).getLast? := by
|
||||
simp [getLast?_cons]
|
||||
|
||||
theorem getLast?_eq_getLast : ∀ l h, @getLast? α l = some (getLast l h)
|
||||
| [], h => nomatch h rfl
|
||||
| _::_, _ => rfl
|
||||
@@ -873,10 +899,10 @@ theorem getLast?_eq_getElem? : ∀ (l : List α), getLast? l = l[l.length - 1]?
|
||||
theorem getLast?_eq_get? (l : List α) : getLast? l = l.get? (l.length - 1) := by
|
||||
simp [getLast?_eq_getElem?]
|
||||
|
||||
@[simp] theorem getLast?_concat (l : List α) : getLast? (l ++ [a]) = some a := by
|
||||
theorem getLast?_concat (l : List α) : getLast? (l ++ [a]) = some a := by
|
||||
simp [getLast?_eq_getElem?, Nat.succ_sub_succ]
|
||||
|
||||
@[simp] theorem getLastD_concat (a b l) : @getLastD α (l ++ [b]) a = b := by
|
||||
theorem getLastD_concat (a b l) : @getLastD α (l ++ [b]) a = b := by
|
||||
rw [getLastD_eq_getLast?, getLast?_concat]; rfl
|
||||
|
||||
/-! ## Head and tail -/
|
||||
@@ -889,6 +915,11 @@ theorem head!_of_head? [Inhabited α] : ∀ {l : List α}, head? l = some a →
|
||||
theorem head?_eq_head : ∀ {l} h, @head? α l = some (head l h)
|
||||
| _::_, _ => rfl
|
||||
|
||||
theorem head_eq_iff_head?_eq_some {xs : List α} (h) : xs.head h = a ↔ xs.head? = some a := by
|
||||
cases xs with
|
||||
| nil => simp at h
|
||||
| cons x xs => simp
|
||||
|
||||
theorem head?_eq_getElem? : ∀ l : List α, head? l = l[0]?
|
||||
| [] => rfl
|
||||
| a::l => by simp
|
||||
@@ -896,6 +927,9 @@ theorem head?_eq_getElem? : ∀ l : List α, head? l = l[0]?
|
||||
@[simp] theorem head?_eq_none_iff : l.head? = none ↔ l = [] := by
|
||||
cases l <;> simp
|
||||
|
||||
theorem head?_eq_some_iff {xs : List α} {a : α} : xs.head? = some a ↔ ∃ ys, xs = a :: ys := by
|
||||
cases xs <;> simp_all
|
||||
|
||||
@[simp] theorem head_mem : ∀ {l : List α} (h : l ≠ []), head l h ∈ l
|
||||
| [], h => absurd rfl h
|
||||
| _::_, _ => .head ..
|
||||
@@ -924,9 +958,16 @@ theorem tail_eq_tail? (l) : @tail α l = (tail? l).getD [] := by simp [tail_eq_t
|
||||
|
||||
/-! ### map -/
|
||||
|
||||
@[simp] theorem map_id (l : List α) : map id l = l := by induction l <;> simp_all
|
||||
@[simp] theorem map_id_fun : map (id : α → α) = id := by
|
||||
funext l
|
||||
induction l <;> simp_all
|
||||
|
||||
@[simp] theorem map_id' (l : List α) : map (fun a => a) l = l := by induction l <;> simp_all
|
||||
@[simp] theorem map_id_fun' : map (fun (a : α) => a) = id := map_id_fun
|
||||
|
||||
theorem map_id (l : List α) : map (id : α → α) l = l := by
|
||||
induction l <;> simp_all
|
||||
|
||||
theorem map_id' (l : List α) : map (fun (a : α) => a) l = l := map_id l
|
||||
|
||||
theorem map_id'' {f : α → α} (h : ∀ x, f x = x) (l : List α) : map f l = l := by
|
||||
simp [show f = id from funext h]
|
||||
@@ -1113,7 +1154,7 @@ theorem filter_length_eq_length {l} : (filter p l).length = l.length ↔ ∀ a
|
||||
· simp_all [or_and_left]
|
||||
· simp_all [or_and_right]
|
||||
|
||||
theorem filter_eq_nil {l} : filter p l = [] ↔ ∀ a, a ∈ l → ¬p a := by
|
||||
@[simp] theorem filter_eq_nil {l} : filter p l = [] ↔ ∀ a, a ∈ l → ¬p a := by
|
||||
simp only [eq_nil_iff_forall_not_mem, mem_filter, not_and]
|
||||
|
||||
theorem forall_mem_filter {l : List α} {p : α → Bool} {P : α → Prop} :
|
||||
@@ -1201,8 +1242,13 @@ theorem head_filter_of_pos {p : α → Bool} {l : List α} (w : l ≠ []) (h : p
|
||||
theorem filterMap_eq_map (f : α → β) : filterMap (some ∘ f) = map f := by
|
||||
funext l; induction l <;> simp [*, filterMap_cons]
|
||||
|
||||
@[simp] theorem filterMap_some (l : List α) : filterMap some l = l := by
|
||||
erw [filterMap_eq_map, map_id]
|
||||
@[simp] theorem filterMap_some_fun : filterMap (some : α → Option α) = id := by
|
||||
funext l
|
||||
erw [filterMap_eq_map]
|
||||
simp
|
||||
|
||||
theorem filterMap_some (l : List α) : filterMap some l = l := by
|
||||
rw [filterMap_some_fun, id]
|
||||
|
||||
theorem map_filterMap_some_eq_filter_map_isSome (f : α → Option β) (l : List α) :
|
||||
(l.filterMap f).map some = (l.map f).filter fun b => b.isSome := by
|
||||
@@ -1277,7 +1323,7 @@ theorem forall_mem_filterMap {f : α → Option β} {l : List α} {P : β → Pr
|
||||
induction l <;> simp [filterMap_cons]; split <;> simp [*]
|
||||
|
||||
theorem map_filterMap_of_inv (f : α → Option β) (g : β → α) (H : ∀ x : α, (f x).map g = some x)
|
||||
(l : List α) : map g (filterMap f l) = l := by simp only [map_filterMap, H, filterMap_some]
|
||||
(l : List α) : map g (filterMap f l) = l := by simp only [map_filterMap, H, filterMap_some, id]
|
||||
|
||||
theorem head_filterMap_of_eq_some {f : α → Option β} {l : List α} (w : l ≠ []) {b : β} (h : f (l.head w) = some b) :
|
||||
(filterMap f l).head ((ne_nil_of_mem (mem_filterMap.2 ⟨_, head_mem w, h⟩))) =
|
||||
@@ -1300,7 +1346,7 @@ theorem forall_none_of_filterMap_eq_nil (h : filterMap f xs = []) : ∀ x ∈ xs
|
||||
| tail _ hmem => exact ih h hmem
|
||||
· contradiction
|
||||
|
||||
theorem filterMap_eq_nil {l} : filterMap f l = [] ↔ ∀ a ∈ l, f a = none := by
|
||||
@[simp] theorem filterMap_eq_nil {l} : filterMap f l = [] ↔ ∀ a ∈ l, f a = none := by
|
||||
constructor
|
||||
· exact forall_none_of_filterMap_eq_nil
|
||||
· intro h
|
||||
@@ -1419,10 +1465,22 @@ theorem append_right_inj {t₁ t₂ : List α} (s) : s ++ t₁ = s ++ t₂ ↔ t
|
||||
theorem append_left_inj {s₁ s₂ : List α} (t) : s₁ ++ t = s₂ ++ t ↔ s₁ = s₂ :=
|
||||
⟨fun h => append_inj_left' h rfl, congrArg (· ++ _)⟩
|
||||
|
||||
@[simp] theorem append_left_eq_self {x y : List α} : x ++ y = y ↔ x = [] := by
|
||||
rw [← append_left_inj (s₁ := x), nil_append]
|
||||
|
||||
@[simp] theorem self_eq_append_left {x y : List α} : y = x ++ y ↔ x = [] := by
|
||||
rw [eq_comm, append_left_eq_self]
|
||||
|
||||
@[simp] theorem append_right_eq_self {x y : List α} : x ++ y = x ↔ y = [] := by
|
||||
rw [← append_right_inj (t₁ := y), append_nil]
|
||||
|
||||
@[simp] theorem self_eq_append_right {x y : List α} : x = x ++ y ↔ y = [] := by
|
||||
rw [eq_comm, append_right_eq_self]
|
||||
|
||||
@[simp] theorem append_eq_nil : p ++ q = [] ↔ p = [] ∧ q = [] := by
|
||||
cases p <;> simp
|
||||
|
||||
@[simp] theorem getLast_concat {a : α} : ∀ (l : List α), getLast (l ++ [a]) (by simp) = a
|
||||
theorem getLast_concat {a : α} : ∀ (l : List α), getLast (l ++ [a]) (by simp) = a
|
||||
| [] => rfl
|
||||
| a::t => by
|
||||
simp [getLast_cons _, getLast_concat t]
|
||||
@@ -1464,9 +1522,9 @@ theorem getElem?_append {l₁ l₂ : List α} {n : Nat} :
|
||||
· exact getElem?_append_left h
|
||||
· exact getElem?_append_right (by simpa using h)
|
||||
|
||||
@[simp] theorem head_append_of_ne_nil {l : List α} (w : l ≠ []) :
|
||||
head (l ++ l') (by simp_all) = head l w := by
|
||||
match l, w with
|
||||
@[simp] theorem head_append_of_ne_nil {l : List α} {w₁} (w₂) :
|
||||
head (l ++ l') w₁ = head l w₂ := by
|
||||
match l, w₂ with
|
||||
| a :: l, _ => rfl
|
||||
|
||||
theorem head_append {l₁ l₂ : List α} (w : l₁ ++ l₂ ≠ []) :
|
||||
@@ -1489,7 +1547,7 @@ theorem head_append {l₁ l₂ : List α} (w : l₁ ++ l₂ ≠ []) :
|
||||
-- `getLast_append_of_ne_nil`, `getLast_append` and `getLast?_append`
|
||||
-- are stated and proved later in the `reverse` section.
|
||||
|
||||
@[simp] theorem nil_eq_append : [] = a ++ b ↔ a = [] ∧ b = [] := by
|
||||
theorem nil_eq_append : [] = a ++ b ↔ a = [] ∧ b = [] := by
|
||||
rw [eq_comm, append_eq_nil]
|
||||
|
||||
theorem append_ne_nil_of_left_ne_nil {s : List α} (h : s ≠ []) (t : List α) : s ++ t ≠ [] := by simp_all
|
||||
@@ -1500,6 +1558,14 @@ theorem append_ne_nil_of_ne_nil_left {s : List α} (h : s ≠ []) (t : List α)
|
||||
@[deprecated append_ne_nil_of_right_ne_nil (since := "2024-07-24")]
|
||||
theorem append_ne_nil_of_ne_nil_right (s : List α) : t ≠ [] → s ++ t ≠ [] := by simp_all
|
||||
|
||||
theorem tail_append (xs ys : List α) :
|
||||
(xs ++ ys).tail = if xs.isEmpty then ys.tail else xs.tail ++ ys := by
|
||||
cases xs <;> simp
|
||||
|
||||
@[simp] theorem tail_append_of_ne_nil (xs ys : List α) (h : xs ≠ []) :
|
||||
(xs ++ ys).tail = xs.tail ++ ys := by
|
||||
simp_all [tail_append]
|
||||
|
||||
theorem append_eq_cons :
|
||||
a ++ b = x :: c ↔ (a = [] ∧ b = x :: c) ∨ (∃ a', a = x :: a' ∧ c = a' ++ b) := by
|
||||
cases a with simp | cons a as => ?_
|
||||
@@ -1693,9 +1759,15 @@ theorem join_singleton (l : List α) : [l].join = l := by simp
|
||||
| [] => by simp
|
||||
| b :: l => by simp [mem_join, or_and_right, exists_or]
|
||||
|
||||
@[simp] theorem join_eq_nil_iff {L : List (List α)} : L.join = [] ↔ ∀ l ∈ L, l = [] := by
|
||||
@[simp] theorem join_eq_nil {L : List (List α)} : L.join = [] ↔ ∀ l ∈ L, l = [] := by
|
||||
induction L <;> simp_all
|
||||
|
||||
@[deprecated join_eq_nil (since := "2024-08-22")]
|
||||
theorem join_eq_nil_iff {L : List (List α)} : L.join = [] ↔ ∀ l ∈ L, l = [] := join_eq_nil
|
||||
|
||||
theorem join_ne_nil (xs : List (List α)) : xs.join ≠ [] ↔ ∃ x, x ∈ xs ∧ x ≠ [] := by
|
||||
simp
|
||||
|
||||
theorem exists_of_mem_join : a ∈ join L → ∃ l, l ∈ L ∧ a ∈ l := mem_join.1
|
||||
|
||||
theorem mem_join_of_mem (lL : l ∈ L) (al : a ∈ l) : a ∈ join L := mem_join.2 ⟨l, lL, al⟩
|
||||
@@ -1750,6 +1822,11 @@ theorem join_filter_ne_nil [DecidablePred fun l : List α => l ≠ []] {L : List
|
||||
simp only [ne_eq, ← isEmpty_iff, Bool.not_eq_true, Bool.decide_eq_false,
|
||||
join_filter_not_isEmpty]
|
||||
|
||||
@[deprecated filter_join (since := "2024-08-26")]
|
||||
theorem join_map_filter (p : α → Bool) (l : List (List α)) :
|
||||
(l.map (filter p)).join = (l.join).filter p := by
|
||||
rw [filter_join]
|
||||
|
||||
@[simp] theorem join_append (L₁ L₂ : List (List α)) : join (L₁ ++ L₂) = join L₁ ++ join L₂ := by
|
||||
induction L₁ <;> simp_all
|
||||
|
||||
@@ -1759,6 +1836,55 @@ theorem join_concat (L : List (List α)) (l : List α) : join (L ++ [l]) = join
|
||||
theorem join_join {L : List (List (List α))} : join (join L) = join (map join L) := by
|
||||
induction L <;> simp_all
|
||||
|
||||
theorem join_eq_cons (xs : List (List α)) (y : α) (ys : List α) :
|
||||
xs.join = y :: ys ↔
|
||||
∃ as bs cs, xs = as ++ (y :: bs) :: cs ∧ (∀ l, l ∈ as → l = []) ∧ ys = bs ++ cs.join := by
|
||||
constructor
|
||||
· induction xs with
|
||||
| nil => simp
|
||||
| cons x xs ih =>
|
||||
intro h
|
||||
simp only [join_cons] at h
|
||||
replace h := h.symm
|
||||
rw [cons_eq_append] at h
|
||||
obtain (⟨rfl, h⟩ | ⟨z⟩) := h
|
||||
· obtain ⟨as, bs, cs, rfl, _, rfl⟩ := ih h
|
||||
refine ⟨[] :: as, bs, cs, ?_⟩
|
||||
simpa
|
||||
· obtain ⟨a', rfl, rfl⟩ := z
|
||||
refine ⟨[], a', xs, ?_⟩
|
||||
simp
|
||||
· rintro ⟨as, bs, cs, rfl, h₁, rfl⟩
|
||||
simp [join_eq_nil.mpr h₁]
|
||||
|
||||
theorem join_eq_append (xs : List (List α)) (ys zs : List α) :
|
||||
xs.join = ys ++ zs ↔
|
||||
(∃ as bs, xs = as ++ bs ∧ ys = as.join ∧ zs = bs.join) ∨
|
||||
∃ as bs c cs ds, xs = as ++ (bs ++ c :: cs) :: ds ∧ ys = as.join ++ bs ∧
|
||||
zs = c :: cs ++ ds.join := by
|
||||
constructor
|
||||
· induction xs generalizing ys with
|
||||
| nil =>
|
||||
simp only [join_nil, nil_eq, append_eq_nil, and_false, cons_append, false_and, exists_const,
|
||||
exists_false, or_false, and_imp, List.cons_ne_nil]
|
||||
rintro rfl rfl
|
||||
exact ⟨[], [], by simp⟩
|
||||
| cons x xs ih =>
|
||||
intro h
|
||||
simp only [join_cons] at h
|
||||
rw [append_eq_append_iff] at h
|
||||
obtain (⟨ys, rfl, h⟩ | ⟨c', rfl, h⟩) := h
|
||||
· obtain (⟨as, bs, rfl, rfl, rfl⟩ | ⟨as, bs, c, cs, ds, rfl, rfl, rfl⟩) := ih _ h
|
||||
· exact .inl ⟨x :: as, bs, by simp⟩
|
||||
· exact .inr ⟨x :: as, bs, c, cs, ds, by simp⟩
|
||||
· simp only [h]
|
||||
cases c' with
|
||||
| nil => exact .inl ⟨[ys], xs, by simp⟩
|
||||
| cons x c' => exact .inr ⟨[], ys, x, c', xs, by simp⟩
|
||||
· rintro (⟨as, bs, rfl, rfl, rfl⟩ | ⟨as, bs, c, cs, ds, rfl, rfl, rfl⟩)
|
||||
· simp
|
||||
· simp
|
||||
|
||||
/-- Two lists of sublists are equal iff their joins coincide, as well as the lengths of the
|
||||
sublists. -/
|
||||
theorem eq_iff_join_eq : ∀ (L L' : List (List α)),
|
||||
@@ -1793,7 +1919,7 @@ theorem mem_bind_of_mem {b : β} {l : List α} {f : α → List β} {a} (al : a
|
||||
|
||||
@[simp]
|
||||
theorem bind_eq_nil {l : List α} {f : α → List β} : List.bind l f = [] ↔ ∀ x ∈ l, f x = [] :=
|
||||
join_eq_nil_iff.trans <| by
|
||||
join_eq_nil.trans <| by
|
||||
simp only [mem_map, forall_exists_index, and_imp, forall_apply_eq_imp_iff₂]
|
||||
|
||||
theorem forall_mem_bind {p : β → Prop} {l : List α} {f : α → List β} :
|
||||
@@ -1883,6 +2009,9 @@ theorem forall_mem_replicate {p : α → Prop} {a : α} {n} :
|
||||
@[simp] theorem replicate_succ_ne_nil (n : Nat) (a : α) : replicate (n+1) a ≠ [] := by
|
||||
simp [replicate_succ]
|
||||
|
||||
@[simp] theorem replicate_eq_nil (n : Nat) (a : α) : replicate n a = [] ↔ n = 0 := by
|
||||
cases n <;> simp
|
||||
|
||||
@[simp] theorem getElem_replicate (a : α) {n : Nat} {m} (h : m < (replicate n a).length) :
|
||||
(replicate n a)[m] = a :=
|
||||
eq_of_mem_replicate (get_mem _ _ _)
|
||||
@@ -1907,6 +2036,9 @@ theorem head?_replicate (a : α) (n : Nat) : (replicate n a).head? = if n = 0 th
|
||||
· simp at w
|
||||
· simp_all [replicate_succ]
|
||||
|
||||
@[simp] theorem tail_replicate (n : Nat) (a : α) : (replicate n a).tail = replicate (n - 1) a := by
|
||||
cases n <;> simp [replicate_succ]
|
||||
|
||||
@[simp] theorem replicate_inj : replicate n a = replicate m b ↔ n = m ∧ (n = 0 ∨ a = b) :=
|
||||
⟨fun h => have eq : n = m := by simpa using congrArg length h
|
||||
⟨eq, by
|
||||
@@ -1965,9 +2097,9 @@ theorem append_eq_replicate {l₁ l₂ : List α} {a : α} :
|
||||
split <;> simp
|
||||
|
||||
theorem filter_replicate : (replicate n a).filter p = if p a then replicate n a else [] := by
|
||||
induction n with
|
||||
cases n with
|
||||
| zero => simp
|
||||
| succ n ih =>
|
||||
| succ n =>
|
||||
simp only [replicate_succ, filter_cons]
|
||||
split <;> simp_all
|
||||
|
||||
@@ -2028,18 +2160,19 @@ theorem bind_replicate {β} (f : α → List β) : (replicate n a).bind f = (rep
|
||||
| nil => rfl
|
||||
| cons a as ih => simp [ih]
|
||||
|
||||
@[simp] theorem mem_reverseAux {x : α} : ∀ {as bs}, x ∈ reverseAux as bs ↔ x ∈ as ∨ x ∈ bs
|
||||
theorem mem_reverseAux {x : α} : ∀ {as bs}, x ∈ reverseAux as bs ↔ x ∈ as ∨ x ∈ bs
|
||||
| [], _ => ⟨.inr, fun | .inr h => h⟩
|
||||
| a :: _, _ => by rw [reverseAux, mem_cons, or_assoc, or_left_comm, mem_reverseAux, mem_cons]
|
||||
|
||||
@[simp] theorem mem_reverse {x : α} {as : List α} : x ∈ reverse as ↔ x ∈ as := by simp [reverse]
|
||||
@[simp] theorem mem_reverse {x : α} {as : List α} : x ∈ reverse as ↔ x ∈ as := by
|
||||
simp [reverse, mem_reverseAux]
|
||||
|
||||
@[simp] theorem reverse_eq_nil_iff {xs : List α} : xs.reverse = [] ↔ xs = [] := by
|
||||
match xs with
|
||||
| [] => simp
|
||||
| x :: xs => simp
|
||||
|
||||
@[simp] theorem reverse_ne_nil_iff {xs : List α} : xs.reverse ≠ [] ↔ xs ≠ [] :=
|
||||
theorem reverse_ne_nil_iff {xs : List α} : xs.reverse ≠ [] ↔ xs ≠ [] :=
|
||||
not_congr reverse_eq_nil_iff
|
||||
|
||||
theorem getElem?_reverse' : ∀ {l : List α} (i j), i + j + 1 = length l →
|
||||
@@ -2085,11 +2218,25 @@ theorem reverseAux_reverseAux_nil (as bs : List α) : reverseAux (reverseAux as
|
||||
theorem reverse_eq_iff {as bs : List α} : as.reverse = bs ↔ as = bs.reverse := by
|
||||
constructor <;> (rintro rfl; simp)
|
||||
|
||||
@[simp] theorem getLast?_reverse (l : List α) : l.reverse.getLast? = l.head? := by cases l <;> simp
|
||||
@[simp] theorem reverse_inj {xs ys : List α} : xs.reverse = ys.reverse ↔ xs = ys := by
|
||||
simp [reverse_eq_iff]
|
||||
|
||||
@[simp] theorem reverse_eq_cons {xs : List α} {a : α} {ys : List α} :
|
||||
xs.reverse = a :: ys ↔ xs = ys.reverse ++ [a] := by
|
||||
rw [reverse_eq_iff, reverse_cons]
|
||||
|
||||
@[simp] theorem getLast?_reverse (l : List α) : l.reverse.getLast? = l.head? := by
|
||||
cases l <;> simp [getLast?_concat]
|
||||
|
||||
@[simp] theorem head?_reverse (l : List α) : l.reverse.head? = l.getLast? := by
|
||||
rw [← getLast?_reverse, reverse_reverse]
|
||||
|
||||
theorem getLast?_eq_head?_reverse {xs : List α} : xs.getLast? = xs.reverse.head? := by
|
||||
simp
|
||||
|
||||
theorem head?_eq_getLast?_reverse {xs : List α} : xs.head? = xs.reverse.getLast? := by
|
||||
simp
|
||||
|
||||
@[simp] theorem map_reverse (f : α → β) (l : List α) : l.reverse.map f = (l.map f).reverse := by
|
||||
induction l <;> simp [*]
|
||||
|
||||
@@ -2114,8 +2261,16 @@ theorem reverse_map (f : α → β) (l : List α) : (l.map f).reverse = l.revers
|
||||
@[simp] theorem reverse_append (as bs : List α) : (as ++ bs).reverse = bs.reverse ++ as.reverse := by
|
||||
induction as <;> simp_all
|
||||
|
||||
theorem reverse_concat (l : List α) (a : α) : (l.concat a).reverse = a :: l.reverse := by
|
||||
rw [concat_eq_append, reverse_append]; rfl
|
||||
@[simp] theorem reverse_eq_append {xs ys zs : List α} :
|
||||
xs.reverse = ys ++ zs ↔ xs = zs.reverse ++ ys.reverse := by
|
||||
rw [reverse_eq_iff, reverse_append]
|
||||
|
||||
theorem reverse_concat (l : List α) (a : α) : (l ++ [a]).reverse = a :: l.reverse := by
|
||||
rw [reverse_append]; rfl
|
||||
|
||||
theorem reverse_eq_concat {xs ys : List α} {a : α} :
|
||||
xs.reverse = ys ++ [a] ↔ xs = a :: ys.reverse := by
|
||||
rw [reverse_eq_iff, reverse_concat]
|
||||
|
||||
/-- Reversing a join is the same as reversing the order of parts and reversing all parts. -/
|
||||
theorem reverse_join (L : List (List α)) :
|
||||
@@ -2133,7 +2288,7 @@ theorem reverse_bind {β} (l : List α) (f : α → List β) : (l.bind f).revers
|
||||
theorem bind_reverse {β} (l : List α) (f : α → List β) : (l.reverse.bind f) = (l.bind (reverse ∘ f)).reverse := by
|
||||
induction l <;> simp_all
|
||||
|
||||
theorem reverseAux_eq (as bs : List α) : reverseAux as bs = reverse as ++ bs :=
|
||||
@[simp] theorem reverseAux_eq (as bs : List α) : reverseAux as bs = reverse as ++ bs :=
|
||||
reverseAux_eq_append ..
|
||||
|
||||
@[simp] theorem foldrM_reverse [Monad m] (l : List α) (f : α → β → m β) (b) :
|
||||
@@ -2159,16 +2314,32 @@ theorem reverseAux_eq (as bs : List α) : reverseAux as bs = reverse as ++ bs :=
|
||||
induction l with
|
||||
| nil => contradiction
|
||||
| cons a l ih =>
|
||||
simp
|
||||
simp only [reverse_cons]
|
||||
by_cases h' : l = []
|
||||
· simp_all
|
||||
· rw [getLast_cons, head_append_of_ne_nil, ih]
|
||||
simp_all
|
||||
· simp only [head_eq_iff_head?_eq_some, head?_reverse] at ih
|
||||
simp [ih, h, h', getLast_cons, head_eq_iff_head?_eq_some]
|
||||
|
||||
theorem getLast_eq_head_reverse {l : List α} (h : l ≠ []) :
|
||||
l.getLast h = l.reverse.head (by simp_all) := by
|
||||
rw [← head_reverse]
|
||||
|
||||
theorem getLast_eq_iff_getLast_eq_some {xs : List α} (h) : xs.getLast h = a ↔ xs.getLast? = some a := by
|
||||
rw [getLast_eq_head_reverse, head_eq_iff_head?_eq_some]
|
||||
simp
|
||||
|
||||
@[simp] theorem getLast?_eq_none_iff {xs : List α} : xs.getLast? = none ↔ xs = [] := by
|
||||
rw [getLast?_eq_head?_reverse, head?_eq_none_iff, reverse_eq_nil_iff]
|
||||
|
||||
theorem getLast?_eq_some_iff {xs : List α} {a : α} : xs.getLast? = some a ↔ ∃ ys, xs = ys ++ [a] := by
|
||||
rw [getLast?_eq_head?_reverse, head?_eq_some_iff]
|
||||
simp only [reverse_eq_cons]
|
||||
exact ⟨fun ⟨ys, h⟩ => ⟨ys.reverse, by simpa using h⟩, fun ⟨ys, h⟩ => ⟨ys.reverse, by simpa using h⟩⟩
|
||||
|
||||
theorem mem_of_getLast?_eq_some {xs : List α} {a : α} (h : xs.getLast? = some a) : a ∈ xs := by
|
||||
obtain ⟨ys, rfl⟩ := getLast?_eq_some_iff.1 h
|
||||
exact mem_concat_self ys a
|
||||
|
||||
@[simp] theorem getLast_reverse {l : List α} (h : l.reverse ≠ []) :
|
||||
l.reverse.getLast h = l.head (by simp_all) := by
|
||||
simp [getLast_eq_head_reverse]
|
||||
@@ -2177,8 +2348,8 @@ theorem head_eq_getLast_reverse {l : List α} (h : l ≠ []) :
|
||||
l.head h = l.reverse.getLast (by simp_all) := by
|
||||
rw [← getLast_reverse]
|
||||
|
||||
@[simp] theorem getLast_append_of_ne_nil {l : List α} (h : l' ≠ []) :
|
||||
(l ++ l').getLast (append_ne_nil_of_right_ne_nil l h) = l'.getLast (by simp_all) := by
|
||||
@[simp] theorem getLast_append_of_ne_nil {l : List α} {h₁} (h₂ : l' ≠ []) :
|
||||
(l ++ l').getLast h₁ = l'.getLast h₂ := by
|
||||
simp only [getLast_eq_head_reverse, reverse_append]
|
||||
rw [head_append_of_ne_nil]
|
||||
|
||||
@@ -2350,8 +2521,8 @@ theorem dropLast_append {l₁ l₂ : List α} :
|
||||
(l₁ ++ l₂).dropLast = if l₂.isEmpty then l₁.dropLast else l₁ ++ l₂.dropLast := by
|
||||
split <;> simp_all
|
||||
|
||||
@[simp] theorem dropLast_append_cons : dropLast (l₁ ++ b::l₂) = l₁ ++ dropLast (b::l₂) := by
|
||||
simp only [ne_eq, not_false_eq_true, dropLast_append_of_ne_nil]
|
||||
theorem dropLast_append_cons : dropLast (l₁ ++ b::l₂) = l₁ ++ dropLast (b::l₂) := by
|
||||
simp
|
||||
|
||||
@[simp 1100] theorem dropLast_concat : dropLast (l₁ ++ [b]) = l₁ := by simp
|
||||
|
||||
@@ -2368,6 +2539,27 @@ theorem dropLast_append {l₁ l₂ : List α} :
|
||||
dropLast (a :: replicate n a) = replicate n a := by
|
||||
rw [← replicate_succ, dropLast_replicate, Nat.add_sub_cancel]
|
||||
|
||||
/-!
|
||||
### splitAt
|
||||
|
||||
We don't provide any API for `splitAt`, beyond the `@[simp]` lemma
|
||||
`splitAt n l = (l.take n, l.drop n)`,
|
||||
which is proved in `Init.Data.List.TakeDrop`.
|
||||
-/
|
||||
|
||||
theorem splitAt_go (n : Nat) (l acc : List α) :
|
||||
splitAt.go l xs n acc =
|
||||
if n < xs.length then (acc.reverse ++ xs.take n, xs.drop n) else (l, []) := by
|
||||
induction xs generalizing n acc with
|
||||
| nil => simp [splitAt.go]
|
||||
| cons x xs ih =>
|
||||
cases n with
|
||||
| zero => simp [splitAt.go]
|
||||
| succ n =>
|
||||
rw [splitAt.go, take_succ_cons, drop_succ_cons, ih n (x :: acc),
|
||||
reverse_cons, append_assoc, singleton_append, length_cons]
|
||||
simp only [Nat.succ_lt_succ_iff]
|
||||
|
||||
/-! ## Manipulating elements -/
|
||||
|
||||
/-! ### replace -/
|
||||
|
||||
@@ -7,6 +7,7 @@ prelude
|
||||
import Init.Data.List.Nat.TakeDrop
|
||||
import Init.Data.List.Range
|
||||
import Init.Data.List.Pairwise
|
||||
import Init.Data.List.Find
|
||||
|
||||
/-!
|
||||
# Lemmas about `List.range` and `List.enum`
|
||||
@@ -23,8 +24,6 @@ open Nat
|
||||
theorem range'_succ (s n step) : range' s (n + 1) step = s :: range' (s + step) n step := by
|
||||
simp [range', Nat.add_succ, Nat.mul_succ]
|
||||
|
||||
@[simp] theorem range'_one {s step : Nat} : range' s 1 step = [s] := rfl
|
||||
|
||||
@[simp] theorem length_range' (s step) : ∀ n : Nat, length (range' s n step) = n
|
||||
| 0 => rfl
|
||||
| _ + 1 => congrArg succ (length_range' _ _ _)
|
||||
@@ -32,6 +31,27 @@ theorem range'_succ (s n step) : range' s (n + 1) step = s :: range' (s + step)
|
||||
@[simp] theorem range'_eq_nil : range' s n step = [] ↔ n = 0 := by
|
||||
rw [← length_eq_zero, length_range']
|
||||
|
||||
theorem range'_ne_nil (s n : Nat) : range' s n ≠ [] ↔ n ≠ 0 := by
|
||||
cases n <;> simp
|
||||
|
||||
@[simp] theorem range'_zero : range' s 0 = [] := by
|
||||
simp
|
||||
|
||||
@[simp] theorem range'_one {s step : Nat} : range' s 1 step = [s] := rfl
|
||||
|
||||
@[simp] theorem range'_inj : range' s n = range' s' n' ↔ n = n' ∧ (n = 0 ∨ s = s') := by
|
||||
constructor
|
||||
· intro h
|
||||
have h' := congrArg List.length h
|
||||
simp at h'
|
||||
subst h'
|
||||
cases n with
|
||||
| zero => simp
|
||||
| succ n =>
|
||||
simp only [range'_succ] at h
|
||||
simp_all
|
||||
· rintro ⟨rfl, rfl | rfl⟩ <;> simp
|
||||
|
||||
theorem mem_range' : ∀{n}, m ∈ range' s n step ↔ ∃ i < n, m = s + step * i
|
||||
| 0 => by simp [range', Nat.not_lt_zero]
|
||||
| n + 1 => by
|
||||
@@ -45,6 +65,29 @@ theorem mem_range' : ∀{n}, m ∈ range' s n step ↔ ∃ i < n, m = s + step *
|
||||
fun ⟨i, h, e⟩ => e ▸ ⟨Nat.le_add_right .., Nat.add_lt_add_left h _⟩,
|
||||
fun ⟨h₁, h₂⟩ => ⟨m - s, Nat.sub_lt_left_of_lt_add h₁ h₂, (Nat.add_sub_cancel' h₁).symm⟩⟩
|
||||
|
||||
theorem head?_range' (n : Nat) : (range' s n).head? = if n = 0 then none else some s := by
|
||||
induction n <;> simp_all [range'_succ, head?_append]
|
||||
|
||||
@[simp] theorem head_range' (n : Nat) (h) : (range' s n).head h = s := by
|
||||
repeat simp_all [head?_range', head_eq_iff_head?_eq_some]
|
||||
|
||||
theorem getLast?_range' (n : Nat) : (range' s n).getLast? = if n = 0 then none else some (s + n - 1) := by
|
||||
induction n generalizing s with
|
||||
| zero => simp
|
||||
| succ n ih =>
|
||||
rw [range'_succ, getLast?_cons, ih]
|
||||
by_cases h : n = 0
|
||||
· rw [if_pos h]
|
||||
simp [h]
|
||||
· rw [if_neg h]
|
||||
simp
|
||||
omega
|
||||
|
||||
@[simp] theorem getLast_range' (n : Nat) (h) : (range' s n).getLast h = s + n - 1 := by
|
||||
cases n with
|
||||
| zero => simp at h
|
||||
| succ n => simp [getLast?_range', getLast_eq_iff_getLast_eq_some]
|
||||
|
||||
theorem pairwise_lt_range' s n (step := 1) (pos : 0 < step := by simp) :
|
||||
Pairwise (· < ·) (range' s n step) :=
|
||||
match s, n, step, pos with
|
||||
@@ -124,6 +167,67 @@ theorem range'_concat (s n : Nat) : range' s (n + 1) step = range' s n step ++ [
|
||||
theorem range'_1_concat (s n : Nat) : range' s (n + 1) = range' s n ++ [s + n] := by
|
||||
simp [range'_concat]
|
||||
|
||||
theorem range'_eq_cons_iff : range' s n = a :: xs ↔ s = a ∧ 0 < n ∧ xs = range' (a + 1) (n - 1) := by
|
||||
induction n generalizing s with
|
||||
| zero => simp
|
||||
| succ n ih =>
|
||||
simp only [range'_succ]
|
||||
simp only [cons.injEq, and_congr_right_iff]
|
||||
rintro rfl
|
||||
simp [eq_comm]
|
||||
|
||||
@[simp] theorem range'_eq_singleton {s n a : Nat} : range' s n = [a] ↔ s = a ∧ n = 1 := by
|
||||
rw [range'_eq_cons_iff]
|
||||
simp only [nil_eq, range'_eq_nil, and_congr_right_iff]
|
||||
rintro rfl
|
||||
omega
|
||||
|
||||
theorem range'_eq_append_iff : range' s n = xs ++ ys ↔ ∃ k, k ≤ n ∧ xs = range' s k ∧ ys = range' (s + k) (n - k) := by
|
||||
induction n generalizing s xs ys with
|
||||
| zero => simp
|
||||
| succ n ih =>
|
||||
simp only [range'_succ]
|
||||
rw [cons_eq_append]
|
||||
constructor
|
||||
· rintro (⟨rfl, rfl⟩ | ⟨a, rfl, h⟩)
|
||||
· exact ⟨0, by simp [range'_succ]⟩
|
||||
· simp only [ih] at h
|
||||
obtain ⟨k, h, rfl, rfl⟩ := h
|
||||
refine ⟨k + 1, ?_⟩
|
||||
simp_all [range'_succ]
|
||||
omega
|
||||
· rintro ⟨k, h, rfl, rfl⟩
|
||||
cases k with
|
||||
| zero => simp [range'_succ]
|
||||
| succ k =>
|
||||
simp only [range'_succ, reduceCtorEq, false_and, cons.injEq, true_and, ih, range'_inj, exists_eq_left', or_true, and_true, false_or]
|
||||
refine ⟨k, ?_⟩
|
||||
simp_all
|
||||
omega
|
||||
|
||||
@[simp] theorem find?_range'_eq_some (s n : Nat) (i : Nat) (p : Nat → Bool) :
|
||||
(range' s n).find? p = some i ↔ p i ∧ i ∈ range' s n ∧ ∀ j, s ≤ j → j < i → !p j := by
|
||||
rw [find?_eq_some]
|
||||
simp only [Bool.not_eq_true', exists_and_right, mem_range'_1, and_congr_right_iff]
|
||||
simp only [range'_eq_append_iff, eq_comm (a := i :: _), range'_eq_cons_iff]
|
||||
intro h
|
||||
constructor
|
||||
· rintro ⟨as, ⟨x, k, h₁, rfl, rfl, h₂, rfl⟩, h₃⟩
|
||||
constructor
|
||||
· omega
|
||||
· simpa using h₃
|
||||
· rintro ⟨⟨h₁, h₂⟩, h₃⟩
|
||||
refine ⟨range' s (i - s), ⟨⟨range' (i + 1) (n - (i - s) - 1), i - s, ?_⟩ , ?_⟩⟩
|
||||
· simp; omega
|
||||
· simp only [mem_range'_1, and_imp]
|
||||
intro a a₁ a₂
|
||||
exact h₃ a a₁ (by omega)
|
||||
|
||||
@[simp] theorem find?_range'_eq_none (s n : Nat) (p : Nat → Bool) :
|
||||
(range' s n).find? p = none ↔ ∀ i, s ≤ i → i < s + n → !p i := by
|
||||
rw [find?_eq_none]
|
||||
simp
|
||||
|
||||
/-! ### range -/
|
||||
|
||||
theorem range_loop_range' : ∀ s n : Nat, range.loop s (range' s n) = range' 0 (n + s)
|
||||
@@ -153,6 +257,9 @@ theorem range'_eq_map_range (s n : Nat) : range' s n = map (s + ·) (range n) :=
|
||||
@[simp] theorem range_eq_nil {n : Nat} : range n = [] ↔ n = 0 := by
|
||||
rw [← length_eq_zero, length_range]
|
||||
|
||||
theorem range_ne_nil (n : Nat) : range n ≠ [] ↔ n ≠ 0 := by
|
||||
cases n <;> simp
|
||||
|
||||
@[simp]
|
||||
theorem range_sublist {m n : Nat} : range m <+ range n ↔ m ≤ n := by
|
||||
simp only [range_eq_range', range'_sublist_right]
|
||||
@@ -188,6 +295,30 @@ theorem range_add (a b : Nat) : range (a + b) = range a ++ (range b).map (a + ·
|
||||
rw [← range'_eq_map_range]
|
||||
simpa [range_eq_range', Nat.add_comm] using (range'_append_1 0 a b).symm
|
||||
|
||||
theorem head?_range (n : Nat) : (range n).head? = if n = 0 then none else some 0 := by
|
||||
induction n with
|
||||
| zero => simp
|
||||
| succ n ih =>
|
||||
simp only [range_succ, head?_append, ih]
|
||||
split <;> simp_all
|
||||
|
||||
@[simp] theorem head_range (n : Nat) (h) : (range n).head h = 0 := by
|
||||
cases n with
|
||||
| zero => simp at h
|
||||
| succ n => simp [head?_range, head_eq_iff_head?_eq_some]
|
||||
|
||||
theorem getLast?_range (n : Nat) : (range n).getLast? = if n = 0 then none else some (n - 1) := by
|
||||
induction n with
|
||||
| zero => simp
|
||||
| succ n ih =>
|
||||
simp only [range_succ, getLast?_append, ih]
|
||||
split <;> simp_all
|
||||
|
||||
@[simp] theorem getLast_range (n : Nat) (h) : (range n).getLast h = n - 1 := by
|
||||
cases n with
|
||||
| zero => simp at h
|
||||
| succ n => simp [getLast?_range, getLast_eq_iff_getLast_eq_some]
|
||||
|
||||
theorem take_range (m n : Nat) : take m (range n) = range (min m n) := by
|
||||
apply List.ext_getElem
|
||||
· simp
|
||||
@@ -196,6 +327,14 @@ theorem take_range (m n : Nat) : take m (range n) = range (min m n) := by
|
||||
theorem nodup_range (n : Nat) : Nodup (range n) := by
|
||||
simp (config := {decide := true}) only [range_eq_range', nodup_range']
|
||||
|
||||
@[simp] theorem find?_range_eq_some (n : Nat) (i : Nat) (p : Nat → Bool) :
|
||||
(range n).find? p = some i ↔ p i ∧ i ∈ range n ∧ ∀ j, j < i → !p j := by
|
||||
simp [range_eq_range']
|
||||
|
||||
@[simp] theorem find?_range_eq_none (n : Nat) (p : Nat → Bool) :
|
||||
(range n).find? p = none ↔ ∀ i, i < n → !p i := by
|
||||
simp [range_eq_range']
|
||||
|
||||
/-! ### iota -/
|
||||
|
||||
theorem iota_eq_reverse_range' : ∀ n : Nat, iota n = reverse (range' 1 n)
|
||||
@@ -204,9 +343,49 @@ theorem iota_eq_reverse_range' : ∀ n : Nat, iota n = reverse (range' 1 n)
|
||||
|
||||
@[simp] theorem length_iota (n : Nat) : length (iota n) = n := by simp [iota_eq_reverse_range']
|
||||
|
||||
@[simp] theorem iota_eq_nil (n : Nat) : iota n = [] ↔ n = 0 := by
|
||||
cases n <;> simp
|
||||
|
||||
theorem iota_ne_nil (n : Nat) : iota n ≠ [] ↔ n ≠ 0 := by
|
||||
cases n <;> simp
|
||||
|
||||
@[simp]
|
||||
theorem mem_iota {m n : Nat} : m ∈ iota n ↔ 1 ≤ m ∧ m ≤ n := by
|
||||
theorem mem_iota {m n : Nat} : m ∈ iota n ↔ 0 < m ∧ m ≤ n := by
|
||||
simp [iota_eq_reverse_range', Nat.add_comm, Nat.lt_succ]
|
||||
omega
|
||||
|
||||
@[simp] theorem iota_inj : iota n = iota n' ↔ n = n' := by
|
||||
constructor
|
||||
· intro h
|
||||
have h' := congrArg List.length h
|
||||
simp at h'
|
||||
exact h'
|
||||
· rintro rfl
|
||||
simp
|
||||
|
||||
theorem iota_eq_cons_iff : iota n = a :: xs ↔ n = a ∧ 0 < n ∧ xs = iota (n - 1) := by
|
||||
simp [iota_eq_reverse_range']
|
||||
simp [range'_eq_append_iff, reverse_eq_iff]
|
||||
constructor
|
||||
· rintro ⟨k, h, rfl, h'⟩
|
||||
rw [eq_comm, range'_eq_singleton] at h'
|
||||
simp only [reverse_inj, range'_inj, or_true, and_true]
|
||||
omega
|
||||
· rintro ⟨rfl, h, rfl⟩
|
||||
refine ⟨n - 1, by simp, rfl, ?_⟩
|
||||
rw [eq_comm, range'_eq_singleton]
|
||||
omega
|
||||
|
||||
theorem iota_eq_append_iff : iota n = xs ++ ys ↔ ∃ k, k ≤ n ∧ xs = (range' (k + 1) (n - k)).reverse ∧ ys = iota k := by
|
||||
simp only [iota_eq_reverse_range']
|
||||
rw [reverse_eq_append]
|
||||
rw [range'_eq_append_iff]
|
||||
simp only [reverse_eq_iff]
|
||||
constructor
|
||||
· rintro ⟨k, h, rfl, rfl⟩
|
||||
simp; omega
|
||||
· rintro ⟨k, h, rfl, rfl⟩
|
||||
exact ⟨k, by simp; omega⟩
|
||||
|
||||
theorem pairwise_gt_iota (n : Nat) : Pairwise (· > ·) (iota n) := by
|
||||
simpa only [iota_eq_reverse_range', pairwise_reverse] using pairwise_lt_range' 1 n
|
||||
@@ -214,12 +393,85 @@ theorem pairwise_gt_iota (n : Nat) : Pairwise (· > ·) (iota n) := by
|
||||
theorem nodup_iota (n : Nat) : Nodup (iota n) :=
|
||||
(pairwise_gt_iota n).imp Nat.ne_of_gt
|
||||
|
||||
@[simp] theorem head?_iota (n : Nat) : (iota n).head? = if n = 0 then none else some n := by
|
||||
cases n <;> simp
|
||||
|
||||
@[simp] theorem head_iota (n : Nat) (h) : (iota n).head h = n := by
|
||||
cases n with
|
||||
| zero => simp at h
|
||||
| succ n => simp
|
||||
|
||||
@[simp] theorem reverse_iota : reverse (iota n) = range' 1 n := by
|
||||
induction n with
|
||||
| zero => simp
|
||||
| succ n ih =>
|
||||
rw [iota_succ, reverse_cons, ih, range'_1_concat, Nat.add_comm]
|
||||
|
||||
@[simp] theorem getLast?_iota (n : Nat) : (iota n).getLast? = if n = 0 then none else some 1 := by
|
||||
rw [getLast?_eq_head?_reverse]
|
||||
simp [head?_range']
|
||||
|
||||
@[simp] theorem getLast_iota (n : Nat) (h) : (iota n).getLast h = 1 := by
|
||||
rw [getLast_eq_head_reverse]
|
||||
simp
|
||||
|
||||
@[simp] theorem find?_iota_eq_none (n : Nat) (p : Nat → Bool) :
|
||||
(iota n).find? p = none ↔ ∀ i, 0 < i → i ≤ n → !p i := by
|
||||
rw [find?_eq_none]
|
||||
simp
|
||||
|
||||
@[simp] theorem find?_iota_eq_some (n : Nat) (i : Nat) (p : Nat → Bool) :
|
||||
(iota n).find? p = some i ↔ p i ∧ i ∈ iota n ∧ ∀ j, i < j → j ≤ n → !p j := by
|
||||
rw [find?_eq_some]
|
||||
simp only [iota_eq_reverse_range', reverse_eq_append, reverse_cons, append_assoc,
|
||||
singleton_append, Bool.not_eq_true', exists_and_right, mem_reverse, mem_range'_1,
|
||||
and_congr_right_iff]
|
||||
intro h
|
||||
constructor
|
||||
· rintro ⟨as, ⟨xs, h⟩, h'⟩
|
||||
constructor
|
||||
· replace h : i ∈ range' 1 n := by
|
||||
rw [h]
|
||||
exact mem_append_cons_self
|
||||
simpa using h
|
||||
· rw [range'_eq_append_iff] at h
|
||||
simp [reverse_eq_iff] at h
|
||||
obtain ⟨k, h₁, rfl, h₂⟩ := h
|
||||
rw [eq_comm, range'_eq_cons_iff, reverse_eq_iff] at h₂
|
||||
obtain ⟨rfl, -, rfl⟩ := h₂
|
||||
intro j j₁ j₂
|
||||
apply h'
|
||||
simp; omega
|
||||
· rintro ⟨⟨i₁, i₂⟩, h⟩
|
||||
refine ⟨(range' (i+1) (n-i)).reverse, ⟨(range' 1 (i-1)).reverse, ?_⟩, ?_⟩
|
||||
· simp [← range'_succ]
|
||||
rw [range'_eq_append_iff]
|
||||
refine ⟨i-1, ?_⟩
|
||||
constructor
|
||||
· omega
|
||||
· simp
|
||||
omega
|
||||
· simp
|
||||
intros a a₁ a₂
|
||||
apply h
|
||||
· omega
|
||||
· omega
|
||||
|
||||
/-! ### enumFrom -/
|
||||
|
||||
@[simp]
|
||||
theorem enumFrom_singleton (x : α) (n : Nat) : enumFrom n [x] = [(n, x)] :=
|
||||
rfl
|
||||
|
||||
@[simp] theorem head?_enumFrom (n : Nat) (l : List α) :
|
||||
(enumFrom n l).head? = l.head?.map fun a => (n, a) := by
|
||||
simp [head?_eq_getElem?]
|
||||
|
||||
@[simp] theorem getLast?_enumFrom (n : Nat) (l : List α) :
|
||||
(enumFrom n l).getLast? = l.getLast?.map fun a => (n + l.length - 1, a) := by
|
||||
simp [getLast?_eq_getElem?]
|
||||
cases l <;> simp; omega
|
||||
|
||||
theorem mk_add_mem_enumFrom_iff_getElem? {n i : Nat} {x : α} {l : List α} :
|
||||
(n + i, x) ∈ enumFrom n l ↔ l[i]? = some x := by
|
||||
simp [mem_iff_get?]
|
||||
@@ -260,9 +512,24 @@ theorem enumFrom_map_snd : ∀ (n) (l : List α), map Prod.snd (enumFrom n l) =
|
||||
theorem snd_mem_of_mem_enumFrom {x : Nat × α} {n : Nat} {l : List α} (h : x ∈ enumFrom n l) : x.2 ∈ l :=
|
||||
enumFrom_map_snd n l ▸ mem_map_of_mem _ h
|
||||
|
||||
theorem mem_enumFrom {x : α} {i j : Nat} (xs : List α) (h : (i, x) ∈ xs.enumFrom j) :
|
||||
j ≤ i ∧ i < j + xs.length ∧ x ∈ xs :=
|
||||
⟨le_fst_of_mem_enumFrom h, fst_lt_add_of_mem_enumFrom h, snd_mem_of_mem_enumFrom h⟩
|
||||
theorem snd_eq_of_mem_enumFrom {x : Nat × α} {n : Nat} {l : List α} (h : x ∈ enumFrom n l) :
|
||||
x.2 = l[x.1 - n]'(by have := le_fst_of_mem_enumFrom h; have := fst_lt_add_of_mem_enumFrom h; omega) := by
|
||||
induction l generalizing n with
|
||||
| nil => cases h
|
||||
| cons hd tl ih =>
|
||||
cases h with
|
||||
| head h => simp
|
||||
| tail h m =>
|
||||
specialize ih m
|
||||
have : x.1 - n = x.1 - (n + 1) + 1 := by
|
||||
have := le_fst_of_mem_enumFrom m
|
||||
omega
|
||||
simp [this, ih]
|
||||
|
||||
theorem mem_enumFrom {x : α} {i j : Nat} {xs : List α} (h : (i, x) ∈ xs.enumFrom j) :
|
||||
j ≤ i ∧ i < j + xs.length ∧
|
||||
x = xs[i - j]'(by have := le_fst_of_mem_enumFrom h; have := fst_lt_add_of_mem_enumFrom h; omega) :=
|
||||
⟨le_fst_of_mem_enumFrom h, fst_lt_add_of_mem_enumFrom h, snd_eq_of_mem_enumFrom h⟩
|
||||
|
||||
theorem enumFrom_cons' (n : Nat) (x : α) (xs : List α) :
|
||||
enumFrom n (x :: xs) = (n, x) :: (enumFrom n xs).map (Prod.map (· + 1) id) := by
|
||||
@@ -317,6 +584,14 @@ theorem getElem_enum (l : List α) (i : Nat) (h : i < l.enum.length) :
|
||||
l.enum[i] = (i, l[i]'(by simpa [enum_length] using h)) := by
|
||||
simp [enum]
|
||||
|
||||
@[simp] theorem head?_enum (l : List α) :
|
||||
l.enum.head? = l.head?.map fun a => (0, a) := by
|
||||
simp [head?_eq_getElem?]
|
||||
|
||||
@[simp] theorem getLast?_enum (l : List α) :
|
||||
l.enum.getLast? = l.getLast?.map fun a => (l.length - 1, a) := by
|
||||
simp [getLast?_eq_getElem?]
|
||||
|
||||
theorem mk_mem_enum_iff_getElem? {i : Nat} {x : α} {l : List α} : (i, x) ∈ enum l ↔ l[i]? = x := by
|
||||
simp [enum, mk_mem_enumFrom_iff_le_and_getElem?_sub]
|
||||
|
||||
@@ -329,6 +604,14 @@ theorem fst_lt_of_mem_enum {x : Nat × α} {l : List α} (h : x ∈ enum l) : x.
|
||||
theorem snd_mem_of_mem_enum {x : Nat × α} {l : List α} (h : x ∈ enum l) : x.2 ∈ l :=
|
||||
snd_mem_of_mem_enumFrom h
|
||||
|
||||
theorem snd_eq_of_mem_enum {x : Nat × α} {l : List α} (h : x ∈ enum l) :
|
||||
x.2 = l[x.1]'(fst_lt_of_mem_enum h) :=
|
||||
snd_eq_of_mem_enumFrom h
|
||||
|
||||
theorem mem_enum {x : α} {i : Nat} {xs : List α} (h : (i, x) ∈ xs.enum) :
|
||||
i < xs.length ∧ x = xs[i]'(fst_lt_of_mem_enum h) :=
|
||||
by simpa using mem_enumFrom h
|
||||
|
||||
theorem map_enum (f : α → β) (l : List α) : map (Prod.map id f) (enum l) = enum (map f l) :=
|
||||
map_enumFrom f 0 l
|
||||
|
||||
|
||||
@@ -126,4 +126,49 @@ theorem prefix_take_le_iff {L : List α} (hm : m < L.length) :
|
||||
simp only [length_cons, Nat.succ_eq_add_one, Nat.add_lt_add_iff_right] at hm
|
||||
simp [← @IH n ls hm, Nat.min_eq_left, Nat.le_of_lt hm]
|
||||
|
||||
@[simp] theorem append_left_sublist_self (xs ys : List α) : xs ++ ys <+ ys ↔ xs = [] := by
|
||||
constructor
|
||||
· intro h
|
||||
replace h := h.length_le
|
||||
simp only [length_append] at h
|
||||
have : xs.length = 0 := by omega
|
||||
simp_all
|
||||
· rintro rfl
|
||||
simp
|
||||
@[simp] theorem append_right_sublist_self (xs ys : List α) : xs ++ ys <+ xs ↔ ys = [] := by
|
||||
constructor
|
||||
· intro h
|
||||
replace h := h.length_le
|
||||
simp only [length_append] at h
|
||||
have : ys.length = 0 := by omega
|
||||
simp_all
|
||||
· rintro rfl
|
||||
simp
|
||||
|
||||
theorem append_sublist_of_sublist_left (xs ys zs : List α) (h : zs <+ xs) :
|
||||
xs ++ ys <+ zs ↔ ys = [] ∧ xs = zs := by
|
||||
constructor
|
||||
· intro h'
|
||||
have hl := h.length_le
|
||||
have hl' := h'.length_le
|
||||
simp only [length_append] at hl'
|
||||
have : ys.length = 0 := by omega
|
||||
simp_all only [Nat.add_zero, length_eq_zero, true_and, append_nil]
|
||||
exact Sublist.eq_of_length_le h' hl
|
||||
· rintro ⟨rfl, rfl⟩
|
||||
simp
|
||||
|
||||
theorem append_sublist_of_sublist_right (xs ys zs : List α) (h : zs <+ ys) :
|
||||
xs ++ ys <+ zs ↔ xs = [] ∧ ys = zs := by
|
||||
constructor
|
||||
· intro h'
|
||||
have hl := h.length_le
|
||||
have hl' := h'.length_le
|
||||
simp only [length_append] at hl'
|
||||
have : xs.length = 0 := by omega
|
||||
simp_all only [Nat.zero_add, length_eq_zero, true_and, append_nil]
|
||||
exact Sublist.eq_of_length_le h' hl
|
||||
· rintro ⟨rfl, rfl⟩
|
||||
simp
|
||||
|
||||
end List
|
||||
|
||||
@@ -70,20 +70,20 @@ theorem get?_take_eq_none {l : List α} {n m : Nat} (h : n ≤ m) :
|
||||
(l.take n).get? m = none := by
|
||||
simp [getElem?_take_eq_none h]
|
||||
|
||||
theorem getElem?_take_eq_if {l : List α} {n m : Nat} :
|
||||
theorem getElem?_take {l : List α} {n m : Nat} :
|
||||
(l.take n)[m]? = if m < n then l[m]? else none := by
|
||||
split
|
||||
· next h => exact getElem?_take h
|
||||
· next h => exact getElem?_take_of_lt h
|
||||
· next h => exact getElem?_take_eq_none (Nat.le_of_not_lt h)
|
||||
|
||||
@[deprecated getElem?_take_eq_if (since := "2024-06-12")]
|
||||
@[deprecated getElem?_take (since := "2024-06-12")]
|
||||
theorem get?_take_eq_if {l : List α} {n m : Nat} :
|
||||
(l.take n).get? m = if m < n then l.get? m else none := by
|
||||
simp [getElem?_take_eq_if]
|
||||
simp [getElem?_take]
|
||||
|
||||
theorem head?_take {l : List α} {n : Nat} :
|
||||
(l.take n).head? = if n = 0 then none else l.head? := by
|
||||
simp [head?_eq_getElem?, getElem?_take_eq_if]
|
||||
simp [head?_eq_getElem?, getElem?_take]
|
||||
split
|
||||
· rw [if_neg (by omega)]
|
||||
· rw [if_pos (by omega)]
|
||||
@@ -95,7 +95,7 @@ theorem head_take {l : List α} {n : Nat} (h : l.take n ≠ []) :
|
||||
simp_all
|
||||
|
||||
theorem getLast?_take {l : List α} : (l.take n).getLast? = if n = 0 then none else l[n - 1]?.or l.getLast? := by
|
||||
rw [getLast?_eq_getElem?, getElem?_take_eq_if, length_take]
|
||||
rw [getLast?_eq_getElem?, getElem?_take, length_take]
|
||||
split
|
||||
· rw [if_neg (by omega)]
|
||||
rw [Nat.min_def]
|
||||
@@ -128,7 +128,7 @@ theorem take_take : ∀ (n m) (l : List α), take n (take m l) = take (min n m)
|
||||
theorem take_set_of_lt (a : α) {n m : Nat} (l : List α) (h : m < n) :
|
||||
(l.set n a).take m = l.take m :=
|
||||
List.ext_getElem? fun i => by
|
||||
rw [getElem?_take_eq_if, getElem?_take_eq_if]
|
||||
rw [getElem?_take, getElem?_take]
|
||||
split
|
||||
· next h' => rw [getElem?_set_ne (by omega)]
|
||||
· rfl
|
||||
@@ -203,7 +203,7 @@ theorem map_eq_append_split {f : α → β} {l : List α} {s₁ s₂ : List β}
|
||||
theorem take_prefix_take_left (l : List α) {m n : Nat} (h : m ≤ n) : take m l <+: take n l := by
|
||||
rw [isPrefix_iff]
|
||||
intro i w
|
||||
rw [getElem?_take, getElem_take', getElem?_eq_getElem]
|
||||
rw [getElem?_take_of_lt, getElem_take', getElem?_eq_getElem]
|
||||
simp only [length_take] at w
|
||||
exact Nat.lt_of_lt_of_le (Nat.lt_of_lt_of_le w (Nat.min_le_left _ _)) h
|
||||
|
||||
@@ -275,7 +275,7 @@ theorem head?_drop (l : List α) (n : Nat) :
|
||||
theorem head_drop {l : List α} {n : Nat} (h : l.drop n ≠ []) :
|
||||
(l.drop n).head h = l[n]'(by simp_all) := by
|
||||
have w : n < l.length := length_lt_of_drop_ne_nil h
|
||||
simpa [head?_eq_head, getElem?_eq_getElem, h, w] using head?_drop l n
|
||||
simpa [getElem?_eq_getElem, h, w, head_eq_iff_head?_eq_some] using head?_drop l n
|
||||
|
||||
theorem getLast?_drop {l : List α} : (l.drop n).getLast? = if l.length ≤ n then none else l.getLast? := by
|
||||
rw [getLast?_eq_getElem?, getElem?_drop]
|
||||
@@ -334,7 +334,7 @@ theorem set_eq_take_append_cons_drop {l : List α} {n : Nat} {a : α} :
|
||||
· ext1 m
|
||||
by_cases h' : m < n
|
||||
· rw [getElem?_append_left (by simp [length_take]; omega), getElem?_set_ne (by omega),
|
||||
getElem?_take h']
|
||||
getElem?_take_of_lt h']
|
||||
· by_cases h'' : m = n
|
||||
· subst h''
|
||||
rw [getElem?_set_eq ‹_›, getElem?_append_right, length_take,
|
||||
@@ -373,40 +373,67 @@ theorem drop_take : ∀ (m n : Nat) (l : List α), drop n (take m l) = take (m -
|
||||
congr 1
|
||||
omega
|
||||
|
||||
theorem take_reverse {α} {xs : List α} {n : Nat} (h : n ≤ xs.length) :
|
||||
theorem take_reverse {α} {xs : List α} {n : Nat} :
|
||||
xs.reverse.take n = (xs.drop (xs.length - n)).reverse := by
|
||||
induction xs generalizing n <;>
|
||||
simp only [reverse_cons, drop, reverse_nil, Nat.zero_sub, length, take_nil]
|
||||
next xs_hd xs_tl xs_ih =>
|
||||
cases Nat.lt_or_eq_of_le h with
|
||||
| inl h' =>
|
||||
have h' := Nat.le_of_succ_le_succ h'
|
||||
rw [take_append_of_le_length, xs_ih h']
|
||||
rw [show xs_tl.length + 1 - n = succ (xs_tl.length - n) from _, drop]
|
||||
· rwa [succ_eq_add_one, Nat.sub_add_comm]
|
||||
· rwa [length_reverse]
|
||||
| inr h' =>
|
||||
subst h'
|
||||
rw [length, Nat.sub_self, drop]
|
||||
suffices xs_tl.length + 1 = (xs_tl.reverse ++ [xs_hd]).length by
|
||||
rw [this, take_length, reverse_cons]
|
||||
rw [length_append, length_reverse]
|
||||
rfl
|
||||
by_cases h : n ≤ xs.length
|
||||
· induction xs generalizing n <;>
|
||||
simp only [reverse_cons, drop, reverse_nil, Nat.zero_sub, length, take_nil]
|
||||
next xs_hd xs_tl xs_ih =>
|
||||
cases Nat.lt_or_eq_of_le h with
|
||||
| inl h' =>
|
||||
have h' := Nat.le_of_succ_le_succ h'
|
||||
rw [take_append_of_le_length, xs_ih h']
|
||||
rw [show xs_tl.length + 1 - n = succ (xs_tl.length - n) from _, drop]
|
||||
· rwa [succ_eq_add_one, Nat.sub_add_comm]
|
||||
· rwa [length_reverse]
|
||||
| inr h' =>
|
||||
subst h'
|
||||
rw [length, Nat.sub_self, drop]
|
||||
suffices xs_tl.length + 1 = (xs_tl.reverse ++ [xs_hd]).length by
|
||||
rw [this, take_length, reverse_cons]
|
||||
rw [length_append, length_reverse]
|
||||
rfl
|
||||
· have w : xs.length - n = 0 := by omega
|
||||
rw [take_of_length_le, w, drop_zero]
|
||||
simp
|
||||
omega
|
||||
|
||||
@[deprecated (since := "2024-06-15")] abbrev reverse_take := @take_reverse
|
||||
|
||||
theorem drop_reverse {α} {xs : List α} {n : Nat} (h : n ≤ xs.length) :
|
||||
theorem drop_reverse {α} {xs : List α} {n : Nat} :
|
||||
xs.reverse.drop n = (xs.take (xs.length - n)).reverse := by
|
||||
conv =>
|
||||
rhs
|
||||
rw [← reverse_reverse xs]
|
||||
rw [← reverse_reverse xs] at h
|
||||
generalize xs.reverse = xs' at h ⊢
|
||||
rw [take_reverse]
|
||||
· simp only [length_reverse, reverse_reverse] at *
|
||||
by_cases h : n ≤ xs.length
|
||||
· conv =>
|
||||
rhs
|
||||
rw [← reverse_reverse xs]
|
||||
rw [← reverse_reverse xs] at h
|
||||
generalize xs.reverse = xs' at h ⊢
|
||||
rw [take_reverse]
|
||||
· simp only [length_reverse, reverse_reverse] at *
|
||||
congr
|
||||
omega
|
||||
· have w : xs.length - n = 0 := by omega
|
||||
rw [drop_of_length_le, w, take_zero, reverse_nil]
|
||||
simp
|
||||
omega
|
||||
|
||||
theorem reverse_take {l : List α} {n : Nat} :
|
||||
(l.take n).reverse = l.reverse.drop (l.length - n) := by
|
||||
by_cases h : n ≤ l.length
|
||||
· rw [drop_reverse]
|
||||
congr
|
||||
omega
|
||||
· simp only [length_reverse, sub_le]
|
||||
· have w : l.length - n = 0 := by omega
|
||||
rw [w, drop_zero, take_of_length_le]
|
||||
omega
|
||||
|
||||
theorem reverse_drop {l : List α} {n : Nat} :
|
||||
(l.drop n).reverse = l.reverse.take (l.length - n) := by
|
||||
by_cases h : n ≤ l.length
|
||||
· rw [take_reverse]
|
||||
congr
|
||||
omega
|
||||
· have w : l.length - n = 0 := by omega
|
||||
rw [w, take_zero, drop_of_length_le, reverse_nil]
|
||||
omega
|
||||
|
||||
/-! ### rotateLeft -/
|
||||
|
||||
|
||||
@@ -123,7 +123,7 @@ theorem pairwise_filterMap (f : β → Option α) {l : List β} :
|
||||
match e : f a with
|
||||
| none =>
|
||||
rw [filterMap_cons_none e, pairwise_cons]
|
||||
simp only [e, false_implies, implies_true, true_and, IH]
|
||||
simp only [e, false_implies, implies_true, true_and, IH, reduceCtorEq]
|
||||
| some b =>
|
||||
rw [filterMap_cons_some e]
|
||||
simpa [IH, e] using fun _ =>
|
||||
@@ -226,6 +226,18 @@ theorem pairwise_iff_forall_sublist : l.Pairwise R ↔ (∀ {a b}, [a,b] <+ l
|
||||
intro a b hab
|
||||
apply h; exact hab.cons _
|
||||
|
||||
theorem Pairwise.rel_of_mem_take_of_mem_drop
|
||||
{l : List α} (h : l.Pairwise R) (hx : x ∈ l.take n) (hy : y ∈ l.drop n) : R x y := by
|
||||
apply pairwise_iff_forall_sublist.mp h
|
||||
rw [← take_append_drop n l, sublist_append_iff]
|
||||
refine ⟨[x], [y], rfl, by simpa, by simpa⟩
|
||||
|
||||
theorem Pairwise.rel_of_mem_append
|
||||
{l₁ l₂ : List α} (h : (l₁ ++ l₂).Pairwise R) (hx : x ∈ l₁) (hy : y ∈ l₂) : R x y := by
|
||||
apply pairwise_iff_forall_sublist.mp h
|
||||
rw [sublist_append_iff]
|
||||
exact ⟨[x], [y], rfl, by simpa, by simpa⟩
|
||||
|
||||
theorem pairwise_of_forall_mem_list {l : List α} {r : α → α → Prop} (h : ∀ a ∈ l, ∀ b ∈ l, r a b) :
|
||||
l.Pairwise r := by
|
||||
rw [pairwise_iff_forall_sublist]
|
||||
|
||||
@@ -123,10 +123,8 @@ theorem Perm.nil_eq {l : List α} (p : [] ~ l) : [] = l := p.symm.eq_nil.symm
|
||||
|
||||
@[simp] theorem nil_perm {l₁ : List α} : [] ~ l₁ ↔ l₁ = [] := perm_comm.trans perm_nil
|
||||
|
||||
@[simp]
|
||||
theorem not_perm_nil_cons (x : α) (l : List α) : ¬[] ~ x :: l := (nomatch ·.symm.eq_nil)
|
||||
|
||||
@[simp]
|
||||
theorem not_perm_cons_nil {l : List α} {a : α} : ¬(Perm (a::l) []) :=
|
||||
fun h => by simpa using h.length_eq
|
||||
|
||||
@@ -400,6 +398,40 @@ theorem Pairwise.perm {R : α → α → Prop} {l l' : List α} (hR : l.Pairwise
|
||||
theorem Perm.pairwise {R : α → α → Prop} {l l' : List α} (hl : l ~ l') (hR : l.Pairwise R)
|
||||
(hsymm : ∀ {x y}, R x y → R y x) : l'.Pairwise R := hR.perm hl hsymm
|
||||
|
||||
/--
|
||||
If two lists are sorted by an antisymmetric relation, and permutations of each other,
|
||||
they must be equal.
|
||||
-/
|
||||
theorem Perm.eq_of_sorted : ∀ {l₁ l₂ : List α}
|
||||
(_ : ∀ a b, a ∈ l₁ → b ∈ l₂ → le a b → le b a → a = b)
|
||||
(_ : l₁.Pairwise le) (_ : l₂.Pairwise le) (_ : l₁ ~ l₂), l₁ = l₂
|
||||
| [], [], _, _, _, _ => rfl
|
||||
| [], b :: l₂, _, _, _, h => by simp_all
|
||||
| a :: l₁, [], _, _, _, h => by simp_all
|
||||
| a :: l₁, b :: l₂, w, h₁, h₂, h => by
|
||||
have am : a ∈ b :: l₂ := h.subset (mem_cons_self _ _)
|
||||
have bm : b ∈ a :: l₁ := h.symm.subset (mem_cons_self _ _)
|
||||
have ab : a = b := by
|
||||
simp only [mem_cons] at am
|
||||
rcases am with rfl | am
|
||||
· rfl
|
||||
· simp only [mem_cons] at bm
|
||||
rcases bm with rfl | bm
|
||||
· rfl
|
||||
· exact w _ _ (mem_cons_self _ _) (mem_cons_self _ _)
|
||||
(rel_of_pairwise_cons h₁ bm) (rel_of_pairwise_cons h₂ am)
|
||||
subst ab
|
||||
simp only [perm_cons] at h
|
||||
have := Perm.eq_of_sorted
|
||||
(fun x y hx hy => w x y (mem_cons_of_mem a hx) (mem_cons_of_mem a hy))
|
||||
h₁.tail h₂.tail h
|
||||
simp_all
|
||||
|
||||
theorem Nodup.perm {l l' : List α} (hR : l.Nodup) (hl : l ~ l') : l'.Nodup :=
|
||||
Pairwise.perm hR hl (by intro x y h h'; simp_all)
|
||||
|
||||
theorem Perm.nodup {l l' : List α} (hl : l ~ l') (hR : l.Nodup) : l'.Nodup := hR.perm hl
|
||||
|
||||
theorem Perm.nodup_iff {l₁ l₂ : List α} : l₁ ~ l₂ → (Nodup l₁ ↔ Nodup l₂) :=
|
||||
Perm.pairwise_iff <| @Ne.symm α
|
||||
|
||||
|
||||
9
src/Init/Data/List/Sort.lean
Normal file
9
src/Init/Data/List/Sort.lean
Normal file
@@ -0,0 +1,9 @@
|
||||
/-
|
||||
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.Sort.Basic
|
||||
import Init.Data.List.Sort.Impl
|
||||
import Init.Data.List.Sort.Lemmas
|
||||
81
src/Init/Data/List/Sort/Basic.lean
Normal file
81
src/Init/Data/List/Sort/Basic.lean
Normal file
@@ -0,0 +1,81 @@
|
||||
/-
|
||||
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.Impl
|
||||
|
||||
/-!
|
||||
# Definition of `merge` and `mergeSort`.
|
||||
|
||||
These definitions are intended for verification purposes,
|
||||
and are replaced at runtime by efficient versions in `Init.Data.List.Sort.Impl`.
|
||||
-/
|
||||
|
||||
namespace List
|
||||
|
||||
/--
|
||||
`O(min |l| |r|)`. Merge two lists using `le` as a switch.
|
||||
|
||||
This version is not tail-recursive,
|
||||
but it is replaced at runtime by `mergeTR` using a `@[csimp]` lemma.
|
||||
-/
|
||||
def merge (le : α → α → Bool) : List α → List α → List α
|
||||
| [], ys => ys
|
||||
| xs, [] => xs
|
||||
| x :: xs, y :: ys =>
|
||||
if le x y then
|
||||
x :: merge le xs (y :: ys)
|
||||
else
|
||||
y :: merge le (x :: xs) ys
|
||||
|
||||
@[simp] theorem nil_merge (ys : List α) : merge le [] ys = ys := by simp [merge]
|
||||
@[simp] theorem merge_right (xs : List α) : merge le xs [] = xs := by
|
||||
induction xs with
|
||||
| nil => simp [merge]
|
||||
| cons x xs ih => simp [merge, ih]
|
||||
|
||||
/--
|
||||
Split a list in two equal parts. If the length is odd, the first part will be one element longer.
|
||||
-/
|
||||
def splitInTwo (l : { l : List α // l.length = n }) :
|
||||
{ l : List α // l.length = (n+1)/2 } × { l : List α // l.length = n/2 } :=
|
||||
let r := splitAt ((n+1)/2) l.1
|
||||
(⟨r.1, by simp [r, splitAt_eq, l.2]; omega⟩, ⟨r.2, by simp [r, splitAt_eq, l.2]; omega⟩)
|
||||
|
||||
/--
|
||||
Simplified implementation of stable merge sort.
|
||||
|
||||
This function is designed for reasoning about the algorithm, and is not efficient.
|
||||
(It particular it uses the non tail-recursive `merge` function,
|
||||
and so can not be run on large lists, but also makes unnecessary traversals of lists.)
|
||||
It is replaced at runtime in the compiler by `mergeSortTR₂` using a `@[csimp]` lemma.
|
||||
|
||||
Because we want the sort to be stable,
|
||||
it is essential that we split the list in two contiguous sublists.
|
||||
-/
|
||||
def mergeSort (le : α → α → Bool) : List α → List α
|
||||
| [] => []
|
||||
| [a] => [a]
|
||||
| a :: b :: xs =>
|
||||
let lr := splitInTwo ⟨a :: b :: xs, rfl⟩
|
||||
have := by simpa using lr.2.2
|
||||
have := by simpa using lr.1.2
|
||||
merge le (mergeSort le lr.1) (mergeSort le lr.2)
|
||||
termination_by l => l.length
|
||||
|
||||
|
||||
/--
|
||||
Given an ordering relation `le : α → α → Bool`,
|
||||
construct the reverse lexicographic ordering on `Nat × α`.
|
||||
which first compares the second components using `le`,
|
||||
but if these are equivalent (in the sense `le a.2 b.2 && le b.2 a.2`)
|
||||
then compares the first components using `≤`.
|
||||
|
||||
This function is only used in stating the stability properties of `mergeSort`.
|
||||
-/
|
||||
def enumLE (le : α → α → Bool) (a b : Nat × α) : Bool :=
|
||||
if le a.2 b.2 then if le b.2 a.2 then a.1 ≤ b.1 else true else false
|
||||
|
||||
end List
|
||||
237
src/Init/Data/List/Sort/Impl.lean
Normal file
237
src/Init/Data/List/Sort/Impl.lean
Normal file
@@ -0,0 +1,237 @@
|
||||
/-
|
||||
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.Sort.Lemmas
|
||||
|
||||
/-!
|
||||
# Replacing `merge` and `mergeSort` at runtime with tail-recursive and faster versions.
|
||||
|
||||
We replace `merge` with `mergeTR` using a `@[csimp]` lemma.
|
||||
|
||||
We replace `mergeSort` in two steps:
|
||||
* first with `mergeSortTR`, which while not tail-recursive itself (it can't be),
|
||||
uses `mergeTR` internally.
|
||||
* second with `mergeSortTR₂`, which achieves an ~20% speed-up over `mergeSortTR`
|
||||
by avoiding some unnecessary list reversals.
|
||||
|
||||
There is no public API in this file; it solely exists to implement the `@[csimp]` lemmas
|
||||
affecting runtime behaviour.
|
||||
|
||||
## Future work
|
||||
The current runtime implementation could be further improved in a number of ways, e.g.:
|
||||
* only walking the list once during splitting,
|
||||
* using insertion sort for small chunks rather than splitting all the way down to singletons,
|
||||
* identifying already sorted or reverse sorted chunks and skipping them.
|
||||
|
||||
Because the theory developed for `mergeSort` is independent of the runtime implementation,
|
||||
as long as such improvements are carefully validated by benchmarking,
|
||||
they can be done without changing the theory, as long as a `@[csimp]` lemma is provided.
|
||||
-/
|
||||
|
||||
open List
|
||||
|
||||
namespace List.MergeSort.Internal
|
||||
|
||||
/--
|
||||
`O(min |l| |r|)`. Merge two lists using `le` as a switch.
|
||||
-/
|
||||
def mergeTR (le : α → α → Bool) (l₁ l₂ : List α) : List α :=
|
||||
go l₁ l₂ []
|
||||
where go : List α → List α → List α → List α
|
||||
| [], l₂, acc => reverseAux acc l₂
|
||||
| l₁, [], acc => reverseAux acc l₁
|
||||
| x :: xs, y :: ys, acc =>
|
||||
if le x y then
|
||||
go xs (y :: ys) (x :: acc)
|
||||
else
|
||||
go (x :: xs) ys (y :: acc)
|
||||
|
||||
theorem mergeTR_go_eq : mergeTR.go le l₁ l₂ acc = acc.reverse ++ merge le l₁ l₂ := by
|
||||
induction l₁ generalizing l₂ acc with
|
||||
| nil => simp [mergeTR.go, merge, reverseAux_eq]
|
||||
| cons x l₁ ih₁ =>
|
||||
induction l₂ generalizing acc with
|
||||
| nil => simp [mergeTR.go, merge, reverseAux_eq]
|
||||
| cons y l₂ ih₂ =>
|
||||
simp [mergeTR.go, merge]
|
||||
split <;> simp [ih₁, ih₂]
|
||||
|
||||
@[csimp] theorem merge_eq_mergeTR : @merge = @mergeTR := by
|
||||
funext
|
||||
simp [mergeTR, mergeTR_go_eq]
|
||||
|
||||
/--
|
||||
Variant of `splitAt`, that does not reverse the first list, i.e
|
||||
`splitRevAt n l = ((l.take n).reverse, l.drop n)`.
|
||||
|
||||
This exists solely as an optimization for `mergeSortTR` and `mergeSortTR₂`,
|
||||
and should not be used elsewhere.
|
||||
-/
|
||||
def splitRevAt (n : Nat) (l : List α) : List α × List α := go l n [] where
|
||||
/-- Auxiliary for `splitAtRev`: `splitAtRev.go xs n acc = ((take n xs).reverse ++ acc, drop n xs)`. -/
|
||||
go : List α → Nat → List α → List α × List α
|
||||
| x :: xs, n+1, acc => go xs n (x :: acc)
|
||||
| xs, _, acc => (acc, xs)
|
||||
|
||||
theorem splitRevAt_go (xs : List α) (n : Nat) (acc : List α) :
|
||||
splitRevAt.go xs n acc = ((take n xs).reverse ++ acc, drop n xs) := by
|
||||
induction xs generalizing n acc with
|
||||
| nil => simp [splitRevAt.go]
|
||||
| cons x xs ih =>
|
||||
cases n with
|
||||
| zero => simp [splitRevAt.go]
|
||||
| succ n =>
|
||||
rw [splitRevAt.go, ih n (x :: acc), take_succ_cons, reverse_cons, drop_succ_cons,
|
||||
append_assoc, singleton_append]
|
||||
|
||||
theorem splitRevAt_eq (n : Nat) (l : List α) : splitRevAt n l = ((l.take n).reverse, l.drop n) := by
|
||||
rw [splitRevAt, splitRevAt_go, append_nil]
|
||||
|
||||
/--
|
||||
An intermediate speed-up for `mergeSort`.
|
||||
This version uses the tail-recurive `mergeTR` function as a subroutine.
|
||||
|
||||
This is not the final version we use at runtime, as `mergeSortTR₂` is faster.
|
||||
This definition is useful as an intermediate step in proving the `@[csimp]` lemma for `mergeSortTR₂`.
|
||||
-/
|
||||
def mergeSortTR (le : α → α → Bool) (l : List α) : List α :=
|
||||
run ⟨l, rfl⟩
|
||||
where run : {n : Nat} → { l : List α // l.length = n } → List α
|
||||
| 0, ⟨[], _⟩ => []
|
||||
| 1, ⟨[a], _⟩ => [a]
|
||||
| n+2, xs =>
|
||||
let (l, r) := splitInTwo xs
|
||||
mergeTR le (run l) (run r)
|
||||
|
||||
/--
|
||||
Split a list in two equal parts, reversing the first part.
|
||||
If the length is odd, the first part will be one element longer.
|
||||
-/
|
||||
def splitRevInTwo (l : { l : List α // l.length = n }) :
|
||||
{ l : List α // l.length = (n+1)/2 } × { l : List α // l.length = n/2 } :=
|
||||
let r := splitRevAt ((n+1)/2) l.1
|
||||
(⟨r.1, by simp [r, splitRevAt_eq, l.2]; omega⟩, ⟨r.2, by simp [r, splitRevAt_eq, l.2]; omega⟩)
|
||||
|
||||
/--
|
||||
Split a list in two equal parts, reversing the first part.
|
||||
If the length is odd, the second part will be one element longer.
|
||||
-/
|
||||
def splitRevInTwo' (l : { l : List α // l.length = n }) :
|
||||
{ l : List α // l.length = n/2 } × { l : List α // l.length = (n+1)/2 } :=
|
||||
let r := splitRevAt (n/2) l.1
|
||||
(⟨r.1, by simp [r, splitRevAt_eq, l.2]; omega⟩, ⟨r.2, by simp [r, splitRevAt_eq, l.2]; omega⟩)
|
||||
|
||||
/--
|
||||
Faster version of `mergeSortTR`, which avoids unnecessary list reversals.
|
||||
-/
|
||||
-- Per the benchmark in `tests/bench/mergeSort/`
|
||||
-- (which averages over 4 use cases: already sorted lists, reverse sorted lists, almost sorted lists, and random lists),
|
||||
-- for lists of length 10^6, `mergeSortTR₂` is about 20% faster than `mergeSortTR`.
|
||||
def mergeSortTR₂ (le : α → α → Bool) (l : List α) : List α :=
|
||||
run ⟨l, rfl⟩
|
||||
where
|
||||
run : {n : Nat} → { l : List α // l.length = n } → List α
|
||||
| 0, ⟨[], _⟩ => []
|
||||
| 1, ⟨[a], _⟩ => [a]
|
||||
| n+2, xs =>
|
||||
let (l, r) := splitRevInTwo xs
|
||||
mergeTR le (run' l) (run r)
|
||||
run' : {n : Nat} → { l : List α // l.length = n } → List α
|
||||
| 0, ⟨[], _⟩ => []
|
||||
| 1, ⟨[a], _⟩ => [a]
|
||||
| n+2, xs =>
|
||||
let (l, r) := splitRevInTwo' xs
|
||||
mergeTR le (run' r) (run l)
|
||||
|
||||
theorem splitRevInTwo'_fst (l : { l : List α // l.length = n }) :
|
||||
(splitRevInTwo' l).1 = ⟨(splitInTwo ⟨l.1.reverse, by simpa using l.2⟩).2.1, by have := l.2; simp; omega⟩ := by
|
||||
simp only [splitRevInTwo', splitRevAt_eq, reverse_take, splitInTwo_snd]
|
||||
congr
|
||||
have := l.2
|
||||
omega
|
||||
theorem splitRevInTwo'_snd (l : { l : List α // l.length = n }) :
|
||||
(splitRevInTwo' l).2 = ⟨(splitInTwo ⟨l.1.reverse, by simpa using l.2⟩).1.1.reverse, by have := l.2; simp; omega⟩ := by
|
||||
simp only [splitRevInTwo', splitRevAt_eq, reverse_take, splitInTwo_fst, reverse_reverse]
|
||||
congr 2
|
||||
have := l.2
|
||||
simp
|
||||
omega
|
||||
theorem splitRevInTwo_fst (l : { l : List α // l.length = n }) :
|
||||
(splitRevInTwo l).1 = ⟨(splitInTwo l).1.1.reverse, by have := l.2; simp; omega⟩ := by
|
||||
simp only [splitRevInTwo, splitRevAt_eq, reverse_take, splitInTwo_fst]
|
||||
theorem splitRevInTwo_snd (l : { l : List α // l.length = n }) :
|
||||
(splitRevInTwo l).2 = ⟨(splitInTwo l).2.1, by have := l.2; simp; omega⟩ := by
|
||||
simp only [splitRevInTwo, splitRevAt_eq, reverse_take, splitInTwo_snd]
|
||||
|
||||
theorem mergeSortTR_run_eq_mergeSort : {n : Nat} → (l : { l : List α // l.length = n }) → mergeSortTR.run le l = mergeSort le l.1
|
||||
| 0, ⟨[], _⟩
|
||||
| 1, ⟨[a], _⟩ => by simp [mergeSortTR.run, mergeSort]
|
||||
| n+2, ⟨a :: b :: l, h⟩ => by
|
||||
cases h
|
||||
simp only [mergeSortTR.run, mergeSortTR.run, mergeSort]
|
||||
rw [merge_eq_mergeTR]
|
||||
rw [mergeSortTR_run_eq_mergeSort, mergeSortTR_run_eq_mergeSort]
|
||||
|
||||
-- We don't make this a `@[csimp]` lemma because `mergeSort_eq_mergeSortTR₂` is faster.
|
||||
theorem mergeSort_eq_mergeSortTR : @mergeSort = @mergeSortTR := by
|
||||
funext
|
||||
rw [mergeSortTR, mergeSortTR_run_eq_mergeSort]
|
||||
|
||||
-- This mutual block is unfortunately quite slow to elaborate.
|
||||
set_option maxHeartbeats 400000 in
|
||||
mutual
|
||||
theorem mergeSortTR₂_run_eq_mergeSort : {n : Nat} → (l : { l : List α // l.length = n }) → mergeSortTR₂.run le l = mergeSort le l.1
|
||||
| 0, ⟨[], _⟩
|
||||
| 1, ⟨[a], _⟩ => by simp [mergeSortTR₂.run, mergeSort]
|
||||
| n+2, ⟨a :: b :: l, h⟩ => by
|
||||
cases h
|
||||
simp only [mergeSortTR₂.run, mergeSort]
|
||||
rw [splitRevInTwo_fst, splitRevInTwo_snd]
|
||||
rw [mergeSortTR₂_run_eq_mergeSort, mergeSortTR₂_run'_eq_mergeSort]
|
||||
rw [merge_eq_mergeTR]
|
||||
rw [reverse_reverse]
|
||||
termination_by n => n
|
||||
|
||||
theorem mergeSortTR₂_run'_eq_mergeSort : {n : Nat} → (l : { l : List α // l.length = n }) → (w : l' = l.1.reverse) → mergeSortTR₂.run' le l = mergeSort le l'
|
||||
| 0, ⟨[], _⟩, w
|
||||
| 1, ⟨[a], _⟩, w => by simp_all [mergeSortTR₂.run', mergeSort]
|
||||
| n+2, ⟨a :: b :: l, h⟩, w => by
|
||||
cases h
|
||||
simp only [mergeSortTR₂.run', mergeSort]
|
||||
rw [splitRevInTwo'_fst, splitRevInTwo'_snd]
|
||||
rw [mergeSortTR₂_run_eq_mergeSort, mergeSortTR₂_run'_eq_mergeSort _ rfl]
|
||||
rw [← merge_eq_mergeTR]
|
||||
have w' := congrArg length w
|
||||
simp at w'
|
||||
cases l' with
|
||||
| nil => simp at w'
|
||||
| cons x l' =>
|
||||
cases l' with
|
||||
| nil => simp at w'
|
||||
| cons y l' =>
|
||||
rw [mergeSort]
|
||||
congr 2
|
||||
· dsimp at w
|
||||
simp only [w]
|
||||
simp only [splitInTwo_fst, splitInTwo_snd, reverse_take, take_reverse]
|
||||
congr 1
|
||||
rw [w, length_reverse]
|
||||
simp
|
||||
· dsimp at w
|
||||
simp only [w]
|
||||
simp only [reverse_cons, append_assoc, singleton_append, splitInTwo_snd, length_cons]
|
||||
congr 1
|
||||
simp at w'
|
||||
omega
|
||||
termination_by n => n
|
||||
|
||||
end
|
||||
|
||||
@[csimp] theorem mergeSort_eq_mergeSortTR₂ : @mergeSort = @mergeSortTR₂ := by
|
||||
funext
|
||||
rw [mergeSortTR₂, mergeSortTR₂_run_eq_mergeSort]
|
||||
|
||||
end List.MergeSort.Internal
|
||||
406
src/Init/Data/List/Sort/Lemmas.lean
Normal file
406
src/Init/Data/List/Sort/Lemmas.lean
Normal file
@@ -0,0 +1,406 @@
|
||||
/-
|
||||
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.Perm
|
||||
import Init.Data.List.Sort.Basic
|
||||
import Init.Data.Bool
|
||||
|
||||
/-!
|
||||
# Basic properties of `mergeSort`.
|
||||
|
||||
* `mergeSort_sorted`: `mergeSort` produces a sorted list.
|
||||
* `mergeSort_perm`: `mergeSort` is a permutation of the input list.
|
||||
* `mergeSort_of_sorted`: `mergeSort` does not change a sorted list.
|
||||
* `mergeSort_cons`: proves `mergeSort le (x :: xs) = l₁ ++ x :: l₂` for some `l₁, l₂`
|
||||
so that `mergeSort le xs = l₁ ++ l₂`, and no `a ∈ l₁` satisfies `le a x`.
|
||||
* `mergeSort_stable`: if `c` is a sorted sublist of `l`, then `c` is still a sublist of `mergeSort le l`.
|
||||
|
||||
-/
|
||||
|
||||
namespace List
|
||||
|
||||
-- We enable this instance locally so we can write `Sorted le` instead of `Sorted (le · ·)` everywhere.
|
||||
attribute [local instance] boolRelToRel
|
||||
|
||||
variable {le : α → α → Bool}
|
||||
|
||||
/-! ### splitInTwo -/
|
||||
|
||||
@[simp] theorem splitInTwo_fst (l : { l : List α // l.length = n }) : (splitInTwo l).1 = ⟨l.1.take ((n+1)/2), by simp [splitInTwo, splitAt_eq, l.2]; omega⟩ := by
|
||||
simp [splitInTwo, splitAt_eq]
|
||||
|
||||
@[simp] theorem splitInTwo_snd (l : { l : List α // l.length = n }) : (splitInTwo l).2 = ⟨l.1.drop ((n+1)/2), by simp [splitInTwo, splitAt_eq, l.2]; omega⟩ := by
|
||||
simp [splitInTwo, splitAt_eq]
|
||||
|
||||
theorem splitInTwo_fst_append_splitInTwo_snd (l : { l : List α // l.length = n }) : (splitInTwo l).1.1 ++ (splitInTwo l).2.1 = l.1 := by
|
||||
simp
|
||||
|
||||
theorem splitInTwo_cons_cons_enumFrom_fst (i : Nat) (l : List α) :
|
||||
(splitInTwo ⟨(i, a) :: (i+1, b) :: l.enumFrom (i+2), rfl⟩).1.1 =
|
||||
(splitInTwo ⟨a :: b :: l, rfl⟩).1.1.enumFrom i := by
|
||||
simp only [length_cons, splitInTwo_fst, enumFrom_length]
|
||||
ext1 j
|
||||
rw [getElem?_take, getElem?_enumFrom, getElem?_take]
|
||||
split
|
||||
· rw [getElem?_cons, getElem?_cons, getElem?_cons, getElem?_cons]
|
||||
split
|
||||
· simp; omega
|
||||
· split
|
||||
· simp; omega
|
||||
· simp only [getElem?_enumFrom]
|
||||
congr
|
||||
ext <;> simp; omega
|
||||
· simp
|
||||
|
||||
theorem splitInTwo_cons_cons_enumFrom_snd (i : Nat) (l : List α) :
|
||||
(splitInTwo ⟨(i, a) :: (i+1, b) :: l.enumFrom (i+2), rfl⟩).2.1 =
|
||||
(splitInTwo ⟨a :: b :: l, rfl⟩).2.1.enumFrom (i+(l.length+3)/2) := by
|
||||
simp only [length_cons, splitInTwo_snd, enumFrom_length]
|
||||
ext1 j
|
||||
rw [getElem?_drop, getElem?_enumFrom, getElem?_drop]
|
||||
rw [getElem?_cons, getElem?_cons, getElem?_cons, getElem?_cons]
|
||||
split
|
||||
· simp; omega
|
||||
· split
|
||||
· simp; omega
|
||||
· simp only [getElem?_enumFrom]
|
||||
congr
|
||||
ext <;> simp; omega
|
||||
|
||||
theorem splitInTwo_fst_sorted (l : { l : List α // l.length = n }) (h : Pairwise le l.1) : Pairwise le (splitInTwo l).1.1 := by
|
||||
rw [splitInTwo_fst]
|
||||
exact h.take
|
||||
|
||||
theorem splitInTwo_snd_sorted (l : { l : List α // l.length = n }) (h : Pairwise le l.1) : Pairwise le (splitInTwo l).2.1 := by
|
||||
rw [splitInTwo_snd]
|
||||
exact h.drop
|
||||
|
||||
theorem splitInTwo_fst_le_splitInTwo_snd {l : { l : List α // l.length = n }} (h : Pairwise le l.1) :
|
||||
∀ a b, a ∈ (splitInTwo l).1.1 → b ∈ (splitInTwo l).2.1 → le a b := by
|
||||
rw [splitInTwo_fst, splitInTwo_snd]
|
||||
intro a b ma mb
|
||||
exact h.rel_of_mem_take_of_mem_drop ma mb
|
||||
|
||||
/-! ### enumLE -/
|
||||
|
||||
theorem enumLE_trans (trans : ∀ a b c, le a b → le b c → le a c)
|
||||
(a b c : Nat × α) : enumLE le a b → enumLE le b c → enumLE le a c := by
|
||||
simp only [enumLE]
|
||||
split <;> split <;> split <;> rename_i ab₂ ba₂ bc₂
|
||||
· simp_all
|
||||
intro ab₁
|
||||
intro h
|
||||
refine ⟨trans _ _ _ ab₂ bc₂, ?_⟩
|
||||
rcases h with (cd₂ | bc₁)
|
||||
· exact Or.inl (Decidable.byContradiction
|
||||
(fun ca₂ => by simp_all [trans _ _ _ (by simpa using ca₂) ab₂]))
|
||||
· exact Or.inr (Nat.le_trans ab₁ bc₁)
|
||||
· simp_all
|
||||
· simp_all
|
||||
intro h
|
||||
refine ⟨trans _ _ _ ab₂ bc₂, ?_⟩
|
||||
left
|
||||
rcases h with (cb₂ | _)
|
||||
· exact (Decidable.byContradiction
|
||||
(fun ca₂ => by simp_all [trans _ _ _ (by simpa using ca₂) ab₂]))
|
||||
· exact (Decidable.byContradiction
|
||||
(fun ca₂ => by simp_all [trans _ _ _ bc₂ (by simpa using ca₂)]))
|
||||
· simp_all
|
||||
· simp_all
|
||||
· simp_all
|
||||
· simp_all
|
||||
· simp_all
|
||||
|
||||
theorem enumLE_total (total : ∀ a b, !le a b → le b a)
|
||||
(a b : Nat × α) : !enumLE le a b → enumLE le b a := by
|
||||
simp only [enumLE]
|
||||
split <;> split
|
||||
· simpa using Nat.le_of_lt
|
||||
· simp
|
||||
· simp
|
||||
· simp_all [total a.2 b.2]
|
||||
|
||||
/-! ### merge -/
|
||||
|
||||
theorem merge_stable : ∀ (xs ys) (_ : ∀ x y, x ∈ xs → y ∈ ys → x.1 ≤ y.1),
|
||||
(merge (enumLE le) xs ys).map (·.2) = merge le (xs.map (·.2)) (ys.map (·.2))
|
||||
| [], ys, _ => by simp [merge]
|
||||
| xs, [], _ => by simp [merge]
|
||||
| (i, x) :: xs, (j, y) :: ys, h => by
|
||||
simp only [merge, enumLE, map_cons]
|
||||
split <;> rename_i w
|
||||
· rw [if_pos (by simp [h _ _ (mem_cons_self ..) (mem_cons_self ..)])]
|
||||
simp only [map_cons, cons.injEq, true_and]
|
||||
rw [merge_stable, map_cons]
|
||||
exact fun x' y' mx my => h x' y' (mem_cons_of_mem (i, x) mx) my
|
||||
· simp only [↓reduceIte, map_cons, cons.injEq, true_and, reduceCtorEq]
|
||||
rw [merge_stable, map_cons]
|
||||
exact fun x' y' mx my => h x' y' mx (mem_cons_of_mem (j, y) my)
|
||||
|
||||
/--
|
||||
The elements of `merge le xs ys` are exactly the elements of `xs` and `ys`.
|
||||
-/
|
||||
-- We subsequently prove that `mergeSort_perm : merge le xs ys ~ xs ++ ys`.
|
||||
theorem mem_merge {a : α} {xs ys : List α} : a ∈ merge le xs ys ↔ a ∈ xs ∨ a ∈ ys := by
|
||||
induction xs generalizing ys with
|
||||
| nil => simp [merge]
|
||||
| cons x xs ih =>
|
||||
induction ys with
|
||||
| nil => simp [merge]
|
||||
| cons y ys ih =>
|
||||
simp only [merge]
|
||||
split <;> rename_i h
|
||||
· simp_all [or_assoc]
|
||||
· simp only [mem_cons, or_assoc, Bool.not_eq_true, ih, ← or_assoc]
|
||||
apply or_congr_left
|
||||
simp only [or_comm (a := a = y), or_assoc]
|
||||
|
||||
/--
|
||||
If the ordering relation `le` is transitive and total (i.e. `le a b ∨ le b a` for all `a, b`)
|
||||
then the `merge` of two sorted lists is sorted.
|
||||
-/
|
||||
theorem merge_sorted
|
||||
(trans : ∀ (a b c : α), le a b → le b c → le a c)
|
||||
(total : ∀ (a b : α), !le a b → le b a)
|
||||
(l₁ l₂ : List α) (h₁ : l₁.Pairwise le) (h₂ : l₂.Pairwise le) : (merge le l₁ l₂).Pairwise le := by
|
||||
induction l₁ generalizing l₂ with
|
||||
| nil => simpa only [merge]
|
||||
| cons x l₁ ih₁ =>
|
||||
induction l₂ with
|
||||
| nil => simpa only [merge]
|
||||
| cons y l₂ ih₂ =>
|
||||
simp only [merge]
|
||||
split <;> rename_i h
|
||||
· apply Pairwise.cons
|
||||
· intro z m
|
||||
rw [mem_merge, mem_cons] at m
|
||||
rcases m with (m|rfl|m)
|
||||
· exact rel_of_pairwise_cons h₁ m
|
||||
· exact h
|
||||
· exact trans _ _ _ h (rel_of_pairwise_cons h₂ m)
|
||||
· exact ih₁ _ h₁.tail h₂
|
||||
· apply Pairwise.cons
|
||||
· intro z m
|
||||
rw [mem_merge, mem_cons] at m
|
||||
rcases m with (⟨rfl|m⟩|m)
|
||||
· exact total _ _ (by simpa using h)
|
||||
· exact trans _ _ _ (total _ _ (by simpa using h)) (rel_of_pairwise_cons h₁ m)
|
||||
· exact rel_of_pairwise_cons h₂ m
|
||||
· exact ih₂ h₂.tail
|
||||
|
||||
theorem merge_of_le : ∀ {xs ys : List α} (_ : ∀ a b, a ∈ xs → b ∈ ys → le a b),
|
||||
merge le xs ys = xs ++ ys
|
||||
| [], ys, _
|
||||
| xs, [], _ => by simp [merge]
|
||||
| x :: xs, y :: ys, h => by
|
||||
simp only [merge, cons_append]
|
||||
rw [if_pos, merge_of_le]
|
||||
· intro a b ma mb
|
||||
exact h a b (mem_cons_of_mem _ ma) mb
|
||||
· exact h x y (mem_cons_self _ _) (mem_cons_self _ _)
|
||||
|
||||
variable (le) in
|
||||
theorem merge_perm_append : ∀ {xs ys : List α}, merge le xs ys ~ xs ++ ys
|
||||
| [], ys => by simp [merge]
|
||||
| xs, [] => by simp [merge]
|
||||
| x :: xs, y :: ys => by
|
||||
simp only [merge]
|
||||
split
|
||||
· exact merge_perm_append.cons x
|
||||
· exact (merge_perm_append.cons y).trans
|
||||
((Perm.swap x y _).trans (perm_middle.symm.cons x))
|
||||
|
||||
/-! ### mergeSort -/
|
||||
|
||||
@[simp] theorem mergeSort_nil : [].mergeSort r = [] := by rw [List.mergeSort]
|
||||
|
||||
@[simp] theorem mergeSort_singleton (a : α) : [a].mergeSort r = [a] := by rw [List.mergeSort]
|
||||
|
||||
variable (le) in
|
||||
theorem mergeSort_perm : ∀ (l : List α), mergeSort le l ~ l
|
||||
| [] => by simp [mergeSort]
|
||||
| [a] => by simp [mergeSort]
|
||||
| a :: b :: xs => by
|
||||
simp only [mergeSort]
|
||||
have : (splitInTwo ⟨a :: b :: xs, rfl⟩).1.1.length < xs.length + 1 + 1 := by simp [splitInTwo_fst]; omega
|
||||
have : (splitInTwo ⟨a :: b :: xs, rfl⟩).2.1.length < xs.length + 1 + 1 := by simp [splitInTwo_snd]; omega
|
||||
exact (merge_perm_append le).trans
|
||||
(((mergeSort_perm _).append (mergeSort_perm _)).trans
|
||||
(Perm.of_eq (splitInTwo_fst_append_splitInTwo_snd _)))
|
||||
termination_by l => l.length
|
||||
|
||||
@[simp] theorem mergeSort_length (l : List α) : (mergeSort le l).length = l.length :=
|
||||
(mergeSort_perm le l).length_eq
|
||||
|
||||
@[simp] theorem mem_mergeSort {a : α} {l : List α} : a ∈ mergeSort le l ↔ a ∈ l :=
|
||||
(mergeSort_perm le l).mem_iff
|
||||
|
||||
/--
|
||||
The result of `mergeSort` is sorted,
|
||||
as long as the comparison function is transitive (`le a b → le b c → le a c`)
|
||||
and total in the sense that `le a b ∨ le b a`.
|
||||
|
||||
The comparison function need not be irreflexive, i.e. `le a b` and `le b a` is allowed even when `a ≠ b`.
|
||||
-/
|
||||
theorem mergeSort_sorted
|
||||
(trans : ∀ (a b c : α), le a b → le b c → le a c)
|
||||
(total : ∀ (a b : α), !le a b → le b a) :
|
||||
(l : List α) → (mergeSort le l).Pairwise le
|
||||
| [] => by simp [mergeSort]
|
||||
| [a] => by simp [mergeSort]
|
||||
| a :: b :: xs => by
|
||||
have : (splitInTwo ⟨a :: b :: xs, rfl⟩).1.1.length < xs.length + 1 + 1 := by simp [splitInTwo_fst]; omega
|
||||
have : (splitInTwo ⟨a :: b :: xs, rfl⟩).2.1.length < xs.length + 1 + 1 := by simp [splitInTwo_snd]; omega
|
||||
rw [mergeSort]
|
||||
apply merge_sorted @trans @total
|
||||
apply mergeSort_sorted trans total
|
||||
apply mergeSort_sorted trans total
|
||||
termination_by l => l.length
|
||||
|
||||
/--
|
||||
If the input list is already sorted, then `mergeSort` does not change the list.
|
||||
-/
|
||||
theorem mergeSort_of_sorted : ∀ {l : List α} (_ : Pairwise le l), mergeSort le l = l
|
||||
| [], _ => by simp [mergeSort]
|
||||
| [a], _ => by simp [mergeSort]
|
||||
| a :: b :: xs, h => by
|
||||
have : (splitInTwo ⟨a :: b :: xs, rfl⟩).1.1.length < xs.length + 1 + 1 := by simp [splitInTwo_fst]; omega
|
||||
have : (splitInTwo ⟨a :: b :: xs, rfl⟩).2.1.length < xs.length + 1 + 1 := by simp [splitInTwo_snd]; omega
|
||||
rw [mergeSort]
|
||||
rw [mergeSort_of_sorted (splitInTwo_fst_sorted ⟨a :: b :: xs, rfl⟩ h)]
|
||||
rw [mergeSort_of_sorted (splitInTwo_snd_sorted ⟨a :: b :: xs, rfl⟩ h)]
|
||||
rw [merge_of_le (splitInTwo_fst_le_splitInTwo_snd h)]
|
||||
rw [splitInTwo_fst_append_splitInTwo_snd]
|
||||
termination_by l => l.length
|
||||
|
||||
/--
|
||||
This merge sort algorithm is stable,
|
||||
in the sense that breaking ties in the ordering function using the position in the list
|
||||
has no effect on the output.
|
||||
|
||||
That is, elements which are equal with respect to the ordering function will remain
|
||||
in the same order in the output list as they were in the input list.
|
||||
|
||||
See also:
|
||||
* `mergeSort_stable`: if `c <+ l` and `c.Pairwise le`, then `c <+ mergeSort le l`.
|
||||
* `mergeSort_stable_pair`: if `[a, b] <+ l` and `le a b`, then `[a, b] <+ mergeSort le l`)
|
||||
-/
|
||||
theorem mergeSort_enum {l : List α} :
|
||||
(mergeSort (enumLE le) (l.enum)).map (·.2) = mergeSort le l :=
|
||||
go 0 l
|
||||
where go : ∀ (i : Nat) (l : List α),
|
||||
(mergeSort (enumLE le) (l.enumFrom i)).map (·.2) = mergeSort le l
|
||||
| _, []
|
||||
| _, [a] => by simp [mergeSort]
|
||||
| _, a :: b :: xs => by
|
||||
have : (splitInTwo ⟨a :: b :: xs, rfl⟩).1.1.length < xs.length + 1 + 1 := by simp [splitInTwo_fst]; omega
|
||||
have : (splitInTwo ⟨a :: b :: xs, rfl⟩).2.1.length < xs.length + 1 + 1 := by simp [splitInTwo_snd]; omega
|
||||
simp only [mergeSort, enumFrom]
|
||||
rw [splitInTwo_cons_cons_enumFrom_fst]
|
||||
rw [splitInTwo_cons_cons_enumFrom_snd]
|
||||
rw [merge_stable]
|
||||
· rw [go, go]
|
||||
· simp only [mem_mergeSort, Prod.forall]
|
||||
intros j x k y mx my
|
||||
have := mem_enumFrom mx
|
||||
have := mem_enumFrom my
|
||||
simp_all
|
||||
omega
|
||||
termination_by _ l => l.length
|
||||
|
||||
theorem mergeSort_cons {le : α → α → Bool}
|
||||
(trans : ∀ (a b c : α), le a b → le b c → le a c)
|
||||
(total : ∀ (a b : α), !le a b → le b a)
|
||||
(a : α) (l : List α) :
|
||||
∃ l₁ l₂, mergeSort le (a :: l) = l₁ ++ a :: l₂ ∧ mergeSort le l = l₁ ++ l₂ ∧
|
||||
∀ b, b ∈ l₁ → !le a b := by
|
||||
rw [← mergeSort_enum]
|
||||
rw [enum_cons]
|
||||
have nd : Nodup ((a :: l).enum.map (·.1)) := by rw [enum_map_fst]; exact nodup_range _
|
||||
have m₁ : (0, a) ∈ mergeSort (enumLE le) ((a :: l).enum) :=
|
||||
mem_mergeSort.mpr (mem_cons_self _ _)
|
||||
obtain ⟨l₁, l₂, h⟩ := append_of_mem m₁
|
||||
have s := mergeSort_sorted (enumLE_trans trans) (enumLE_total total) ((a :: l).enum)
|
||||
rw [h] at s
|
||||
have p := mergeSort_perm (enumLE le) ((a :: l).enum)
|
||||
rw [h] at p
|
||||
refine ⟨l₁.map (·.2), l₂.map (·.2), ?_, ?_, ?_⟩
|
||||
· simpa using congrArg (·.map (·.2)) h
|
||||
· rw [← mergeSort_enum.go 1, ← map_append]
|
||||
congr 1
|
||||
have q : mergeSort (enumLE le) (enumFrom 1 l) ~ l₁ ++ l₂ :=
|
||||
(mergeSort_perm (enumLE le) (enumFrom 1 l)).trans
|
||||
(p.symm.trans perm_middle).cons_inv
|
||||
apply Perm.eq_of_sorted (le := enumLE le)
|
||||
· rintro ⟨i, a⟩ ⟨j, b⟩ ha hb
|
||||
simp only [mem_mergeSort] at ha
|
||||
simp only [← q.mem_iff, mem_mergeSort] at hb
|
||||
simp only [enumLE]
|
||||
simp only [Bool.if_false_right, Bool.and_eq_true, Prod.mk.injEq, and_imp]
|
||||
intro ab h ba h'
|
||||
simp only [Bool.decide_eq_true] at ba
|
||||
replace h : i ≤ j := by simpa [ab, ba] using h
|
||||
replace h' : j ≤ i := by simpa [ab, ba] using h'
|
||||
cases Nat.le_antisymm h h'
|
||||
constructor
|
||||
· rfl
|
||||
· have := mem_enumFrom ha
|
||||
have := mem_enumFrom hb
|
||||
simp_all
|
||||
· exact mergeSort_sorted (enumLE_trans trans) (enumLE_total total) ..
|
||||
· exact s.sublist ((sublist_cons_self (0, a) l₂).append_left l₁)
|
||||
· exact q
|
||||
· intro b m
|
||||
simp only [mem_map, Prod.exists, exists_eq_right] at m
|
||||
obtain ⟨j, m⟩ := m
|
||||
replace p := p.map (·.1)
|
||||
have nd' := nd.perm p.symm
|
||||
rw [map_append] at nd'
|
||||
have j0 := nd'.rel_of_mem_append
|
||||
(mem_map_of_mem (·.1) m) (mem_map_of_mem _ (mem_cons_self _ _))
|
||||
simp only [ne_eq] at j0
|
||||
have r := s.rel_of_mem_append m (mem_cons_self _ _)
|
||||
simp_all [enumLE]
|
||||
|
||||
/--
|
||||
Another statement of stability of merge sort.
|
||||
If `c` is a sorted sublist of `l`,
|
||||
then `c` is still a sublist of `mergeSort le l`.
|
||||
-/
|
||||
theorem mergeSort_stable
|
||||
(trans : ∀ (a b c : α), le a b → le b c → le a c)
|
||||
(total : ∀ (a b : α), !le a b → le b a) :
|
||||
∀ {c : List α} (_ : c.Pairwise le) (_ : c <+ l),
|
||||
c <+ mergeSort le l
|
||||
| _, _, .slnil => nil_sublist _
|
||||
| c, hc, @Sublist.cons _ _ l a h => by
|
||||
obtain ⟨l₁, l₂, h₁, h₂, -⟩ := mergeSort_cons trans total a l
|
||||
rw [h₁]
|
||||
have h' := mergeSort_stable trans total hc h
|
||||
rw [h₂] at h'
|
||||
exact h'.middle a
|
||||
| _, _, @Sublist.cons₂ _ l₁ l₂ a h => by
|
||||
rename_i hc
|
||||
obtain ⟨l₃, l₄, h₁, h₂, h₃⟩ := mergeSort_cons trans total a l₂
|
||||
rw [h₁]
|
||||
have h' := mergeSort_stable trans total hc.tail h
|
||||
rw [h₂] at h'
|
||||
simp only [Bool.not_eq_true', tail_cons] at h₃ h'
|
||||
exact
|
||||
sublist_append_of_sublist_right (Sublist.cons₂ a
|
||||
((fun w => Sublist.of_sublist_append_right w h') fun b m₁ m₃ =>
|
||||
(Bool.eq_not_self true).mp ((rel_of_pairwise_cons hc m₁).symm.trans (h₃ b m₃))))
|
||||
|
||||
/--
|
||||
Another statement of stability of merge sort.
|
||||
If a pair `[a, b]` is a sublist of `l` and `le a b`,
|
||||
then `[a, b]` is still a sublist of `mergeSort le l`.
|
||||
-/
|
||||
theorem mergeSort_stable_pair
|
||||
(trans : ∀ (a b c : α), le a b → le b c → le a c)
|
||||
(total : ∀ (a b : α), !le a b → le b a)
|
||||
(hab : le a b) (h : [a, b] <+ l) : [a, b] <+ mergeSort le l :=
|
||||
mergeSort_stable trans total (pairwise_pair.mpr hab) h
|
||||
@@ -62,18 +62,18 @@ theorem subset_def {l₁ l₂ : List α} : l₁ ⊆ l₂ ↔ ∀ {a : α}, a ∈
|
||||
theorem Subset.trans {l₁ l₂ l₃ : List α} (h₁ : l₁ ⊆ l₂) (h₂ : l₂ ⊆ l₃) : l₁ ⊆ l₃ :=
|
||||
fun _ i => h₂ (h₁ i)
|
||||
|
||||
instance : Trans (Membership.mem : α → List α → Prop) Subset Membership.mem :=
|
||||
⟨fun h₁ h₂ => h₂ h₁⟩
|
||||
instance : Trans (fun l₁ l₂ => Subset l₂ l₁) (Membership.mem : List α → α → Prop) Membership.mem :=
|
||||
⟨fun h₁ h₂ => h₁ h₂⟩
|
||||
|
||||
instance : Trans (Subset : List α → List α → Prop) Subset Subset :=
|
||||
⟨Subset.trans⟩
|
||||
|
||||
@[simp] theorem subset_cons_self (a : α) (l : List α) : l ⊆ a :: l := fun _ => Mem.tail _
|
||||
theorem subset_cons_self (a : α) (l : List α) : l ⊆ a :: l := fun _ => Mem.tail _
|
||||
|
||||
theorem subset_of_cons_subset {a : α} {l₁ l₂ : List α} : a :: l₁ ⊆ l₂ → l₁ ⊆ l₂ :=
|
||||
fun s _ i => s (mem_cons_of_mem _ i)
|
||||
|
||||
theorem subset_cons_of_subset (a : α) {l₁ l₂ : List α} : l₁ ⊆ l₂ → l₁ ⊆ a :: l₂ :=
|
||||
@[simp] theorem subset_cons_of_subset (a : α) {l₁ l₂ : List α} : l₁ ⊆ l₂ → l₁ ⊆ a :: l₂ :=
|
||||
fun s _ i => .tail _ (s i)
|
||||
|
||||
theorem cons_subset_cons {l₁ l₂ : List α} (a : α) (s : l₁ ⊆ l₂) : a :: l₁ ⊆ a :: l₂ :=
|
||||
@@ -100,14 +100,14 @@ theorem filterMap_subset {l₁ l₂ : List α} (f : α → Option β) (H : l₁
|
||||
rintro ⟨a, h, w⟩
|
||||
exact ⟨a, H h, w⟩
|
||||
|
||||
@[simp] theorem subset_append_left (l₁ l₂ : List α) : l₁ ⊆ l₁ ++ l₂ := fun _ => mem_append_left _
|
||||
theorem subset_append_left (l₁ l₂ : List α) : l₁ ⊆ l₁ ++ l₂ := fun _ => mem_append_left _
|
||||
|
||||
@[simp] theorem subset_append_right (l₁ l₂ : List α) : l₂ ⊆ l₁ ++ l₂ := fun _ => mem_append_right _
|
||||
theorem subset_append_right (l₁ l₂ : List α) : l₂ ⊆ l₁ ++ l₂ := fun _ => mem_append_right _
|
||||
|
||||
theorem subset_append_of_subset_left (l₂ : List α) : l ⊆ l₁ → l ⊆ l₁ ++ l₂ :=
|
||||
@[simp] theorem subset_append_of_subset_left (l₂ : List α) : l ⊆ l₁ → l ⊆ l₁ ++ l₂ :=
|
||||
fun s => Subset.trans s <| subset_append_left _ _
|
||||
|
||||
theorem subset_append_of_subset_right (l₁ : List α) : l ⊆ l₂ → l ⊆ l₁ ++ l₂ :=
|
||||
@[simp] theorem subset_append_of_subset_right (l₁ : List α) : l ⊆ l₂ → l ⊆ l₁ ++ l₂ :=
|
||||
fun s => Subset.trans s <| subset_append_right _ _
|
||||
|
||||
@[simp] theorem append_subset {l₁ l₂ l : List α} :
|
||||
@@ -155,7 +155,9 @@ theorem Sublist.trans {l₁ l₂ l₃ : List α} (h₁ : l₁ <+ l₂) (h₂ : l
|
||||
|
||||
instance : Trans (@Sublist α) Sublist Sublist := ⟨Sublist.trans⟩
|
||||
|
||||
@[simp] theorem sublist_cons_self (a : α) (l : List α) : l <+ a :: l := (Sublist.refl l).cons _
|
||||
attribute [simp] Sublist.cons
|
||||
|
||||
theorem sublist_cons_self (a : α) (l : List α) : l <+ a :: l := (Sublist.refl l).cons _
|
||||
|
||||
theorem sublist_of_cons_sublist : a :: l₁ <+ l₂ → l₁ <+ l₂ :=
|
||||
(sublist_cons_self a l₁).trans
|
||||
@@ -183,14 +185,20 @@ theorem Sublist.subset : l₁ <+ l₂ → l₁ ⊆ l₂
|
||||
protected theorem Sublist.mem (hx : a ∈ l₁) (hl : l₁ <+ l₂) : a ∈ l₂ :=
|
||||
hl.subset hx
|
||||
|
||||
theorem Sublist.head_mem (s : ys <+ xs) (h) : ys.head h ∈ xs :=
|
||||
s.mem (List.head_mem h)
|
||||
|
||||
theorem Sublist.getLast_mem (s : ys <+ xs) (h) : ys.getLast h ∈ xs :=
|
||||
s.mem (List.getLast_mem h)
|
||||
|
||||
instance : Trans (@Sublist α) Subset Subset :=
|
||||
⟨fun h₁ h₂ => trans h₁.subset h₂⟩
|
||||
|
||||
instance : Trans Subset (@Sublist α) Subset :=
|
||||
⟨fun h₁ h₂ => trans h₁ h₂.subset⟩
|
||||
|
||||
instance : Trans (Membership.mem : α → List α → Prop) Sublist Membership.mem :=
|
||||
⟨fun h₁ h₂ => h₂.subset h₁⟩
|
||||
instance : Trans (fun l₁ l₂ => Sublist l₂ l₁) (Membership.mem : List α → α → Prop) Membership.mem :=
|
||||
⟨fun h₁ h₂ => h₁.subset h₂⟩
|
||||
|
||||
theorem mem_of_cons_sublist {a : α} {l₁ l₂ : List α} (s : a :: l₁ <+ l₂) : a ∈ l₂ :=
|
||||
(cons_subset.1 s.subset).1
|
||||
@@ -244,6 +252,12 @@ protected theorem Sublist.filterMap (f : α → Option β) (s : l₁ <+ l₂) :
|
||||
protected theorem Sublist.filter (p : α → Bool) {l₁ l₂} (s : l₁ <+ l₂) : filter p l₁ <+ filter p l₂ := by
|
||||
rw [← filterMap_eq_filter]; apply s.filterMap
|
||||
|
||||
theorem head_filter_mem (xs : List α) (p : α → Bool) (h) : (xs.filter p).head h ∈ xs :=
|
||||
(filter_sublist xs).head_mem h
|
||||
|
||||
theorem getLast_filter_mem (xs : List α) (p : α → Bool) (h) : (xs.filter p).getLast h ∈ xs :=
|
||||
(filter_sublist xs).getLast_mem h
|
||||
|
||||
theorem sublist_filterMap_iff {l₁ : List β} {f : α → Option β} :
|
||||
l₁ <+ l₂.filterMap f ↔ ∃ l', l' <+ l₂ ∧ l₁ = l'.filterMap f := by
|
||||
induction l₂ generalizing l₁ with
|
||||
@@ -286,11 +300,11 @@ theorem sublist_filter_iff {l₁ : List α} {p : α → Bool} :
|
||||
l₁ <+ l₂.filter p ↔ ∃ l', l' <+ l₂ ∧ l₁ = l'.filter p := by
|
||||
simp only [← filterMap_eq_filter, sublist_filterMap_iff]
|
||||
|
||||
@[simp] theorem sublist_append_left : ∀ l₁ l₂ : List α, l₁ <+ l₁ ++ l₂
|
||||
theorem sublist_append_left : ∀ l₁ l₂ : List α, l₁ <+ l₁ ++ l₂
|
||||
| [], _ => nil_sublist _
|
||||
| _ :: l₁, l₂ => (sublist_append_left l₁ l₂).cons₂ _
|
||||
|
||||
@[simp] theorem sublist_append_right : ∀ l₁ l₂ : List α, l₂ <+ l₁ ++ l₂
|
||||
theorem sublist_append_right : ∀ l₁ l₂ : List α, l₂ <+ l₁ ++ l₂
|
||||
| [], _ => Sublist.refl _
|
||||
| _ :: l₁, l₂ => (sublist_append_right l₁ l₂).cons _
|
||||
|
||||
@@ -299,10 +313,10 @@ theorem sublist_filter_iff {l₁ : List α} {p : α → Bool} :
|
||||
obtain ⟨_, _, rfl⟩ := append_of_mem h
|
||||
exact ((nil_sublist _).cons₂ _).trans (sublist_append_right ..)
|
||||
|
||||
theorem sublist_append_of_sublist_left (s : l <+ l₁) : l <+ l₁ ++ l₂ :=
|
||||
@[simp] theorem sublist_append_of_sublist_left (s : l <+ l₁) : l <+ l₁ ++ l₂ :=
|
||||
s.trans <| sublist_append_left ..
|
||||
|
||||
theorem sublist_append_of_sublist_right (s : l <+ l₂) : l <+ l₁ ++ l₂ :=
|
||||
@[simp] theorem sublist_append_of_sublist_right (s : l <+ l₂) : l <+ l₁ ++ l₂ :=
|
||||
s.trans <| sublist_append_right ..
|
||||
|
||||
@[simp] theorem append_sublist_append_left : ∀ l, l ++ l₁ <+ l ++ l₂ ↔ l₁ <+ l₂
|
||||
@@ -398,6 +412,27 @@ theorem append_sublist_iff {l₁ l₂ : List α} :
|
||||
· rintro ⟨r₁, r₂, rfl, h₁, h₂⟩
|
||||
exact Sublist.append h₁ h₂
|
||||
|
||||
theorem Sublist.of_sublist_append_left (w : ∀ a, a ∈ l → a ∉ l₂) (h : l <+ l₁ ++ l₂) : l <+ l₁ := by
|
||||
rw [sublist_append_iff] at h
|
||||
obtain ⟨l₁', l₂', rfl, h₁, h₂⟩ := h
|
||||
have : l₂' = [] := by
|
||||
rw [eq_nil_iff_forall_not_mem]
|
||||
exact fun x m => w x (mem_append_of_mem_right l₁' m) (h₂.mem m)
|
||||
simp_all
|
||||
|
||||
theorem Sublist.of_sublist_append_right (w : ∀ a, a ∈ l → a ∉ l₁) (h : l <+ l₁ ++ l₂) : l <+ l₂ := by
|
||||
rw [sublist_append_iff] at h
|
||||
obtain ⟨l₁', l₂', rfl, h₁, h₂⟩ := h
|
||||
have : l₁' = [] := by
|
||||
rw [eq_nil_iff_forall_not_mem]
|
||||
exact fun x m => w x (mem_append_of_mem_left l₂' m) (h₁.mem m)
|
||||
simp_all
|
||||
|
||||
theorem Sublist.middle {l : List α} (h : l <+ l₁ ++ l₂) (a : α) : l <+ l₁ ++ a :: l₂ := by
|
||||
rw [sublist_append_iff] at h
|
||||
obtain ⟨l₁', l₂', rfl, h₁, h₂⟩ := h
|
||||
exact Sublist.append h₁ (h₂.cons a)
|
||||
|
||||
theorem Sublist.reverse : l₁ <+ l₂ → l₁.reverse <+ l₂.reverse
|
||||
| .slnil => Sublist.refl _
|
||||
| .cons _ h => by rw [reverse_cons]; exact sublist_append_of_sublist_left h.reverse
|
||||
@@ -467,7 +502,7 @@ theorem sublist_join_iff {L : List (List α)} {l} :
|
||||
subst w
|
||||
exact ⟨[], by simp, fun i x => by cases x⟩
|
||||
· rintro ⟨L', rfl, h⟩
|
||||
simp only [join_nil, sublist_nil, join_eq_nil_iff]
|
||||
simp only [join_nil, sublist_nil, join_eq_nil]
|
||||
simp only [getElem?_nil, Option.getD_none, sublist_nil] at h
|
||||
exact (forall_getElem L' (· = [])).1 h
|
||||
| cons l' L ih =>
|
||||
@@ -732,7 +767,7 @@ theorem prefix_cons_iff : l₁ <+: a :: l₂ ↔ l₁ = [] ∨ ∃ t, l₁ = a :
|
||||
refine ⟨s, by simp [h']⟩
|
||||
|
||||
@[simp] theorem cons_prefix_cons : a :: l₁ <+: b :: l₂ ↔ a = b ∧ l₁ <+: l₂ := by
|
||||
simp only [prefix_cons_iff, cons.injEq, false_or]
|
||||
simp only [prefix_cons_iff, cons.injEq, false_or, List.cons_ne_nil]
|
||||
constructor
|
||||
· rintro ⟨t, ⟨rfl, rfl⟩, h⟩
|
||||
exact ⟨rfl, h⟩
|
||||
@@ -763,12 +798,12 @@ theorem infix_cons_iff : l₁ <:+: a :: l₂ ↔ l₁ <+: a :: l₂ ∨ l₁ <:+
|
||||
|
||||
theorem prefix_concat_iff {l₁ l₂ : List α} {a : α} :
|
||||
l₁ <+: l₂ ++ [a] ↔ l₁ = l₂ ++ [a] ∨ l₁ <+: l₂ := by
|
||||
simp only [← concat_eq_append, ← reverse_suffix, reverse_concat, suffix_cons_iff]
|
||||
simp only [← reverse_suffix, reverse_concat, suffix_cons_iff]
|
||||
simp only [concat_eq_append, ← reverse_concat, reverse_eq_iff, reverse_reverse]
|
||||
|
||||
theorem suffix_concat_iff {l₁ l₂ : List α} {a : α} :
|
||||
l₁ <:+ l₂ ++ [a] ↔ l₁ = [] ∨ ∃ t, l₁ = t ++ [a] ∧ t <:+ l₂ := by
|
||||
rw [← reverse_prefix, ← concat_eq_append, reverse_concat, prefix_cons_iff]
|
||||
rw [← reverse_prefix, reverse_concat, prefix_cons_iff]
|
||||
simp only [reverse_eq_nil_iff]
|
||||
apply or_congr_right
|
||||
constructor
|
||||
@@ -779,7 +814,7 @@ theorem suffix_concat_iff {l₁ l₂ : List α} {a : α} :
|
||||
|
||||
theorem infix_concat_iff {l₁ l₂ : List α} {a : α} :
|
||||
l₁ <:+: l₂ ++ [a] ↔ l₁ <:+ l₂ ++ [a] ∨ l₁ <:+: l₂ := by
|
||||
rw [← reverse_infix, ← concat_eq_append, reverse_concat, infix_cons_iff, reverse_infix,
|
||||
rw [← reverse_infix, reverse_concat, infix_cons_iff, reverse_infix,
|
||||
← reverse_prefix, reverse_concat]
|
||||
|
||||
theorem isPrefix_iff : l₁ <+: l₂ ↔ ∀ i (h : i < l₁.length), l₂[i]? = some l₁[i] := by
|
||||
@@ -886,7 +921,6 @@ theorem infix_of_mem_join : ∀ {L : List (List α)}, l ∈ L → l <:+: join L
|
||||
theorem prefix_append_right_inj (l) : l ++ l₁ <+: l ++ l₂ ↔ l₁ <+: l₂ :=
|
||||
exists_congr fun r => by rw [append_assoc, append_right_inj]
|
||||
|
||||
@[simp]
|
||||
theorem prefix_cons_inj (a) : a :: l₁ <+: a :: l₂ ↔ l₁ <+: l₂ :=
|
||||
prefix_append_right_inj [a]
|
||||
|
||||
|
||||
@@ -20,6 +20,11 @@ Further results on `List.take` and `List.drop`, which rely on stronger automatio
|
||||
are given in `Init.Data.List.TakeDrop`.
|
||||
-/
|
||||
|
||||
theorem take_cons {l : List α} (h : 0 < n) : take n (a :: l) = a :: take (n - 1) l := by
|
||||
cases n with
|
||||
| zero => exact absurd h (Nat.lt_irrefl _)
|
||||
| succ n => rfl
|
||||
|
||||
@[simp]
|
||||
theorem drop_one : ∀ l : List α, drop 1 l = tail l
|
||||
| [] | _ :: _ => rfl
|
||||
@@ -74,7 +79,7 @@ theorem drop_eq_get_cons {n} {l : List α} (h) : drop n l = get l ⟨n, h⟩ ::
|
||||
simp [drop_eq_getElem_cons]
|
||||
|
||||
@[simp]
|
||||
theorem getElem?_take {l : List α} {n m : Nat} (h : m < n) : (l.take n)[m]? = l[m]? := by
|
||||
theorem getElem?_take_of_lt {l : List α} {n m : Nat} (h : m < n) : (l.take n)[m]? = l[m]? := by
|
||||
induction n generalizing l m with
|
||||
| zero =>
|
||||
exact absurd h (Nat.not_lt_of_le m.zero_le)
|
||||
@@ -86,13 +91,11 @@ theorem getElem?_take {l : List α} {n m : Nat} (h : m < n) : (l.take n)[m]? = l
|
||||
· simp
|
||||
· simpa using hn (Nat.lt_of_succ_lt_succ h)
|
||||
|
||||
@[deprecated getElem?_take (since := "2024-06-12")]
|
||||
@[deprecated getElem?_take_of_lt (since := "2024-06-12")]
|
||||
theorem get?_take {l : List α} {n m : Nat} (h : m < n) : (l.take n).get? m = l.get? m := by
|
||||
simp [getElem?_take, h]
|
||||
simp [getElem?_take_of_lt, h]
|
||||
|
||||
@[simp]
|
||||
theorem getElem?_take_of_succ {l : List α} {n : Nat} : (l.take (n + 1))[n]? = l[n]? :=
|
||||
getElem?_take (Nat.lt_succ_self n)
|
||||
theorem getElem?_take_of_succ {l : List α} {n : Nat} : (l.take (n + 1))[n]? = l[n]? := by simp
|
||||
|
||||
@[simp] theorem drop_drop (n : Nat) : ∀ (m) (l : List α), drop n (drop m l) = drop (n + m) l
|
||||
| m, [] => by simp
|
||||
@@ -433,6 +436,12 @@ theorem take_takeWhile {l : List α} (p : α → Bool) n :
|
||||
| nil => rfl
|
||||
| cons h t ih => by_cases p h <;> simp_all
|
||||
|
||||
/-! ### splitAt -/
|
||||
|
||||
@[simp] theorem splitAt_eq (n : Nat) (l : List α) : splitAt n l = (l.take n, l.drop n) := by
|
||||
rw [splitAt, splitAt_go, reverse_nil, nil_append]
|
||||
split <;> simp_all [take_of_length_le, drop_of_length_le]
|
||||
|
||||
/-! ### rotateLeft -/
|
||||
|
||||
@[simp] theorem rotateLeft_zero (l : List α) : rotateLeft l 0 = l := by
|
||||
|
||||
@@ -78,13 +78,13 @@ theorem map_prod_left_eq_zip {l : List α} (f : α → β) :
|
||||
(l.map fun x => (x, f x)) = l.zip (l.map f) := by
|
||||
rw [← zip_map']
|
||||
congr
|
||||
exact map_id _
|
||||
simp
|
||||
|
||||
theorem map_prod_right_eq_zip {l : List α} (f : α → β) :
|
||||
(l.map fun x => (f x, x)) = (l.map f).zip l := by
|
||||
rw [← zip_map']
|
||||
congr
|
||||
exact map_id _
|
||||
simp
|
||||
|
||||
/-- See also `List.zip_replicate` in `Init.Data.List.TakeDrop` for a generalization with different lengths. -/
|
||||
@[simp] theorem zip_replicate' {a : α} {b : β} {n : Nat} :
|
||||
|
||||
@@ -158,7 +158,7 @@ theorem add_one (n : Nat) : n + 1 = succ n :=
|
||||
rfl
|
||||
|
||||
@[simp] theorem add_one_ne_zero (n : Nat) : n + 1 ≠ 0 := nofun
|
||||
@[simp] theorem zero_ne_add_one (n : Nat) : 0 ≠ n + 1 := nofun
|
||||
theorem zero_ne_add_one (n : Nat) : 0 ≠ n + 1 := by simp
|
||||
|
||||
protected theorem add_comm : ∀ (n m : Nat), n + m = m + n
|
||||
| n, 0 => Eq.symm (Nat.zero_add n)
|
||||
@@ -779,6 +779,11 @@ theorem pow_le_pow_of_le_right {n : Nat} (hx : n > 0) {i : Nat} : ∀ {j}, i ≤
|
||||
theorem pos_pow_of_pos {n : Nat} (m : Nat) (h : 0 < n) : 0 < n^m :=
|
||||
pow_le_pow_of_le_right h (Nat.zero_le _)
|
||||
|
||||
@[simp] theorem zero_pow_of_pos (n : Nat) (h : 0 < n) : 0 ^ n = 0 := by
|
||||
cases n with
|
||||
| zero => cases h
|
||||
| succ n => simp [Nat.pow_succ]
|
||||
|
||||
/-! # min/max -/
|
||||
|
||||
/--
|
||||
@@ -887,7 +892,7 @@ theorem sub_succ_lt_self (a i : Nat) (h : i < a) : a - (i + 1) < a - i := by
|
||||
|
||||
theorem sub_ne_zero_of_lt : {a b : Nat} → a < b → b - a ≠ 0
|
||||
| 0, 0, h => absurd h (Nat.lt_irrefl 0)
|
||||
| 0, succ b, _ => by simp only [Nat.sub_zero, ne_eq, not_false_eq_true]
|
||||
| 0, succ b, _ => by simp only [Nat.sub_zero, ne_eq, not_false_eq_true, Nat.succ_ne_zero]
|
||||
| succ a, 0, h => absurd h (Nat.not_lt_zero a.succ)
|
||||
| succ a, succ b, h => by rw [Nat.succ_sub_succ]; exact sub_ne_zero_of_lt (Nat.lt_of_succ_lt_succ h)
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ An induction principal that works on divison by two.
|
||||
-/
|
||||
noncomputable def div2Induction {motive : Nat → Sort u}
|
||||
(n : Nat) (ind : ∀(n : Nat), (n > 0 → motive (n/2)) → motive n) : motive n := by
|
||||
induction n using Nat.strongInductionOn with
|
||||
induction n using Nat.strongRecOn with
|
||||
| ind n hyp =>
|
||||
apply ind
|
||||
intro n_pos
|
||||
@@ -86,6 +86,12 @@ noncomputable def div2Induction {motive : Nat → Sort u}
|
||||
@[simp] theorem testBit_zero (x : Nat) : testBit x 0 = decide (x % 2 = 1) := by
|
||||
cases mod_two_eq_zero_or_one x with | _ p => simp [testBit, p]
|
||||
|
||||
theorem mod_two_eq_one_iff_testBit_zero : (x % 2 = 1) ↔ x.testBit 0 = true := by
|
||||
cases mod_two_eq_zero_or_one x <;> simp_all
|
||||
|
||||
theorem mod_two_eq_zero_iff_testBit_zero : (x % 2 = 0) ↔ x.testBit 0 = false := by
|
||||
cases mod_two_eq_zero_or_one x <;> simp_all
|
||||
|
||||
theorem testBit_succ (x i : Nat) : testBit x (succ i) = testBit (x/2) i := by
|
||||
unfold testBit
|
||||
simp [shiftRight_succ_inside]
|
||||
@@ -94,6 +100,9 @@ theorem testBit_succ (x i : Nat) : testBit x (succ i) = testBit (x/2) i := by
|
||||
unfold testBit
|
||||
simp [shiftRight_succ_inside]
|
||||
|
||||
theorem testBit_div_two (x i : Nat) : testBit (x / 2) i = testBit x (i + 1) := by
|
||||
simp
|
||||
|
||||
theorem testBit_to_div_mod {x : Nat} : testBit x i = decide (x / 2^i % 2 = 1) := by
|
||||
induction i generalizing x with
|
||||
| zero =>
|
||||
@@ -114,7 +123,7 @@ theorem ne_zero_implies_bit_true {x : Nat} (xnz : x ≠ 0) : ∃ i, testBit x i
|
||||
match mod_two_eq_zero_or_one x with
|
||||
| Or.inl mod2_eq =>
|
||||
rw [←div_add_mod x 2] at xnz
|
||||
simp only [mod2_eq, ne_eq, Nat.mul_eq_zero, Nat.add_zero, false_or] at xnz
|
||||
simp only [mod2_eq, ne_eq, Nat.mul_eq_zero, Nat.add_zero, false_or, reduceCtorEq] at xnz
|
||||
have ⟨d, dif⟩ := hyp x_pos xnz
|
||||
apply Exists.intro (d+1)
|
||||
simp_all
|
||||
@@ -200,7 +209,7 @@ theorem lt_pow_two_of_testBit (x : Nat) (p : ∀i, i ≥ n → testBit x i = fal
|
||||
have x_ge_n := Nat.ge_of_not_lt not_lt
|
||||
have ⟨i, ⟨i_ge_n, test_true⟩⟩ := ge_two_pow_implies_high_bit_true x_ge_n
|
||||
have test_false := p _ i_ge_n
|
||||
simp only [test_true] at test_false
|
||||
simp [test_true] at test_false
|
||||
|
||||
private theorem succ_mod_two : succ x % 2 = 1 - x % 2 := by
|
||||
induction x with
|
||||
@@ -249,7 +258,7 @@ theorem testBit_two_pow_add_gt {i j : Nat} (j_lt_i : j < i) (x : Nat) :
|
||||
|
||||
@[simp] theorem testBit_mod_two_pow (x j i : Nat) :
|
||||
testBit (x % 2^j) i = (decide (i < j) && testBit x i) := by
|
||||
induction x using Nat.strongInductionOn generalizing j i with
|
||||
induction x using Nat.strongRecOn generalizing j i with
|
||||
| ind x hyp =>
|
||||
rw [mod_eq]
|
||||
rcases Nat.lt_or_ge x (2^j) with x_lt_j | x_ge_j
|
||||
@@ -315,12 +324,44 @@ theorem testBit_one_eq_true_iff_self_eq_zero {i : Nat} :
|
||||
Nat.testBit 1 i = true ↔ i = 0 := by
|
||||
cases i <;> simp
|
||||
|
||||
theorem testBit_two_pow {n m : Nat} : testBit (2 ^ n) m = decide (n = m) := by
|
||||
rw [testBit, shiftRight_eq_div_pow]
|
||||
by_cases h : n = m
|
||||
· simp [h, Nat.div_self (Nat.pow_pos Nat.zero_lt_two)]
|
||||
· simp only [h]
|
||||
cases Nat.lt_or_lt_of_ne h
|
||||
· rw [div_eq_of_lt (Nat.pow_lt_pow_of_lt (by omega) (by omega))]
|
||||
simp
|
||||
· rw [Nat.pow_div _ Nat.two_pos,
|
||||
← Nat.sub_add_cancel (succ_le_of_lt <| Nat.sub_pos_of_lt (by omega))]
|
||||
simp [Nat.pow_succ, and_one_is_mod, mul_mod_left]
|
||||
omega
|
||||
|
||||
@[simp]
|
||||
theorem testBit_two_pow_self {n : Nat} : testBit (2 ^ n) n = true := by
|
||||
simp [testBit_two_pow]
|
||||
|
||||
@[simp]
|
||||
theorem testBit_two_pow_of_ne {n m : Nat} (hm : n ≠ m) : testBit (2 ^ n) m = false := by
|
||||
simp [testBit_two_pow]
|
||||
omega
|
||||
|
||||
@[simp] theorem two_pow_sub_one_mod_two : (2 ^ n - 1) % 2 = 1 % 2 ^ n := by
|
||||
cases n with
|
||||
| zero => simp
|
||||
| succ n =>
|
||||
rw [mod_eq_of_lt (a := 1) (Nat.one_lt_two_pow (by omega)), mod_two_eq_one_iff_testBit_zero, testBit_two_pow_sub_one ]
|
||||
simp only [zero_lt_succ, decide_True]
|
||||
|
||||
@[simp] theorem mod_two_pos_mod_two_eq_one : x % 2 ^ j % 2 = 1 ↔ (0 < j) ∧ x % 2 = 1 := by
|
||||
rw [mod_two_eq_one_iff_testBit_zero, testBit_mod_two_pow]
|
||||
simp
|
||||
|
||||
/-! ### bitwise -/
|
||||
|
||||
theorem testBit_bitwise
|
||||
(false_false_axiom : f false false = false) (x y i : Nat)
|
||||
: (bitwise f x y).testBit i = f (x.testBit i) (y.testBit i) := by
|
||||
induction i using Nat.strongInductionOn generalizing x y with
|
||||
theorem testBit_bitwise (false_false_axiom : f false false = false) (x y i : Nat) :
|
||||
(bitwise f x y).testBit i = f (x.testBit i) (y.testBit i) := by
|
||||
induction i using Nat.strongRecOn generalizing x y with
|
||||
| ind i hyp =>
|
||||
unfold bitwise
|
||||
if x_zero : x = 0 then
|
||||
@@ -417,6 +458,11 @@ theorem and_pow_two_identity {x : Nat} (lt : x < 2^n) : x &&& 2^n-1 = x := by
|
||||
rw [and_pow_two_is_mod]
|
||||
apply Nat.mod_eq_of_lt lt
|
||||
|
||||
@[simp] theorem and_mod_two_eq_one : (a &&& b) % 2 = 1 ↔ a % 2 = 1 ∧ b % 2 = 1 := by
|
||||
simp only [mod_two_eq_one_iff_testBit_zero]
|
||||
rw [testBit_and]
|
||||
simp
|
||||
|
||||
/-! ### lor -/
|
||||
|
||||
@[simp] theorem zero_or (x : Nat) : 0 ||| x = x := by
|
||||
@@ -435,6 +481,11 @@ theorem and_pow_two_identity {x : Nat} (lt : x < 2^n) : x &&& 2^n-1 = x := by
|
||||
theorem or_lt_two_pow {x y n : Nat} (left : x < 2^n) (right : y < 2^n) : x ||| y < 2^n :=
|
||||
bitwise_lt_two_pow left right
|
||||
|
||||
@[simp] theorem or_mod_two_eq_one : (a ||| b) % 2 = 1 ↔ a % 2 = 1 ∨ b % 2 = 1 := by
|
||||
simp only [mod_two_eq_one_iff_testBit_zero]
|
||||
rw [testBit_or]
|
||||
simp
|
||||
|
||||
/-! ### xor -/
|
||||
|
||||
@[simp] theorem testBit_xor (x y i : Nat) :
|
||||
@@ -444,6 +495,19 @@ theorem or_lt_two_pow {x y n : Nat} (left : x < 2^n) (right : y < 2^n) : x ||| y
|
||||
theorem xor_lt_two_pow {x y n : Nat} (left : x < 2^n) (right : y < 2^n) : x ^^^ y < 2^n :=
|
||||
bitwise_lt_two_pow left right
|
||||
|
||||
theorem and_xor_distrib_right {a b c : Nat} : (a ^^^ b) &&& c = (a &&& c) ^^^ (b &&& c) := by
|
||||
apply Nat.eq_of_testBit_eq
|
||||
simp [Bool.and_xor_distrib_right]
|
||||
|
||||
theorem and_xor_distrib_left {a b c : Nat} : a &&& (b ^^^ c) = (a &&& b) ^^^ (a &&& c) := by
|
||||
apply Nat.eq_of_testBit_eq
|
||||
simp [Bool.and_xor_distrib_left]
|
||||
|
||||
@[simp] theorem xor_mod_two_eq_one : ((a ^^^ b) % 2 = 1) ↔ ¬ ((a % 2 = 1) ↔ (b % 2 = 1)) := by
|
||||
simp only [mod_two_eq_one_iff_testBit_zero]
|
||||
rw [testBit_xor]
|
||||
simp
|
||||
|
||||
/-! ### Arithmetic -/
|
||||
|
||||
theorem testBit_mul_pow_two_add (a : Nat) {b i : Nat} (b_lt : b < 2^i) (j : Nat) :
|
||||
@@ -505,6 +569,15 @@ theorem mul_add_lt_is_or {b : Nat} (b_lt : b < 2^i) (a : Nat) : 2^i * a + b = 2^
|
||||
@[simp] theorem testBit_shiftRight (x : Nat) : testBit (x >>> i) j = testBit x (i+j) := by
|
||||
simp [testBit, ←shiftRight_add]
|
||||
|
||||
@[simp] theorem shiftLeft_mod_two_eq_one : x <<< i % 2 = 1 ↔ i = 0 ∧ x % 2 = 1 := by
|
||||
rw [mod_two_eq_one_iff_testBit_zero, testBit_shiftLeft]
|
||||
simp
|
||||
|
||||
@[simp] theorem decide_shiftRight_mod_two_eq_one :
|
||||
decide (x >>> i % 2 = 1) = x.testBit i := by
|
||||
simp only [testBit, one_and_eq_mod_two, mod_two_bne_zero]
|
||||
exact (Bool.beq_eq_decide_eq _ _).symm
|
||||
|
||||
/-! ### le -/
|
||||
|
||||
theorem le_of_testBit {n m : Nat} (h : ∀ i, n.testBit i = true → m.testBit i = true) : n ≤ m := by
|
||||
|
||||
@@ -48,7 +48,7 @@ def div.inductionOn.{u}
|
||||
decreasing_by apply div_rec_lemma; assumption
|
||||
|
||||
theorem div_le_self (n k : Nat) : n / k ≤ n := by
|
||||
induction n using Nat.strongInductionOn with
|
||||
induction n using Nat.strongRecOn with
|
||||
| ind n ih =>
|
||||
rw [div_eq]
|
||||
-- Note: manual split to avoid Classical.em which is not yet defined
|
||||
@@ -221,7 +221,7 @@ theorem le_div_iff_mul_le (k0 : 0 < k) : x ≤ y / k ↔ x * k ≤ y := by
|
||||
induction y, k using mod.inductionOn generalizing x with
|
||||
(rw [div_eq]; simp [h]; cases x with | zero => simp [zero_le] | succ x => ?_)
|
||||
| base y k h =>
|
||||
simp only [add_one, succ_mul, false_iff, Nat.not_le]
|
||||
simp only [add_one, succ_mul, false_iff, Nat.not_le, Nat.succ_ne_zero]
|
||||
refine Nat.lt_of_lt_of_le ?_ (Nat.le_add_left ..)
|
||||
exact Nat.not_le.1 fun h' => h ⟨k0, h'⟩
|
||||
| ind y k h IH =>
|
||||
@@ -334,7 +334,7 @@ theorem mul_mod_mul_left (z x y : Nat) : (z * x) % (z * y) = z * (x % y) :=
|
||||
else if z0 : z = 0 then by
|
||||
rw [z0, Nat.zero_mul, Nat.zero_mul, Nat.zero_mul, mod_zero]
|
||||
else by
|
||||
induction x using Nat.strongInductionOn with
|
||||
induction x using Nat.strongRecOn with
|
||||
| _ n IH =>
|
||||
have y0 : y > 0 := Nat.pos_of_ne_zero y0
|
||||
have z0 : z > 0 := Nat.pos_of_ne_zero z0
|
||||
|
||||
@@ -75,7 +75,7 @@ theorem gcd_rec (m n : Nat) : gcd m n = gcd (n % m) m :=
|
||||
|
||||
@[elab_as_elim] theorem gcd.induction {P : Nat → Nat → Prop} (m n : Nat)
|
||||
(H0 : ∀n, P 0 n) (H1 : ∀ m n, 0 < m → P (n % m) m → P m n) : P m n :=
|
||||
Nat.strongInductionOn (motive := fun m => ∀ n, P m n) m
|
||||
Nat.strongRecOn (motive := fun m => ∀ n, P m n) m
|
||||
(fun
|
||||
| 0, _ => H0
|
||||
| _+1, IH => fun _ => H1 _ _ (succ_pos _) (IH _ (mod_lt _ (succ_pos _)) _) )
|
||||
|
||||
@@ -27,6 +27,11 @@ namespace Nat
|
||||
⟨fun ⟨n, h, w⟩ => by cases n with | zero => simp at h | succ n => exact ⟨n, w⟩,
|
||||
fun ⟨n, w⟩ => ⟨n + 1, by simp, w⟩⟩
|
||||
|
||||
@[simp] theorem exists_eq_add_one : (∃ n, a = n + 1) ↔ 0 < a :=
|
||||
⟨fun ⟨n, h⟩ => by omega, fun h => ⟨a - 1, by omega⟩⟩
|
||||
@[simp] theorem exists_add_one_eq : (∃ n, n + 1 = a) ↔ 0 < a :=
|
||||
⟨fun ⟨n, h⟩ => by omega, fun h => ⟨a - 1, by omega⟩⟩
|
||||
|
||||
/-! ## add -/
|
||||
|
||||
protected theorem add_add_add_comm (a b c d : Nat) : (a + b) + (c + d) = (a + c) + (b + d) := by
|
||||
@@ -50,6 +55,11 @@ protected theorem add_eq_zero_iff : n + m = 0 ↔ n = 0 ∧ m = 0 :=
|
||||
@[simp] protected theorem add_right_cancel_iff {n : Nat} : m + n = k + n ↔ m = k :=
|
||||
⟨Nat.add_right_cancel, fun | rfl => rfl⟩
|
||||
|
||||
@[simp] protected theorem add_left_eq_self {a b : Nat} : a + b = b ↔ a = 0 := by omega
|
||||
@[simp] protected theorem add_right_eq_self {a b : Nat} : a + b = a ↔ b = 0 := by omega
|
||||
@[simp] protected theorem self_eq_add_right {a b : Nat} : a = a + b ↔ b = 0 := by omega
|
||||
@[simp] protected theorem self_eq_add_left {a b : Nat} : a = b + a ↔ b = 0 := by omega
|
||||
|
||||
@[simp] protected theorem add_le_add_iff_left {n : Nat} : n + m ≤ n + k ↔ m ≤ k :=
|
||||
⟨Nat.le_of_add_le_add_left, fun h => Nat.add_le_add_left h _⟩
|
||||
|
||||
@@ -147,17 +157,9 @@ protected theorem sub_le_iff_le_add' {a b c : Nat} : a - b ≤ c ↔ a ≤ b + c
|
||||
protected theorem le_sub_iff_add_le {n : Nat} (h : k ≤ m) : n ≤ m - k ↔ n + k ≤ m :=
|
||||
⟨Nat.add_le_of_le_sub h, Nat.le_sub_of_add_le⟩
|
||||
|
||||
@[deprecated Nat.le_sub_iff_add_le (since := "2024-02-19")]
|
||||
protected theorem add_le_to_le_sub (n : Nat) (h : m ≤ k) : n + m ≤ k ↔ n ≤ k - m :=
|
||||
(Nat.le_sub_iff_add_le h).symm
|
||||
|
||||
protected theorem add_le_of_le_sub' {n k m : Nat} (h : m ≤ k) : n ≤ k - m → m + n ≤ k :=
|
||||
Nat.add_comm .. ▸ Nat.add_le_of_le_sub h
|
||||
|
||||
@[deprecated Nat.add_le_of_le_sub' (since := "2024-02-19")]
|
||||
protected theorem add_le_of_le_sub_left {n k m : Nat} (h : m ≤ k) : n ≤ k - m → m + n ≤ k :=
|
||||
Nat.add_le_of_le_sub' h
|
||||
|
||||
protected theorem le_sub_of_add_le' {n k m : Nat} : m + n ≤ k → n ≤ k - m :=
|
||||
Nat.add_comm .. ▸ Nat.le_sub_of_add_le
|
||||
|
||||
@@ -419,14 +421,6 @@ protected theorem mul_min_mul_left (a b c : Nat) : min (a * b) (a * c) = a * min
|
||||
|
||||
/-! ### mul -/
|
||||
|
||||
@[deprecated Nat.mul_le_mul_left (since := "2024-02-19")]
|
||||
protected theorem mul_le_mul_of_nonneg_left {a b c : Nat} : a ≤ b → c * a ≤ c * b :=
|
||||
Nat.mul_le_mul_left c
|
||||
|
||||
@[deprecated Nat.mul_le_mul_right (since := "2024-02-19")]
|
||||
protected theorem mul_le_mul_of_nonneg_right {a b c : Nat} : a ≤ b → a * c ≤ b * c :=
|
||||
Nat.mul_le_mul_right c
|
||||
|
||||
protected theorem mul_right_comm (n m k : Nat) : n * m * k = n * k * m := by
|
||||
rw [Nat.mul_assoc, Nat.mul_comm m, ← Nat.mul_assoc]
|
||||
|
||||
@@ -539,6 +533,11 @@ theorem mod_two_eq_zero_or_one (n : Nat) : n % 2 = 0 ∨ n % 2 = 1 :=
|
||||
| 0, _ => .inl rfl
|
||||
| 1, _ => .inr rfl
|
||||
|
||||
@[simp] theorem mod_two_bne_zero : ((a % 2) != 0) = (a % 2 == 1) := by
|
||||
cases mod_two_eq_zero_or_one a <;> simp_all
|
||||
@[simp] theorem mod_two_bne_one : ((a % 2) != 1) = (a % 2 == 0) := by
|
||||
cases mod_two_eq_zero_or_one a <;> simp_all
|
||||
|
||||
theorem le_of_mod_lt {a b : Nat} (h : a % b < a) : b ≤ a :=
|
||||
Nat.not_lt.1 fun hf => (ne_of_lt h).elim (Nat.mod_eq_of_lt hf)
|
||||
|
||||
@@ -649,6 +648,16 @@ protected theorem one_le_two_pow : 1 ≤ 2 ^ n :=
|
||||
else
|
||||
Nat.le_of_lt (Nat.one_lt_two_pow h)
|
||||
|
||||
@[simp] theorem one_mod_two_pow_eq_one : 1 % 2 ^ n = 1 ↔ 0 < n := by
|
||||
cases n with
|
||||
| zero => simp
|
||||
| succ n =>
|
||||
rw [mod_eq_of_lt (a := 1) (Nat.one_lt_two_pow (by omega))]
|
||||
simp
|
||||
|
||||
@[simp] theorem one_mod_two_pow (h : 0 < n) : 1 % 2 ^ n = 1 :=
|
||||
one_mod_two_pow_eq_one.mpr h
|
||||
|
||||
protected theorem pow_pos (h : 0 < a) : 0 < a^n :=
|
||||
match n with
|
||||
| 0 => Nat.zero_lt_one
|
||||
@@ -700,6 +709,36 @@ protected theorem pow_lt_pow_iff_right {a n m : Nat} (h : 1 < a) :
|
||||
· intro w
|
||||
exact Nat.pow_lt_pow_of_lt h w
|
||||
|
||||
@[simp]
|
||||
protected theorem pow_pred_mul {x w : Nat} (h : 0 < w) :
|
||||
x ^ (w - 1) * x = x ^ w := by
|
||||
simp [← Nat.pow_succ, succ_eq_add_one, Nat.sub_add_cancel h]
|
||||
|
||||
protected theorem pow_pred_lt_pow {x w : Nat} (h₁ : 1 < x) (h₂ : 0 < w) :
|
||||
x ^ (w - 1) < x ^ w :=
|
||||
Nat.pow_lt_pow_of_lt h₁ (by omega)
|
||||
|
||||
protected theorem two_pow_pred_lt_two_pow {w : Nat} (h : 0 < w) :
|
||||
2 ^ (w - 1) < 2 ^ w :=
|
||||
Nat.pow_pred_lt_pow (by omega) h
|
||||
|
||||
@[simp]
|
||||
protected theorem two_pow_pred_add_two_pow_pred (h : 0 < w) :
|
||||
2 ^ (w - 1) + 2 ^ (w - 1) = 2 ^ w := by
|
||||
rw [← Nat.pow_pred_mul h]
|
||||
omega
|
||||
|
||||
@[simp]
|
||||
protected theorem two_pow_sub_two_pow_pred (h : 0 < w) :
|
||||
2 ^ w - 2 ^ (w - 1) = 2 ^ (w - 1) := by
|
||||
simp [← Nat.two_pow_pred_add_two_pow_pred h]
|
||||
|
||||
@[simp]
|
||||
protected theorem two_pow_pred_mod_two_pow (h : 0 < w) :
|
||||
2 ^ (w - 1) % 2 ^ w = 2 ^ (w - 1) := by
|
||||
rw [mod_eq_of_lt]
|
||||
apply Nat.pow_pred_lt_pow (by omega) h
|
||||
|
||||
/-! ### log2 -/
|
||||
|
||||
@[simp]
|
||||
|
||||
@@ -73,4 +73,10 @@ theorem mod_pow_succ {x b k : Nat} :
|
||||
x % b ^ (k + 1) = x % b ^ k + b ^ k * ((x / b ^ k) % b) := by
|
||||
rw [Nat.pow_succ, Nat.mod_mul]
|
||||
|
||||
@[simp] theorem two_pow_mod_two_eq_zero (n : Nat) : 2 ^ n % 2 = 0 ↔ 0 < n := by
|
||||
cases n <;> simp [Nat.pow_succ]
|
||||
|
||||
@[simp] theorem two_pow_mod_two_eq_one (n : Nat) : 2 ^ n % 2 = 1 ↔ n = 0 := by
|
||||
cases n <;> simp [Nat.pow_succ]
|
||||
|
||||
end Nat
|
||||
|
||||
@@ -19,7 +19,7 @@ theorem eq_of_eq_some {α : Type u} : ∀ {x y : Option α}, (∀z, x = some z
|
||||
theorem eq_none_of_isNone {α : Type u} : ∀ {o : Option α}, o.isNone → o = none
|
||||
| none, _ => rfl
|
||||
|
||||
instance : Membership α (Option α) := ⟨fun a b => b = some a⟩
|
||||
instance : Membership α (Option α) := ⟨fun b a => b = some a⟩
|
||||
|
||||
@[simp] theorem mem_def {a : α} {b : Option α} : a ∈ b ↔ b = some a := .rfl
|
||||
|
||||
|
||||
@@ -87,6 +87,9 @@ theorem eq_some_iff_get_eq : o = some a ↔ ∃ h : o.isSome, o.get h = a := by
|
||||
theorem eq_some_of_isSome : ∀ {o : Option α} (h : o.isSome), o = some (o.get h)
|
||||
| some _, _ => rfl
|
||||
|
||||
theorem isSome_iff_ne_none : o.isSome ↔ o ≠ none := by
|
||||
cases o <;> simp
|
||||
|
||||
theorem not_isSome_iff_eq_none : ¬o.isSome ↔ o = none := by
|
||||
cases o <;> simp
|
||||
|
||||
@@ -159,7 +162,7 @@ theorem map_some : f <$> some a = some (f a) := rfl
|
||||
theorem map_eq_some : f <$> x = some b ↔ ∃ a, x = some a ∧ f a = b := map_eq_some'
|
||||
|
||||
@[simp] theorem map_eq_none' : x.map f = none ↔ x = none := by
|
||||
cases x <;> simp only [map_none', map_some', eq_self_iff_true]
|
||||
cases x <;> simp [map_none', map_some', eq_self_iff_true]
|
||||
|
||||
theorem isSome_map {x : Option α} : (f <$> x).isSome = x.isSome := by
|
||||
cases x <;> simp
|
||||
@@ -178,8 +181,19 @@ theorem map_eq_bind {x : Option α} : x.map f = x.bind (some ∘ f) := by
|
||||
theorem map_congr {x : Option α} (h : ∀ a, a ∈ x → f a = g a) : x.map f = x.map g := by
|
||||
cases x <;> simp only [map_none', map_some', h, mem_def]
|
||||
|
||||
@[simp] theorem map_id' : Option.map (@id α) = id := map_id
|
||||
@[simp] theorem map_id'' {x : Option α} : (x.map fun a => a) = x := congrFun map_id x
|
||||
@[simp] theorem map_id_fun {α : Type u} : Option.map (id : α → α) = id := by
|
||||
funext; simp [map_id]
|
||||
|
||||
theorem map_id' {x : Option α} : (x.map fun a => a) = x := congrFun map_id x
|
||||
|
||||
@[simp] theorem map_id_fun' {α : Type u} : Option.map (fun (a : α) => a) = id := by
|
||||
funext; simp [map_id']
|
||||
|
||||
theorem get_map {f : α → β} {o : Option α} {h : (o.map f).isSome} :
|
||||
(o.map f).get h = f (o.get (by simpa using h)) := by
|
||||
cases o with
|
||||
| none => simp at h
|
||||
| some a => simp
|
||||
|
||||
@[simp] theorem map_map (h : β → γ) (g : α → β) (x : Option α) :
|
||||
(x.map g).map h = x.map (h ∘ g) := by
|
||||
@@ -238,6 +252,15 @@ theorem map_orElse {x y : Option α} : (x <|> y).map f = (x.map f <|> y.map f) :
|
||||
@[simp] theorem guard_eq_some [DecidablePred p] : guard p a = some b ↔ a = b ∧ p a :=
|
||||
if h : p a then by simp [Option.guard, h] else by simp [Option.guard, h]
|
||||
|
||||
@[simp] theorem guard_isSome [DecidablePred p] : (Option.guard p a).isSome ↔ p a :=
|
||||
if h : p a then by simp [Option.guard, h] else by simp [Option.guard, h]
|
||||
|
||||
@[simp] theorem guard_eq_none [DecidablePred p] : Option.guard p a = none ↔ ¬ p a :=
|
||||
if h : p a then by simp [Option.guard, h] else by simp [Option.guard, h]
|
||||
|
||||
@[simp] theorem guard_pos [DecidablePred p] (h : p a) : Option.guard p a = some a := by
|
||||
simp [Option.guard, h]
|
||||
|
||||
theorem liftOrGet_eq_or_eq {f : α → α → α} (h : ∀ a b, f a b = a ∨ f a b = b) :
|
||||
∀ o₁ o₂, liftOrGet f o₁ o₂ = o₁ ∨ liftOrGet f o₁ o₂ = o₂
|
||||
| none, none => .inl rfl
|
||||
@@ -261,7 +284,7 @@ theorem liftOrGet_eq_or_eq {f : α → α → α} (h : ∀ a b, f a b = a ∨ f
|
||||
@[simp] theorem getD_map (f : α → β) (x : α) (o : Option α) :
|
||||
(o.map f).getD (f x) = f (getD o x) := by cases o <;> rfl
|
||||
|
||||
section
|
||||
section choice
|
||||
|
||||
attribute [local instance] Classical.propDecidable
|
||||
|
||||
@@ -277,7 +300,7 @@ theorem choice_eq {α : Type _} [Subsingleton α] (a : α) : choice α = some a
|
||||
theorem choice_isSome_iff_nonempty {α : Type _} : (choice α).isSome ↔ Nonempty α :=
|
||||
⟨fun h => ⟨(choice α).get h⟩, fun h => by simp only [choice, dif_pos h, isSome_some]⟩
|
||||
|
||||
end
|
||||
end choice
|
||||
|
||||
@[simp] theorem toList_some (a : α) : (a : Option α).toList = [a] := rfl
|
||||
|
||||
@@ -298,7 +321,7 @@ theorem or_eq_bif : or o o' = bif o.isSome then o else o' := by
|
||||
@[simp] theorem or_eq_none : or o o' = none ↔ o = none ∧ o' = none := by
|
||||
cases o <;> simp
|
||||
|
||||
theorem or_eq_some : or o o' = some a ↔ o = some a ∨ (o = none ∧ o' = some a) := by
|
||||
@[simp] theorem or_eq_some : or o o' = some a ↔ o = some a ∨ (o = none ∧ o' = some a) := by
|
||||
cases o <;> simp
|
||||
|
||||
theorem or_assoc : or (or o₁ o₂) o₃ = or o₁ (or o₂ o₃) := by
|
||||
@@ -333,3 +356,43 @@ theorem or_of_isSome {o o' : Option α} (h : o.isSome) : o.or o' = o := by
|
||||
theorem or_of_isNone {o o' : Option α} (h : o.isNone) : o.or o' = o' := by
|
||||
match o, h with
|
||||
| none, _ => simp
|
||||
|
||||
section ite
|
||||
|
||||
@[simp] theorem isSome_dite {p : Prop} [Decidable p] {b : p → β} :
|
||||
(if h : p then some (b h) else none).isSome = true ↔ p := by
|
||||
split <;> simpa
|
||||
@[simp] theorem isSome_ite {p : Prop} [Decidable p] :
|
||||
(if p then some b else none).isSome = true ↔ p := by
|
||||
split <;> simpa
|
||||
@[simp] theorem isSome_dite' {p : Prop} [Decidable p] {b : ¬ p → β} :
|
||||
(if h : p then none else some (b h)).isSome = true ↔ ¬ p := by
|
||||
split <;> simpa
|
||||
@[simp] theorem isSome_ite' {p : Prop} [Decidable p] :
|
||||
(if p then none else some b).isSome = true ↔ ¬ p := by
|
||||
split <;> simpa
|
||||
|
||||
@[simp] theorem get_dite {p : Prop} [Decidable p] (b : p → β) (w) :
|
||||
(if h : p then some (b h) else none).get w = b (by simpa using w) := by
|
||||
split
|
||||
· simp
|
||||
· exfalso
|
||||
simp at w
|
||||
contradiction
|
||||
@[simp] theorem get_ite {p : Prop} [Decidable p] (h) :
|
||||
(if p then some b else none).get h = b := by
|
||||
simpa using get_dite (p := p) (fun _ => b) (by simpa using h)
|
||||
@[simp] theorem get_dite' {p : Prop} [Decidable p] (b : ¬ p → β) (w) :
|
||||
(if h : p then none else some (b h)).get w = b (by simpa using w) := by
|
||||
split
|
||||
· exfalso
|
||||
simp at w
|
||||
contradiction
|
||||
· simp
|
||||
@[simp] theorem get_ite' {p : Prop} [Decidable p] (h) :
|
||||
(if p then none else some b).get h = b := by
|
||||
simpa using get_dite' (p := p) (fun _ => b) (by simpa using h)
|
||||
|
||||
end ite
|
||||
|
||||
end Option
|
||||
|
||||
12
src/Init/Data/PLift.lean
Normal file
12
src/Init/Data/PLift.lean
Normal file
@@ -0,0 +1,12 @@
|
||||
/-
|
||||
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.Core
|
||||
|
||||
deriving instance DecidableEq for PLift
|
||||
|
||||
instance [Subsingleton α] : Subsingleton (PLift α) where
|
||||
allEq := fun ⟨a⟩ ⟨b⟩ => congrArg PLift.up (Subsingleton.elim a b)
|
||||
@@ -15,7 +15,7 @@ structure Range where
|
||||
step : Nat := 1
|
||||
|
||||
instance : Membership Nat Range where
|
||||
mem i r := r.start ≤ i ∧ i < r.stop
|
||||
mem r i := r.start ≤ i ∧ i < r.stop
|
||||
|
||||
namespace Range
|
||||
universe u v
|
||||
|
||||
@@ -227,7 +227,7 @@ Examples:
|
||||
* `"abc".front = 'a'`
|
||||
* `"".front = (default : Char)`
|
||||
-/
|
||||
def front (s : String) : Char :=
|
||||
@[inline] def front (s : String) : Char :=
|
||||
get s 0
|
||||
|
||||
/--
|
||||
@@ -237,7 +237,7 @@ Examples:
|
||||
* `"abc".back = 'c'`
|
||||
* `"".back = (default : Char)`
|
||||
-/
|
||||
def back (s : String) : Char :=
|
||||
@[inline] def back (s : String) : Char :=
|
||||
get s (prev s s.endPos)
|
||||
|
||||
/--
|
||||
@@ -374,7 +374,7 @@ Examples:
|
||||
* `"abba".posOf 'z' = none`
|
||||
* `"L∃∀N".posOf '∀' = some ⟨4⟩`
|
||||
-/
|
||||
def revPosOf (s : String) (c : Char) : Option Pos :=
|
||||
@[inline] def revPosOf (s : String) (c : Char) : Option Pos :=
|
||||
revPosOfAux s c s.endPos
|
||||
|
||||
def findAux (s : String) (p : Char → Bool) (stopPos : Pos) (pos : Pos) : Pos :=
|
||||
@@ -398,7 +398,7 @@ def revFindAux (s : String) (p : Char → Bool) (pos : Pos) : Option Pos :=
|
||||
else revFindAux s p pos
|
||||
termination_by pos.1
|
||||
|
||||
def revFind (s : String) (p : Char → Bool) : Option Pos :=
|
||||
@[inline] def revFind (s : String) (p : Char → Bool) : Option Pos :=
|
||||
revFindAux s p s.endPos
|
||||
|
||||
abbrev Pos.min (p₁ p₂ : Pos) : Pos :=
|
||||
@@ -505,7 +505,7 @@ The default separator is `" "`. The separators are not included in the returned
|
||||
"ababacabac".splitOn "aba" = ["", "bac", "c"]
|
||||
```
|
||||
-/
|
||||
def splitOn (s : String) (sep : String := " ") : List String :=
|
||||
@[inline] def splitOn (s : String) (sep : String := " ") : List String :=
|
||||
if sep == "" then [s] else splitOnAux s sep 0 0 0 []
|
||||
|
||||
instance : Inhabited String := ⟨""⟩
|
||||
@@ -515,16 +515,16 @@ instance : Append String := ⟨String.append⟩
|
||||
@[deprecated push (since := "2024-04-06")]
|
||||
def str : String → Char → String := push
|
||||
|
||||
def pushn (s : String) (c : Char) (n : Nat) : String :=
|
||||
@[inline] def pushn (s : String) (c : Char) (n : Nat) : String :=
|
||||
n.repeat (fun s => s.push c) s
|
||||
|
||||
def isEmpty (s : String) : Bool :=
|
||||
@[inline] def isEmpty (s : String) : Bool :=
|
||||
s.endPos == 0
|
||||
|
||||
def join (l : List String) : String :=
|
||||
@[inline] def join (l : List String) : String :=
|
||||
l.foldl (fun r s => r ++ s) ""
|
||||
|
||||
def singleton (c : Char) : String :=
|
||||
@[inline] def singleton (c : Char) : String :=
|
||||
"".push c
|
||||
|
||||
def intercalate (s : String) : List String → String
|
||||
@@ -558,10 +558,10 @@ structure Iterator where
|
||||
`Iterator.next` when `Iterator.atEnd` is true. If the position is not valid, then the
|
||||
current character is `(default : Char)`, similar to `String.get` on an invalid position. -/
|
||||
i : Pos
|
||||
deriving DecidableEq
|
||||
deriving DecidableEq, Inhabited
|
||||
|
||||
/-- Creates an iterator at the beginning of a string. -/
|
||||
def mkIterator (s : String) : Iterator :=
|
||||
@[inline] def mkIterator (s : String) : Iterator :=
|
||||
⟨s, 0⟩
|
||||
|
||||
@[inherit_doc mkIterator]
|
||||
@@ -575,66 +575,74 @@ theorem Iterator.sizeOf_eq (i : String.Iterator) : sizeOf i = i.1.utf8ByteSize -
|
||||
rfl
|
||||
|
||||
namespace Iterator
|
||||
@[inherit_doc Iterator.s]
|
||||
@[inline, inherit_doc Iterator.s]
|
||||
def toString := Iterator.s
|
||||
|
||||
/-- Number of bytes remaining in the iterator. -/
|
||||
def remainingBytes : Iterator → Nat
|
||||
@[inline] def remainingBytes : Iterator → Nat
|
||||
| ⟨s, i⟩ => s.endPos.byteIdx - i.byteIdx
|
||||
|
||||
@[inherit_doc Iterator.i]
|
||||
@[inline, inherit_doc Iterator.i]
|
||||
def pos := Iterator.i
|
||||
|
||||
/-- The character at the current position.
|
||||
|
||||
On an invalid position, returns `(default : Char)`. -/
|
||||
def curr : Iterator → Char
|
||||
@[inline] def curr : Iterator → Char
|
||||
| ⟨s, i⟩ => get s i
|
||||
|
||||
/-- Moves the iterator's position forward by one character, unconditionally.
|
||||
|
||||
It is only valid to call this function if the iterator is not at the end of the string, *i.e.*
|
||||
`Iterator.atEnd` is `false`; otherwise, the resulting iterator will be invalid. -/
|
||||
def next : Iterator → Iterator
|
||||
@[inline] def next : Iterator → Iterator
|
||||
| ⟨s, i⟩ => ⟨s, s.next i⟩
|
||||
|
||||
/-- Decreases the iterator's position.
|
||||
|
||||
If the position is zero, this function is the identity. -/
|
||||
def prev : Iterator → Iterator
|
||||
@[inline] def prev : Iterator → Iterator
|
||||
| ⟨s, i⟩ => ⟨s, s.prev i⟩
|
||||
|
||||
/-- True if the iterator is past the string's last character. -/
|
||||
def atEnd : Iterator → Bool
|
||||
@[inline] def atEnd : Iterator → Bool
|
||||
| ⟨s, i⟩ => i.byteIdx ≥ s.endPos.byteIdx
|
||||
|
||||
/-- True if the iterator is not past the string's last character. -/
|
||||
def hasNext : Iterator → Bool
|
||||
@[inline] def hasNext : Iterator → Bool
|
||||
| ⟨s, i⟩ => i.byteIdx < s.endPos.byteIdx
|
||||
|
||||
/-- True if the position is not zero. -/
|
||||
def hasPrev : Iterator → Bool
|
||||
@[inline] def hasPrev : Iterator → Bool
|
||||
| ⟨_, i⟩ => i.byteIdx > 0
|
||||
|
||||
@[inline] def curr' (it : Iterator) (h : it.hasNext) : Char :=
|
||||
match it with
|
||||
| ⟨s, i⟩ => get' s i (by simpa only [hasNext, endPos, decide_eq_true_eq, String.atEnd, ge_iff_le, Nat.not_le] using h)
|
||||
|
||||
@[inline] def next' (it : Iterator) (h : it.hasNext) : Iterator :=
|
||||
match it with
|
||||
| ⟨s, i⟩ => ⟨s, s.next' i (by simpa only [hasNext, endPos, decide_eq_true_eq, String.atEnd, ge_iff_le, Nat.not_le] using h)⟩
|
||||
|
||||
/-- Replaces the current character in the string.
|
||||
|
||||
Does nothing if the iterator is at the end of the string. If the iterator contains the only
|
||||
reference to its string, this function will mutate the string in-place instead of allocating a new
|
||||
one. -/
|
||||
def setCurr : Iterator → Char → Iterator
|
||||
@[inline] def setCurr : Iterator → Char → Iterator
|
||||
| ⟨s, i⟩, c => ⟨s.set i c, i⟩
|
||||
|
||||
/-- Moves the iterator's position to the end of the string.
|
||||
|
||||
Note that `i.toEnd.atEnd` is always `true`. -/
|
||||
def toEnd : Iterator → Iterator
|
||||
@[inline] def toEnd : Iterator → Iterator
|
||||
| ⟨s, _⟩ => ⟨s, s.endPos⟩
|
||||
|
||||
/-- Extracts the substring between the positions of two iterators.
|
||||
|
||||
Returns the empty string if the iterators are for different strings, or if the position of the first
|
||||
iterator is past the position of the second iterator. -/
|
||||
def extract : Iterator → Iterator → String
|
||||
@[inline] def extract : Iterator → Iterator → String
|
||||
| ⟨s₁, b⟩, ⟨s₂, e⟩ =>
|
||||
if s₁ ≠ s₂ || b > e then ""
|
||||
else s₁.extract b e
|
||||
@@ -648,7 +656,7 @@ def forward : Iterator → Nat → Iterator
|
||||
| it, n+1 => forward it.next n
|
||||
|
||||
/-- The remaining characters in an iterator, as a string. -/
|
||||
def remainingToString : Iterator → String
|
||||
@[inline] def remainingToString : Iterator → String
|
||||
| ⟨s, i⟩ => s.extract i s.endPos
|
||||
|
||||
@[inherit_doc forward]
|
||||
@@ -673,7 +681,7 @@ def offsetOfPosAux (s : String) (pos : Pos) (i : Pos) (offset : Nat) : Nat :=
|
||||
offsetOfPosAux s pos (s.next i) (offset+1)
|
||||
termination_by s.endPos.1 - i.1
|
||||
|
||||
def offsetOfPos (s : String) (pos : Pos) : Nat :=
|
||||
@[inline] def offsetOfPos (s : String) (pos : Pos) : Nat :=
|
||||
offsetOfPosAux s pos 0 0
|
||||
|
||||
@[specialize] def foldlAux {α : Type u} (f : α → Char → α) (s : String) (stopPos : Pos) (i : Pos) (a : α) : α :=
|
||||
@@ -714,7 +722,7 @@ termination_by stopPos.1 - i.1
|
||||
@[inline] def all (s : String) (p : Char → Bool) : Bool :=
|
||||
!s.any (fun c => !p c)
|
||||
|
||||
def contains (s : String) (c : Char) : Bool :=
|
||||
@[inline] def contains (s : String) (c : Char) : Bool :=
|
||||
s.any (fun a => a == c)
|
||||
|
||||
theorem utf8SetAux_of_gt (c' : Char) : ∀ (cs : List Char) {i p : Pos}, i > p → utf8SetAux c' cs i p = cs
|
||||
@@ -770,7 +778,7 @@ termination_by s.endPos.1 - i.1
|
||||
@[inline] def map (f : Char → Char) (s : String) : String :=
|
||||
mapAux f 0 s
|
||||
|
||||
def isNat (s : String) : Bool :=
|
||||
@[inline] def isNat (s : String) : Bool :=
|
||||
!s.isEmpty && s.all (·.isDigit)
|
||||
|
||||
def toNat? (s : String) : Option Nat :=
|
||||
@@ -940,7 +948,7 @@ def splitOn (s : Substring) (sep : String := " ") : List Substring :=
|
||||
@[inline] def all (s : Substring) (p : Char → Bool) : Bool :=
|
||||
!s.any (fun c => !p c)
|
||||
|
||||
def contains (s : Substring) (c : Char) : Bool :=
|
||||
@[inline] def contains (s : Substring) (c : Char) : Bool :=
|
||||
s.any (fun a => a == c)
|
||||
|
||||
@[specialize] def takeWhileAux (s : String) (stopPos : String.Pos) (p : Char → Bool) (i : String.Pos) : String.Pos :=
|
||||
@@ -995,7 +1003,7 @@ termination_by i.1
|
||||
let e := takeRightWhileAux s b Char.isWhitespace e
|
||||
⟨s, b, e⟩
|
||||
|
||||
def isNat (s : Substring) : Bool :=
|
||||
@[inline] def isNat (s : Substring) : Bool :=
|
||||
s.all fun c => c.isDigit
|
||||
|
||||
def toNat? (s : Substring) : Option Nat :=
|
||||
@@ -1017,43 +1025,43 @@ end Substring
|
||||
|
||||
namespace String
|
||||
|
||||
def drop (s : String) (n : Nat) : String :=
|
||||
@[inline] def drop (s : String) (n : Nat) : String :=
|
||||
(s.toSubstring.drop n).toString
|
||||
|
||||
def dropRight (s : String) (n : Nat) : String :=
|
||||
@[inline] def dropRight (s : String) (n : Nat) : String :=
|
||||
(s.toSubstring.dropRight n).toString
|
||||
|
||||
def take (s : String) (n : Nat) : String :=
|
||||
@[inline] def take (s : String) (n : Nat) : String :=
|
||||
(s.toSubstring.take n).toString
|
||||
|
||||
def takeRight (s : String) (n : Nat) : String :=
|
||||
@[inline] def takeRight (s : String) (n : Nat) : String :=
|
||||
(s.toSubstring.takeRight n).toString
|
||||
|
||||
def takeWhile (s : String) (p : Char → Bool) : String :=
|
||||
@[inline] def takeWhile (s : String) (p : Char → Bool) : String :=
|
||||
(s.toSubstring.takeWhile p).toString
|
||||
|
||||
def dropWhile (s : String) (p : Char → Bool) : String :=
|
||||
@[inline] def dropWhile (s : String) (p : Char → Bool) : String :=
|
||||
(s.toSubstring.dropWhile p).toString
|
||||
|
||||
def takeRightWhile (s : String) (p : Char → Bool) : String :=
|
||||
@[inline] def takeRightWhile (s : String) (p : Char → Bool) : String :=
|
||||
(s.toSubstring.takeRightWhile p).toString
|
||||
|
||||
def dropRightWhile (s : String) (p : Char → Bool) : String :=
|
||||
@[inline] def dropRightWhile (s : String) (p : Char → Bool) : String :=
|
||||
(s.toSubstring.dropRightWhile p).toString
|
||||
|
||||
def startsWith (s pre : String) : Bool :=
|
||||
@[inline] def startsWith (s pre : String) : Bool :=
|
||||
s.toSubstring.take pre.length == pre.toSubstring
|
||||
|
||||
def endsWith (s post : String) : Bool :=
|
||||
@[inline] def endsWith (s post : String) : Bool :=
|
||||
s.toSubstring.takeRight post.length == post.toSubstring
|
||||
|
||||
def trimRight (s : String) : String :=
|
||||
@[inline] def trimRight (s : String) : String :=
|
||||
s.toSubstring.trimRight.toString
|
||||
|
||||
def trimLeft (s : String) : String :=
|
||||
@[inline] def trimLeft (s : String) : String :=
|
||||
s.toSubstring.trimLeft.toString
|
||||
|
||||
def trim (s : String) : String :=
|
||||
@[inline] def trim (s : String) : String :=
|
||||
s.toSubstring.trim.toString
|
||||
|
||||
@[inline] def nextWhile (s : String) (p : Char → Bool) (i : String.Pos) : String.Pos :=
|
||||
@@ -1062,23 +1070,23 @@ def trim (s : String) : String :=
|
||||
@[inline] def nextUntil (s : String) (p : Char → Bool) (i : String.Pos) : String.Pos :=
|
||||
nextWhile s (fun c => !p c) i
|
||||
|
||||
def toUpper (s : String) : String :=
|
||||
@[inline] def toUpper (s : String) : String :=
|
||||
s.map Char.toUpper
|
||||
|
||||
def toLower (s : String) : String :=
|
||||
@[inline] def toLower (s : String) : String :=
|
||||
s.map Char.toLower
|
||||
|
||||
def capitalize (s : String) :=
|
||||
@[inline] def capitalize (s : String) :=
|
||||
s.set 0 <| s.get 0 |>.toUpper
|
||||
|
||||
def decapitalize (s : String) :=
|
||||
@[inline] def decapitalize (s : String) :=
|
||||
s.set 0 <| s.get 0 |>.toLower
|
||||
|
||||
end String
|
||||
|
||||
namespace Char
|
||||
|
||||
protected def toString (c : Char) : String :=
|
||||
@[inline] protected def toString (c : Char) : String :=
|
||||
String.singleton c
|
||||
|
||||
@[simp] theorem length_toString (c : Char) : c.toString.length = 1 := rfl
|
||||
|
||||
12
src/Init/Data/ULift.lean
Normal file
12
src/Init/Data/ULift.lean
Normal file
@@ -0,0 +1,12 @@
|
||||
/-
|
||||
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.Core
|
||||
|
||||
deriving instance DecidableEq for ULift
|
||||
|
||||
instance [Subsingleton α] : Subsingleton (ULift α) where
|
||||
allEq := fun ⟨a⟩ ⟨b⟩ => congrArg ULift.up (Subsingleton.elim a b)
|
||||
@@ -184,7 +184,7 @@ instance [GetElem? cont Nat elem dom] [h : LawfulGetElem cont Nat elem dom] :
|
||||
@[simp] theorem getElem!_fin [GetElem? Cont Nat Elem Dom] (a : Cont) (i : Fin n) [Inhabited Elem] : a[i]! = a[i.1]! := rfl
|
||||
|
||||
macro_rules
|
||||
| `(tactic| get_elem_tactic_trivial) => `(tactic| apply Fin.val_lt_of_le; get_elem_tactic_trivial; done)
|
||||
| `(tactic| get_elem_tactic_trivial) => `(tactic| (with_reducible apply Fin.val_lt_of_le); get_elem_tactic_trivial; done)
|
||||
|
||||
end Fin
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ See #2572.
|
||||
opaque Internal.hasLLVMBackend (u : Unit) : Bool
|
||||
|
||||
/-- Valid identifier names -/
|
||||
def isGreek (c : Char) : Bool :=
|
||||
@[inline] def isGreek (c : Char) : Bool :=
|
||||
0x391 ≤ c.val && c.val ≤ 0x3dd
|
||||
|
||||
def isLetterLike (c : Char) : Bool :=
|
||||
@@ -86,7 +86,7 @@ def isLetterLike (c : Char) : Bool :=
|
||||
(0x2100 ≤ c.val && c.val ≤ 0x214f) || -- Letter like block
|
||||
(0x1d49c ≤ c.val && c.val ≤ 0x1d59f) -- Latin letters, Script, Double-struck, Fractur
|
||||
|
||||
def isNumericSubscript (c : Char) : Bool :=
|
||||
@[inline] def isNumericSubscript (c : Char) : Bool :=
|
||||
0x2080 ≤ c.val && c.val ≤ 0x2089
|
||||
|
||||
def isSubScriptAlnum (c : Char) : Bool :=
|
||||
@@ -94,16 +94,16 @@ def isSubScriptAlnum (c : Char) : Bool :=
|
||||
(0x2090 ≤ c.val && c.val ≤ 0x209c) ||
|
||||
(0x1d62 ≤ c.val && c.val ≤ 0x1d6a)
|
||||
|
||||
def isIdFirst (c : Char) : Bool :=
|
||||
@[inline] def isIdFirst (c : Char) : Bool :=
|
||||
c.isAlpha || c = '_' || isLetterLike c
|
||||
|
||||
def isIdRest (c : Char) : Bool :=
|
||||
@[inline] def isIdRest (c : Char) : Bool :=
|
||||
c.isAlphanum || c = '_' || c = '\'' || c == '!' || c == '?' || isLetterLike c || isSubScriptAlnum c
|
||||
|
||||
def idBeginEscape := '«'
|
||||
def idEndEscape := '»'
|
||||
def isIdBeginEscape (c : Char) : Bool := c = idBeginEscape
|
||||
def isIdEndEscape (c : Char) : Bool := c = idEndEscape
|
||||
@[inline] def isIdBeginEscape (c : Char) : Bool := c = idBeginEscape
|
||||
@[inline] def isIdEndEscape (c : Char) : Bool := c = idEndEscape
|
||||
namespace Name
|
||||
|
||||
def getRoot : Name → Name
|
||||
@@ -388,9 +388,9 @@ def getSubstring? (stx : Syntax) (withLeading := true) (withTrailing := true) :
|
||||
partial def setTailInfoAux (info : SourceInfo) : Syntax → Option Syntax
|
||||
| atom _ val => some <| atom info val
|
||||
| ident _ rawVal val pre => some <| ident info rawVal val pre
|
||||
| node info k args =>
|
||||
| node info' k args =>
|
||||
match updateLast args (setTailInfoAux info) args.size with
|
||||
| some args => some <| node info k args
|
||||
| some args => some <| node info' k args
|
||||
| none => none
|
||||
| _ => none
|
||||
|
||||
|
||||
@@ -104,6 +104,11 @@ structure Config where
|
||||
That is, given a local context containing entry `x : t := e`, the free variable `x` reduces to `e`.
|
||||
-/
|
||||
zetaDelta : Bool := false
|
||||
/--
|
||||
When `index` (default : `true`) is `false`, `simp` will only use the root symbol
|
||||
to find candidate `simp` theorems. It approximates Lean 3 `simp` behavior.
|
||||
-/
|
||||
index : Bool := true
|
||||
deriving Inhabited, BEq
|
||||
|
||||
end DSimp
|
||||
|
||||
@@ -336,7 +336,7 @@ macro_rules | `($x == $y) => `(binrel_no_prop% BEq.beq $x $y)
|
||||
@[inherit_doc] infixl:30 " || " => or
|
||||
@[inherit_doc] notation:max "!" b:40 => not b
|
||||
|
||||
@[inherit_doc] infix:50 " ∈ " => Membership.mem
|
||||
@[inherit_doc] notation:50 a:50 " ∈ " b:50 => Membership.mem b a
|
||||
/-- `a ∉ b` is negated elementhood. It is notation for `¬ (a ∈ b)`. -/
|
||||
notation:50 a:50 " ∉ " b:50 => ¬ (a ∈ b)
|
||||
|
||||
|
||||
@@ -300,6 +300,8 @@ theorem normalize_sat {s x v} (w : s.sat' x v) :
|
||||
· split
|
||||
· simp
|
||||
· dsimp [Constraint.sat'] at w
|
||||
simp only [IntList.gcd_eq_zero] at h
|
||||
simp only [IntList.dot_eq_zero_of_left_eq_zero h] at w
|
||||
simp_all
|
||||
· split
|
||||
· exact w
|
||||
|
||||
@@ -116,7 +116,7 @@ theorem ofNat_max (a b : Nat) : ((max a b : Nat) : Int) = max (a : Int) (b : Int
|
||||
split <;> rfl
|
||||
|
||||
theorem ofNat_natAbs (a : Int) : (a.natAbs : Int) = if 0 ≤ a then a else -a := by
|
||||
rw [Int.natAbs]
|
||||
rw [Int.natAbs.eq_def]
|
||||
split <;> rename_i n
|
||||
· simp only [Int.ofNat_eq_coe]
|
||||
rw [if_pos (Int.ofNat_nonneg n)]
|
||||
|
||||
@@ -352,7 +352,6 @@ attribute [simp] Int.zero_dvd
|
||||
theorem gcd_dvd_dot_left (xs ys : IntList) : (xs.gcd : Int) ∣ dot xs ys :=
|
||||
Int.dvd_of_emod_eq_zero (dot_mod_gcd_left xs ys)
|
||||
|
||||
@[simp]
|
||||
theorem dot_eq_zero_of_left_eq_zero {xs ys : IntList} (h : ∀ x, x ∈ xs → x = 0) : dot xs ys = 0 := by
|
||||
induction xs generalizing ys with
|
||||
| nil => rfl
|
||||
@@ -363,6 +362,8 @@ theorem dot_eq_zero_of_left_eq_zero {xs ys : IntList} (h : ∀ x, x ∈ xs → x
|
||||
rw [dot_cons₂, h x (List.mem_cons_self _ _), ih (fun x m => h x (List.mem_cons_of_mem _ m)),
|
||||
Int.zero_mul, Int.add_zero]
|
||||
|
||||
@[simp] theorem nil_dot (xs : IntList) : dot [] xs = 0 := rfl
|
||||
|
||||
theorem dot_sdiv_left (xs ys : IntList) {d : Int} (h : d ∣ xs.gcd) :
|
||||
dot (xs.sdiv d) ys = (dot xs ys) / d := by
|
||||
induction xs generalizing ys with
|
||||
|
||||
@@ -1515,7 +1515,7 @@ of the elements of the container.
|
||||
-/
|
||||
class Membership (α : outParam (Type u)) (γ : Type v) where
|
||||
/-- The membership relation `a ∈ s : Prop` where `a : α`, `s : γ`. -/
|
||||
mem : α → γ → Prop
|
||||
mem : γ → α → Prop
|
||||
|
||||
set_option bootstrap.genMatcherCode false in
|
||||
/--
|
||||
|
||||
@@ -169,12 +169,23 @@ theorem if_true_right [h : Decidable p] :
|
||||
@[simp] theorem ite_not (p : Prop) [Decidable p] (x y : α) : ite (¬p) x y = ite p y x :=
|
||||
dite_not (fun _ => x) (fun _ => y)
|
||||
|
||||
@[simp] theorem ite_true_same (p q : Prop) [h : Decidable p] : (if p then p else q) = (¬p → q) := by
|
||||
@[simp] theorem ite_then_self (p q : Prop) [h : Decidable p] : (if p then p else q) = (¬p → q) := by
|
||||
cases h <;> (rename_i g; simp [g])
|
||||
|
||||
@[simp] theorem ite_false_same (p q : Prop) [h : Decidable p] : (if p then q else p) = (p ∧ q) := by
|
||||
@[simp] theorem ite_else_self (p q : Prop) [h : Decidable p] : (if p then q else p) = (p ∧ q) := by
|
||||
cases h <;> (rename_i g; simp [g])
|
||||
|
||||
@[simp] theorem ite_then_not_self (p : Prop) [Decidable p] (q : Prop) : (if p then ¬p else q) ↔ ¬p ∧ q := by
|
||||
split <;> simp_all
|
||||
|
||||
@[simp] theorem ite_else_not_self (p : Prop) [Decidable p] (q : Prop) : (if p then q else ¬p) ↔ p → q := by
|
||||
split <;> simp_all
|
||||
|
||||
@[deprecated ite_then_self (since := "2024-08-28")]
|
||||
theorem ite_true_same (p q : Prop) [Decidable p] : (if p then p else q) = (¬p → q) := ite_then_self p q
|
||||
@[deprecated ite_else_self (since := "2024-08-28")]
|
||||
theorem ite_false_same (p q : Prop) [Decidable p] : (if p then q else p) = (p ∧ q) := ite_else_self p q
|
||||
|
||||
/-! ## exists and forall -/
|
||||
|
||||
section quantifiers
|
||||
@@ -326,6 +337,9 @@ theorem not_forall_of_exists_not {p : α → Prop} : (∃ x, ¬p x) → ¬∀ x,
|
||||
@[simp] theorem exists_or_eq_left' (y : α) (p : α → Prop) : ∃ x : α, y = x ∨ p x := ⟨y, .inl rfl⟩
|
||||
@[simp] theorem exists_or_eq_right' (y : α) (p : α → Prop) : ∃ x : α, p x ∨ y = x := ⟨y, .inr rfl⟩
|
||||
|
||||
@[simp] theorem exists_prop' (p : Prop) : (∃ _ : α, p) ↔ Nonempty α ∧ p :=
|
||||
⟨fun ⟨a, h⟩ => ⟨⟨a⟩, h⟩, fun ⟨⟨a⟩, h⟩ => ⟨a, h⟩⟩
|
||||
|
||||
@[simp] theorem exists_prop : (∃ _h : a, b) ↔ a ∧ b :=
|
||||
⟨fun ⟨hp, hq⟩ => ⟨hp, hq⟩, fun ⟨hp, hq⟩ => ⟨hp, hq⟩⟩
|
||||
|
||||
@@ -355,6 +369,11 @@ theorem forall_prop_of_false {p : Prop} {q : p → Prop} (hn : ¬p) : (∀ h' :
|
||||
|
||||
end quantifiers
|
||||
|
||||
/-! ## Nonempty -/
|
||||
|
||||
@[simp] theorem nonempty_prop (p : Prop) : Nonempty p ↔ p :=
|
||||
⟨fun ⟨h⟩ => h, fun h => ⟨h⟩⟩
|
||||
|
||||
/-! ## decidable -/
|
||||
|
||||
@[simp] theorem Decidable.not_not [Decidable p] : ¬¬p ↔ p := ⟨of_not_not, not_not_intro⟩
|
||||
@@ -390,7 +409,7 @@ else isTrue fun h2 => absurd h2 h
|
||||
|
||||
theorem decide_eq_true_iff (p : Prop) [Decidable p] : (decide p = true) ↔ p := by simp
|
||||
|
||||
@[simp] theorem decide_eq_decide {p q : Prop} {_ : Decidable p} {_ : Decidable q} :
|
||||
@[simp, boolToPropSimps] theorem decide_eq_decide {p q : Prop} {_ : Decidable p} {_ : Decidable q} :
|
||||
decide p = decide q ↔ (p ↔ q) :=
|
||||
⟨fun h => by rw [← decide_eq_true_iff p, h, decide_eq_true_iff], fun h => by simp [h]⟩
|
||||
|
||||
@@ -403,7 +422,7 @@ theorem Decidable.not_imp_symm [Decidable a] (h : ¬a → b) (hb : ¬b) : a :=
|
||||
theorem Decidable.not_imp_comm [Decidable a] [Decidable b] : (¬a → b) ↔ (¬b → a) :=
|
||||
⟨not_imp_symm, not_imp_symm⟩
|
||||
|
||||
@[simp] theorem Decidable.not_imp_self [Decidable a] : (¬a → a) ↔ a := by
|
||||
theorem Decidable.not_imp_self [Decidable a] : (¬a → a) ↔ a := by
|
||||
have := @imp_not_self (¬a); rwa [not_not] at this
|
||||
|
||||
theorem Decidable.or_iff_not_imp_left [Decidable a] : a ∨ b ↔ (¬a → b) :=
|
||||
@@ -486,7 +505,7 @@ theorem Decidable.imp_iff_right_iff [Decidable a] : (a → b ↔ b) ↔ a ∨ b
|
||||
(fun h => (Decidable.em a).imp_right fun ha' => h.mp fun ha => (ha' ha).elim)
|
||||
(fun ab => ab.elim imp_iff_right fun hb => iff_of_true (fun _ => hb) hb)
|
||||
|
||||
theorem Decidable.imp_iff_left_iff [Decidable a] : (b ↔ a → b) ↔ a ∨ b :=
|
||||
theorem Decidable.imp_iff_left_iff [Decidable a] : (b ↔ a → b) ↔ a ∨ b :=
|
||||
propext (@Iff.comm (a → b) b) ▸ (@Decidable.imp_iff_right_iff a b _)
|
||||
|
||||
theorem Decidable.and_or_imp [Decidable a] : a ∧ b ∨ (a → c) ↔ a → b ∨ c :=
|
||||
@@ -573,12 +592,66 @@ theorem decide_ite (u : Prop) [du : Decidable u] (p q : Prop)
|
||||
decide (ite u p q) = ite u (decide p) (decide q) := by
|
||||
cases du <;> simp [*]
|
||||
|
||||
/- Confluence for `ite_true_same` and `decide_ite`. -/
|
||||
@[simp] theorem ite_true_decide_same (p : Prop) [h : Decidable p] (b : Bool) :
|
||||
(if p then decide p else b) = (decide p || b) := by
|
||||
cases h <;> (rename_i pt; simp [pt])
|
||||
/- Confluence for `ite_then_self` and `decide_ite`. -/
|
||||
@[simp] theorem ite_then_decide_self (p : Prop) [h : Decidable p] {w : Decidable p} (q : Bool) :
|
||||
(@ite _ p h (decide p) q) = (decide p || q) := by
|
||||
split <;> simp_all
|
||||
|
||||
/- Confluence for `ite_false_same` and `decide_ite`. -/
|
||||
@[simp] theorem ite_false_decide_same (p : Prop) [h : Decidable p] (b : Bool) :
|
||||
(if p then b else decide p) = (decide p && b) := by
|
||||
cases h <;> (rename_i pt; simp [pt])
|
||||
/- Confluence for `ite_else_self` and `decide_ite`. -/
|
||||
@[simp] theorem ite_else_decide_self (p : Prop) [h : Decidable p] {w : Decidable p} (q : Bool) :
|
||||
(@ite _ p h q (decide p)) = (decide p && q) := by
|
||||
split <;> simp_all
|
||||
|
||||
@[deprecated ite_then_decide_self]
|
||||
theorem ite_true_decide_same (p : Prop) [Decidable p] (b : Bool) :
|
||||
(if p then decide p else b) = (decide p || b) := ite_then_decide_self p b
|
||||
|
||||
@[deprecated ite_false_decide_same]
|
||||
theorem ite_false_decide_same (p : Prop) [Decidable p] (b : Bool) :
|
||||
(if p then b else decide p) = (decide p && b) := ite_else_decide_self p b
|
||||
|
||||
@[simp] theorem ite_then_decide_not_self (p : Prop) [h : Decidable p] {w : Decidable p} (q : Bool) :
|
||||
(@ite _ p h (!decide p) q) = (!decide p && q) := by
|
||||
split <;> simp_all
|
||||
|
||||
@[simp] theorem ite_else_decide_not_self (p : Prop) [h : Decidable p] {w : Decidable p} (q : Bool) :
|
||||
(@ite _ p h q (!decide p)) = (!decide p || q) := by
|
||||
split <;> simp_all
|
||||
|
||||
attribute [local simp] Decidable.imp_iff_left_iff
|
||||
|
||||
@[simp] theorem dite_eq_then (p : Prop) [Decidable p] {x : α} {y : ¬ p → α} : (if h : p then x else y h) = x ↔ ∀ h : ¬ p, y h = x := by
|
||||
split <;> simp_all
|
||||
|
||||
@[simp] theorem dite_eq_else (p : Prop) [Decidable p] {x : p → α} {y : α} : (if h : p then x h else y) = y ↔ ∀ h : p, x h = y := by
|
||||
split <;> simp_all
|
||||
|
||||
@[simp] theorem dite_iff_then (p : Prop) [Decidable p] {x : Prop} {y : ¬ p → Prop} : ((if h : p then x else y h) ↔ x) ↔ ∀ h : ¬ p, y h ↔ x := by
|
||||
split <;> simp_all
|
||||
|
||||
@[simp] theorem dite_iff_else (p : Prop) [Decidable p] {x : p → Prop} {y : Prop} : ((if h : p then x h else y) ↔ y) ↔ ∀ h : p, x h ↔ y := by
|
||||
split <;> simp_all
|
||||
|
||||
@[simp] theorem ite_eq_then (p : Prop) [Decidable p] (x y : α) : (if p then x else y) = x ↔ ¬ p → y = x := by
|
||||
split <;> simp_all
|
||||
|
||||
@[simp] theorem ite_eq_else (p : Prop) [Decidable p] (x y : α) : (if p then x else y) = y ↔ p → x = y := by
|
||||
split <;> simp_all
|
||||
|
||||
@[simp] theorem ite_iff_then (p : Prop) [Decidable p] (x y : Prop) : ((if p then x else y) ↔ x) ↔ ¬ p → y = x := by
|
||||
split <;> simp_all
|
||||
|
||||
@[simp] theorem ite_iff_else (p : Prop) [Decidable p] (x y : Prop) : ((if p then x else y) ↔ y) ↔ p → x = y := by
|
||||
split <;> simp_all
|
||||
|
||||
@[simp] theorem dite_then_false (p : Prop) [Decidable p] {x : ¬ p → Prop} : (if h : p then False else x h) ↔ ∃ h : ¬ p, x h := by
|
||||
split <;> simp_all
|
||||
|
||||
@[simp] theorem dite_else_false (p : Prop) [Decidable p] {x : p → Prop} : (if h : p then x h else False) ↔ ∃ h : p, x h := by
|
||||
split <;> simp_all
|
||||
|
||||
@[simp] theorem dite_then_true (p : Prop) [Decidable p] {x : ¬ p → Prop} : (if h : p then True else x h) ↔ ∀ h : ¬ p, x h := by
|
||||
split <;> simp_all
|
||||
|
||||
@[simp] theorem dite_else_true (p : Prop) [Decidable p] {x : p → Prop} : (if h : p then x h else True) ↔ ∀ h : p, x h := by
|
||||
split <;> simp_all
|
||||
|
||||
@@ -244,7 +244,7 @@ instance : Std.Associative (· || ·) := ⟨Bool.or_assoc⟩
|
||||
|
||||
@[simp] theorem decide_not [g : Decidable p] [h : Decidable (Not p)] : decide (Not p) = !(decide p) := by
|
||||
cases g <;> (rename_i gp; simp [gp]; rfl)
|
||||
@[simp] theorem not_decide_eq_true [h : Decidable p] : ((!decide p) = true) = ¬ p := by simp
|
||||
theorem not_decide_eq_true [h : Decidable p] : ((!decide p) = true) = ¬ p := by simp
|
||||
|
||||
@[simp] theorem heq_eq_eq (a b : α) : HEq a b = (a = b) := propext <| Iff.intro eq_of_heq heq_of_eq
|
||||
|
||||
|
||||
@@ -447,6 +447,7 @@ see there for more information.
|
||||
@[extern "lean_io_remove_dir"] opaque removeDir : @& FilePath → IO Unit
|
||||
@[extern "lean_io_create_dir"] opaque createDir : @& FilePath → IO Unit
|
||||
|
||||
|
||||
/--
|
||||
Moves a file or directory `old` to the new location `new`.
|
||||
|
||||
@@ -455,6 +456,16 @@ see there for more information.
|
||||
-/
|
||||
@[extern "lean_io_rename"] opaque rename (old new : @& FilePath) : IO Unit
|
||||
|
||||
/--
|
||||
Creates a temporary file in the most secure manner possible. There are no race conditions in the
|
||||
file’s creation. The file is readable and writable only by the creating user ID. Additionally
|
||||
on UNIX style platforms the file is executable by nobody. The function returns both a `Handle`
|
||||
to the already opened file as well as its `FilePath`.
|
||||
|
||||
Note that it is the caller's job to remove the file after use.
|
||||
-/
|
||||
@[extern "lean_io_create_tempfile"] opaque createTempFile : IO (Handle × FilePath)
|
||||
|
||||
end FS
|
||||
|
||||
@[extern "lean_io_getenv"] opaque getEnv (var : @& String) : BaseIO (Option String)
|
||||
@@ -467,6 +478,17 @@ namespace FS
|
||||
def withFile (fn : FilePath) (mode : Mode) (f : Handle → IO α) : IO α :=
|
||||
Handle.mk fn mode >>= f
|
||||
|
||||
/--
|
||||
Like `createTempFile` but also takes care of removing the file after usage.
|
||||
-/
|
||||
def withTempFile [Monad m] [MonadFinally m] [MonadLiftT IO m] (f : Handle → FilePath → m α) :
|
||||
m α := do
|
||||
let (handle, path) ← createTempFile
|
||||
try
|
||||
f handle path
|
||||
finally
|
||||
removeFile path
|
||||
|
||||
def Handle.putStrLn (h : Handle) (s : String) : IO Unit :=
|
||||
h.putStr (s.push '\n')
|
||||
|
||||
|
||||
@@ -552,9 +552,9 @@ The `simp` tactic uses lemmas and hypotheses to simplify the main goal target or
|
||||
non-dependent hypotheses. It has many variants:
|
||||
- `simp` simplifies the main goal target using lemmas tagged with the attribute `[simp]`.
|
||||
- `simp [h₁, h₂, ..., hₙ]` simplifies the main goal target using the lemmas tagged
|
||||
with the attribute `[simp]` and the given `hᵢ`'s, where the `hᵢ`'s are expressions.
|
||||
If an `hᵢ` is a defined constant `f`, then the equational lemmas associated with
|
||||
`f` are used. This provides a convenient way to unfold `f`.
|
||||
with the attribute `[simp]` and the given `hᵢ`'s, where the `hᵢ`'s are expressions.-
|
||||
- If an `hᵢ` is a defined constant `f`, then `f` is unfolded. If `f` has equational lemmas associated
|
||||
with it (and is not a projection or a `reducible` definition), these are used to rewrite with `f`.
|
||||
- `simp [*]` simplifies the main goal target using the lemmas tagged with the
|
||||
attribute `[simp]` and all hypotheses.
|
||||
- `simp only [h₁, h₂, ..., hₙ]` is like `simp [h₁, h₂, ..., hₙ]` but does not use `[simp]` lemmas.
|
||||
@@ -679,9 +679,9 @@ syntax (name := delta) "delta" (ppSpace colGt ident)+ (location)? : tactic
|
||||
* `unfold id` unfolds definition `id`.
|
||||
* `unfold id1 id2 ...` is equivalent to `unfold id1; unfold id2; ...`.
|
||||
|
||||
For non-recursive definitions, this tactic is identical to `delta`.
|
||||
For definitions by pattern matching, it uses "equation lemmas" which are
|
||||
autogenerated for each match arm.
|
||||
For non-recursive definitions, this tactic is identical to `delta`. For recursive definitions,
|
||||
it uses the "unfolding lemma" `id.eq_def`, which is generated for each recursive definition,
|
||||
to unfold according to the recursive definition given by the user.
|
||||
-/
|
||||
syntax (name := unfold) "unfold" (ppSpace colGt ident)+ (location)? : tactic
|
||||
|
||||
|
||||
@@ -190,21 +190,36 @@ def lt_wfRel : WellFoundedRelation Nat where
|
||||
| Or.inl e => subst e; assumption
|
||||
| Or.inr e => exact Acc.inv ih e
|
||||
|
||||
protected noncomputable def strongInductionOn
|
||||
@[elab_as_elim] protected noncomputable def strongRecOn
|
||||
{motive : Nat → Sort u}
|
||||
(n : Nat)
|
||||
(ind : ∀ n, (∀ m, m < n → motive m) → motive n) : motive n :=
|
||||
Nat.lt_wfRel.wf.fix ind n
|
||||
|
||||
@[deprecated Nat.strongRecOn (since := "2024-08-27")]
|
||||
protected noncomputable def strongInductionOn
|
||||
{motive : Nat → Sort u}
|
||||
(n : Nat)
|
||||
(ind : ∀ n, (∀ m, m < n → motive m) → motive n) : motive n :=
|
||||
Nat.strongRecOn n ind
|
||||
|
||||
@[elab_as_elim] protected noncomputable def caseStrongRecOn
|
||||
{motive : Nat → Sort u}
|
||||
(a : Nat)
|
||||
(zero : motive 0)
|
||||
(ind : ∀ n, (∀ m, m ≤ n → motive m) → motive (succ n)) : motive a :=
|
||||
Nat.strongRecOn a fun n =>
|
||||
match n with
|
||||
| 0 => fun _ => zero
|
||||
| n+1 => fun h₁ => ind n (λ _ h₂ => h₁ _ (lt_succ_of_le h₂))
|
||||
|
||||
@[deprecated Nat.caseStrongRecOn (since := "2024-08-27")]
|
||||
protected noncomputable def caseStrongInductionOn
|
||||
{motive : Nat → Sort u}
|
||||
(a : Nat)
|
||||
(zero : motive 0)
|
||||
(ind : ∀ n, (∀ m, m ≤ n → motive m) → motive (succ n)) : motive a :=
|
||||
Nat.strongInductionOn a fun n =>
|
||||
match n with
|
||||
| 0 => fun _ => zero
|
||||
| n+1 => fun h₁ => ind n (λ _ h₂ => h₁ _ (lt_succ_of_le h₂))
|
||||
Nat.caseStrongRecOn a zero ind
|
||||
|
||||
end Nat
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ macro "clean_wf" : tactic =>
|
||||
`(tactic| simp
|
||||
(config := { unfoldPartialApp := true, zetaDelta := true, failIfUnchanged := false })
|
||||
only [invImage, InvImage, Prod.lex, sizeOfWFRel, measure, Nat.lt_wfRel,
|
||||
WellFoundedRelation.rel, sizeOf_nat])
|
||||
WellFoundedRelation.rel, sizeOf_nat, reduceCtorEq])
|
||||
|
||||
/-- Extensible helper tactic for `decreasing_tactic`. This handles the "base case"
|
||||
reasoning after applying lexicographic order lemmas.
|
||||
|
||||
@@ -18,7 +18,6 @@ import Lean.Data.Name
|
||||
import Lean.Data.NameMap
|
||||
import Lean.Data.OpenDecl
|
||||
import Lean.Data.Options
|
||||
import Lean.Data.Parsec
|
||||
import Lean.Data.PersistentArray
|
||||
import Lean.Data.PersistentHashMap
|
||||
import Lean.Data.PersistentHashSet
|
||||
|
||||
@@ -6,23 +6,22 @@ Authors: Gabriel Ebner, Marc Huisinga
|
||||
-/
|
||||
prelude
|
||||
import Lean.Data.Json.Basic
|
||||
import Lean.Data.Parsec
|
||||
import Lean.Data.RBMap
|
||||
import Std.Internal.Parsec
|
||||
|
||||
open Std.Internal.Parsec
|
||||
open Std.Internal.Parsec.String
|
||||
|
||||
namespace Lean.Json.Parser
|
||||
|
||||
open Lean.Parsec
|
||||
open Lean.Parsec.String
|
||||
|
||||
@[inline]
|
||||
def hexChar : Parser Nat := do
|
||||
let c ← any
|
||||
if '0' ≤ c ∧ c ≤ '9' then
|
||||
pure $ c.val.toNat - '0'.val.toNat
|
||||
else if 'a' ≤ c ∧ c ≤ 'f' then
|
||||
pure $ c.val.toNat - 'a'.val.toNat + 10
|
||||
else if 'A' ≤ c ∧ c ≤ 'F' then
|
||||
pure $ c.val.toNat - 'A'.val.toNat + 10
|
||||
if '0' <= c && c <= '9' then
|
||||
pure $ (c.val - '0'.val).toNat
|
||||
else if 'a' <= c && c <= 'f' then
|
||||
pure $ (c.val - 'a'.val + 10).toNat
|
||||
else if 'A' <= c && c <= 'F' then
|
||||
pure $ (c.val - 'A'.val + 10).toNat
|
||||
else
|
||||
fail "invalid hex character"
|
||||
|
||||
@@ -44,31 +43,46 @@ def escapedChar : Parser Char := do
|
||||
|
||||
partial def strCore (acc : String) : Parser String := do
|
||||
let c ← peek!
|
||||
if c = '"' then -- "
|
||||
if c == '"' then
|
||||
skip
|
||||
return acc
|
||||
else
|
||||
let c ← any
|
||||
if c = '\\' then
|
||||
if c == '\\' then
|
||||
strCore (acc.push (← escapedChar))
|
||||
-- as to whether c.val > 0xffff should be split up and encoded with multiple \u,
|
||||
-- the JSON standard is not definite: both directly printing the character
|
||||
-- and encoding it with multiple \u is allowed. we choose the former.
|
||||
else if 0x0020 ≤ c.val ∧ c.val ≤ 0x10ffff then
|
||||
else if 0x0020 <= c.val && c.val <= 0x10ffff then
|
||||
strCore (acc.push c)
|
||||
else
|
||||
fail "unexpected character in string"
|
||||
|
||||
def str : Parser String := strCore ""
|
||||
@[inline] def str : Parser String := strCore ""
|
||||
|
||||
partial def natCore (acc digits : Nat) : Parser (Nat × Nat) := do
|
||||
let some c ← peek? | return (acc, digits)
|
||||
if '0' ≤ c ∧ c ≤ '9' then
|
||||
skip
|
||||
let acc' := 10*acc + (c.val.toNat - '0'.val.toNat)
|
||||
natCore acc' (digits+1)
|
||||
partial def natCore (acc : Nat) : Parser Nat := do
|
||||
if ← isEof then
|
||||
return acc
|
||||
else
|
||||
let c ← peek!
|
||||
if '0' <= c && c <= '9' then
|
||||
skip
|
||||
let acc' := 10*acc + (c.val - '0'.val).toNat
|
||||
natCore acc'
|
||||
else
|
||||
return acc
|
||||
|
||||
partial def natCoreNumDigits (acc digits : Nat) : Parser (Nat × Nat) := do
|
||||
if ← isEof then
|
||||
return (acc, digits)
|
||||
else
|
||||
let c ← peek!
|
||||
if '0' <= c && c <= '9' then
|
||||
skip
|
||||
let acc' := 10*acc + (c.val - '0'.val).toNat
|
||||
natCoreNumDigits acc' (digits+1)
|
||||
else
|
||||
return (acc, digits)
|
||||
|
||||
@[inline]
|
||||
def lookahead (p : Char → Prop) (desc : String) [DecidablePred p] : Parser Unit := do
|
||||
@@ -80,129 +94,152 @@ def lookahead (p : Char → Prop) (desc : String) [DecidablePred p] : Parser Uni
|
||||
|
||||
@[inline]
|
||||
def natNonZero : Parser Nat := do
|
||||
lookahead (fun c => '1' ≤ c ∧ c ≤ '9') "1-9"
|
||||
let (n, _) ← natCore 0 0
|
||||
return n
|
||||
lookahead (fun c => '1' <= c && c <= '9') "1-9"
|
||||
natCore 0
|
||||
|
||||
@[inline]
|
||||
def natNumDigits : Parser (Nat × Nat) := do
|
||||
lookahead (fun c => '0' ≤ c ∧ c ≤ '9') "digit"
|
||||
natCore 0 0
|
||||
lookahead (fun c => '0' <= c && c <= '9') "digit"
|
||||
natCoreNumDigits 0 0
|
||||
|
||||
@[inline]
|
||||
def natMaybeZero : Parser Nat := do
|
||||
let (n, _) ← natNumDigits
|
||||
return n
|
||||
lookahead (fun c => '0' <= c && c <= '9') "0-9"
|
||||
natCore 0
|
||||
|
||||
def num : Parser JsonNumber := do
|
||||
@[inline]
|
||||
def numSign : Parser Int := do
|
||||
let c ← peek!
|
||||
let sign ← if c = '-' then
|
||||
let sign ← if c == '-' then
|
||||
skip
|
||||
pure (-1 : Int)
|
||||
return (-1 : Int)
|
||||
else
|
||||
pure 1
|
||||
return 1
|
||||
|
||||
@[inline]
|
||||
def nat : Parser Nat := do
|
||||
let c ← peek!
|
||||
let res ← if c = '0' then
|
||||
if c == '0' then
|
||||
skip
|
||||
pure 0
|
||||
return 0
|
||||
else
|
||||
natNonZero
|
||||
let c? ← peek?
|
||||
let res : JsonNumber ← if c? = some '.' then
|
||||
skip
|
||||
let (n, d) ← natNumDigits
|
||||
if d > USize.size then fail "too many decimals"
|
||||
let mantissa' := sign * (res * (10^d : Nat) + n)
|
||||
let exponent' := d
|
||||
pure <| JsonNumber.mk mantissa' exponent'
|
||||
|
||||
@[inline]
|
||||
def numWithDecimals : Parser JsonNumber := do
|
||||
let sign ← numSign
|
||||
let whole ← nat
|
||||
if ← isEof then
|
||||
pure <| JsonNumber.fromInt (sign * whole)
|
||||
else
|
||||
pure <| JsonNumber.fromInt (sign * res)
|
||||
let c? ← peek?
|
||||
if c? = some 'e' ∨ c? = some 'E' then
|
||||
skip
|
||||
let c ← peek!
|
||||
if c = '-' then
|
||||
if c == '.' then
|
||||
skip
|
||||
let n ← natMaybeZero
|
||||
return res.shiftr n
|
||||
let (n, d) ← natNumDigits
|
||||
if d > USize.size then fail "too many decimals"
|
||||
let mantissa' := sign * (whole * (10^d : Nat) + n)
|
||||
let exponent' := d
|
||||
pure <| JsonNumber.mk mantissa' exponent'
|
||||
else
|
||||
if c = '+' then skip
|
||||
let n ← natMaybeZero
|
||||
if n > USize.size then fail "exp too large"
|
||||
return res.shiftl n
|
||||
else
|
||||
return res
|
||||
pure <| JsonNumber.fromInt (sign * whole)
|
||||
|
||||
partial def arrayCore (anyCore : Parser Json) (acc : Array Json) : Parser (Array Json) := do
|
||||
let hd ← anyCore
|
||||
let acc' := acc.push hd
|
||||
let c ← any
|
||||
if c = ']' then
|
||||
ws
|
||||
return acc'
|
||||
else if c = ',' then
|
||||
ws
|
||||
arrayCore anyCore acc'
|
||||
@[inline]
|
||||
def exponent (value : JsonNumber) : Parser JsonNumber := do
|
||||
if ← isEof then
|
||||
return value
|
||||
else
|
||||
fail "unexpected character in array"
|
||||
|
||||
partial def objectCore (anyCore : Parser Json) : Parser (RBNode String (fun _ => Json)) := do
|
||||
lookahead (fun c => c = '"') "\""; skip; -- "
|
||||
let k ← strCore ""; ws
|
||||
lookahead (fun c => c = ':') ":"; skip; ws
|
||||
let v ← anyCore
|
||||
let c ← any
|
||||
if c = '}' then
|
||||
ws
|
||||
return RBNode.singleton k v
|
||||
else if c = ',' then
|
||||
ws
|
||||
let kvs ← objectCore anyCore
|
||||
return kvs.insert compare k v
|
||||
else
|
||||
fail "unexpected character in object"
|
||||
|
||||
partial def anyCore : Parser Json := do
|
||||
let c ← peek!
|
||||
if c = '[' then
|
||||
skip; ws
|
||||
let c ← peek!
|
||||
if c = ']' then
|
||||
skip; ws
|
||||
return Json.arr (Array.mkEmpty 0)
|
||||
if c == 'e' || c == 'E' then
|
||||
skip
|
||||
let c ← peek!
|
||||
if c == '-' then
|
||||
skip
|
||||
let n ← natMaybeZero
|
||||
return value.shiftr n
|
||||
else
|
||||
if c = '+' then skip
|
||||
let n ← natMaybeZero
|
||||
if n > USize.size then fail "exp too large"
|
||||
return value.shiftl n
|
||||
else
|
||||
let a ← arrayCore anyCore (Array.mkEmpty 4)
|
||||
return Json.arr a
|
||||
else if c = '{' then
|
||||
skip; ws
|
||||
let c ← peek!
|
||||
if c = '}' then
|
||||
skip; ws
|
||||
return Json.obj (RBNode.leaf)
|
||||
else
|
||||
let kvs ← objectCore anyCore
|
||||
return Json.obj kvs
|
||||
else if c = '\"' then
|
||||
skip
|
||||
let s ← strCore ""
|
||||
ws
|
||||
return Json.str s
|
||||
else if c = 'f' then
|
||||
skipString "false"; ws
|
||||
return Json.bool false
|
||||
else if c = 't' then
|
||||
skipString "true"; ws
|
||||
return Json.bool true
|
||||
else if c = 'n' then
|
||||
skipString "null"; ws
|
||||
return Json.null
|
||||
else if c = '-' ∨ ('0' ≤ c ∧ c ≤ '9') then
|
||||
let n ← num
|
||||
ws
|
||||
return Json.num n
|
||||
else
|
||||
fail "unexpected input"
|
||||
return value
|
||||
|
||||
def num : Parser JsonNumber := do
|
||||
let res : JsonNumber ← numWithDecimals
|
||||
exponent res
|
||||
|
||||
mutual
|
||||
|
||||
partial def arrayCore (acc : Array Json) : Parser (Array Json) := do
|
||||
let hd ← anyCore
|
||||
let acc' := acc.push hd
|
||||
let c ← any
|
||||
if c == ']' then
|
||||
ws
|
||||
return acc'
|
||||
else if c == ',' then
|
||||
ws
|
||||
arrayCore acc'
|
||||
else
|
||||
fail "unexpected character in array"
|
||||
|
||||
partial def objectCore (kvs : RBNode String (fun _ => Json)) : Parser (RBNode String (fun _ => Json)) := do
|
||||
lookahead (fun c => c == '"') "\""; skip;
|
||||
let k ← str; ws
|
||||
lookahead (fun c => c == ':') ":"; skip; ws
|
||||
let v ← anyCore
|
||||
let c ← any
|
||||
if c == '}' then
|
||||
ws
|
||||
return kvs.insert compare k v
|
||||
else if c == ',' then
|
||||
ws
|
||||
objectCore (kvs.insert compare k v)
|
||||
else
|
||||
fail "unexpected character in object"
|
||||
|
||||
partial def anyCore : Parser Json := do
|
||||
let c ← peek!
|
||||
if c == '[' then
|
||||
skip; ws
|
||||
let c ← peek!
|
||||
if c == ']' then
|
||||
skip; ws
|
||||
return Json.arr (Array.mkEmpty 0)
|
||||
else
|
||||
let a ← arrayCore (Array.mkEmpty 4)
|
||||
return Json.arr a
|
||||
else if c == '{' then
|
||||
skip; ws
|
||||
let c ← peek!
|
||||
if c == '}' then
|
||||
skip; ws
|
||||
return Json.obj (RBNode.leaf)
|
||||
else
|
||||
let kvs ← objectCore RBNode.leaf
|
||||
return Json.obj kvs
|
||||
else if c == '\"' then
|
||||
skip
|
||||
let s ← str
|
||||
ws
|
||||
return Json.str s
|
||||
else if c == 'f' then
|
||||
skipString "false"; ws
|
||||
return Json.bool false
|
||||
else if c == 't' then
|
||||
skipString "true"; ws
|
||||
return Json.bool true
|
||||
else if c == 'n' then
|
||||
skipString "null"; ws
|
||||
return Json.null
|
||||
else if c == '-' || ('0' <= c && c <= '9') then
|
||||
let n ← num
|
||||
ws
|
||||
return Json.num n
|
||||
else
|
||||
fail "unexpected input"
|
||||
|
||||
end
|
||||
|
||||
def any : Parser Json := do
|
||||
ws
|
||||
@@ -215,9 +252,7 @@ end Json.Parser
|
||||
namespace Json
|
||||
|
||||
def parse (s : String) : Except String Lean.Json :=
|
||||
match Json.Parser.any s.mkIterator with
|
||||
| Parsec.ParseResult.success _ res => Except.ok res
|
||||
| Parsec.ParseResult.error it err => Except.error s!"offset {repr it.i.byteIdx}: {err}"
|
||||
Parser.run Json.Parser.any s
|
||||
|
||||
end Json
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ Authors: Gabriel Ebner, Marc Huisinga, Wojciech Nawrocki
|
||||
prelude
|
||||
import Lean.Data.Format
|
||||
import Lean.Data.Json.Basic
|
||||
import Init.Data.List.Impl
|
||||
|
||||
namespace Lean
|
||||
namespace Json
|
||||
|
||||
@@ -22,27 +22,23 @@ reduce the size of the resulting JSON. -/
|
||||
/--
|
||||
Identifier of a reference.
|
||||
-/
|
||||
-- Names are represented by strings to avoid having to parse them to `Name`,
|
||||
-- which is relatively expensive. Most uses of these names only need equality, anyways.
|
||||
inductive RefIdent where
|
||||
/-- Named identifier. These are used in all references that are globally available. -/
|
||||
| const (moduleName : Name) (identName : Name) : RefIdent
|
||||
| const (moduleName : String) (identName : String) : RefIdent
|
||||
/-- Unnamed identifier. These are used for all local references. -/
|
||||
| fvar (moduleName : Name) (id : FVarId) : RefIdent
|
||||
| fvar (moduleName : String) (id : String) : RefIdent
|
||||
deriving BEq, Hashable, Inhabited
|
||||
|
||||
namespace RefIdent
|
||||
|
||||
instance : ToJson FVarId where
|
||||
toJson id := toJson id.name
|
||||
|
||||
instance : FromJson FVarId where
|
||||
fromJson? s := return ⟨← fromJson? s⟩
|
||||
|
||||
/-- Shortened representation of `RefIdent` for more compact serialization. -/
|
||||
inductive RefIdentJsonRepr
|
||||
/-- Shortened representation of `RefIdent.const` for more compact serialization. -/
|
||||
| c (m n : Name)
|
||||
| c (m n : String)
|
||||
/-- Shortened representation of `RefIdent.fvar` for more compact serialization. -/
|
||||
| f (m : Name) (i : FVarId)
|
||||
| f (m : String) (i : String)
|
||||
deriving FromJson, ToJson
|
||||
|
||||
/-- Converts `id` to its compact serialization representation. -/
|
||||
@@ -74,7 +70,7 @@ end RefIdent
|
||||
/-- Information about the declaration surrounding a reference. -/
|
||||
structure RefInfo.ParentDecl where
|
||||
/-- Name of the declaration surrounding a reference. -/
|
||||
name : Name
|
||||
name : String
|
||||
/-- Range of the declaration surrounding a reference. -/
|
||||
range : Lsp.Range
|
||||
/-- Selection range of the declaration surrounding a reference. -/
|
||||
@@ -104,7 +100,7 @@ instance : ToJson RefInfo where
|
||||
let rangeToList (r : Lsp.Range) : List Nat :=
|
||||
[r.start.line, r.start.character, r.end.line, r.end.character]
|
||||
let parentDeclToList (d : RefInfo.ParentDecl) : List Json :=
|
||||
let name := d.name.toString |> toJson
|
||||
let name := d.name |> toJson
|
||||
let range := rangeToList d.range |>.map toJson
|
||||
let selectionRange := rangeToList d.selectionRange |>.map toJson
|
||||
[name] ++ range ++ selectionRange
|
||||
@@ -118,34 +114,42 @@ instance : ToJson RefInfo where
|
||||
]
|
||||
|
||||
instance : FromJson RefInfo where
|
||||
-- This implementation is optimized to prevent redundant intermediate allocations.
|
||||
fromJson? j := do
|
||||
let toRange : List Nat → Except String Lsp.Range
|
||||
| [sLine, sChar, eLine, eChar] => pure ⟨⟨sLine, sChar⟩, ⟨eLine, eChar⟩⟩
|
||||
| l => throw s!"Expected list of length 4, not {l.length}"
|
||||
let toParentDecl (a : Array Json) : Except String RefInfo.ParentDecl := do
|
||||
let name := String.toName <| ← fromJson? a[0]!
|
||||
let range ← a[1:5].toArray.toList |>.mapM fromJson?
|
||||
let range ← toRange range
|
||||
let selectionRange ← a[5:].toArray.toList |>.mapM fromJson?
|
||||
let selectionRange ← toRange selectionRange
|
||||
let toRange (a : Array Json) (i : Nat) : Except String Lsp.Range :=
|
||||
if h : a.size < i + 4 then
|
||||
throw s!"Expected list of length 4, not {a.size}"
|
||||
else
|
||||
return {
|
||||
start := {
|
||||
line := ← fromJson? a[i]
|
||||
character := ← fromJson? a[i+1]
|
||||
}
|
||||
«end» := {
|
||||
line := ← fromJson? a[i+2]
|
||||
character := ← fromJson? a[i+3]
|
||||
}
|
||||
}
|
||||
let toParentDecl (a : Array Json) (i : Nat) : Except String RefInfo.ParentDecl := do
|
||||
let name ← fromJson? a[i]!
|
||||
let range ← toRange a (i + 1)
|
||||
let selectionRange ← toRange a (i + 5)
|
||||
return ⟨name, range, selectionRange⟩
|
||||
let toLocation (l : List Json) : Except String RefInfo.Location := do
|
||||
let l := l.toArray
|
||||
if l.size != 4 && l.size != 13 then
|
||||
let toLocation (a : Array Json) : Except String RefInfo.Location := do
|
||||
if a.size != 4 && a.size != 13 then
|
||||
.error "Expected list of length 4 or 13, not {l.size}"
|
||||
let range ← l[:4].toArray.toList |>.mapM fromJson?
|
||||
let range ← toRange range
|
||||
if l.size == 13 then
|
||||
let parentDecl ← toParentDecl l[4:].toArray
|
||||
let range ← toRange a 0
|
||||
if a.size == 13 then
|
||||
let parentDecl ← toParentDecl a 4
|
||||
return ⟨range, parentDecl⟩
|
||||
else
|
||||
return ⟨range, none⟩
|
||||
|
||||
let definition? ← j.getObjValAs? (Option $ List Json) "definition"
|
||||
let definition? ← j.getObjValAs? (Option $ Array Json) "definition"
|
||||
let definition? ← match definition? with
|
||||
| none => pure none
|
||||
| some list => some <$> toLocation list
|
||||
let usages ← j.getObjValAs? (Array $ List Json) "usages"
|
||||
| some array => some <$> toLocation array
|
||||
let usages ← j.getObjValAs? (Array $ Array Json) "usages"
|
||||
let usages ← usages.mapM toLocation
|
||||
pure { definition?, usages }
|
||||
|
||||
|
||||
@@ -4,18 +4,19 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Author: Dany Fabian
|
||||
-/
|
||||
prelude
|
||||
import Lean.Data.Parsec
|
||||
import Std.Internal.Parsec
|
||||
import Lean.Data.Xml.Basic
|
||||
|
||||
open System
|
||||
open Lean
|
||||
|
||||
namespace Lean
|
||||
namespace Xml
|
||||
|
||||
namespace Parser
|
||||
open Std.Internal.Parsec
|
||||
open Std.Internal.Parsec.String
|
||||
|
||||
open Lean.Parsec
|
||||
open Lean.Parsec.String
|
||||
namespace Parser
|
||||
|
||||
abbrev LeanChar := Char
|
||||
|
||||
@@ -481,8 +482,6 @@ def document : Parser Element := prolog *> element <* many Misc <* eof
|
||||
end Parser
|
||||
|
||||
def parse (s : String) : Except String Element :=
|
||||
match Xml.Parser.document s.mkIterator with
|
||||
| .success _ res => Except.ok res
|
||||
| .error it err => Except.error s!"offset {it.i.byteIdx.repr}: {err}\n{(it.prevn 10).extract it}"
|
||||
Parser.run Xml.Parser.document s
|
||||
|
||||
end Xml
|
||||
|
||||
@@ -17,7 +17,7 @@ register_builtin_option autoImplicit : Bool := {
|
||||
|
||||
register_builtin_option relaxedAutoImplicit : Bool := {
|
||||
defValue := true
|
||||
descr := "When \"relaxed\" mode is enabled, any atomic nonempty identifier is eligible for auto bound implicit locals (see optin `autoBoundImplicitLocal`."
|
||||
descr := "When \"relaxed\" mode is enabled, any atomic nonempty identifier is eligible for auto bound implicit locals (see option `autoImplicit`)."
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -176,7 +176,8 @@ private def replaceBinderAnnotation (binder : TSyntax ``Parser.Term.bracketedBin
|
||||
let mut binderIds := binderIds
|
||||
let mut binderIdsIniSize := binderIds.size
|
||||
let mut modifiedVarDecls := false
|
||||
for varDecl in varDecls do
|
||||
-- Go through declarations in reverse to respect shadowing
|
||||
for varDecl in varDecls.reverse do
|
||||
let (ids, ty?, explicit') ← match varDecl with
|
||||
| `(bracketedBinderF|($ids* $[: $ty?]? $(annot?)?)) =>
|
||||
if annot?.isSome then
|
||||
@@ -208,7 +209,7 @@ private def replaceBinderAnnotation (binder : TSyntax ``Parser.Term.bracketedBin
|
||||
`(bracketedBinderF| ($id $[: $ty?]?))
|
||||
else
|
||||
`(bracketedBinderF| {$id $[: $ty?]?})
|
||||
for id in ids do
|
||||
for id in ids.reverse do
|
||||
if let some idx := binderIds.findIdx? fun binderId => binderId.raw.isIdent && binderId.raw.getId == id.raw.getId then
|
||||
binderIds := binderIds.eraseIdx idx
|
||||
modifiedVarDecls := true
|
||||
@@ -216,7 +217,7 @@ private def replaceBinderAnnotation (binder : TSyntax ``Parser.Term.bracketedBin
|
||||
else
|
||||
varDeclsNew := varDeclsNew.push (← mkBinder id explicit')
|
||||
if modifiedVarDecls then
|
||||
modifyScope fun scope => { scope with varDecls := varDeclsNew }
|
||||
modifyScope fun scope => { scope with varDecls := varDeclsNew.reverse }
|
||||
if binderIds.size != binderIdsIniSize then
|
||||
binderIds.mapM fun binderId =>
|
||||
if explicit then
|
||||
@@ -228,15 +229,14 @@ private def replaceBinderAnnotation (binder : TSyntax ``Parser.Term.bracketedBin
|
||||
|
||||
@[builtin_command_elab «variable»] def elabVariable : CommandElab
|
||||
| `(variable $binders*) => do
|
||||
let binders ← binders.concatMapM replaceBinderAnnotation
|
||||
-- Try to elaborate `binders` for sanity checking
|
||||
runTermElabM fun _ => Term.withSynthesize <| Term.withAutoBoundImplicit <|
|
||||
Term.elabBinders binders fun _ => pure ()
|
||||
-- Remark: if we want to produce error messages when variables shadow existing ones, here is the place to do it.
|
||||
for binder in binders do
|
||||
let binders ← replaceBinderAnnotation binder
|
||||
-- Remark: if we want to produce error messages when variables shadow existing ones, here is the place to do it.
|
||||
for binder in binders do
|
||||
let varUIds ← getBracketedBinderIds binder |>.mapM (withFreshMacroScope ∘ MonadQuotation.addMacroScope)
|
||||
modifyScope fun scope => { scope with varDecls := scope.varDecls.push binder, varUIds := scope.varUIds ++ varUIds }
|
||||
let varUIds ← (← getBracketedBinderIds binder) |>.mapM (withFreshMacroScope ∘ MonadQuotation.addMacroScope)
|
||||
modifyScope fun scope => { scope with varDecls := scope.varDecls.push binder, varUIds := scope.varUIds ++ varUIds }
|
||||
| _ => throwUnsupportedSyntax
|
||||
|
||||
open Meta
|
||||
@@ -504,12 +504,60 @@ def elabRunMeta : CommandElab := fun stx =>
|
||||
|
||||
@[builtin_command_elab Lean.Parser.Command.include] def elabInclude : CommandElab
|
||||
| `(Lean.Parser.Command.include| include $ids*) => do
|
||||
let vars := (← getScope).varDecls.concatMap getBracketedBinderIds
|
||||
let sc ← getScope
|
||||
let vars ← sc.varDecls.concatMapM getBracketedBinderIds
|
||||
let mut uids := #[]
|
||||
for id in ids do
|
||||
unless vars.contains id.getId do
|
||||
if let some idx := vars.findIdx? (· == id.getId) then
|
||||
uids := uids.push sc.varUIds[idx]!
|
||||
else
|
||||
throwError "invalid 'include', variable '{id}' has not been declared in the current scope"
|
||||
modifyScope fun sc =>
|
||||
{ sc with includedVars := sc.includedVars ++ ids.toList.map (·.getId) }
|
||||
modifyScope fun sc => { sc with
|
||||
includedVars := sc.includedVars ++ uids.toList
|
||||
omittedVars := sc.omittedVars.filter (!uids.contains ·) }
|
||||
| _ => throwUnsupportedSyntax
|
||||
|
||||
@[builtin_command_elab Lean.Parser.Command.omit] def elabOmit : CommandElab
|
||||
| `(Lean.Parser.Command.omit| omit $omits*) => do
|
||||
-- TODO: this really shouldn't have to re-elaborate section vars... they should come
|
||||
-- pre-elaborated
|
||||
let omittedVars ← runTermElabM fun vars => do
|
||||
Term.synthesizeSyntheticMVarsNoPostponing
|
||||
-- We don't want to store messages produced when elaborating `(getVarDecls s)` because they have already been saved when we elaborated the `variable`(s) command.
|
||||
-- So, we use `Core.resetMessageLog`.
|
||||
Core.resetMessageLog
|
||||
-- resolve each omit to variable user name or type pattern
|
||||
let elaboratedOmits : Array (Sum Name Expr) ← omits.mapM fun
|
||||
| `(ident| $id:ident) => pure <| Sum.inl id.getId
|
||||
| `(Lean.Parser.Term.instBinder| [$id : $_]) => pure <| Sum.inl id.getId
|
||||
| `(Lean.Parser.Term.instBinder| [$ty]) =>
|
||||
Sum.inr <$> Term.withoutErrToSorry (Term.elabTermAndSynthesize ty none)
|
||||
| _ => throwUnsupportedSyntax
|
||||
-- check that each omit is actually used in the end
|
||||
let mut omitsUsed := omits.map fun _ => false
|
||||
let mut omittedVars := #[]
|
||||
let mut revSectionFVars : Std.HashMap FVarId Name := {}
|
||||
for (uid, var) in (← read).sectionFVars do
|
||||
revSectionFVars := revSectionFVars.insert var.fvarId! uid
|
||||
for var in vars do
|
||||
let ldecl ← var.fvarId!.getDecl
|
||||
if let some idx := (← elaboratedOmits.findIdxM? fun
|
||||
| .inl id => return ldecl.userName == id
|
||||
| .inr ty => do
|
||||
let mctx ← getMCtx
|
||||
isDefEq ty ldecl.type <* setMCtx mctx) then
|
||||
if let some uid := revSectionFVars[var.fvarId!]? then
|
||||
omittedVars := omittedVars.push uid
|
||||
omitsUsed := omitsUsed.set! idx true
|
||||
else
|
||||
throwError "invalid 'omit', '{ldecl.userName}' has not been declared in the current scope"
|
||||
for o in omits, used in omitsUsed do
|
||||
unless used do
|
||||
throwError "'{o}' did not match any variables in the current scope"
|
||||
return omittedVars
|
||||
modifyScope fun sc => { sc with
|
||||
omittedVars := sc.omittedVars ++ omittedVars.toList
|
||||
includedVars := sc.includedVars.filter (!omittedVars.contains ·) }
|
||||
| _ => throwUnsupportedSyntax
|
||||
|
||||
@[builtin_command_elab Parser.Command.exit] def elabExit : CommandElab := fun _ =>
|
||||
|
||||
@@ -51,8 +51,6 @@ structure Scope where
|
||||
even if they do not work with binders per se.
|
||||
-/
|
||||
varDecls : Array (TSyntax ``Parser.Term.bracketedBinder) := #[]
|
||||
/-- `include`d section variable names -/
|
||||
includedVars : List Name := []
|
||||
/--
|
||||
Globally unique internal identifiers for the `varDecls`.
|
||||
There is one identifier per variable introduced by the binders
|
||||
@@ -62,6 +60,10 @@ structure Scope where
|
||||
that capture these variables.
|
||||
-/
|
||||
varUIds : Array Name := #[]
|
||||
/-- `include`d section variable names (from `varUIds`) -/
|
||||
includedVars : List Name := []
|
||||
/-- `omit`ted section variable names (from `varUIds`) -/
|
||||
omittedVars : List Name := []
|
||||
/--
|
||||
If true (default: false), all declarations that fail to compile
|
||||
automatically receive the `noncomputable` modifier.
|
||||
@@ -555,19 +557,21 @@ private def mkMetaContext : Meta.Context := {
|
||||
|
||||
open Lean.Parser.Term in
|
||||
/-- Return identifier names in the given bracketed binder. -/
|
||||
def getBracketedBinderIds : Syntax → Array Name
|
||||
| `(bracketedBinderF|($ids* $[: $ty?]? $(_annot?)?)) => ids.map Syntax.getId
|
||||
| `(bracketedBinderF|{$ids* $[: $ty?]?}) => ids.map Syntax.getId
|
||||
| `(bracketedBinderF|[$id : $_]) => #[id.getId]
|
||||
| `(bracketedBinderF|[$_]) => #[Name.anonymous]
|
||||
| _ => #[]
|
||||
def getBracketedBinderIds : Syntax → CommandElabM (Array Name)
|
||||
| `(bracketedBinderF|($ids* $[: $ty?]? $(_annot?)?)) => return ids.map Syntax.getId
|
||||
| `(bracketedBinderF|{$ids* $[: $ty?]?}) => return ids.map Syntax.getId
|
||||
| `(bracketedBinderF|⦃$ids* : $_⦄) => return ids.map Syntax.getId
|
||||
| `(bracketedBinderF|[$id : $_]) => return #[id.getId]
|
||||
| `(bracketedBinderF|[$_]) => return #[Name.anonymous]
|
||||
| _ => throwUnsupportedSyntax
|
||||
|
||||
private def mkTermContext (ctx : Context) (s : State) : Term.Context := Id.run do
|
||||
private def mkTermContext (ctx : Context) (s : State) : CommandElabM Term.Context := do
|
||||
let scope := s.scopes.head!
|
||||
let mut sectionVars := {}
|
||||
for id in scope.varDecls.concatMap getBracketedBinderIds, uid in scope.varUIds do
|
||||
for id in (← scope.varDecls.concatMapM getBracketedBinderIds), uid in scope.varUIds do
|
||||
sectionVars := sectionVars.insert id uid
|
||||
{ macroStack := ctx.macroStack
|
||||
return {
|
||||
macroStack := ctx.macroStack
|
||||
sectionVars := sectionVars
|
||||
isNoncomputableSection := scope.isNoncomputable
|
||||
tacticCache? := ctx.tacticCache? }
|
||||
@@ -607,7 +611,7 @@ def liftTermElabM (x : TermElabM α) : CommandElabM α := do
|
||||
-- make sure `observing` below also catches runtime exceptions (like we do by default in
|
||||
-- `CommandElabM`)
|
||||
let _ := MonadAlwaysExcept.except (m := TermElabM)
|
||||
let x : MetaM _ := (observing (try x finally Meta.reportDiag)).run (mkTermContext ctx s) { levelNames := scope.levelNames }
|
||||
let x : MetaM _ := (observing (try x finally Meta.reportDiag)).run (← mkTermContext ctx s) { levelNames := scope.levelNames }
|
||||
let x : CoreM _ := x.run mkMetaContext {}
|
||||
let ((ea, _), _) ← runCore x
|
||||
MonadExcept.ofExcept ea
|
||||
@@ -704,7 +708,7 @@ def expandDeclId (declId : Syntax) (modifiers : Modifiers) : CommandElabM Expand
|
||||
let currNamespace ← getCurrNamespace
|
||||
let currLevelNames ← getLevelNames
|
||||
let r ← Elab.expandDeclId currNamespace currLevelNames declId modifiers
|
||||
for id in (← getScope).varDecls.concatMap getBracketedBinderIds do
|
||||
for id in (← (← getScope).varDecls.concatMapM getBracketedBinderIds) do
|
||||
if id == r.shortName then
|
||||
throwError "invalid declaration name '{r.shortName}', there is a section variable with the same name"
|
||||
return r
|
||||
|
||||
@@ -163,7 +163,7 @@ def MessageOrdering.apply (mode : MessageOrdering) (msgs : List String) : List S
|
||||
let diff := Diff.diff (expected.split (· == '\n')).toArray (res.split (· == '\n')).toArray
|
||||
Diff.linesToString diff
|
||||
else res
|
||||
logErrorAt tk m!"❌ Docstring on `#guard_msgs` does not match generated message:\n\n{feedback}"
|
||||
logErrorAt tk m!"❌️ Docstring on `#guard_msgs` does not match generated message:\n\n{feedback}"
|
||||
pushInfoLeaf (.ofCustomInfo { stx := ← getRef, value := Dynamic.mk (GuardMsgFailure.mk res) })
|
||||
| _ => throwUnsupportedSyntax
|
||||
|
||||
|
||||
@@ -336,30 +336,42 @@ def instantiateMVarsProfiling (e : Expr) : MetaM Expr := do
|
||||
/--
|
||||
Runs `k` with a restricted local context where only section variables from `vars` are included that
|
||||
* are directly referenced in any `headers`,
|
||||
* are included in `includedVars` (via the `include` command),
|
||||
* are included in `sc.includedVars` (via the `include` command),
|
||||
* are directly referenced in any variable included by these rules, OR
|
||||
* are instance-implicit variables that only reference section variables included by these rules.
|
||||
* are instance-implicit variables that only reference section variables included by these rules AND
|
||||
are not listed in `sc.omittedVars` (via `omit`; note that `omit` also subtracts from
|
||||
`sc.includedVars`).
|
||||
-/
|
||||
private def withHeaderSecVars {α} (vars : Array Expr) (includedVars : List Name) (headers : Array DefViewElabHeader)
|
||||
private def withHeaderSecVars {α} (vars : Array Expr) (sc : Command.Scope) (headers : Array DefViewElabHeader)
|
||||
(k : Array Expr → TermElabM α) : TermElabM α := do
|
||||
let (_, used) ← collectUsed.run {}
|
||||
let mut revSectionFVars : Std.HashMap FVarId Name := {}
|
||||
for (uid, var) in (← read).sectionFVars do
|
||||
revSectionFVars := revSectionFVars.insert var.fvarId! uid
|
||||
let (_, used) ← collectUsed revSectionFVars |>.run {}
|
||||
let (lctx, localInsts, vars) ← removeUnused vars used
|
||||
withLCtx lctx localInsts <| k vars
|
||||
where
|
||||
collectUsed : StateRefT CollectFVars.State MetaM Unit := do
|
||||
collectUsed revSectionFVars : StateRefT CollectFVars.State MetaM Unit := do
|
||||
-- directly referenced in headers
|
||||
headers.forM (·.type.collectFVars)
|
||||
-- included by `include`
|
||||
vars.forM fun var => do
|
||||
let ldecl ← getFVarLocalDecl var
|
||||
if includedVars.contains ldecl.userName then
|
||||
modify (·.add ldecl.fvarId)
|
||||
for var in vars do
|
||||
if let some uid := revSectionFVars[var.fvarId!]? then
|
||||
if sc.includedVars.contains uid then
|
||||
modify (·.add var.fvarId!)
|
||||
-- transitively referenced
|
||||
get >>= (·.addDependencies) >>= set
|
||||
for var in (← get).fvarIds do
|
||||
if let some uid := revSectionFVars[var]? then
|
||||
if sc.omittedVars.contains uid then
|
||||
throwError "cannot omit referenced section variable '{Expr.fvar var}'"
|
||||
-- instances (`addDependencies` unnecessary as by definition they may only reference variables
|
||||
-- already included)
|
||||
vars.forM fun var => do
|
||||
for var in vars do
|
||||
let ldecl ← getFVarLocalDecl var
|
||||
if let some uid := revSectionFVars[var.fvarId!]? then
|
||||
if sc.omittedVars.contains uid then
|
||||
continue
|
||||
let st ← get
|
||||
if ldecl.binderInfo.isInstImplicit && (← getFVars ldecl.type).all st.fvarSet.contains then
|
||||
modify (·.add ldecl.fvarId)
|
||||
@@ -371,7 +383,12 @@ register_builtin_option deprecated.oldSectionVars : Bool := {
|
||||
descr := "re-enable deprecated behavior of including exactly the section variables used in a declaration"
|
||||
}
|
||||
|
||||
private def elabFunValues (headers : Array DefViewElabHeader) (vars : Array Expr) (includedVars : List Name) : TermElabM (Array Expr) :=
|
||||
register_builtin_option linter.unusedSectionVars : Bool := {
|
||||
defValue := true
|
||||
descr := "enable the 'unused section variables in theorem body' linter"
|
||||
}
|
||||
|
||||
private def elabFunValues (headers : Array DefViewElabHeader) (vars : Array Expr) (sc : Command.Scope) : TermElabM (Array Expr) :=
|
||||
headers.mapM fun header => do
|
||||
let mut reusableResult? := none
|
||||
if let some snap := header.bodySnap? then
|
||||
@@ -386,7 +403,7 @@ private def elabFunValues (headers : Array DefViewElabHeader) (vars : Array Expr
|
||||
withReuseContext header.value do
|
||||
withDeclName header.declName <| withLevelNames header.levelNames do
|
||||
let valStx ← liftMacroM <| declValToTerm header.value
|
||||
(if header.kind.isTheorem && !deprecated.oldSectionVars.get (← getOptions) then withHeaderSecVars vars includedVars #[header] else fun x => x #[]) fun vars => do
|
||||
(if header.kind.isTheorem && !deprecated.oldSectionVars.get (← getOptions) then withHeaderSecVars vars sc #[header] else fun x => x #[]) fun vars => do
|
||||
forallBoundedTelescope header.type header.numParams fun xs type => do
|
||||
-- Add new info nodes for new fvars. The server will detect all fvars of a binder by the binder's source location.
|
||||
for i in [0:header.binderIds.size] do
|
||||
@@ -399,18 +416,25 @@ private def elabFunValues (headers : Array DefViewElabHeader) (vars : Array Expr
|
||||
-- leads to more section variables being included than necessary
|
||||
let val ← instantiateMVarsProfiling val
|
||||
let val ← mkLambdaFVars xs val
|
||||
unless header.type.hasSorry || val.hasSorry do
|
||||
for var in vars do
|
||||
unless header.type.containsFVar var.fvarId! ||
|
||||
val.containsFVar var.fvarId! ||
|
||||
(← vars.anyM (fun v => return (← v.fvarId!.getType).containsFVar var.fvarId!)) do
|
||||
let varDecl ← var.fvarId!.getDecl
|
||||
let var := if varDecl.userName.hasMacroScopes && varDecl.binderInfo.isInstImplicit then
|
||||
m!"[{varDecl.type}]".group
|
||||
if linter.unusedSectionVars.get (← getOptions) && !header.type.hasSorry && !val.hasSorry then
|
||||
let unusedVars ← vars.filterMapM fun var => do
|
||||
let varDecl ← var.fvarId!.getDecl
|
||||
return if sc.includedVars.contains varDecl.userName ||
|
||||
header.type.containsFVar var.fvarId! || val.containsFVar var.fvarId! ||
|
||||
(← vars.anyM (fun v => return (← v.fvarId!.getType).containsFVar var.fvarId!)) then
|
||||
none
|
||||
else
|
||||
if varDecl.userName.hasMacroScopes && varDecl.binderInfo.isInstImplicit then
|
||||
some m!"[{varDecl.type}]"
|
||||
else
|
||||
var
|
||||
logWarningAt header.ref m!"included section variable '{var}' is not used in \
|
||||
'{header.declName}', consider excluding it"
|
||||
some m!"{var}"
|
||||
if unusedVars.size > 0 then
|
||||
Linter.logLint linter.unusedSectionVars header.ref
|
||||
m!"automatically included section variable(s) unused in theorem '{header.declName}':\
|
||||
\n {MessageData.joinSep unusedVars.toList "\n "}\
|
||||
\nconsider restructuring your `variable` declarations so that the variables are not \
|
||||
in scope or explicitly omit them:\
|
||||
\n omit {MessageData.joinSep unusedVars.toList " "} in theorem ..."
|
||||
return val
|
||||
if let some snap := header.bodySnap? then
|
||||
snap.new.resolve <| some {
|
||||
@@ -962,7 +986,7 @@ partial def checkForHiddenUnivLevels (allUserLevelNames : List Name) (preDefs :
|
||||
for preDef in preDefs do
|
||||
checkPreDef preDef
|
||||
|
||||
def elabMutualDef (vars : Array Expr) (includedVars : List Name) (views : Array DefView) : TermElabM Unit :=
|
||||
def elabMutualDef (vars : Array Expr) (sc : Command.Scope) (views : Array DefView) : TermElabM Unit :=
|
||||
if isExample views then
|
||||
withoutModifyingEnv do
|
||||
-- save correct environment in info tree
|
||||
@@ -983,7 +1007,7 @@ where
|
||||
addLocalVarInfo view.declId funFVar
|
||||
let values ←
|
||||
try
|
||||
let values ← elabFunValues headers vars includedVars
|
||||
let values ← elabFunValues headers vars sc
|
||||
Term.synthesizeSyntheticMVarsNoPostponing
|
||||
values.mapM (instantiateMVarsProfiling ·)
|
||||
catch ex =>
|
||||
@@ -993,7 +1017,7 @@ where
|
||||
let letRecsToLift ← getLetRecsToLift
|
||||
let letRecsToLift ← letRecsToLift.mapM instantiateMVarsAtLetRecToLift
|
||||
checkLetRecsToLiftTypes funFVars letRecsToLift
|
||||
(if headers.all (·.kind.isTheorem) && !deprecated.oldSectionVars.get (← getOptions) then withHeaderSecVars vars includedVars headers else withUsed vars headers values letRecsToLift) fun vars => do
|
||||
(if headers.all (·.kind.isTheorem) && !deprecated.oldSectionVars.get (← getOptions) then withHeaderSecVars vars sc headers else withUsed vars headers values letRecsToLift) fun vars => do
|
||||
let preDefs ← MutualClosure.main vars headers funFVars values letRecsToLift
|
||||
for preDef in preDefs do
|
||||
trace[Elab.definition] "{preDef.declName} : {preDef.type} :=\n{preDef.value}"
|
||||
@@ -1064,8 +1088,8 @@ def elabMutualDef (ds : Array Syntax) : CommandElabM Unit := do
|
||||
if let some snap := snap? then
|
||||
-- no non-fatal diagnostics at this point
|
||||
snap.new.resolve <| .ofTyped { defs, diagnostics := .empty : DefsParsedSnapshot }
|
||||
let includedVars := (← getScope).includedVars
|
||||
runTermElabM fun vars => Term.elabMutualDef vars includedVars views
|
||||
let sc ← getScope
|
||||
runTermElabM fun vars => Term.elabMutualDef vars sc views
|
||||
|
||||
builtin_initialize
|
||||
registerTraceClass `Elab.definition.mkClosure
|
||||
|
||||
@@ -10,3 +10,5 @@ import Lean.Elab.PreDefinition.Main
|
||||
import Lean.Elab.PreDefinition.MkInhabitant
|
||||
import Lean.Elab.PreDefinition.WF
|
||||
import Lean.Elab.PreDefinition.Eqns
|
||||
import Lean.Elab.PreDefinition.Nonrec.Eqns
|
||||
import Lean.Elab.PreDefinition.EqUnfold
|
||||
|
||||
@@ -12,6 +12,7 @@ import Lean.Util.NumApps
|
||||
import Lean.PrettyPrinter
|
||||
import Lean.Meta.AbstractNestedProofs
|
||||
import Lean.Meta.ForEachExpr
|
||||
import Lean.Meta.Eqns
|
||||
import Lean.Elab.RecAppSyntax
|
||||
import Lean.Elab.DefView
|
||||
import Lean.Elab.PreDefinition.TerminationHint
|
||||
@@ -153,6 +154,7 @@ private def addNonRecAux (preDef : PreDefinition) (compile : Bool) (all : List N
|
||||
if compile && shouldGenCodeFor preDef then
|
||||
discard <| compileDecl decl
|
||||
if applyAttrAfterCompilation then
|
||||
generateEagerEqns preDef.declName
|
||||
applyAttributesOf #[preDef] AttributeApplicationTime.afterCompilation
|
||||
|
||||
def addAndCompileNonRec (preDef : PreDefinition) (all : List Name := [preDef.declName]) : TermElabM Unit := do
|
||||
|
||||
62
src/Lean/Elab/PreDefinition/EqUnfold.lean
Normal file
62
src/Lean/Elab/PreDefinition/EqUnfold.lean
Normal file
@@ -0,0 +1,62 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Joachim Breitner
|
||||
-/
|
||||
prelude
|
||||
import Lean.Meta.Eqns
|
||||
import Lean.Meta.Tactic.Util
|
||||
import Lean.Meta.Tactic.Rfl
|
||||
import Lean.Meta.Tactic.Intro
|
||||
import Lean.Meta.Tactic.Apply
|
||||
|
||||
namespace Lean.Meta
|
||||
|
||||
/-- Try to close goal using `rfl` with smart unfolding turned off. -/
|
||||
def tryURefl (mvarId : MVarId) : MetaM Bool :=
|
||||
withOptions (smartUnfolding.set · false) do
|
||||
try mvarId.refl; return true catch _ => return false
|
||||
|
||||
/--
|
||||
Returns the "const unfold" theorem (`f.eq_unfold`) for the given declaration.
|
||||
This is not extensible, and always builds on the unfold theorem (`f.eq_def`).
|
||||
-/
|
||||
def getConstUnfoldEqnFor? (declName : Name) : MetaM (Option Name) := do
|
||||
let some unfoldEqnName ← getUnfoldEqnFor? (nonRec := true) declName | return none
|
||||
let info ← getConstInfo unfoldEqnName
|
||||
let type ← forallTelescope info.type fun xs eq => do
|
||||
let some (_, lhs, rhs) := eq.eq? | throwError "Unexpected unfold theorem type {info.type}"
|
||||
unless lhs.getAppFn.isConstOf declName do
|
||||
throwError "Unexpected unfold theorem type {info.type}"
|
||||
unless lhs.getAppArgs == xs do
|
||||
throwError "Unexpected unfold theorem type {info.type}"
|
||||
let type ← mkEq lhs.getAppFn (← mkLambdaFVars xs rhs)
|
||||
return type
|
||||
let value ← withNewMCtxDepth do
|
||||
let main ← mkFreshExprSyntheticOpaqueMVar type
|
||||
if (← tryURefl main.mvarId!) then -- try to make a rfl lemma if possible
|
||||
instantiateMVars main
|
||||
else forallTelescope info.type fun xs _eq => do
|
||||
let mut proof ← mkConstWithLevelParams unfoldEqnName
|
||||
proof := mkAppN proof xs
|
||||
for x in xs.reverse do
|
||||
proof ← mkLambdaFVars #[x] proof
|
||||
proof ← mkAppM ``funext #[proof]
|
||||
return proof
|
||||
let name := .str declName eqUnfoldThmSuffix
|
||||
addDecl <| Declaration.thmDecl {
|
||||
name, type, value
|
||||
levelParams := info.levelParams
|
||||
}
|
||||
return some name
|
||||
|
||||
|
||||
builtin_initialize
|
||||
registerReservedNameAction fun name => do
|
||||
let .str p s := name | return false
|
||||
unless (← getEnv).isSafeDefinition p do return false
|
||||
if s == eqUnfoldThmSuffix then
|
||||
return (← MetaM.run' <| getConstUnfoldEqnFor? p).isSome
|
||||
return false
|
||||
|
||||
end Lean.Meta
|
||||
@@ -43,15 +43,6 @@ def expandRHS? (mvarId : MVarId) : MetaM (Option MVarId) := do
|
||||
let (true, rhs') := expand false rhs | return none
|
||||
return some (← mvarId.replaceTargetDefEq (← mkEq lhs rhs'))
|
||||
|
||||
def funext? (mvarId : MVarId) : MetaM (Option MVarId) := do
|
||||
let target ← mvarId.getType'
|
||||
let some (_, _, rhs) := target.eq? | return none
|
||||
unless rhs.isLambda do return none
|
||||
commitWhenSome? do
|
||||
let [mvarId] ← mvarId.apply (← mkConstWithFreshMVarLevels ``funext) | return none
|
||||
let (_, mvarId) ← mvarId.intro1
|
||||
return some mvarId
|
||||
|
||||
def simpMatch? (mvarId : MVarId) : MetaM (Option MVarId) := do
|
||||
let mvarId' ← Split.simpMatchTarget mvarId
|
||||
if mvarId != mvarId' then return some mvarId' else return none
|
||||
@@ -60,7 +51,8 @@ def simpIf? (mvarId : MVarId) : MetaM (Option MVarId) := do
|
||||
let mvarId' ← simpIfTarget mvarId (useDecide := true)
|
||||
if mvarId != mvarId' then return some mvarId' else return none
|
||||
|
||||
private def findMatchToSplit? (env : Environment) (e : Expr) (declNames : Array Name) (exceptionSet : ExprSet) : Option Expr :=
|
||||
private def findMatchToSplit? (deepRecursiveSplit : Bool) (env : Environment) (e : Expr)
|
||||
(declNames : Array Name) (exceptionSet : ExprSet) : Option Expr :=
|
||||
e.findExt? fun e => Id.run do
|
||||
if e.hasLooseBVars || exceptionSet.contains e then
|
||||
return Expr.FindStep.visit
|
||||
@@ -75,7 +67,14 @@ private def findMatchToSplit? (env : Environment) (e : Expr) (declNames : Array
|
||||
break
|
||||
unless hasFVarDiscr do
|
||||
return Expr.FindStep.visit
|
||||
-- At least one alternative must contain a `declNames` application with loose bound variables.
|
||||
-- For non-recursive functions (`declNames` empty), we split here
|
||||
if declNames.isEmpty then
|
||||
return Expr.FindStep.found
|
||||
-- For recursive functions, the “new” behavior is to likewise split
|
||||
if deepRecursiveSplit then
|
||||
return Expr.FindStep.found
|
||||
-- Else, the “old” behavior is split only when at least one alternative contains a `declNames`
|
||||
-- application with loose bound variables.
|
||||
for i in [info.getFirstAltPos : info.getFirstAltPos + info.numAlts] do
|
||||
let alt := args[i]!
|
||||
if Option.isSome <| alt.find? fun e => declNames.any e.isAppOf && e.hasLooseBVars then
|
||||
@@ -92,7 +91,8 @@ private def findMatchToSplit? (env : Environment) (e : Expr) (declNames : Array
|
||||
partial def splitMatch? (mvarId : MVarId) (declNames : Array Name) : MetaM (Option (List MVarId)) := commitWhenSome? do
|
||||
let target ← mvarId.getType'
|
||||
let rec go (badCases : ExprSet) : MetaM (Option (List MVarId)) := do
|
||||
if let some e := findMatchToSplit? (← getEnv) target declNames badCases then
|
||||
if let some e := findMatchToSplit? (eqns.deepRecursiveSplit.get (← getOptions)) (← getEnv)
|
||||
target declNames badCases then
|
||||
try
|
||||
Meta.Split.splitMatch mvarId e
|
||||
catch _ =>
|
||||
@@ -102,9 +102,6 @@ partial def splitMatch? (mvarId : MVarId) (declNames : Array Name) : MetaM (Opti
|
||||
return none
|
||||
go {}
|
||||
|
||||
structure Context where
|
||||
declNames : Array Name
|
||||
|
||||
private def lhsDependsOn (type : Expr) (fvarId : FVarId) : MetaM Bool :=
|
||||
forallTelescope type fun _ type => do
|
||||
if let some (_, lhs, _) ← matchEq? type then
|
||||
@@ -228,25 +225,16 @@ private def shouldUseSimpMatch (e : Expr) : MetaM Bool := do
|
||||
throwThe Unit ()
|
||||
return (← (find e).run) matches .error _
|
||||
|
||||
partial def mkEqnTypes (tryRefl : Bool) (declNames : Array Name) (mvarId : MVarId) : MetaM (Array Expr) := do
|
||||
let (_, eqnTypes) ← go mvarId |>.run { declNames } |>.run #[]
|
||||
partial def mkEqnTypes (declNames : Array Name) (mvarId : MVarId) : MetaM (Array Expr) := do
|
||||
let (_, eqnTypes) ← go mvarId |>.run #[]
|
||||
return eqnTypes
|
||||
where
|
||||
go (mvarId : MVarId) : ReaderT Context (StateRefT (Array Expr) MetaM) Unit := do
|
||||
go (mvarId : MVarId) : StateRefT (Array Expr) MetaM Unit := do
|
||||
trace[Elab.definition.eqns] "mkEqnTypes step\n{MessageData.ofGoal mvarId}"
|
||||
if tryRefl then
|
||||
if (← tryURefl mvarId) then
|
||||
saveEqn mvarId
|
||||
return ()
|
||||
|
||||
if let some mvarId ← expandRHS? mvarId then
|
||||
return (← go mvarId)
|
||||
|
||||
-- The following `funext?` was producing an overapplied `lhs`. Possible refinement: only do it
|
||||
-- if we want to apply `splitMatch` on the body of the lambda
|
||||
/- if let some mvarId ← funext? mvarId then
|
||||
return (← go mvarId) -/
|
||||
|
||||
if (← shouldUseSimpMatch (← mvarId.getType')) then
|
||||
if let some mvarId ← simpMatch? mvarId then
|
||||
return (← go mvarId)
|
||||
@@ -346,9 +334,6 @@ partial def mkUnfoldProof (declName : Name) (mvarId : MVarId) : MetaM Unit := do
|
||||
let rec go (mvarId : MVarId) : MetaM Unit := do
|
||||
if (← tryEqns mvarId) then
|
||||
return ()
|
||||
-- Remark: we removed funext? from `mkEqnTypes`
|
||||
-- else if let some mvarId ← funext? mvarId then
|
||||
-- go mvarId
|
||||
|
||||
if (← shouldUseSimpMatch (← mvarId.getType')) then
|
||||
if let some mvarId ← simpMatch? mvarId then
|
||||
|
||||
108
src/Lean/Elab/PreDefinition/Nonrec/Eqns.lean
Normal file
108
src/Lean/Elab/PreDefinition/Nonrec/Eqns.lean
Normal file
@@ -0,0 +1,108 @@
|
||||
/-
|
||||
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura, Joachim Breitner
|
||||
-/
|
||||
prelude
|
||||
import Lean.Meta.Tactic.Rewrite
|
||||
import Lean.Meta.Tactic.Split
|
||||
import Lean.Elab.PreDefinition.Basic
|
||||
import Lean.Elab.PreDefinition.Eqns
|
||||
import Lean.Meta.ArgsPacker.Basic
|
||||
import Init.Data.Array.Basic
|
||||
|
||||
namespace Lean.Elab.Nonrec
|
||||
open Meta
|
||||
open Eqns
|
||||
|
||||
/--
|
||||
Simple, coarse-grained equation theorem for nonrecursive definitions.
|
||||
-/
|
||||
private def mkSimpleEqThm (declName : Name) (suffix := Name.mkSimple unfoldThmSuffix) : MetaM (Option Name) := do
|
||||
if let some (.defnInfo info) := (← getEnv).find? declName then
|
||||
lambdaTelescope (cleanupAnnotations := true) info.value fun xs body => do
|
||||
let lhs := mkAppN (mkConst info.name <| info.levelParams.map mkLevelParam) xs
|
||||
let type ← mkForallFVars xs (← mkEq lhs body)
|
||||
let value ← mkLambdaFVars xs (← mkEqRefl lhs)
|
||||
let name := declName ++ suffix
|
||||
addDecl <| Declaration.thmDecl {
|
||||
name, type, value
|
||||
levelParams := info.levelParams
|
||||
}
|
||||
return some name
|
||||
else
|
||||
return none
|
||||
|
||||
private partial def mkProof (declName : Name) (type : Expr) : MetaM Expr := do
|
||||
trace[Elab.definition.eqns] "proving: {type}"
|
||||
withNewMCtxDepth do
|
||||
let main ← mkFreshExprSyntheticOpaqueMVar type
|
||||
let (_, mvarId) ← main.mvarId!.intros
|
||||
let rec go (mvarId : MVarId) : MetaM Unit := do
|
||||
trace[Elab.definition.eqns] "step\n{MessageData.ofGoal mvarId}"
|
||||
if ← withAtLeastTransparency .all (tryURefl mvarId) then
|
||||
return ()
|
||||
else if (← tryContradiction mvarId) then
|
||||
return ()
|
||||
else if let some mvarId ← simpMatch? mvarId then
|
||||
go mvarId
|
||||
else if let some mvarId ← simpIf? mvarId then
|
||||
go mvarId
|
||||
else if let some mvarId ← whnfReducibleLHS? mvarId then
|
||||
go mvarId
|
||||
else match (← simpTargetStar mvarId { config.dsimp := false } (simprocs := {})).1 with
|
||||
| TacticResultCNM.closed => return ()
|
||||
| TacticResultCNM.modified mvarId => go mvarId
|
||||
| TacticResultCNM.noChange =>
|
||||
if let some mvarIds ← casesOnStuckLHS? mvarId then
|
||||
mvarIds.forM go
|
||||
else if let some mvarIds ← splitTarget? mvarId then
|
||||
mvarIds.forM go
|
||||
else
|
||||
throwError "failed to generate equational theorem for '{declName}'\n{MessageData.ofGoal mvarId}"
|
||||
|
||||
-- Try rfl before deltaLHS to avoid `id` checkpoints in the proof, which would make
|
||||
-- the lemma ineligible for dsimp
|
||||
unless ← withAtLeastTransparency .all (tryURefl mvarId) do
|
||||
go (← deltaLHS mvarId)
|
||||
instantiateMVars main
|
||||
|
||||
def mkEqns (declName : Name) (info : DefinitionVal) : MetaM (Array Name) :=
|
||||
withOptions (tactic.hygienic.set · false) do
|
||||
let baseName := declName
|
||||
let eqnTypes ← withNewMCtxDepth <| lambdaTelescope (cleanupAnnotations := true) info.value fun xs body => do
|
||||
let us := info.levelParams.map mkLevelParam
|
||||
let target ← mkEq (mkAppN (Lean.mkConst declName us) xs) body
|
||||
let goal ← mkFreshExprSyntheticOpaqueMVar target
|
||||
withReducible do
|
||||
mkEqnTypes #[] goal.mvarId!
|
||||
let mut thmNames := #[]
|
||||
for i in [: eqnTypes.size] do
|
||||
let type := eqnTypes[i]!
|
||||
trace[Elab.definition.eqns] "eqnType[{i}]: {eqnTypes[i]!}"
|
||||
let name := (Name.str baseName eqnThmSuffixBase).appendIndexAfter (i+1)
|
||||
thmNames := thmNames.push name
|
||||
let value ← mkProof declName type
|
||||
let (type, value) ← removeUnusedEqnHypotheses type value
|
||||
addDecl <| Declaration.thmDecl {
|
||||
name, type, value
|
||||
levelParams := info.levelParams
|
||||
}
|
||||
return thmNames
|
||||
|
||||
def getEqnsFor? (declName : Name) : MetaM (Option (Array Name)) := do
|
||||
if (← isRecursiveDefinition declName) then
|
||||
return none
|
||||
if let some (.defnInfo info) := (← getEnv).find? declName then
|
||||
if eqns.nonrecursive.get (← getOptions) then
|
||||
mkEqns declName info
|
||||
else
|
||||
let o ← mkSimpleEqThm declName
|
||||
return o.map (#[·])
|
||||
else
|
||||
return none
|
||||
|
||||
builtin_initialize
|
||||
registerGetEqnsFn getEqnsFor?
|
||||
|
||||
end Lean.Elab.Nonrec
|
||||
@@ -64,7 +64,7 @@ def mkEqns (info : EqnInfo) : MetaM (Array Name) :=
|
||||
let us := info.levelParams.map mkLevelParam
|
||||
let target ← mkEq (mkAppN (Lean.mkConst info.declName us) xs) body
|
||||
let goal ← mkFreshExprSyntheticOpaqueMVar target
|
||||
mkEqnTypes (tryRefl := true) info.declNames goal.mvarId!
|
||||
mkEqnTypes info.declNames goal.mvarId!
|
||||
let baseName := info.declName
|
||||
let mut thmNames := #[]
|
||||
for i in [: eqnTypes.size] do
|
||||
|
||||
@@ -65,7 +65,7 @@ where
|
||||
go (fvars : Array Expr) (vals : Array Expr) : M α := do
|
||||
if !(vals.all fun val => val.isLambda) then
|
||||
k fvars vals
|
||||
else if !(← vals.allM fun val => return val.bindingName! == vals[0]!.bindingName! && val.binderInfo == vals[0]!.binderInfo && (← isDefEq val.bindingDomain! vals[0]!.bindingDomain!)) then
|
||||
else if !(← vals.allM fun val=> isDefEq val.bindingDomain! vals[0]!.bindingDomain!) then
|
||||
k fvars vals
|
||||
else
|
||||
withLocalDecl vals[0]!.bindingName! vals[0]!.binderInfo vals[0]!.bindingDomain! fun x =>
|
||||
@@ -193,6 +193,7 @@ def structuralRecursion (preDefs : Array PreDefinition) (termArg?s : Array (Opti
|
||||
registerEqnsInfo preDef (preDefs.map (·.declName)) recArgPos numFixed
|
||||
addSmartUnfoldingDef preDef recArgPos
|
||||
markAsRecursive preDef.declName
|
||||
generateEagerEqns preDef.declName
|
||||
applyAttributesOf preDefsNonRec AttributeApplicationTime.afterCompilation
|
||||
|
||||
|
||||
|
||||
@@ -9,17 +9,17 @@ import Lean.Elab.Tactic.Basic
|
||||
|
||||
namespace Lean.Elab.WF
|
||||
|
||||
register_builtin_option cleanDecreasingByGoal : Bool := {
|
||||
defValue := true
|
||||
descr := "Cleans up internal implementation details in the proof goals presented by \
|
||||
`decreasing_by`, using the `clean_wf` tactic. Can be disabled for debugging \
|
||||
purposes. Please report an issue if you have to disable this option."
|
||||
register_builtin_option debug.rawDecreasingByGoal : Bool := {
|
||||
defValue := false
|
||||
descr := "Shows the raw `decreasing_by` goal including internal implementation detail \
|
||||
intead of cleaning it up with the `clean_wf` tactic. Can be enabled for debugging \
|
||||
purposes. Please report an issue if you have to use this option for other reasons."
|
||||
}
|
||||
|
||||
open Lean Elab Tactic
|
||||
|
||||
def applyCleanWfTactic : TacticM Unit := do
|
||||
if cleanDecreasingByGoal.get (← getOptions) then
|
||||
unless debug.rawDecreasingByGoal.get (← getOptions) do
|
||||
Tactic.evalTactic (← `(tactic| all_goals clean_wf))
|
||||
|
||||
end Lean.Elab.WF
|
||||
|
||||
@@ -81,7 +81,7 @@ def mkEqns (declName : Name) (info : EqnInfo) : MetaM (Array Name) :=
|
||||
let target ← mkEq (mkAppN (Lean.mkConst declName us) xs) body
|
||||
let goal ← mkFreshExprSyntheticOpaqueMVar target
|
||||
withReducible do
|
||||
mkEqnTypes (tryRefl := false) info.declNames goal.mvarId!
|
||||
mkEqnTypes info.declNames goal.mvarId!
|
||||
let mut thmNames := #[]
|
||||
for i in [: eqnTypes.size] do
|
||||
let type := eqnTypes[i]!
|
||||
|
||||
@@ -36,7 +36,7 @@ where
|
||||
go (fvars : Array Expr) (vals : Array Expr) : TermElabM α := do
|
||||
if !(vals.all fun val => val.isLambda) then
|
||||
k fvars vals
|
||||
else if !(← vals.allM fun val => return val.bindingName! == vals[0]!.bindingName! && val.binderInfo == vals[0]!.binderInfo && (← isDefEq val.bindingDomain! vals[0]!.bindingDomain!)) then
|
||||
else if !(← vals.allM fun val => isDefEq val.bindingDomain! vals[0]!.bindingDomain!) then
|
||||
k fvars vals
|
||||
else
|
||||
withLocalDecl vals[0]!.bindingName! vals[0]!.binderInfo vals[0]!.bindingDomain! fun x =>
|
||||
@@ -145,6 +145,7 @@ def wfRecursion (preDefs : Array PreDefinition) (termArg?s : Array (Option Termi
|
||||
registerEqnsInfo preDefs preDefNonRec.declName fixedPrefixSize argsPacker
|
||||
for preDef in preDefs do
|
||||
markAsRecursive preDef.declName
|
||||
generateEagerEqns preDef.declName
|
||||
applyAttributesOf #[preDef] AttributeApplicationTime.afterCompilation
|
||||
-- Unless the user asks for something else, mark the definition as irreducible
|
||||
unless preDef.modifiers.attrs.any fun a =>
|
||||
|
||||
@@ -134,7 +134,7 @@ private def printAxiomsOf (constName : Name) : CommandElabM Unit := do
|
||||
| _ => throwUnsupportedSyntax
|
||||
|
||||
private def printEqnsOf (constName : Name) : CommandElabM Unit := do
|
||||
let some eqns ← liftTermElabM <| Meta.getEqnsFor? constName (nonRec := true) |
|
||||
let some eqns ← liftTermElabM <| Meta.getEqnsFor? constName |
|
||||
logInfo m!"'{constName}' does not have equations"
|
||||
let mut m := m!"equations:"
|
||||
for eq in eqns do
|
||||
|
||||
@@ -42,3 +42,4 @@ import Lean.Elab.Tactic.Rfl
|
||||
import Lean.Elab.Tactic.Rewrites
|
||||
import Lean.Elab.Tactic.DiscrTreeKey
|
||||
import Lean.Elab.Tactic.BVDecide
|
||||
import Lean.Elab.Tactic.BoolToPropSimps
|
||||
|
||||
@@ -4,10 +4,75 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Henrik Böving
|
||||
-/
|
||||
prelude
|
||||
import Lean.Elab.Tactic.BVDecide.Bitblast
|
||||
import Lean.Elab.Tactic.BVDecide.LRAT
|
||||
import Lean.Elab.Tactic.BVDecide.External
|
||||
import Lean.Elab.Tactic.BVDecide.Frontend
|
||||
|
||||
/-!
|
||||
This directory implements the `bv_decide` tactic as a verified bitblaster with subterm sharing.
|
||||
It makes use of proof by reflection and `ofReduceBool`, thus adding the Lean compiler to the trusted
|
||||
code base.
|
||||
This directory offers three different SAT tactics for proving goals involving `BitVec` and `Bool`:
|
||||
1. `bv_decide` takes the goal, hands it over to a SAT solver and verifies the generated LRAT
|
||||
UNSAT proof to prove the goal.
|
||||
2. `bv_check file.lrat` can prove the same things as `bv_decide`. However instead of
|
||||
dynamically handing the goal to a SAT solver to obtain an LRAT proof, the LRAT proof is read from
|
||||
`file.lrat`. This allows users that do not have a SAT solver installed to verify proofs.
|
||||
3. `bv_decide?` offers a code action to turn a `bv_decide` invocation automatically into a
|
||||
`bv_check` one.
|
||||
|
||||
There are also some options to influence the behavior of `bv_decide` and friends:
|
||||
- `sat.solver`: the name of the SAT solver used by `bv_decide`. It goes through 3 steps to determine
|
||||
which solver to use:
|
||||
1. If sat.solver is set to something != "" it will use that.
|
||||
2. If sat.solver is set to "" it will check if there is a cadical binary next to the executing
|
||||
program. Usually that program is going to be `lean` itself and we do ship a `cadical` next to it.
|
||||
3. If that does not succeed try to call `cadical` from PATH.
|
||||
- `sat.timeout`: The timeout for waiting for the SAT solver in seconds, default 10.
|
||||
- `sat.trimProofs`: Whether to run the trimming algorithm on LRAT proofs, default true.
|
||||
- `sat.binaryProofs`: Whether to use the binary LRAT proof format, default true.
|
||||
- `trace.Meta.Tactic.bv` and `trace.Meta.Tactic.sat` for inspecting the inner workings of `bv_decide`.
|
||||
- `debug.skipKernelTC`: may be set to true to disable actually checking the LRAT proof.
|
||||
`bv_decide` will still run bitblasting + SAT solving so this option essentially trusts the SAT
|
||||
solver.
|
||||
|
||||
## Architecture
|
||||
`bv_decide` roughly runs through the following steps:
|
||||
1. Apply `false_or_by_contra` to start a proof by contradiction.
|
||||
2. Apply the `bv_normalize` and `seval` simp set to all hypotheses. This has two effects:
|
||||
1. It applies a subset of the rewrite rules from [Bitwuzla](https://github.com/bitwuzla/bitwuzla)
|
||||
for simplification of the expressions.
|
||||
2. It turns all hypotheses that might be of interest for the remainder of the tactic into the form
|
||||
`x = true` where `x` is a mixture of `Bool` and fixed width `BitVec` expressions.
|
||||
3. Use proof by reflection to reduce the proof to showing that an SMTLIB-syntax-like value that
|
||||
represents the conjunction of all relevant assumptions is UNSAT.
|
||||
4. Use a verified bitblasting algorithm to turn that expression into an AIG.
|
||||
The bitblasting algorithms are collected from various other bitblasters, including Bitwuzla and
|
||||
Z3 and verified using Lean's `BitVec` theory.
|
||||
5. Turn the AIG into a CNF.
|
||||
6. Run CaDiCal on the CNF to obtain an LRAT proof that the CNF is UNSAT. If CaDiCal returns SAT
|
||||
instead the tactic aborts here and presents a counterexample.
|
||||
7. Use an LRAT checker with a soundness proof in Lean to show that the LRAT proof is correct.
|
||||
8. Chain all the proofs so far to demonstrate that the original goal holds.
|
||||
|
||||
## Axioms
|
||||
`bv_decide` makes use of proof by reflection and `ofReduceBool`, thus adding the Lean compiler to
|
||||
the trusted code base.
|
||||
|
||||
|
||||
## Adding a new primitive
|
||||
`bv_decide` knows two kinds of primitives:
|
||||
1. The ones that can be reduced to already existing ones.
|
||||
2. The ones that cannot.
|
||||
|
||||
For the first kind the steps to adding them are very simple, go to `Std.Tactic.BVDecide.Normalize`
|
||||
and add the reduction lemma into the `bv_normalize` simp set. Don't forget to add a test!
|
||||
|
||||
For the second kind more steps are involved:
|
||||
1. Add a new constructor to `BVExpr`/`BVPred`
|
||||
2. Add a bitblasting algorithm for the new constructor to `Std.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Impl`.
|
||||
3. Verify that algorithm in `Std.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Lemmas`.
|
||||
4. Integrate it with either the expression or predicate bitblaster and use the proof above to verify it.
|
||||
5. Add simplification lemmas for the primitive to `bv_normalize` in `Std.Tactic.BVDecide.Normalize`.
|
||||
If there are mutliple ways to write the primitive (e.g. with TC based notation and without) you
|
||||
should normalize for one notation here.
|
||||
6. Add the reflection code to `Lean.Elab.Tactic.BVDecide.Frontend.BVDecide`
|
||||
7. Add a test!
|
||||
-/
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
/-
|
||||
Copyright (c) 2024 FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Henrik Böving
|
||||
-/
|
||||
prelude
|
||||
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Impl.Operations.Add
|
||||
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Impl.Operations.Append
|
||||
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Impl.Operations.Eq
|
||||
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Impl.Operations.Extract
|
||||
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Impl.Operations.GetLsb
|
||||
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Impl.Operations.Mul
|
||||
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Impl.Operations.Not
|
||||
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Impl.Operations.Replicate
|
||||
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Impl.Operations.RotateLeft
|
||||
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Impl.Operations.RotateRight
|
||||
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Impl.Operations.ShiftLeft
|
||||
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Impl.Operations.ShiftRight
|
||||
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Impl.Operations.SignExtend
|
||||
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Impl.Operations.Ult
|
||||
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Impl.Operations.ZeroExtend
|
||||
|
||||
/-!
|
||||
This directory contains the implementations of bitblasters for all basic operations on `BVExpr`
|
||||
and `BVPred`.
|
||||
-/
|
||||
@@ -1,26 +0,0 @@
|
||||
/-
|
||||
Copyright (c) 2024 FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Henrik Böving
|
||||
-/
|
||||
prelude
|
||||
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Lemmas.Operations.Add
|
||||
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Lemmas.Operations.Append
|
||||
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Lemmas.Operations.Eq
|
||||
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Lemmas.Operations.Extract
|
||||
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Lemmas.Operations.GetLsb
|
||||
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Lemmas.Operations.Mul
|
||||
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Lemmas.Operations.Not
|
||||
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Lemmas.Operations.Replicate
|
||||
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Lemmas.Operations.RotateLeft
|
||||
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Lemmas.Operations.RotateRight
|
||||
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Lemmas.Operations.ShiftLeft
|
||||
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Lemmas.Operations.ShiftRight
|
||||
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Lemmas.Operations.SignExtend
|
||||
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Lemmas.Operations.Ult
|
||||
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Lemmas.Operations.ZeroExtend
|
||||
|
||||
/-!
|
||||
This directory contains the verification of bitblasters for all basic operations on `BVExpr` and
|
||||
`BVPred`.
|
||||
-/
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user