mirror of
https://github.com/leanprover/lean4.git
synced 2026-03-21 04:14:07 +00:00
Compare commits
76 Commits
misc_list
...
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 |
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
|
||||
/*{
|
||||
|
||||
@@ -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
|
||||
'';
|
||||
|
||||
@@ -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] :
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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⟩
|
||||
|
||||
@@ -38,6 +38,45 @@ 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 [*]
|
||||
@@ -81,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
|
||||
@@ -200,8 +241,23 @@ theorem mem_of_find?_eq_some : ∀ {l}, find? p l = some a → a ∈ l
|
||||
· 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 => match f a with | none => false | some b => p b)).map f := by
|
||||
(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 =>
|
||||
@@ -236,10 +292,54 @@ 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
|
||||
@@ -254,7 +354,8 @@ theorem find?_replicate : find? p (replicate n a) = if n = 0 then none else if p
|
||||
@[simp] theorem find?_replicate_of_neg (h : ¬ p a) : find? p (replicate n a) = none := by
|
||||
simp [find?_replicate, h]
|
||||
|
||||
@[simp] theorem find?_replicate_eq_none (n : Nat) (a : α) (p : α → Bool) :
|
||||
-- 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]
|
||||
|
||||
@@ -297,6 +398,12 @@ theorem IsInfix.find?_eq_none {l₁ l₂ : List α} {p : α → Bool} (h : 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 α) :
|
||||
@@ -632,7 +739,7 @@ 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_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₂) :
|
||||
|
||||
@@ -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⟩
|
||||
@@ -347,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 :=
|
||||
@@ -366,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)]
|
||||
|
||||
@@ -510,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 -/
|
||||
@@ -549,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
|
||||
@@ -607,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 : α} :
|
||||
@@ -622,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
|
||||
@@ -884,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 -/
|
||||
@@ -900,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
|
||||
@@ -907,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 ..
|
||||
@@ -1442,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]
|
||||
@@ -1487,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₂ ≠ []) :
|
||||
@@ -1512,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
|
||||
@@ -1523,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 => ?_
|
||||
@@ -1779,11 +1822,10 @@ 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]
|
||||
|
||||
@[simp] theorem join_map_filter (p : α → Bool) (l : List (List α)) : (l.map (filter p)).join = (l.join).filter p := by
|
||||
induction l with
|
||||
| nil => simp
|
||||
| cons x xs ih =>
|
||||
simp only [ih, map_cons, join_cons, filter_append]
|
||||
@[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
|
||||
@@ -1794,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 α)),
|
||||
@@ -2069,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 →
|
||||
@@ -2126,7 +2218,15 @@ 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]
|
||||
@@ -2161,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 α)) :
|
||||
@@ -2206,16 +2314,32 @@ theorem bind_reverse {β} (l : List α) (f : α → List β) : (l.reverse.bind f
|
||||
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]
|
||||
@@ -2224,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]
|
||||
|
||||
@@ -2397,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
|
||||
|
||||
|
||||
@@ -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`
|
||||
@@ -38,6 +39,19 @@ theorem range'_ne_nil (s n : Nat) : range' s n ≠ [] ↔ n ≠ 0 := by
|
||||
|
||||
@[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
|
||||
@@ -54,6 +68,9 @@ theorem mem_range' : ∀{n}, m ∈ range' s n step ↔ ∃ i < n, m = s + step *
|
||||
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
|
||||
@@ -66,6 +83,11 @@ theorem getLast?_range' (n : Nat) : (range' s n).getLast? = if n = 0 then none e
|
||||
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
|
||||
@@ -145,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)
|
||||
@@ -219,6 +302,23 @@ theorem head?_range (n : Nat) : (range n).head? = if n = 0 then none else some 0
|
||||
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
|
||||
@@ -227,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)
|
||||
@@ -242,8 +350,42 @@ 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
|
||||
@@ -251,7 +393,6 @@ 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
|
||||
|
||||
@@ -270,12 +411,67 @@ theorem nodup_iota (n : Nat) : Nodup (iota n) :=
|
||||
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?]
|
||||
@@ -388,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]
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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 _ =>
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -136,7 +136,7 @@ theorem merge_stable : ∀ (xs ys) (_ : ∀ x y, x ∈ xs → y ∈ ys → x.1
|
||||
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]
|
||||
· 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)
|
||||
|
||||
|
||||
@@ -62,8 +62,8 @@ 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⟩
|
||||
@@ -185,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
|
||||
@@ -246,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
|
||||
@@ -755,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⟩
|
||||
@@ -786,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
|
||||
@@ -802,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
|
||||
@@ -909,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]
|
||||
|
||||
|
||||
@@ -95,9 +95,7 @@ theorem getElem?_take_of_lt {l : List α} {n m : Nat} (h : m < n) : (l.take n)[m
|
||||
theorem get?_take {l : List α} {n m : Nat} (h : m < n) : (l.take n).get? m = l.get? m := by
|
||||
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_of_lt (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
|
||||
|
||||
@@ -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
|
||||
@@ -152,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
|
||||
|
||||
@@ -424,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]
|
||||
|
||||
@@ -544,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)
|
||||
|
||||
@@ -654,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
|
||||
@@ -705,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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -561,7 +561,7 @@ structure Iterator where
|
||||
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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -9,20 +9,19 @@ import Lean.Data.Json.Basic
|
||||
import Lean.Data.RBMap
|
||||
import Std.Internal.Parsec
|
||||
|
||||
namespace Lean.Json.Parser
|
||||
|
||||
open Std.Internal.Parsec
|
||||
open Std.Internal.Parsec.String
|
||||
|
||||
@[inline]
|
||||
namespace Lean.Json.Parser
|
||||
|
||||
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
|
||||
| .success _ res => Except.ok res
|
||||
| .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 }
|
||||
|
||||
|
||||
@@ -13,11 +13,11 @@ open Lean
|
||||
namespace Lean
|
||||
namespace Xml
|
||||
|
||||
namespace Parser
|
||||
|
||||
open Std.Internal.Parsec
|
||||
open Std.Internal.Parsec.String
|
||||
|
||||
namespace Parser
|
||||
|
||||
abbrev LeanChar := Char
|
||||
|
||||
/-- consume a newline character sequence pretending, that we read '\n'. As per spec:
|
||||
@@ -482,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
|
||||
@@ -505,7 +505,7 @@ def elabRunMeta : CommandElab := fun stx =>
|
||||
@[builtin_command_elab Lean.Parser.Command.include] def elabInclude : CommandElab
|
||||
| `(Lean.Parser.Command.include| include $ids*) => do
|
||||
let sc ← getScope
|
||||
let vars := sc.varDecls.concatMap getBracketedBinderIds
|
||||
let vars ← sc.varDecls.concatMapM getBracketedBinderIds
|
||||
let mut uids := #[]
|
||||
for id in ids do
|
||||
if let some idx := vars.findIdx? (· == id.getId) then
|
||||
|
||||
@@ -557,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? }
|
||||
@@ -609,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
|
||||
@@ -706,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
|
||||
|
||||
|
||||
@@ -383,6 +383,11 @@ register_builtin_option deprecated.oldSectionVars : Bool := {
|
||||
descr := "re-enable deprecated behavior of including exactly the section variables used in a declaration"
|
||||
}
|
||||
|
||||
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
|
||||
@@ -411,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 {
|
||||
|
||||
@@ -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
|
||||
@@ -229,20 +226,15 @@ private def shouldUseSimpMatch (e : Expr) : MetaM Bool := do
|
||||
return (← (find e).run) matches .error _
|
||||
|
||||
partial def mkEqnTypes (declNames : Array Name) (mvarId : MVarId) : MetaM (Array Expr) := do
|
||||
let (_, eqnTypes) ← go mvarId |>.run { declNames } |>.run #[]
|
||||
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 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)
|
||||
@@ -342,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
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -6,9 +6,73 @@ Authors: Henrik Böving
|
||||
prelude
|
||||
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!
|
||||
-/
|
||||
|
||||
@@ -4,7 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Henrik Böving
|
||||
-/
|
||||
prelude
|
||||
import Lean.Elab.Tactic.BVDecide.LRAT.Parser
|
||||
import Std.Tactic.BVDecide.LRAT.Parser
|
||||
import Lean.CoreM
|
||||
import Std.Internal.Parsec
|
||||
|
||||
@@ -17,6 +17,8 @@ namespace Lean.Elab.Tactic.BVDecide
|
||||
|
||||
namespace External
|
||||
|
||||
open Std.Tactic.BVDecide
|
||||
|
||||
/--
|
||||
The result of calling a SAT solver.
|
||||
-/
|
||||
@@ -34,6 +36,7 @@ namespace ModelParser
|
||||
|
||||
open Std.Internal.Parsec
|
||||
open Std.Internal.Parsec.ByteArray
|
||||
open LRAT.Parser.Text (skipNewline)
|
||||
|
||||
def parsePartialAssignment : Parser (Bool × (Array (Bool × Nat))) := do
|
||||
skipByteChar 'v'
|
||||
@@ -43,7 +46,7 @@ def parsePartialAssignment : Parser (Bool × (Array (Bool × Nat))) := do
|
||||
(skipString " 0")
|
||||
(csuccess := fun _ => pure (true, idents))
|
||||
(cerror := fun _ => do
|
||||
skipByteChar '\n'
|
||||
skipNewline
|
||||
return (false, idents)
|
||||
)
|
||||
where
|
||||
@@ -65,7 +68,8 @@ where
|
||||
|
||||
@[inline]
|
||||
def parseHeader : Parser Unit := do
|
||||
skipString "s SATISFIABLE\n"
|
||||
skipString "s SATISFIABLE"
|
||||
skipNewline
|
||||
|
||||
/--
|
||||
Parse the witness format of a SAT solver. The rough grammar for this is:
|
||||
@@ -81,41 +85,56 @@ end ModelParser
|
||||
|
||||
open Lean (CoreM)
|
||||
|
||||
inductive TimedOut (α : Type u) where
|
||||
| success (x : α)
|
||||
| timeout
|
||||
|
||||
/--
|
||||
Run a process with `args` until it terminates or the cancellation token in `CoreM` tells us to abort.
|
||||
Run a process with `args` until it terminates or the cancellation token in `CoreM` tells us to abort
|
||||
or `timeout` seconds have passed.
|
||||
-/
|
||||
partial def runInterruptible (args : IO.Process.SpawnArgs) : CoreM IO.Process.Output := do
|
||||
partial def runInterruptible (timeout : Nat) (args : IO.Process.SpawnArgs) :
|
||||
CoreM (TimedOut IO.Process.Output) := do
|
||||
let child ← IO.Process.spawn { args with stdout := .piped, stderr := .piped, stdin := .null }
|
||||
let stdout ← IO.asTask child.stdout.readToEnd Task.Priority.dedicated
|
||||
let stderr ← IO.asTask child.stderr.readToEnd Task.Priority.dedicated
|
||||
if let some tk := (← read).cancelTk? then
|
||||
go child stdout stderr tk
|
||||
else
|
||||
let stdout ← IO.ofExcept stdout.get
|
||||
let stderr ← IO.ofExcept stderr.get
|
||||
let exitCode ← child.wait
|
||||
return { exitCode := exitCode, stdout := stdout, stderr := stderr }
|
||||
go (timeout * 1000) child stdout stderr
|
||||
where
|
||||
go {cfg} (child : IO.Process.Child cfg) (stdout stderr : Task (Except IO.Error String))
|
||||
(tk : IO.CancelToken) : CoreM IO.Process.Output := do
|
||||
withInterruptCheck tk child.kill do
|
||||
go {cfg} (budgetMs : Nat) (child : IO.Process.Child cfg) (stdout stderr : Task (Except IO.Error String)) :
|
||||
CoreM (TimedOut IO.Process.Output) := do
|
||||
let cleanup := killAndWait child
|
||||
withTimeoutCheck budgetMs cleanup do
|
||||
withInterruptCheck cleanup do
|
||||
match ← child.tryWait with
|
||||
| some exitCode =>
|
||||
let stdout ← IO.ofExcept stdout.get
|
||||
let stderr ← IO.ofExcept stderr.get
|
||||
return { exitCode := exitCode, stdout := stdout, stderr := stderr }
|
||||
return .success { exitCode := exitCode, stdout := stdout, stderr := stderr }
|
||||
| none =>
|
||||
IO.sleep 50
|
||||
go child stdout stderr tk
|
||||
let sleepMs : Nat := 50
|
||||
IO.sleep sleepMs.toUInt32
|
||||
go (budgetMs - sleepMs) child stdout stderr
|
||||
|
||||
withInterruptCheck {α : Type} (tk : IO.CancelToken) (interrupted : CoreM Unit) (x : CoreM α) :
|
||||
CoreM α := do
|
||||
if ← tk.isSet then
|
||||
interrupted
|
||||
throw <| .internal Core.interruptExceptionId
|
||||
killAndWait {cfg} (child : IO.Process.Child cfg) : IO Unit := do
|
||||
child.kill
|
||||
discard child.wait
|
||||
|
||||
withTimeoutCheck {α : Type} (budgetMs : Nat) (cleanup : CoreM Unit) (x : CoreM (TimedOut α)) :
|
||||
CoreM (TimedOut α) := do
|
||||
if budgetMs == 0 then
|
||||
cleanup
|
||||
return .timeout
|
||||
else
|
||||
x
|
||||
|
||||
withInterruptCheck {α : Type} (cleanup : CoreM Unit) (x : CoreM α) :
|
||||
CoreM α := do
|
||||
if let some tk := (← read).cancelTk? then
|
||||
if ← tk.isSet then
|
||||
cleanup
|
||||
throw <| .internal Core.interruptExceptionId
|
||||
x
|
||||
|
||||
/--
|
||||
Call the SAT solver in `solverPath` with `problemPath` as CNF input and ask it to output an LRAT
|
||||
UNSAT proof (binary or non-binary depending on `binaryProofs`) into `proofOutput`. To avoid runaway
|
||||
@@ -123,40 +142,50 @@ solvers the solver is run with `timeout` in seconds as a maximum time limit to s
|
||||
|
||||
Note: This function currently assume that the solver has the same CLI as CaDiCal.
|
||||
-/
|
||||
def satQuery (solverPath : String) (problemPath : System.FilePath) (proofOutput : System.FilePath)
|
||||
(timeout : Nat := 10) (binaryProofs : Bool := true) :
|
||||
def satQuery (solverPath : System.FilePath) (problemPath : System.FilePath) (proofOutput : System.FilePath)
|
||||
(timeout : Nat) (binaryProofs : Bool) :
|
||||
CoreM SolverResult := do
|
||||
let cmd := solverPath
|
||||
let args := #[
|
||||
let cmd := solverPath.toString
|
||||
let mut args := #[
|
||||
problemPath.toString,
|
||||
proofOutput.toString,
|
||||
"-t",
|
||||
s!"{timeout}",
|
||||
"--lrat",
|
||||
s!"--binary={binaryProofs}",
|
||||
"--quiet",
|
||||
"--unsat" -- This sets the magic parameters of cadical to optimize for UNSAT search.
|
||||
/-
|
||||
This sets the magic parameters of cadical to optimize for UNSAT search.
|
||||
Given the fact that we are mostly interested in proving things and expect user goals to be
|
||||
provable this is a fine value to set
|
||||
-/
|
||||
"--unsat",
|
||||
/-
|
||||
Bitwuzla sets this option and it does improve performance practically:
|
||||
https://github.com/bitwuzla/bitwuzla/blob/0e81e616af4d4421729884f01928b194c3536c76/src/sat/cadical.cpp#L34
|
||||
-/
|
||||
"--shrink=0"
|
||||
]
|
||||
|
||||
let out ← runInterruptible { cmd, args, stdin := .piped, stdout := .piped, stderr := .null }
|
||||
if out.exitCode == 255 then
|
||||
throwError s!"Failed to execute external prover:\n{out.stderr}"
|
||||
else
|
||||
let stdout := out.stdout
|
||||
if stdout.startsWith "s UNSATISFIABLE" then
|
||||
return .unsat
|
||||
else if stdout.startsWith "s SATISFIABLE" then
|
||||
match ModelParser.parse.run stdout.toUTF8 with
|
||||
| .ok assignment =>
|
||||
return .sat assignment
|
||||
| .error err =>
|
||||
throwError s!"Error {err} while parsing:\n{stdout}"
|
||||
else if stdout.startsWith "c UNKNOWN" then
|
||||
let mut err := "The SAT solver timed out while solving the problem."
|
||||
err := err ++ "\nConsider increasing the timeout with `set_option sat.timeout <sec>`"
|
||||
throwError err
|
||||
-- We implement timeouting ourselves because cadicals -t option is not available on Windows.
|
||||
let out? ← runInterruptible timeout { cmd, args, stdin := .piped, stdout := .piped, stderr := .null }
|
||||
match out? with
|
||||
| .timeout =>
|
||||
let mut err := "The SAT solver timed out while solving the problem."
|
||||
err := err ++ "\nConsider increasing the timeout with `set_option sat.timeout <sec>`"
|
||||
throwError err
|
||||
| .success { exitCode := exitCode, stdout := stdout, stderr := stderr} =>
|
||||
if exitCode == 255 then
|
||||
throwError s!"Failed to execute external prover:\n{stderr}"
|
||||
else
|
||||
throwError s!"The external prover produced unexpected output:\n{stdout}"
|
||||
if stdout.startsWith "s UNSATISFIABLE" then
|
||||
return .unsat
|
||||
else if stdout.startsWith "s SATISFIABLE" then
|
||||
match ModelParser.parse.run stdout.toUTF8 with
|
||||
| .ok assignment =>
|
||||
return .sat assignment
|
||||
| .error err =>
|
||||
throwError s!"Error {err} while parsing:\n{stdout}"
|
||||
else
|
||||
throwError s!"The external prover produced unexpected output, stdout:\n{stdout}stderr:\n{stderr}"
|
||||
|
||||
end External
|
||||
|
||||
|
||||
19
src/Lean/Elab/Tactic/BVDecide/Frontend.lean
Normal file
19
src/Lean/Elab/Tactic/BVDecide/Frontend.lean
Normal file
@@ -0,0 +1,19 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean 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.Frontend.Attr
|
||||
import Lean.Elab.Tactic.BVDecide.Frontend.BVCheck
|
||||
import Lean.Elab.Tactic.BVDecide.Frontend.BVDecide
|
||||
import Lean.Elab.Tactic.BVDecide.Frontend.BVTrace
|
||||
import Lean.Elab.Tactic.BVDecide.Frontend.LRAT
|
||||
import Lean.Elab.Tactic.BVDecide.Frontend.Normalize
|
||||
|
||||
/-!
|
||||
This module provides the tactic frontends, consisting of:
|
||||
- `bv_decide`, the bitblasting based `BitVec` decision procedure itself.
|
||||
- `bv_check`, like `bv_decide` but the LRAT proof is provided as a file so no need to call a SAT solver.
|
||||
- `bv_decide?`, converts `bv_decide?` into `bv_check` calls.
|
||||
-/
|
||||
87
src/Lean/Elab/Tactic/BVDecide/Frontend/Attr.lean
Normal file
87
src/Lean/Elab/Tactic/BVDecide/Frontend/Attr.lean
Normal file
@@ -0,0 +1,87 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Kim Morrison, Henrik Böving
|
||||
-/
|
||||
prelude
|
||||
import Lean.Util.Trace
|
||||
import Lean.Elab.Tactic.Simp
|
||||
|
||||
/-!
|
||||
Provides environment extensions around the `bv_decide` tactic frontends.
|
||||
-/
|
||||
|
||||
namespace Lean.Elab.Tactic.BVDecide
|
||||
namespace Frontend
|
||||
|
||||
open Lean
|
||||
open Lean.Meta.Simp
|
||||
|
||||
builtin_initialize registerTraceClass `Meta.Tactic.sat
|
||||
builtin_initialize registerTraceClass `Meta.Tactic.bv
|
||||
|
||||
register_builtin_option sat.solver : String := {
|
||||
defValue := ""
|
||||
descr :=
|
||||
"Name of the SAT solver used by Lean.Elab.Tactic.BVDecide tactics.\n
|
||||
1. If this is set to something besides the emtpy string they will use that binary.\n
|
||||
2. If this is set to the empty string they 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.\n
|
||||
3. If that does not succeed try to call `cadical` from PATH. The empty string default indicates\
|
||||
to use the one that ships with Lean."
|
||||
}
|
||||
|
||||
register_builtin_option sat.timeout : Nat := {
|
||||
defValue := 10
|
||||
descr := "the number of seconds that the sat solver is run before aborting"
|
||||
}
|
||||
|
||||
register_builtin_option sat.trimProofs : Bool := {
|
||||
defValue := true
|
||||
descr := "Whether to run the trimming algorithm on LRAT proofs"
|
||||
}
|
||||
|
||||
register_builtin_option sat.binaryProofs : Bool := {
|
||||
defValue := true
|
||||
descr := "Whether to use the binary LRAT proof format. Currently set to false and ignored on Windows due to a bug in CaDiCal."
|
||||
}
|
||||
|
||||
register_builtin_option debug.bv.graphviz : Bool := {
|
||||
defValue := false
|
||||
descr := "Output the AIG of bv_decide as graphviz into a file called aig.gv in the working directory of the Lean process."
|
||||
}
|
||||
|
||||
builtin_initialize bvNormalizeExt : Meta.SimpExtension ←
|
||||
Meta.registerSimpAttr `bv_normalize "simp theorems used by bv_normalize"
|
||||
|
||||
/-- Builtin `bv_normalize` simprocs. -/
|
||||
builtin_initialize builtinBVNormalizeSimprocsRef : IO.Ref Meta.Simp.Simprocs ← IO.mkRef {}
|
||||
|
||||
builtin_initialize bvNormalizeSimprocExt : Meta.Simp.SimprocExtension ←
|
||||
Meta.Simp.registerSimprocAttr `bv_normalize_proc "simprocs used by bv_normalize" (some builtinBVNormalizeSimprocsRef)
|
||||
|
||||
private def addBuiltin (declName : Name) (stx : Syntax) (addDeclName : Name) : AttrM Unit := do
|
||||
let post := if stx[1].isNone then true else stx[1][0].getKind == ``Lean.Parser.Tactic.simpPost
|
||||
let procExpr ← match (← getConstInfo declName).type with
|
||||
| .const ``Simproc _ => pure <| mkApp3 (mkConst ``Sum.inl [0, 0]) (mkConst ``Simproc) (mkConst ``DSimproc) (mkConst declName)
|
||||
| _ => throwError "unexpected type at bv_normalize simproc"
|
||||
let val := mkAppN (mkConst addDeclName) #[toExpr declName, toExpr post, procExpr]
|
||||
let initDeclName ← mkFreshUserName (declName ++ `declare)
|
||||
declareBuiltin initDeclName val
|
||||
|
||||
def addBVNormalizeProcBuiltinAttr (declName : Name) (post : Bool) (proc : Sum Simproc DSimproc) : IO Unit :=
|
||||
addSimprocBuiltinAttrCore builtinBVNormalizeSimprocsRef declName post proc
|
||||
|
||||
builtin_initialize
|
||||
registerBuiltinAttribute {
|
||||
ref := by exact decl_name%
|
||||
name := `bvNormalizeProcBuiltinAttr
|
||||
descr := "Builtin bv_normalize simproc"
|
||||
applicationTime := AttributeApplicationTime.afterCompilation
|
||||
erase := fun _ => throwError "Not implemented yet, [-builtin_bv_normalize_proc]"
|
||||
add := fun declName stx _ => addBuiltin declName stx ``addBVNormalizeProcBuiltinAttr
|
||||
}
|
||||
|
||||
end Frontend
|
||||
namespace Lean.Elab.Tactic.BVDecide
|
||||
68
src/Lean/Elab/Tactic/BVDecide/Frontend/BVCheck.lean
Normal file
68
src/Lean/Elab/Tactic/BVDecide/Frontend/BVCheck.lean
Normal file
@@ -0,0 +1,68 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean 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.Frontend.BVDecide
|
||||
import Lean.Meta.Tactic.TryThis
|
||||
import Std.Tactic.BVDecide.Syntax
|
||||
|
||||
/-!
|
||||
This modules provides the implementation of `bv_check`.
|
||||
-/
|
||||
|
||||
namespace Lean.Elab.Tactic.BVDecide
|
||||
namespace Frontend.BVCheck
|
||||
|
||||
open Std.Tactic.BVDecide
|
||||
open Std.Tactic.BVDecide.Reflect
|
||||
|
||||
/--
|
||||
Get the directory that contains the Lean file which is currently being elaborated.
|
||||
-/
|
||||
def getSrcDir : TermElabM System.FilePath := do
|
||||
let ctx ← readThe Lean.Core.Context
|
||||
let srcPath := System.FilePath.mk ctx.fileName
|
||||
let some srcDir := srcPath.parent
|
||||
| throwError "cannot compute parent directory of '{srcPath}'"
|
||||
return srcDir
|
||||
|
||||
def mkContext (lratPath : System.FilePath) : TermElabM TacticContext := do
|
||||
let lratPath := (← getSrcDir) / lratPath
|
||||
TacticContext.new lratPath
|
||||
|
||||
/--
|
||||
Prepare an `Expr` that proves `bvExpr.unsat` using `ofReduceBool`.
|
||||
-/
|
||||
def lratChecker (cfg : TacticContext) (bvExpr : BVLogicalExpr) : MetaM Expr := do
|
||||
let cert ← LratCert.ofFile cfg.lratPath cfg.trimProofs
|
||||
cert.toReflectionProof cfg bvExpr ``verifyBVExpr ``unsat_of_verifyBVExpr_eq_true
|
||||
|
||||
@[inherit_doc Lean.Parser.Tactic.bvCheck]
|
||||
def bvCheck (g : MVarId) (cfg : TacticContext) : MetaM Unit := do
|
||||
let unsatProver : UnsatProver := fun bvExpr _ => do
|
||||
withTraceNode `sat (fun _ => return "Preparing LRAT reflection term") do
|
||||
let proof ← lratChecker cfg bvExpr
|
||||
return ⟨proof, ""⟩
|
||||
let _ ← closeWithBVReflection g unsatProver
|
||||
return ()
|
||||
|
||||
|
||||
open Lean.Meta.Tactic in
|
||||
@[builtin_tactic Lean.Parser.Tactic.bvCheck]
|
||||
def evalBvCheck : Tactic := fun
|
||||
| `(tactic| bv_check%$tk $path:str) => do
|
||||
let cfg ← BVDecide.Frontend.BVCheck.mkContext path.getString
|
||||
liftMetaFinishingTactic fun g => do
|
||||
let res ← Normalize.bvNormalize g
|
||||
match res.goal with
|
||||
| some g => bvCheck g cfg
|
||||
| none =>
|
||||
let bvNormalizeStx ← `(tactic| bv_normalize)
|
||||
TryThis.addSuggestion tk bvNormalizeStx (origSpan? := ← getRef)
|
||||
throwError m!"This goal can be closed by only applying bv_normalize, no need to keep the LRAT proof around."
|
||||
| _ => throwUnsupportedSyntax
|
||||
|
||||
end Frontend.BVCheck
|
||||
end Lean.Elab.Tactic.BVDecide
|
||||
165
src/Lean/Elab/Tactic/BVDecide/Frontend/BVDecide.lean
Normal file
165
src/Lean/Elab/Tactic/BVDecide/Frontend/BVDecide.lean
Normal file
@@ -0,0 +1,165 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Henrik Böving
|
||||
-/
|
||||
prelude
|
||||
import Std.Sat.AIG.CNF
|
||||
import Std.Sat.AIG.RelabelNat
|
||||
import Std.Tactic.BVDecide.Bitblast
|
||||
import Std.Tactic.BVDecide.Syntax
|
||||
import Lean.Elab.Tactic.BVDecide.Frontend.BVDecide.SatAtBVLogical
|
||||
import Lean.Elab.Tactic.BVDecide.Frontend.Normalize
|
||||
import Lean.Elab.Tactic.BVDecide.Frontend.LRAT
|
||||
|
||||
/-!
|
||||
This module provides the implementation of the `bv_decide` frontend itself.
|
||||
-/
|
||||
|
||||
namespace Lean.Elab.Tactic.BVDecide
|
||||
namespace Frontend
|
||||
|
||||
open Std.Sat
|
||||
open Std.Tactic.BVDecide
|
||||
open Std.Tactic.BVDecide.Reflect
|
||||
open Lean.Meta
|
||||
|
||||
/--
|
||||
Given:
|
||||
- `var2Cnf`: The mapping from AIG to CNF variables.
|
||||
- `assignments`: A model for the CNF as provided by a SAT solver.
|
||||
- `aigSize`: The amount of nodes in the AIG that was used to produce the CNF.
|
||||
- `atomsAssignment`: The mapping of the reflection monad from atom indices to `Expr`.
|
||||
|
||||
Reconstruct bit by bit which value expression must have had which `BitVec` value and return all
|
||||
expression - pair values.
|
||||
-/
|
||||
def reconstructCounterExample (var2Cnf : Std.HashMap BVBit Nat) (assignment : Array (Bool × Nat))
|
||||
(aigSize : Nat) (atomsAssignment : Std.HashMap Nat Expr) :
|
||||
Array (Expr × BVExpr.PackedBitVec) := Id.run do
|
||||
let mut sparseMap : Std.HashMap Nat (RBMap Nat Bool Ord.compare) := {}
|
||||
for (bitVar, cnfVar) in var2Cnf.toArray do
|
||||
/-
|
||||
The setup of the variables in CNF is as follows:
|
||||
1. One auxiliary variable for each node in the AIG
|
||||
2. The actual BitVec bitwise variables
|
||||
Hence we access the assignment array offset by the AIG size to obtain the value for a BitVec bit.
|
||||
We assume that a variable can be found at its index as CaDiCal prints them in order.
|
||||
-/
|
||||
let (varSet, _) := assignment[cnfVar + aigSize]!
|
||||
let mut bitMap := sparseMap.getD bitVar.var {}
|
||||
bitMap := bitMap.insert bitVar.idx varSet
|
||||
sparseMap := sparseMap.insert bitVar.var bitMap
|
||||
|
||||
let mut finalMap := #[]
|
||||
for (bitVecVar, bitMap) in sparseMap.toArray do
|
||||
let mut value : Nat := 0
|
||||
let mut currentBit := 0
|
||||
for (bitIdx, bitValue) in bitMap.toList do
|
||||
assert! bitIdx == currentBit
|
||||
if bitValue then
|
||||
value := value ||| (1 <<< currentBit)
|
||||
currentBit := currentBit + 1
|
||||
let atomExpr := atomsAssignment.get! bitVecVar
|
||||
finalMap := finalMap.push (atomExpr, ⟨BitVec.ofNat currentBit value⟩)
|
||||
return finalMap
|
||||
|
||||
structure UnsatProver.Result where
|
||||
proof : Expr
|
||||
lratCert : LratCert
|
||||
|
||||
abbrev UnsatProver := BVLogicalExpr → Std.HashMap Nat Expr → MetaM UnsatProver.Result
|
||||
|
||||
def lratBitblaster (cfg : TacticContext) (bv : BVLogicalExpr)
|
||||
(atomsAssignment : Std.HashMap Nat Expr) :
|
||||
MetaM UnsatProver.Result := do
|
||||
let entry ←
|
||||
withTraceNode `bv (fun _ => return "Bitblasting BVLogicalExpr to AIG") do
|
||||
-- lazyPure to prevent compiler lifting
|
||||
IO.lazyPure (fun _ => bv.bitblast)
|
||||
let aigSize := entry.aig.decls.size
|
||||
trace[Meta.Tactic.bv] s!"AIG has {aigSize} nodes."
|
||||
|
||||
if cfg.graphviz then
|
||||
IO.FS.writeFile ("." / "aig.gv") <| AIG.toGraphviz entry
|
||||
|
||||
let (cnf, map) ←
|
||||
withTraceNode `sat (fun _ => return "Converting AIG to CNF") do
|
||||
-- lazyPure to prevent compiler lifting
|
||||
IO.lazyPure (fun _ =>
|
||||
let (entry, map) := entry.relabelNat'
|
||||
let cnf := AIG.toCNF entry
|
||||
(cnf, map)
|
||||
)
|
||||
|
||||
let res ←
|
||||
withTraceNode `sat (fun _ => return "Obtaining external proof certificate") do
|
||||
runExternal cnf cfg.solver cfg.lratPath cfg.trimProofs cfg.timeout cfg.binaryProofs
|
||||
|
||||
match res with
|
||||
| .ok cert =>
|
||||
let proof ← cert.toReflectionProof cfg bv ``verifyBVExpr ``unsat_of_verifyBVExpr_eq_true
|
||||
return ⟨proof, cert⟩
|
||||
| .error assignment =>
|
||||
let reconstructed := reconstructCounterExample map assignment aigSize atomsAssignment
|
||||
let mut error := m!"The prover found a potential counterexample, consider the following assignment:\n"
|
||||
for (var, value) in reconstructed do
|
||||
error := error ++ m!"{var} = {value.bv}\n"
|
||||
throwError error
|
||||
|
||||
def reflectBV (g : MVarId) : M (BVLogicalExpr × (Expr → M Expr)) := g.withContext do
|
||||
let hyps ← getLocalHyps
|
||||
let sats ← hyps.filterMapM SatAtBVLogical.of
|
||||
if sats.size = 0 then
|
||||
let mut error := "None of the hypotheses are in the supported BitVec fragment.\n"
|
||||
error := error ++ "There are two potential fixes for this:\n"
|
||||
error := error ++ "1. If you are using custom BitVec constructs simplify them to built-in ones.\n"
|
||||
error := error ++ "2. If your problem is using only built-in ones it might currently be out of reach.\n"
|
||||
error := error ++ " Consider expressing it in terms of different operations that are better supported."
|
||||
throwError error
|
||||
let sat := sats.foldl (init := SatAtBVLogical.trivial) SatAtBVLogical.and
|
||||
return (sat.bvExpr, sat.proveFalse)
|
||||
|
||||
def closeWithBVReflection (g : MVarId) (unsatProver : UnsatProver) :
|
||||
MetaM LratCert := M.run do
|
||||
g.withContext do
|
||||
let (bvLogicalExpr, f) ←
|
||||
withTraceNode `bv (fun _ => return "Reflecting goal into BVLogicalExpr") do
|
||||
reflectBV g
|
||||
trace[Meta.Tactic.bv] "Reflected bv logical expression: {bvLogicalExpr}"
|
||||
|
||||
let atomsPairs := (← getThe State).atoms.toList.map (fun (expr, _, ident) => (ident, expr))
|
||||
let atomsAssignment := Std.HashMap.ofList atomsPairs
|
||||
let ⟨bvExprUnsat, cert⟩ ← unsatProver bvLogicalExpr atomsAssignment
|
||||
let proveFalse ← f bvExprUnsat
|
||||
g.assign proveFalse
|
||||
return cert
|
||||
|
||||
def bvUnsat (g : MVarId) (cfg : TacticContext) : MetaM LratCert := M.run do
|
||||
let unsatProver : UnsatProver := fun bvExpr atomsAssignment => do
|
||||
withTraceNode `bv (fun _ => return "Preparing LRAT reflection term") do
|
||||
lratBitblaster cfg bvExpr atomsAssignment
|
||||
closeWithBVReflection g unsatProver
|
||||
|
||||
structure Result where
|
||||
simpTrace : Simp.Stats
|
||||
lratCert : Option LratCert
|
||||
|
||||
def bvDecide (g : MVarId) (cfg : TacticContext) : MetaM Result := do
|
||||
let ⟨g?, simpTrace⟩ ← Normalize.bvNormalize g
|
||||
let some g := g? | return ⟨simpTrace, none⟩
|
||||
let lratCert ← bvUnsat g cfg
|
||||
return ⟨simpTrace, some lratCert⟩
|
||||
|
||||
@[builtin_tactic Lean.Parser.Tactic.bvDecide]
|
||||
def evalBvTrace : Tactic := fun
|
||||
| `(tactic| bv_decide) => do
|
||||
IO.FS.withTempFile fun _ lratFile => do
|
||||
let cfg ← BVDecide.Frontend.TacticContext.new lratFile
|
||||
liftMetaFinishingTactic fun g => do
|
||||
discard <| bvDecide g cfg
|
||||
| _ => throwUnsupportedSyntax
|
||||
|
||||
end Frontend
|
||||
end Lean.Elab.Tactic.BVDecide
|
||||
|
||||
175
src/Lean/Elab/Tactic/BVDecide/Frontend/BVDecide/Reflect.lean
Normal file
175
src/Lean/Elab/Tactic/BVDecide/Frontend/BVDecide/Reflect.lean
Normal file
@@ -0,0 +1,175 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Henrik Böving
|
||||
-/
|
||||
prelude
|
||||
import Std.Data.HashMap
|
||||
import Std.Tactic.BVDecide.Bitblast.BVExpr.Basic
|
||||
import Lean.Meta.AppBuilder
|
||||
import Lean.ToExpr
|
||||
|
||||
/-!
|
||||
This module contains the implementation of the reflection monad, used by all other components of this
|
||||
directory.
|
||||
-/
|
||||
|
||||
namespace Lean.Elab.Tactic.BVDecide
|
||||
namespace Frontend
|
||||
|
||||
open Std.Tactic.BVDecide
|
||||
|
||||
instance : ToExpr BVBinOp where
|
||||
toExpr x :=
|
||||
match x with
|
||||
| .and => mkConst ``BVBinOp.and
|
||||
| .or => mkConst ``BVBinOp.or
|
||||
| .xor => mkConst ``BVBinOp.xor
|
||||
| .add => mkConst ``BVBinOp.add
|
||||
| .mul => mkConst ``BVBinOp.mul
|
||||
toTypeExpr := mkConst ``BVBinOp
|
||||
|
||||
instance : ToExpr BVUnOp where
|
||||
toExpr x :=
|
||||
match x with
|
||||
| .not => mkConst ``BVUnOp.not
|
||||
| .shiftLeftConst n => mkApp (mkConst ``BVUnOp.shiftLeftConst) (toExpr n)
|
||||
| .shiftRightConst n => mkApp (mkConst ``BVUnOp.shiftRightConst) (toExpr n)
|
||||
| .rotateLeft n => mkApp (mkConst ``BVUnOp.rotateLeft) (toExpr n)
|
||||
| .rotateRight n => mkApp (mkConst ``BVUnOp.rotateRight) (toExpr n)
|
||||
| .arithShiftRightConst n => mkApp (mkConst ``BVUnOp.arithShiftRightConst) (toExpr n)
|
||||
toTypeExpr := mkConst ``BVUnOp
|
||||
|
||||
instance : ToExpr (BVExpr w) where
|
||||
toExpr x := go x
|
||||
toTypeExpr := mkApp (mkConst ``BVExpr) (toExpr w)
|
||||
where
|
||||
go {w : Nat} : BVExpr w → Expr
|
||||
| .var idx => mkApp2 (mkConst ``BVExpr.var) (toExpr w) (toExpr idx)
|
||||
| .const val => mkApp2 (mkConst ``BVExpr.const) (toExpr w) (toExpr val)
|
||||
| .zeroExtend (w := oldWidth) val inner =>
|
||||
mkApp3 (mkConst ``BVExpr.zeroExtend) (toExpr oldWidth) (toExpr val) (go inner)
|
||||
| .signExtend (w := oldWidth) val inner =>
|
||||
mkApp3 (mkConst ``BVExpr.signExtend) (toExpr oldWidth) (toExpr val) (go inner)
|
||||
| .bin lhs op rhs => mkApp4 (mkConst ``BVExpr.bin) (toExpr w) (go lhs) (toExpr op) (go rhs)
|
||||
| .un op operand => mkApp3 (mkConst ``BVExpr.un) (toExpr w) (toExpr op) (go operand)
|
||||
| .append (l := l) (r := r) lhs rhs =>
|
||||
mkApp4 (mkConst ``BVExpr.append) (toExpr l) (toExpr r) (go lhs) (go rhs)
|
||||
| .replicate (w := oldWidth) w inner =>
|
||||
mkApp3 (mkConst ``BVExpr.replicate) (toExpr oldWidth) (toExpr w) (go inner)
|
||||
| .extract (w := oldWidth) hi lo expr =>
|
||||
mkApp4 (mkConst ``BVExpr.extract) (toExpr oldWidth) (toExpr hi) (toExpr lo) (go expr)
|
||||
| .shiftLeft (m := m) (n := n) lhs rhs =>
|
||||
mkApp4 (mkConst ``BVExpr.shiftLeft) (toExpr m) (toExpr n) (go lhs) (go rhs)
|
||||
| .shiftRight (m := m) (n := n) lhs rhs =>
|
||||
mkApp4 (mkConst ``BVExpr.shiftRight) (toExpr m) (toExpr n) (go lhs) (go rhs)
|
||||
|
||||
instance : ToExpr BVBinPred where
|
||||
toExpr x :=
|
||||
match x with
|
||||
| .eq => mkConst ``BVBinPred.eq
|
||||
| .ult => mkConst ``BVBinPred.ult
|
||||
toTypeExpr := mkConst ``BVBinPred
|
||||
|
||||
instance : ToExpr Gate where
|
||||
toExpr x :=
|
||||
match x with
|
||||
| .and => mkConst ``Gate.and
|
||||
| .or => mkConst ``Gate.or
|
||||
| .xor => mkConst ``Gate.xor
|
||||
| .imp => mkConst ``Gate.imp
|
||||
| .beq => mkConst ``Gate.beq
|
||||
toTypeExpr := mkConst ``Gate
|
||||
|
||||
instance : ToExpr BVPred where
|
||||
toExpr x := go x
|
||||
toTypeExpr := mkConst ``BVPred
|
||||
where
|
||||
go : BVPred → Expr
|
||||
| .bin (w := w) lhs op rhs =>
|
||||
mkApp4 (mkConst ``BVPred.bin) (toExpr w) (toExpr lhs) (toExpr op) (toExpr rhs)
|
||||
| .getLsb (w := w) expr idx =>
|
||||
mkApp3 (mkConst ``BVPred.getLsb) (toExpr w) (toExpr expr) (toExpr idx)
|
||||
|
||||
|
||||
instance [ToExpr α] : ToExpr (BoolExpr α) where
|
||||
toExpr x := go x
|
||||
toTypeExpr := mkApp (mkConst ``BoolExpr) (toTypeExpr α)
|
||||
where
|
||||
go : (BoolExpr α) → Expr
|
||||
| .literal lit => mkApp2 (mkConst ``BoolExpr.literal) (toTypeExpr α) (toExpr lit)
|
||||
| .const b => mkApp2 (mkConst ``BoolExpr.const) (toTypeExpr α) (toExpr b)
|
||||
| .not x => mkApp2 (mkConst ``BoolExpr.not) (toTypeExpr α) (go x)
|
||||
| .gate g x y => mkApp4 (mkConst ``BoolExpr.gate) (toTypeExpr α) (toExpr g) (go x) (go y)
|
||||
|
||||
|
||||
open Lean.Meta
|
||||
|
||||
/--
|
||||
The state of the reflection monad
|
||||
-/
|
||||
structure State where
|
||||
/--
|
||||
The atoms encountered so far. Saved as a map from `BitVec` expressions to a (width, atomNumber)
|
||||
pair.
|
||||
-/
|
||||
atoms : Std.HashMap Expr (Nat × Nat) := {}
|
||||
/--
|
||||
A cache for `atomsAssignment`.
|
||||
-/
|
||||
atomsAssignmentCache : Expr := mkConst ``List.nil [.zero]
|
||||
|
||||
/--
|
||||
The reflection monad, used to track `BitVec` variables that we see as we traverse the context.
|
||||
-/
|
||||
abbrev M := StateRefT State MetaM
|
||||
|
||||
namespace M
|
||||
|
||||
/--
|
||||
Run a reflection computation as a `MetaM` one.
|
||||
-/
|
||||
def run (m : M α) : MetaM α :=
|
||||
m.run' { }
|
||||
|
||||
/--
|
||||
Retrieve the atoms as pairs of their width and expression.
|
||||
-/
|
||||
def atoms : M (List (Nat × Expr)) := do
|
||||
let sortedAtoms := (← getThe State).atoms.toArray.qsort (·.2.2 < ·.2.2)
|
||||
return sortedAtoms.map (fun (expr, width, _) => (width, expr)) |>.toList
|
||||
|
||||
/--
|
||||
Retrieve a `BitVec.Assignment` representing the atoms we found so far.
|
||||
-/
|
||||
def atomsAssignment : M Expr := do
|
||||
return (← getThe State).atomsAssignmentCache
|
||||
|
||||
/--
|
||||
Look up an expression in the atoms, recording it if it has not previously appeared.
|
||||
-/
|
||||
def lookup (e : Expr) (width : Nat) : M Nat := do
|
||||
match (← getThe State).atoms[e]? with
|
||||
| some (width', ident) =>
|
||||
if width != width' then
|
||||
panic! "The same atom occurs with different widths, this is a bug"
|
||||
return ident
|
||||
| none =>
|
||||
trace[Meta.Tactic.bv] "New atom of width {width}: {e}"
|
||||
let ident ← modifyGetThe State fun s =>
|
||||
(s.atoms.size, { s with atoms := s.atoms.insert e (width, s.atoms.size) })
|
||||
updateAtomsAssignment
|
||||
return ident
|
||||
where
|
||||
updateAtomsAssignment : M Unit := do
|
||||
let as ← atoms
|
||||
let packed :=
|
||||
as.map (fun (width, expr) => mkApp2 (mkConst ``BVExpr.PackedBitVec.mk) (toExpr width) expr)
|
||||
let packedType := mkConst ``BVExpr.PackedBitVec
|
||||
let newAtomsAssignment ← mkListLit packedType packed
|
||||
modify fun s => { s with atomsAssignmentCache := newAtomsAssignment }
|
||||
|
||||
end M
|
||||
|
||||
end Frontend
|
||||
end Lean.Elab.Tactic.BVDecide
|
||||
@@ -0,0 +1,357 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean 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.Frontend.BVDecide.Reflect
|
||||
import Std.Tactic.BVDecide.Reflect
|
||||
|
||||
/-!
|
||||
Provides the logic for reifying `BitVec` expressions.
|
||||
-/
|
||||
|
||||
namespace Lean.Elab.Tactic.BVDecide
|
||||
namespace Frontend
|
||||
|
||||
open Lean.Meta
|
||||
open Std.Tactic.BVDecide
|
||||
open Std.Tactic.BVDecide.Reflect.BitVec
|
||||
|
||||
/--
|
||||
A reified version of an `Expr` representing a `BVExpr`.
|
||||
-/
|
||||
structure ReifiedBVExpr where
|
||||
width : Nat
|
||||
/--
|
||||
The reified expression.
|
||||
-/
|
||||
bvExpr : BVExpr width
|
||||
/--
|
||||
A proof that `bvExpr.eval atomsAssignment = originalBVExpr`.
|
||||
-/
|
||||
evalsAtAtoms : M Expr
|
||||
/--
|
||||
A cache for `toExpr bvExpr`.
|
||||
-/
|
||||
expr : Expr
|
||||
|
||||
namespace ReifiedBVExpr
|
||||
|
||||
def mkEvalExpr (w : Nat) (expr : Expr) : M Expr := do
|
||||
return mkApp3 (mkConst ``BVExpr.eval) (toExpr w) (← M.atomsAssignment) expr
|
||||
|
||||
def mkBVRefl (w : Nat) (expr : Expr) : Expr :=
|
||||
mkApp2
|
||||
(mkConst ``Eq.refl [1])
|
||||
(mkApp (mkConst ``BitVec) (toExpr w))
|
||||
expr
|
||||
|
||||
def mkAtom (e : Expr) (width : Nat) : M ReifiedBVExpr := do
|
||||
let ident ← M.lookup e width
|
||||
let expr := mkApp2 (mkConst ``BVExpr.var) (toExpr width) (toExpr ident)
|
||||
let proof := do
|
||||
let evalExpr ← mkEvalExpr width expr
|
||||
return mkBVRefl width evalExpr
|
||||
return ⟨width, .var ident, proof, expr⟩
|
||||
|
||||
def getNatOrBvValue? (ty : Expr) (expr : Expr) : M (Option Nat) := do
|
||||
match_expr ty with
|
||||
| Nat =>
|
||||
getNatValue? expr
|
||||
| BitVec _ =>
|
||||
let some ⟨_, distance⟩ ← getBitVecValue? expr | return none
|
||||
return some distance.toNat
|
||||
| _ => return none
|
||||
|
||||
/--
|
||||
Reify an `Expr` that's a `BitVec`.
|
||||
-/
|
||||
partial def of (x : Expr) : M (Option ReifiedBVExpr) := do
|
||||
match_expr x with
|
||||
| BitVec.ofNat _ _ => goBvLit x
|
||||
| HAnd.hAnd _ _ _ _ lhsExpr rhsExpr =>
|
||||
binaryReflection lhsExpr rhsExpr .and ``Std.Tactic.BVDecide.Reflect.BitVec.and_congr
|
||||
| HOr.hOr _ _ _ _ lhsExpr rhsExpr =>
|
||||
binaryReflection lhsExpr rhsExpr .or ``Std.Tactic.BVDecide.Reflect.BitVec.or_congr
|
||||
| HXor.hXor _ _ _ _ lhsExpr rhsExpr =>
|
||||
binaryReflection lhsExpr rhsExpr .xor ``Std.Tactic.BVDecide.Reflect.BitVec.xor_congr
|
||||
| HAdd.hAdd _ _ _ _ lhsExpr rhsExpr =>
|
||||
binaryReflection lhsExpr rhsExpr .add ``Std.Tactic.BVDecide.Reflect.BitVec.add_congr
|
||||
| HMul.hMul _ _ _ _ lhsExpr rhsExpr =>
|
||||
binaryReflection lhsExpr rhsExpr .mul ``Std.Tactic.BVDecide.Reflect.BitVec.mul_congr
|
||||
| Complement.complement _ _ innerExpr =>
|
||||
unaryReflection innerExpr .not ``Std.Tactic.BVDecide.Reflect.BitVec.not_congr
|
||||
| HShiftLeft.hShiftLeft _ β _ _ innerExpr distanceExpr =>
|
||||
let distance? ← getNatOrBvValue? β distanceExpr
|
||||
if distance?.isSome then
|
||||
shiftConstReflection
|
||||
β
|
||||
distanceExpr
|
||||
innerExpr
|
||||
.shiftLeftConst
|
||||
``BVUnOp.shiftLeftConst
|
||||
``Std.Tactic.BVDecide.Reflect.BitVec.shiftLeftNat_congr
|
||||
else
|
||||
shiftReflection
|
||||
β
|
||||
distanceExpr
|
||||
innerExpr
|
||||
.shiftLeft
|
||||
``BVExpr.shiftLeft
|
||||
``Std.Tactic.BVDecide.Reflect.BitVec.shiftLeft_congr
|
||||
| HShiftRight.hShiftRight _ β _ _ innerExpr distanceExpr =>
|
||||
let distance? ← getNatOrBvValue? β distanceExpr
|
||||
if distance?.isSome then
|
||||
shiftConstReflection
|
||||
β
|
||||
distanceExpr
|
||||
innerExpr
|
||||
.shiftRightConst
|
||||
``BVUnOp.shiftRightConst
|
||||
``Std.Tactic.BVDecide.Reflect.BitVec.shiftRightNat_congr
|
||||
else
|
||||
shiftReflection
|
||||
β
|
||||
distanceExpr
|
||||
innerExpr
|
||||
.shiftRight
|
||||
``BVExpr.shiftRight
|
||||
``Std.Tactic.BVDecide.Reflect.BitVec.shiftRight_congr
|
||||
| BitVec.sshiftRight _ innerExpr distanceExpr =>
|
||||
let some distance ← getNatValue? distanceExpr | return ← ofAtom x
|
||||
shiftConstLikeReflection
|
||||
distance
|
||||
innerExpr
|
||||
.arithShiftRightConst
|
||||
``BVUnOp.arithShiftRightConst
|
||||
``Std.Tactic.BVDecide.Reflect.BitVec.arithShiftRight_congr
|
||||
| BitVec.zeroExtend _ newWidthExpr innerExpr =>
|
||||
let some newWidth ← getNatValue? newWidthExpr | return ← ofAtom x
|
||||
let some inner ← ofOrAtom innerExpr | return none
|
||||
let bvExpr := .zeroExtend newWidth inner.bvExpr
|
||||
let expr :=
|
||||
mkApp3
|
||||
(mkConst ``BVExpr.zeroExtend)
|
||||
(toExpr inner.width)
|
||||
newWidthExpr
|
||||
inner.expr
|
||||
let proof := do
|
||||
let innerEval ← mkEvalExpr inner.width inner.expr
|
||||
let innerProof ← inner.evalsAtAtoms
|
||||
return mkApp5 (mkConst ``Std.Tactic.BVDecide.Reflect.BitVec.zeroExtend_congr)
|
||||
newWidthExpr
|
||||
(toExpr inner.width)
|
||||
innerExpr
|
||||
innerEval
|
||||
innerProof
|
||||
return some ⟨newWidth, bvExpr, proof, expr⟩
|
||||
| BitVec.signExtend _ newWidthExpr innerExpr =>
|
||||
let some newWidth ← getNatValue? newWidthExpr | return ← ofAtom x
|
||||
let some inner ← ofOrAtom innerExpr | return none
|
||||
let bvExpr := .signExtend newWidth inner.bvExpr
|
||||
let expr :=
|
||||
mkApp3
|
||||
(mkConst ``BVExpr.signExtend)
|
||||
(toExpr inner.width)
|
||||
newWidthExpr
|
||||
inner.expr
|
||||
let proof := do
|
||||
let innerEval ← mkEvalExpr inner.width inner.expr
|
||||
let innerProof ← inner.evalsAtAtoms
|
||||
return mkApp5 (mkConst ``Std.Tactic.BVDecide.Reflect.BitVec.signExtend_congr)
|
||||
newWidthExpr
|
||||
(toExpr inner.width)
|
||||
innerExpr
|
||||
innerEval
|
||||
innerProof
|
||||
return some ⟨newWidth, bvExpr, proof, expr⟩
|
||||
| HAppend.hAppend _ _ _ _ lhsExpr rhsExpr =>
|
||||
let some lhs ← ofOrAtom lhsExpr | return none
|
||||
let some rhs ← ofOrAtom rhsExpr | return none
|
||||
let bvExpr := .append lhs.bvExpr rhs.bvExpr
|
||||
let expr := mkApp4 (mkConst ``BVExpr.append)
|
||||
(toExpr lhs.width)
|
||||
(toExpr rhs.width)
|
||||
lhs.expr rhs.expr
|
||||
let proof := do
|
||||
let lhsEval ← mkEvalExpr lhs.width lhs.expr
|
||||
let lhsProof ← lhs.evalsAtAtoms
|
||||
let rhsProof ← rhs.evalsAtAtoms
|
||||
let rhsEval ← mkEvalExpr rhs.width rhs.expr
|
||||
return mkApp8 (mkConst ``Std.Tactic.BVDecide.Reflect.BitVec.append_congr)
|
||||
(toExpr lhs.width) (toExpr rhs.width)
|
||||
lhsExpr lhsEval
|
||||
rhsExpr rhsEval
|
||||
lhsProof rhsProof
|
||||
return some ⟨lhs.width + rhs.width, bvExpr, proof, expr⟩
|
||||
| BitVec.replicate _ nExpr innerExpr =>
|
||||
let some inner ← ofOrAtom innerExpr | return none
|
||||
let some n ← getNatValue? nExpr | return ← ofAtom x
|
||||
let bvExpr := .replicate n inner.bvExpr
|
||||
let expr := mkApp3 (mkConst ``BVExpr.replicate)
|
||||
(toExpr inner.width)
|
||||
(toExpr n)
|
||||
inner.expr
|
||||
let proof := do
|
||||
let innerEval ← mkEvalExpr inner.width inner.expr
|
||||
let innerProof ← inner.evalsAtAtoms
|
||||
return mkApp5 (mkConst ``Std.Tactic.BVDecide.Reflect.BitVec.replicate_congr)
|
||||
(toExpr n)
|
||||
(toExpr inner.width)
|
||||
innerExpr
|
||||
innerEval
|
||||
innerProof
|
||||
return some ⟨inner.width * n, bvExpr, proof, expr⟩
|
||||
| BitVec.extractLsb _ hiExpr loExpr innerExpr =>
|
||||
let some hi ← getNatValue? hiExpr | return ← ofAtom x
|
||||
let some lo ← getNatValue? loExpr | return ← ofAtom x
|
||||
let some inner ← ofOrAtom innerExpr | return none
|
||||
let bvExpr := .extract hi lo inner.bvExpr
|
||||
let expr := mkApp4 (mkConst ``BVExpr.extract)
|
||||
(toExpr inner.width)
|
||||
hiExpr
|
||||
loExpr
|
||||
inner.expr
|
||||
let proof := do
|
||||
let innerEval ← mkEvalExpr inner.width inner.expr
|
||||
let innerProof ← inner.evalsAtAtoms
|
||||
return mkApp6 (mkConst ``Std.Tactic.BVDecide.Reflect.BitVec.extract_congr)
|
||||
hiExpr
|
||||
loExpr
|
||||
(toExpr inner.width)
|
||||
innerExpr
|
||||
innerEval
|
||||
innerProof
|
||||
return some ⟨hi - lo + 1, bvExpr, proof, expr⟩
|
||||
| BitVec.rotateLeft _ innerExpr distanceExpr =>
|
||||
rotateReflection
|
||||
distanceExpr
|
||||
innerExpr
|
||||
.rotateLeft
|
||||
``BVUnOp.rotateLeft
|
||||
``Std.Tactic.BVDecide.Reflect.BitVec.rotateLeft_congr
|
||||
| BitVec.rotateRight _ innerExpr distanceExpr =>
|
||||
rotateReflection
|
||||
distanceExpr
|
||||
innerExpr
|
||||
.rotateRight
|
||||
``BVUnOp.rotateRight
|
||||
``Std.Tactic.BVDecide.Reflect.BitVec.rotateRight_congr
|
||||
| _ => ofAtom x
|
||||
where
|
||||
ofAtom (x : Expr) : M (Option ReifiedBVExpr) := do
|
||||
let t ← instantiateMVars (← whnfR (← inferType x))
|
||||
let_expr BitVec widthExpr := t | return none
|
||||
let some width ← getNatValue? widthExpr | return none
|
||||
let atom ← mkAtom x width
|
||||
return some atom
|
||||
|
||||
ofOrAtom (x : Expr) : M (Option ReifiedBVExpr) := do
|
||||
let res ← of x
|
||||
match res with
|
||||
| some exp => return some exp
|
||||
| none => ofAtom x
|
||||
|
||||
shiftConstLikeReflection (distance : Nat) (innerExpr : Expr) (shiftOp : Nat → BVUnOp)
|
||||
(shiftOpName : Name) (congrThm : Name) :
|
||||
M (Option ReifiedBVExpr) := do
|
||||
let some inner ← ofOrAtom innerExpr | return none
|
||||
let bvExpr : BVExpr inner.width := .un (shiftOp distance) inner.bvExpr
|
||||
let expr :=
|
||||
mkApp3
|
||||
(mkConst ``BVExpr.un)
|
||||
(toExpr inner.width)
|
||||
(mkApp (mkConst shiftOpName) (toExpr distance))
|
||||
inner.expr
|
||||
let congrProof :=
|
||||
mkApp
|
||||
(mkConst congrThm)
|
||||
(toExpr distance)
|
||||
let proof := unaryCongrProof inner innerExpr congrProof
|
||||
return some ⟨inner.width, bvExpr, proof, expr⟩
|
||||
|
||||
rotateReflection (distanceExpr : Expr) (innerExpr : Expr) (rotateOp : Nat → BVUnOp)
|
||||
(rotateOpName : Name) (congrThm : Name) :
|
||||
M (Option ReifiedBVExpr) := do
|
||||
-- Either the shift values are constant or we abstract the entire term as atoms
|
||||
let some distance ← getNatValue? distanceExpr | return ← ofAtom x
|
||||
shiftConstLikeReflection distance innerExpr rotateOp rotateOpName congrThm
|
||||
|
||||
shiftConstReflection (β : Expr) (distanceExpr : Expr) (innerExpr : Expr) (shiftOp : Nat → BVUnOp)
|
||||
(shiftOpName : Name) (congrThm : Name) :
|
||||
M (Option ReifiedBVExpr) := do
|
||||
-- Either the shift values are constant or we abstract the entire term as atoms
|
||||
let some distance ← getNatOrBvValue? β distanceExpr | return ← ofAtom x
|
||||
shiftConstLikeReflection distance innerExpr shiftOp shiftOpName congrThm
|
||||
|
||||
shiftReflection (β : Expr) (distanceExpr : Expr) (innerExpr : Expr)
|
||||
(shiftOp : {m n : Nat} → BVExpr m → BVExpr n → BVExpr m) (shiftOpName : Name)
|
||||
(congrThm : Name) :
|
||||
M (Option ReifiedBVExpr) := do
|
||||
let_expr BitVec _ ← β | return ← ofAtom x
|
||||
let some inner ← of innerExpr | return none
|
||||
let some distance ← of distanceExpr | return none
|
||||
let bvExpr : BVExpr inner.width := shiftOp inner.bvExpr distance.bvExpr
|
||||
let expr :=
|
||||
mkApp4
|
||||
(mkConst shiftOpName)
|
||||
(toExpr inner.width)
|
||||
(toExpr distance.width)
|
||||
inner.expr
|
||||
distance.expr
|
||||
let congrProof :=
|
||||
mkApp2
|
||||
(mkConst congrThm)
|
||||
(toExpr inner.width)
|
||||
(toExpr distance.width)
|
||||
let proof := binaryCongrProof inner distance innerExpr distanceExpr congrProof
|
||||
return some ⟨inner.width, bvExpr, proof, expr⟩
|
||||
|
||||
binaryReflection (lhsExpr rhsExpr : Expr) (op : BVBinOp) (congrThm : Name) :
|
||||
M (Option ReifiedBVExpr) := do
|
||||
let some lhs ← ofOrAtom lhsExpr | return none
|
||||
let some rhs ← ofOrAtom rhsExpr | return none
|
||||
if h : rhs.width = lhs.width then
|
||||
let bvExpr : BVExpr lhs.width := .bin lhs.bvExpr op (h ▸ rhs.bvExpr)
|
||||
let expr := mkApp4 (mkConst ``BVExpr.bin) (toExpr lhs.width) lhs.expr (toExpr op) rhs.expr
|
||||
let congrThm := mkApp (mkConst congrThm) (toExpr lhs.width)
|
||||
let proof := binaryCongrProof lhs rhs lhsExpr rhsExpr congrThm
|
||||
return some ⟨lhs.width, bvExpr, proof, expr⟩
|
||||
else
|
||||
return none
|
||||
|
||||
binaryCongrProof (lhs rhs : ReifiedBVExpr) (lhsExpr rhsExpr : Expr) (congrThm : Expr) :
|
||||
M Expr := do
|
||||
let lhsEval ← mkEvalExpr lhs.width lhs.expr
|
||||
let lhsProof ← lhs.evalsAtAtoms
|
||||
let rhsProof ← rhs.evalsAtAtoms
|
||||
let rhsEval ← mkEvalExpr rhs.width rhs.expr
|
||||
return mkApp6 congrThm lhsExpr rhsExpr lhsEval rhsEval lhsProof rhsProof
|
||||
|
||||
unaryReflection (innerExpr : Expr) (op : BVUnOp) (congrThm : Name) :
|
||||
M (Option ReifiedBVExpr) := do
|
||||
let some inner ← ofOrAtom innerExpr | return none
|
||||
let bvExpr := .un op inner.bvExpr
|
||||
let expr := mkApp3 (mkConst ``BVExpr.un) (toExpr inner.width) (toExpr op) inner.expr
|
||||
let proof := unaryCongrProof inner innerExpr (mkConst congrThm)
|
||||
return some ⟨inner.width, bvExpr, proof, expr⟩
|
||||
|
||||
unaryCongrProof (inner : ReifiedBVExpr) (innerExpr : Expr) (congrProof : Expr) : M Expr := do
|
||||
let innerEval ← mkEvalExpr inner.width inner.expr
|
||||
let innerProof ← inner.evalsAtAtoms
|
||||
return mkApp4 congrProof (toExpr inner.width) innerExpr innerEval innerProof
|
||||
|
||||
goBvLit (x : Expr) : M (Option ReifiedBVExpr) := do
|
||||
let some ⟨width, bvVal⟩ ← getBitVecValue? x | return none
|
||||
let bvExpr : BVExpr width := .const bvVal
|
||||
let expr := mkApp2 (mkConst ``BVExpr.const) (toExpr width) (toExpr bvVal)
|
||||
let proof := do
|
||||
let evalExpr ← mkEvalExpr width expr
|
||||
return mkBVRefl width evalExpr
|
||||
return some ⟨width, bvExpr, proof, expr⟩
|
||||
|
||||
end ReifiedBVExpr
|
||||
|
||||
end Frontend
|
||||
end Lean.Elab.Tactic.BVDecide
|
||||
@@ -0,0 +1,112 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean 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.Frontend.BVDecide.ReifiedBVPred
|
||||
|
||||
/-!
|
||||
Provides the logic for reifying `BitVec` problems with boolean substructure.
|
||||
-/
|
||||
|
||||
namespace Lean.Elab.Tactic.BVDecide
|
||||
namespace Frontend
|
||||
|
||||
open Std.Tactic.BVDecide
|
||||
open Std.Tactic.BVDecide.Reflect.Bool
|
||||
|
||||
/--
|
||||
A reified version of an `Expr` representing a `BVLogicalExpr`.
|
||||
-/
|
||||
structure ReifiedBVLogical where
|
||||
/--
|
||||
The reified expression.
|
||||
-/
|
||||
bvExpr : BVLogicalExpr
|
||||
/--
|
||||
A proof that `bvExpr.eval atomsAssignment = originalBVLogicalExpr`.
|
||||
-/
|
||||
evalsAtAtoms : M Expr
|
||||
/--
|
||||
A cache for `toExpr bvExpr`
|
||||
-/
|
||||
expr : Expr
|
||||
|
||||
namespace ReifiedBVLogical
|
||||
|
||||
def mkRefl (expr : Expr) : Expr :=
|
||||
mkApp2 (mkConst ``Eq.refl [1]) (mkConst ``Bool) expr
|
||||
|
||||
def mkTrans (x y z : Expr) (hxy hyz : Expr) : Expr :=
|
||||
mkApp6 (mkConst ``Eq.trans [1]) (mkConst ``Bool) x y z hxy hyz
|
||||
|
||||
def mkEvalExpr (expr : Expr) : M Expr := do
|
||||
return mkApp2 (mkConst ``BVLogicalExpr.eval) (← M.atomsAssignment) expr
|
||||
|
||||
partial def of (t : Expr) : M (Option ReifiedBVLogical) := do
|
||||
match_expr t with
|
||||
| Bool.true =>
|
||||
let boolExpr := .const true
|
||||
let expr := mkApp2 (mkConst ``BoolExpr.const) (mkConst ``BVPred) (toExpr Bool.true)
|
||||
let proof := return mkRefl (mkConst ``Bool.true)
|
||||
return some ⟨boolExpr, proof, expr⟩
|
||||
| Bool.false =>
|
||||
let boolExpr := .const false
|
||||
let expr := mkApp2 (mkConst ``BoolExpr.const) (mkConst ``BVPred) (toExpr Bool.false)
|
||||
let proof := return mkRefl (mkConst ``Bool.false)
|
||||
return some ⟨boolExpr, proof, expr⟩
|
||||
| not subExpr =>
|
||||
let some sub ← of subExpr | return none
|
||||
let boolExpr := .not sub.bvExpr
|
||||
let expr := mkApp2 (mkConst ``BoolExpr.not) (mkConst ``BVPred) sub.expr
|
||||
let proof := do
|
||||
let subEvalExpr ← mkEvalExpr sub.expr
|
||||
let subProof ← sub.evalsAtAtoms
|
||||
return mkApp3 (mkConst ``Std.Tactic.BVDecide.Reflect.Bool.not_congr) subExpr subEvalExpr subProof
|
||||
return some ⟨boolExpr, proof, expr⟩
|
||||
| or lhsExpr rhsExpr => gateReflection lhsExpr rhsExpr .or ``Std.Tactic.BVDecide.Reflect.Bool.or_congr
|
||||
| and lhsExpr rhsExpr => gateReflection lhsExpr rhsExpr .and ``Std.Tactic.BVDecide.Reflect.Bool.and_congr
|
||||
| xor lhsExpr rhsExpr => gateReflection lhsExpr rhsExpr .xor ``Std.Tactic.BVDecide.Reflect.Bool.xor_congr
|
||||
| BEq.beq α _ lhsExpr rhsExpr =>
|
||||
match_expr α with
|
||||
| Bool => gateReflection lhsExpr rhsExpr .beq ``Std.Tactic.BVDecide.Reflect.Bool.beq_congr
|
||||
| BitVec _ => goPred t
|
||||
| _ => return none
|
||||
| _ => goPred t
|
||||
where
|
||||
gateReflection (lhsExpr rhsExpr : Expr) (gate : Gate) (congrThm : Name) :
|
||||
M (Option ReifiedBVLogical) := do
|
||||
let some lhs ← of lhsExpr | return none
|
||||
let some rhs ← of rhsExpr | return none
|
||||
let boolExpr := .gate gate lhs.bvExpr rhs.bvExpr
|
||||
let expr :=
|
||||
mkApp4
|
||||
(mkConst ``BoolExpr.gate)
|
||||
(mkConst ``BVPred)
|
||||
(toExpr gate)
|
||||
lhs.expr
|
||||
rhs.expr
|
||||
let proof := do
|
||||
let lhsEvalExpr ← mkEvalExpr lhs.expr
|
||||
let rhsEvalExpr ← mkEvalExpr rhs.expr
|
||||
let lhsProof ← lhs.evalsAtAtoms
|
||||
let rhsProof ← rhs.evalsAtAtoms
|
||||
return mkApp6
|
||||
(mkConst congrThm)
|
||||
lhsExpr rhsExpr
|
||||
lhsEvalExpr rhsEvalExpr
|
||||
lhsProof rhsProof
|
||||
return some ⟨boolExpr, proof, expr⟩
|
||||
|
||||
goPred (t : Expr) : M (Option ReifiedBVLogical) := do
|
||||
let some bvPred ← ReifiedBVPred.of t | return none
|
||||
let boolExpr := .literal bvPred.bvPred
|
||||
let expr := mkApp2 (mkConst ``BoolExpr.literal) (mkConst ``BVPred) bvPred.expr
|
||||
let proof := bvPred.evalsAtAtoms
|
||||
return some ⟨boolExpr, proof, expr⟩
|
||||
|
||||
end ReifiedBVLogical
|
||||
|
||||
end Frontend
|
||||
end Lean.Elab.Tactic.BVDecide
|
||||
@@ -0,0 +1,120 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean 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.Frontend.BVDecide.ReifiedBVExpr
|
||||
|
||||
/-!
|
||||
Provides the logic for reifying expressions consisting of predicates over `BitVec`s.
|
||||
-/
|
||||
|
||||
namespace Lean.Elab.Tactic.BVDecide
|
||||
namespace Frontend
|
||||
|
||||
open Lean.Meta
|
||||
open Std.Tactic.BVDecide
|
||||
open Std.Tactic.BVDecide.Reflect.BitVec
|
||||
|
||||
/--
|
||||
A reified version of an `Expr` representing a `BVPred`.
|
||||
-/
|
||||
structure ReifiedBVPred where
|
||||
/--
|
||||
The reified expression.
|
||||
-/
|
||||
bvPred : BVPred
|
||||
/--
|
||||
A proof that `bvPred.eval atomsAssignment = originalBVPredExpr`.
|
||||
-/
|
||||
evalsAtAtoms : M Expr
|
||||
/--
|
||||
A cache for `toExpr bvPred`
|
||||
-/
|
||||
expr : Expr
|
||||
|
||||
namespace ReifiedBVPred
|
||||
|
||||
/--
|
||||
Reify an `Expr` that is a proof of a predicate about `BitVec`.
|
||||
-/
|
||||
def of (t : Expr) : M (Option ReifiedBVPred) := do
|
||||
match_expr t with
|
||||
| BEq.beq α _ lhsExpr rhsExpr =>
|
||||
let_expr BitVec _ := α | return none
|
||||
binaryReflection lhsExpr rhsExpr .eq ``Std.Tactic.BVDecide.Reflect.BitVec.beq_congr
|
||||
| BitVec.ult _ lhsExpr rhsExpr =>
|
||||
binaryReflection lhsExpr rhsExpr .ult ``Std.Tactic.BVDecide.Reflect.BitVec.ult_congr
|
||||
| BitVec.getLsb _ subExpr idxExpr =>
|
||||
let some sub ← ReifiedBVExpr.of subExpr | return none
|
||||
let some idx ← getNatValue? idxExpr | return none
|
||||
let bvExpr : BVPred := .getLsb sub.bvExpr idx
|
||||
let expr := mkApp3 (mkConst ``BVPred.getLsb) (toExpr sub.width) sub.expr idxExpr
|
||||
let proof := do
|
||||
let subEval ← ReifiedBVExpr.mkEvalExpr sub.width sub.expr
|
||||
let subProof ← sub.evalsAtAtoms
|
||||
return mkApp5
|
||||
(mkConst ``Std.Tactic.BVDecide.Reflect.BitVec.getLsb_congr)
|
||||
idxExpr
|
||||
(toExpr sub.width)
|
||||
subExpr
|
||||
subEval
|
||||
subProof
|
||||
return some ⟨bvExpr, proof, expr⟩
|
||||
| _ =>
|
||||
/-
|
||||
Idea: we have t : Bool here, let's construct:
|
||||
BitVec.ofBool t : BitVec 1
|
||||
as an atom. Then construct the BVPred corresponding to
|
||||
BitVec.getLsb (BitVec.ofBool t) 0 : Bool
|
||||
We can prove that this is equivalent to `t`. This allows us to have boolean variables in BVPred.
|
||||
-/
|
||||
let ty ← inferType t
|
||||
let_expr Bool := ty | return none
|
||||
let atom ← ReifiedBVExpr.mkAtom (mkApp (mkConst ``BitVec.ofBool) t) 1
|
||||
let bvExpr : BVPred := .getLsb atom.bvExpr 0
|
||||
let expr := mkApp3 (mkConst ``BVPred.getLsb) (toExpr 1) atom.expr (toExpr 0)
|
||||
let proof := do
|
||||
let atomEval ← ReifiedBVExpr.mkEvalExpr atom.width atom.expr
|
||||
let atomProof ← atom.evalsAtAtoms
|
||||
return mkApp3
|
||||
(mkConst ``Std.Tactic.BVDecide.Reflect.BitVec.ofBool_congr)
|
||||
t
|
||||
atomEval
|
||||
atomProof
|
||||
return some ⟨bvExpr, proof, expr⟩
|
||||
where
|
||||
binaryReflection (lhsExpr rhsExpr : Expr) (pred : BVBinPred) (congrThm : Name) :
|
||||
M (Option ReifiedBVPred) := do
|
||||
let some lhs ← ReifiedBVExpr.of lhsExpr | return none
|
||||
let some rhs ← ReifiedBVExpr.of rhsExpr | return none
|
||||
if h:lhs.width = rhs.width then
|
||||
let bvExpr : BVPred := .bin (w := lhs.width) lhs.bvExpr pred (h ▸ rhs.bvExpr)
|
||||
let expr :=
|
||||
mkApp4
|
||||
(mkConst ``BVPred.bin)
|
||||
(toExpr lhs.width)
|
||||
lhs.expr
|
||||
(toExpr pred)
|
||||
rhs.expr
|
||||
let proof := do
|
||||
let lhsEval ← ReifiedBVExpr.mkEvalExpr lhs.width lhs.expr
|
||||
let lhsProof ← lhs.evalsAtAtoms
|
||||
let rhsEval ← ReifiedBVExpr.mkEvalExpr rhs.width rhs.expr
|
||||
let rhsProof ← rhs.evalsAtAtoms
|
||||
return mkApp7
|
||||
(mkConst congrThm)
|
||||
(toExpr lhs.width)
|
||||
lhsExpr rhsExpr lhsEval rhsEval
|
||||
lhsProof
|
||||
rhsProof
|
||||
return some ⟨bvExpr, proof, expr⟩
|
||||
else
|
||||
return none
|
||||
|
||||
|
||||
end ReifiedBVPred
|
||||
|
||||
end Frontend
|
||||
end Lean.Elab.Tactic.BVDecide
|
||||
@@ -0,0 +1,101 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean 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.Frontend.BVDecide.ReifiedBVLogical
|
||||
|
||||
/-!
|
||||
This module is the main entry point for reifying `BitVec` problems with boolean substructure.
|
||||
Given some proof `h : exp = true` where `exp` is a `BitVec` problem with boolean substructure, it
|
||||
returns a `SatAtBVLogical`, containing the reified version as well as a proof that the reified
|
||||
version must be equal to true.
|
||||
-/
|
||||
|
||||
namespace Lean.Elab.Tactic.BVDecide
|
||||
namespace Frontend
|
||||
|
||||
open Lean.Meta
|
||||
open Std.Tactic.BVDecide
|
||||
|
||||
/--
|
||||
A reified version of an `Expr` representing a `BVLogicalExpr` that we know to be true.
|
||||
-/
|
||||
structure SatAtBVLogical where
|
||||
/--
|
||||
The reified expression.
|
||||
-/
|
||||
bvExpr : BVLogicalExpr
|
||||
/--
|
||||
A proof that `bvExpr.eval atomsAssignment = true`.
|
||||
-/
|
||||
satAtAtoms : M Expr
|
||||
/--
|
||||
A cache for `toExpr bvExpr`
|
||||
-/
|
||||
expr : Expr
|
||||
|
||||
namespace SatAtBVLogical
|
||||
|
||||
/--
|
||||
Reify an `Expr` that is a proof of some boolean structure on top of predicates about `BitVec`s.
|
||||
-/
|
||||
partial def of (h : Expr) : M (Option SatAtBVLogical) := do
|
||||
let t ← instantiateMVars (← whnfR (← inferType h))
|
||||
match_expr t with
|
||||
| Eq α lhsExpr rhsExpr =>
|
||||
let_expr Bool := α | return none
|
||||
let_expr Bool.true := rhsExpr | return none
|
||||
-- We now know that `h : lhsExpr = true`
|
||||
-- We attempt to reify lhsExpr into a BVLogicalExpr, then prove that evaluating
|
||||
-- this BVLogicalExpr must eval to true due to `h`
|
||||
let some bvLogical ← ReifiedBVLogical.of lhsExpr | return none
|
||||
let proof := do
|
||||
let evalLogic ← ReifiedBVLogical.mkEvalExpr bvLogical.expr
|
||||
-- this is evalLogic = lhsExpr
|
||||
let evalProof ← bvLogical.evalsAtAtoms
|
||||
-- h is lhsExpr = true
|
||||
-- we prove evalLogic = true by evalLogic = lhsExpr = true
|
||||
return ReifiedBVLogical.mkTrans evalLogic lhsExpr (mkConst ``Bool.true) evalProof h
|
||||
return some ⟨bvLogical.bvExpr, proof, bvLogical.expr⟩
|
||||
| _ => return none
|
||||
|
||||
/--
|
||||
The trivially true `BVLogicalExpr`.
|
||||
-/
|
||||
def trivial : SatAtBVLogical where
|
||||
bvExpr := .const true
|
||||
expr := toExpr (.const true : BVLogicalExpr)
|
||||
satAtAtoms := return mkApp (mkConst ``BVLogicalExpr.sat_true) (← M.atomsAssignment)
|
||||
|
||||
/--
|
||||
Logical conjunction of two `ReifiedBVLogical`.
|
||||
-/
|
||||
def and (x y : SatAtBVLogical) : SatAtBVLogical where
|
||||
bvExpr := .gate .and x.bvExpr y.bvExpr
|
||||
expr := mkApp4 (mkConst ``BoolExpr.gate) (mkConst ``BVPred) (mkConst ``Gate.and) x.expr y.expr
|
||||
satAtAtoms :=
|
||||
return mkApp5
|
||||
(mkConst ``BVLogicalExpr.sat_and)
|
||||
x.expr
|
||||
y.expr
|
||||
(← M.atomsAssignment)
|
||||
(← x.satAtAtoms)
|
||||
(← y.satAtAtoms)
|
||||
|
||||
/-- Given a proof that `x.expr.Unsat`, produce a proof of `False`. -/
|
||||
def proveFalse (x : SatAtBVLogical) (h : Expr) : M Expr := do
|
||||
let atomsList ← M.atomsAssignment
|
||||
let evalExpr := mkApp2 (mkConst ``BVLogicalExpr.eval) atomsList x.expr
|
||||
return mkApp3
|
||||
(mkConst ``Std.Tactic.BVDecide.Reflect.Bool.false_of_eq_true_of_eq_false)
|
||||
evalExpr
|
||||
(← x.satAtAtoms)
|
||||
(.app h atomsList)
|
||||
|
||||
|
||||
end SatAtBVLogical
|
||||
|
||||
end Frontend
|
||||
end Lean.Elab.Tactic.BVDecide
|
||||
70
src/Lean/Elab/Tactic/BVDecide/Frontend/BVTrace.lean
Normal file
70
src/Lean/Elab/Tactic/BVDecide/Frontend/BVTrace.lean
Normal file
@@ -0,0 +1,70 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean 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.Frontend.BVDecide
|
||||
import Lean.Elab.Tactic.BVDecide.Frontend.BVCheck
|
||||
import Lean.Elab.Tactic.BVDecide.LRAT.Trim
|
||||
import Lean.Meta.Tactic.TryThis
|
||||
import Std.Tactic.BVDecide.Syntax
|
||||
|
||||
/-!
|
||||
This module contains the implementation of `bv_decide?`.
|
||||
-/
|
||||
|
||||
namespace Lean.Elab.Tactic.BVDecide
|
||||
namespace Frontend.BVTrace
|
||||
|
||||
|
||||
-- TODO: think of a more maintainable file pattern for this stuff.
|
||||
/--
|
||||
Produce a file with the pattern:
|
||||
LeanFileName-DeclName-Line-Col.lrat
|
||||
-/
|
||||
def getLratFileName : TermElabM System.FilePath := do
|
||||
let some baseName := System.FilePath.mk (← getFileName) |>.fileName | throwError "could not find file name"
|
||||
let some declName ← Term.getDeclName? | throwError "could not find declaration name"
|
||||
let pos := (← getFileMap).toPosition (← getRefPos)
|
||||
return s!"{baseName}-{declName}-{pos.line}-{pos.column}.lrat"
|
||||
|
||||
open Std.Tactic.BVDecide.LRAT in
|
||||
open Lean.Meta.Tactic in
|
||||
open Lean.Elab.Tactic.BVDecide.LRAT in
|
||||
@[builtin_tactic Lean.Parser.Tactic.bvTrace]
|
||||
def evalBvTrace : Tactic := fun
|
||||
| `(tactic| bv_decide?%$tk) => do
|
||||
let lratFile : System.FilePath ← BVTrace.getLratFileName
|
||||
let cfg := { (← BVCheck.mkContext lratFile) with trimProofs := false }
|
||||
let g ← getMainGoal
|
||||
let trace ← g.withContext do
|
||||
bvDecide g cfg
|
||||
/-
|
||||
Ideally trace.lratCert would be the `ByteArray` version of the proof already and we just write
|
||||
it. This isn't yet possible so instead we do the following:
|
||||
1. Produce the proof in the tactic.
|
||||
2. Skip trimming it in the tactic.
|
||||
3. Run trimming on the LRAT file that was produced by the SAT solver directly, emitting the
|
||||
correct binary format according to `sat.binaryProofs`.
|
||||
TODO: Fix this hack:
|
||||
1. Introduce `ByteArray` literals to the kernel.
|
||||
2. Just return the fully trimmed proof in the format desired by the configuration from `bvDecide`.
|
||||
3. Write it to the file directly.
|
||||
-/
|
||||
match trace.lratCert with
|
||||
| none =>
|
||||
let normalizeStx ← `(tactic| bv_normalize)
|
||||
TryThis.addSuggestion tk normalizeStx (origSpan? := ← getRef)
|
||||
| some .. =>
|
||||
if sat.trimProofs.get (← getOptions) then
|
||||
let lratPath := (← BVCheck.getSrcDir) / lratFile
|
||||
let proof ← loadLRATProof lratPath
|
||||
let trimmed ← IO.ofExcept <| trim proof
|
||||
dumpLRATProof lratPath trimmed cfg.binaryProofs
|
||||
let bvCheckStx ← `(tactic| bv_check $(quote lratFile.toString))
|
||||
TryThis.addSuggestion tk bvCheckStx (origSpan? := ← getRef)
|
||||
| _ => throwUnsupportedSyntax
|
||||
|
||||
end Frontend.BVTrace
|
||||
end Lean.Elab.Tactic.BVDecide
|
||||
210
src/Lean/Elab/Tactic/BVDecide/Frontend/LRAT.lean
Normal file
210
src/Lean/Elab/Tactic/BVDecide/Frontend/LRAT.lean
Normal file
@@ -0,0 +1,210 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean 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.Frontend.Attr
|
||||
import Lean.Elab.Tactic.BVDecide.LRAT.Trim
|
||||
import Lean.Elab.Tactic.BVDecide.External
|
||||
import Std.Tactic.BVDecide.LRAT.Checker
|
||||
import Std.Sat.CNF.Dimacs
|
||||
|
||||
/-!
|
||||
This module contains the logic around writing proofs of UNSAT, using LRAT proofs, as meta code.
|
||||
-/
|
||||
|
||||
namespace Lean.Elab.Tactic.BVDecide
|
||||
namespace Frontend
|
||||
|
||||
open Std.Sat
|
||||
open Std.Tactic.BVDecide
|
||||
open Lean.Meta
|
||||
|
||||
/--
|
||||
The context for the `bv_decide` tactic.
|
||||
-/
|
||||
structure TacticContext where
|
||||
exprDef : Name
|
||||
certDef : Name
|
||||
reflectionDef : Name
|
||||
solver : System.FilePath
|
||||
lratPath : System.FilePath
|
||||
graphviz : Bool
|
||||
timeout : Nat
|
||||
trimProofs : Bool
|
||||
binaryProofs : Bool
|
||||
|
||||
def TacticContext.new (lratPath : System.FilePath) : Lean.Elab.TermElabM TacticContext := do
|
||||
let exprDef ← Lean.Elab.Term.mkAuxName `_expr_def
|
||||
let certDef ← Lean.Elab.Term.mkAuxName `_cert_def
|
||||
let reflectionDef ← Lean.Elab.Term.mkAuxName `_reflection_def
|
||||
let opts ← getOptions
|
||||
let solver ← determineSolver
|
||||
trace[Meta.Tactic.sat] m!"Using SAT solver at '{solver}'"
|
||||
let timeout := sat.timeout.get opts
|
||||
let graphviz := debug.bv.graphviz.get opts
|
||||
let trimProofs := sat.trimProofs.get opts
|
||||
let binaryProofs :=
|
||||
-- Account for: https://github.com/arminbiere/cadical/issues/112
|
||||
if System.Platform.isWindows then
|
||||
false
|
||||
else
|
||||
sat.binaryProofs.get opts
|
||||
return {
|
||||
exprDef,
|
||||
certDef,
|
||||
reflectionDef,
|
||||
solver,
|
||||
lratPath,
|
||||
graphviz,
|
||||
timeout,
|
||||
trimProofs,
|
||||
binaryProofs
|
||||
}
|
||||
where
|
||||
determineSolver : Lean.Elab.TermElabM System.FilePath := do
|
||||
let opts ← getOptions
|
||||
let option := sat.solver.get opts
|
||||
if option == "" then
|
||||
let cadicalPath := (← IO.appPath).parent.get! / "cadical" |>.withExtension System.FilePath.exeExtension
|
||||
if ← cadicalPath.pathExists then
|
||||
return cadicalPath
|
||||
else
|
||||
return "cadical"
|
||||
else
|
||||
return option
|
||||
|
||||
/-- An LRAT proof read from a file. This will get parsed using ofReduceBool. -/
|
||||
abbrev LratCert := String
|
||||
|
||||
instance : ToExpr LRAT.IntAction where
|
||||
toExpr action :=
|
||||
let beta := mkApp (mkConst ``Array [.zero]) (mkConst ``Int)
|
||||
let alpha := mkConst ``Nat
|
||||
match action with
|
||||
| .addEmpty id hints =>
|
||||
mkApp4 (mkConst ``LRAT.Action.addEmpty [.zero, .zero]) beta alpha (toExpr id) (toExpr hints)
|
||||
| .addRup id c hints =>
|
||||
mkApp5 (mkConst ``LRAT.Action.addRup [.zero, .zero])
|
||||
beta
|
||||
alpha
|
||||
(toExpr id)
|
||||
(toExpr c)
|
||||
(toExpr hints)
|
||||
| .addRat id c pivot rupHints ratHints =>
|
||||
mkApp7 (mkConst ``LRAT.Action.addRat [.zero, .zero])
|
||||
beta
|
||||
alpha
|
||||
(toExpr id)
|
||||
(toExpr c)
|
||||
(toExpr pivot)
|
||||
(toExpr rupHints)
|
||||
(toExpr ratHints)
|
||||
| .del ids =>
|
||||
mkApp3 (mkConst ``LRAT.Action.del [.zero, .zero]) beta alpha (toExpr ids)
|
||||
toTypeExpr := mkConst ``LRAT.IntAction
|
||||
|
||||
def LratCert.ofFile (lratPath : System.FilePath) (trimProofs : Bool) : MetaM LratCert := do
|
||||
let proofInput ← IO.FS.readBinFile lratPath
|
||||
let proof ←
|
||||
withTraceNode `sat (fun _ => return s!"Parsing LRAT file") do
|
||||
-- lazyPure to prevent compiler lifting
|
||||
let proof? ← IO.lazyPure (fun _ => LRAT.parseLRATProof proofInput)
|
||||
match proof? with
|
||||
| .ok proof => pure proof
|
||||
| .error err => throwError "SAT solver produced invalid LRAT: {err}"
|
||||
|
||||
trace[Meta.Tactic.sat] s!"LRAT proof has {proof.size} steps before trimming"
|
||||
|
||||
let proof ←
|
||||
if trimProofs then
|
||||
withTraceNode `sat (fun _ => return "Trimming LRAT proof") do
|
||||
-- lazyPure to prevent compiler lifting
|
||||
let trimmed ← IO.lazyPure (fun _ => LRAT.trim proof)
|
||||
IO.ofExcept trimmed
|
||||
else
|
||||
pure proof
|
||||
|
||||
trace[Meta.Tactic.sat] s!"LRAT proof has {proof.size} steps after trimming"
|
||||
|
||||
-- This is necessary because the proof might be in the binary format in which case we cannot
|
||||
-- store it as a string in the environment (yet) due to missing support for binary literals.
|
||||
let newProof := LRAT.lratProofToString proof
|
||||
return newProof
|
||||
|
||||
/--
|
||||
Run an external SAT solver on the `CNF` to obtain an LRAT proof.
|
||||
|
||||
This will obtain an `LratCert` if the formula is UNSAT and throw errors otherwise.
|
||||
-/
|
||||
def runExternal (cnf : CNF Nat) (solver : System.FilePath) (lratPath : System.FilePath)
|
||||
(trimProofs : Bool) (timeout : Nat) (binaryProofs : Bool)
|
||||
: MetaM (Except (Array (Bool × Nat)) LratCert) := do
|
||||
IO.FS.withTempFile fun _ cnfPath => do
|
||||
withTraceNode `sat (fun _ => return "Serializing SAT problem to DIMACS file") do
|
||||
-- lazyPure to prevent compiler lifting
|
||||
IO.FS.writeFile cnfPath (← IO.lazyPure (fun _ => cnf.dimacs))
|
||||
|
||||
let res ←
|
||||
withTraceNode `sat (fun _ => return "Running SAT solver") do
|
||||
External.satQuery solver cnfPath lratPath timeout binaryProofs
|
||||
if let .sat assignment := res then
|
||||
return .error assignment
|
||||
|
||||
let lratProof ←
|
||||
withTraceNode `sat (fun _ => return "Obtaining LRAT certificate") do
|
||||
LratCert.ofFile lratPath trimProofs
|
||||
|
||||
return .ok lratProof
|
||||
|
||||
/--
|
||||
Add an auxiliary declaration. Only used to create constants that appear in our reflection proof.
|
||||
-/
|
||||
def mkAuxDecl (name : Name) (value type : Expr) : MetaM Unit :=
|
||||
addAndCompile <| .defnDecl {
|
||||
name := name,
|
||||
levelParams := [],
|
||||
type := type,
|
||||
value := value,
|
||||
hints := .abbrev,
|
||||
safety := .safe
|
||||
}
|
||||
|
||||
/--
|
||||
Turn an `LratCert` into a proof that some `reflected` expression is UNSAT by providing a `verifier`
|
||||
function together with a correctness theorem for it.
|
||||
|
||||
- `verifier` is expected to have type `α → LratCert → Bool`
|
||||
- `unsat_of_verifier_eq_true` is expected to have type
|
||||
`∀ (b : α) (c : LratCert), verifier b c = true → unsat b`
|
||||
-/
|
||||
def LratCert.toReflectionProof [ToExpr α] (cert : LratCert) (cfg : TacticContext) (reflected : α)
|
||||
(verifier : Name) (unsat_of_verifier_eq_true : Name) :
|
||||
MetaM Expr := do
|
||||
withTraceNode `sat (fun _ => return "Compiling expr term") do
|
||||
mkAuxDecl cfg.exprDef (toExpr reflected) (toTypeExpr α)
|
||||
|
||||
let certType := toTypeExpr LratCert
|
||||
|
||||
withTraceNode `sat (fun _ => return "Compiling proof certificate term") do
|
||||
mkAuxDecl cfg.certDef (toExpr cert) certType
|
||||
|
||||
let reflectedExpr := mkConst cfg.exprDef
|
||||
let certExpr := mkConst cfg.certDef
|
||||
|
||||
withTraceNode `sat (fun _ => return "Compiling reflection proof term") do
|
||||
let auxValue := mkApp2 (mkConst verifier) reflectedExpr certExpr
|
||||
mkAuxDecl cfg.reflectionDef auxValue (mkConst ``Bool)
|
||||
|
||||
let nativeProof :=
|
||||
mkApp3
|
||||
(mkConst ``Lean.ofReduceBool)
|
||||
(mkConst cfg.reflectionDef)
|
||||
(toExpr true)
|
||||
(← mkEqRefl (toExpr true))
|
||||
return mkApp3 (mkConst unsat_of_verifier_eq_true) reflectedExpr certExpr nativeProof
|
||||
|
||||
|
||||
end Frontend
|
||||
end Lean.Elab.Tactic.BVDecide
|
||||
90
src/Lean/Elab/Tactic/BVDecide/Frontend/Normalize.lean
Normal file
90
src/Lean/Elab/Tactic/BVDecide/Frontend/Normalize.lean
Normal file
@@ -0,0 +1,90 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Henrik Böving
|
||||
-/
|
||||
prelude
|
||||
import Lean.Meta.AppBuilder
|
||||
import Lean.Elab.Tactic.Simp
|
||||
import Lean.Elab.Tactic.FalseOrByContra
|
||||
import Lean.Elab.Tactic.BVDecide.Frontend.Attr
|
||||
import Std.Tactic.BVDecide.Normalize
|
||||
import Std.Tactic.BVDecide.Syntax
|
||||
|
||||
/-!
|
||||
This module contains the implementation of `bv_normalize` which is effectively a custom `bv_normalize`
|
||||
simp set that is called like this: `simp only [seval, bv_normalize]`. The rules in `bv_normalize`
|
||||
fulfill two goals:
|
||||
1. Turn all hypothesis involving `Bool` and `BitVec` into the form `x = true` where `x` only consists
|
||||
of a operations on `Bool` and `BitVec`. In particular no `Prop` should be contained. This makes
|
||||
the reflection procedure further down the pipeline much easier to implement.
|
||||
2. Apply simplification rules from the Bitwuzla SMT solver.
|
||||
-/
|
||||
|
||||
namespace Lean.Elab.Tactic.BVDecide
|
||||
namespace Frontend.Normalize
|
||||
|
||||
open Lean.Meta
|
||||
open Std.Tactic.BVDecide.Normalize
|
||||
|
||||
/--
|
||||
The bitblaster for multiplication introduces symbolic branches over the right hand side.
|
||||
If we have an expression of the form `c * x` where `c` is constant we should change it to `x * c`
|
||||
such that these symbolic branches get constant folded by the AIG framework.
|
||||
-/
|
||||
builtin_simproc [bv_normalize] mulConst ((_ : BitVec _) * (_ : BitVec _)) := fun e => do
|
||||
let_expr HMul.hMul _ _ _ _ lhs rhs := e | return .continue
|
||||
let some ⟨width, _⟩ ← Lean.Meta.getBitVecValue? lhs | return .continue
|
||||
let new ← mkAppM ``HMul.hMul #[rhs, lhs]
|
||||
let proof := mkApp3 (mkConst ``BitVec.mul_comm) (toExpr width) lhs rhs
|
||||
return .done { expr := new, proof? := some proof }
|
||||
|
||||
builtin_simproc [bv_normalize] eqToBEq (((_ : Bool) = (_ : Bool))) := fun e => do
|
||||
let_expr Eq _ lhs rhs := e | return .continue
|
||||
match_expr rhs with
|
||||
| Bool.true => return .continue
|
||||
| _ =>
|
||||
let beqApp ← mkAppM ``BEq.beq #[lhs, rhs]
|
||||
let new := mkApp3 (mkConst ``Eq [1]) (mkConst ``Bool) beqApp (mkConst ``Bool.true)
|
||||
let proof := mkApp2 (mkConst ``Bool.eq_to_beq) lhs rhs
|
||||
return .done { expr := new, proof? := some proof }
|
||||
|
||||
structure Result where
|
||||
goal : Option MVarId := none
|
||||
stats : Simp.Stats := {}
|
||||
|
||||
def bvNormalize (g : MVarId) : MetaM Result := do
|
||||
withTraceNode `bv (fun _ => return "Normalizing goal") do
|
||||
-- Contradiction proof
|
||||
let some g ← g.falseOrByContra | return {}
|
||||
|
||||
-- Normalization by simp
|
||||
let bvThms ← bvNormalizeExt.getTheorems
|
||||
let bvSimprocs ← bvNormalizeSimprocExt.getSimprocs
|
||||
let sevalThms ← getSEvalTheorems
|
||||
let sevalSimprocs ← Simp.getSEvalSimprocs
|
||||
|
||||
let simpCtx : Simp.Context := {
|
||||
simpTheorems := #[bvThms, sevalThms]
|
||||
congrTheorems := (← getSimpCongrTheorems)
|
||||
}
|
||||
|
||||
let hyps ← g.getNondepPropHyps
|
||||
let ⟨result?, stats⟩ ← simpGoal g
|
||||
(ctx := simpCtx)
|
||||
(simprocs := #[bvSimprocs, sevalSimprocs])
|
||||
(fvarIdsToSimp := hyps)
|
||||
let some (_, g) := result? | return ⟨none, stats⟩
|
||||
return ⟨some g, stats⟩
|
||||
|
||||
@[builtin_tactic Lean.Parser.Tactic.bvNormalize]
|
||||
def evalBVNormalize : Tactic := fun
|
||||
| `(tactic| bv_normalize) => do
|
||||
liftMetaFinishingTactic fun g => do
|
||||
discard <| bvNormalize g
|
||||
| _ => throwUnsupportedSyntax
|
||||
|
||||
end Frontend.Normalize
|
||||
end Lean.Elab.Tactic.BVDecide
|
||||
|
||||
|
||||
@@ -5,10 +5,9 @@ Authors: Henrik Böving
|
||||
-/
|
||||
prelude
|
||||
import Lean.Elab.Tactic.BVDecide.LRAT.Trim
|
||||
import Lean.Elab.Tactic.BVDecide.LRAT.Parser
|
||||
|
||||
/-!
|
||||
This directory contains the implementation of the LRAT parsing and trimming algorithms.
|
||||
They mostly live here because they used datastructures and parsing infrastructure from `Lean`.
|
||||
This directory contains the implementation of the LRAT trimming algorithms.
|
||||
It lives here because it uses datastructures and parsing infrastructure from `Lean`.
|
||||
Otherwise they could be put into `Std.Tactic.BVDecide.LRAT`.
|
||||
-/
|
||||
|
||||
12
src/Lean/Elab/Tactic/BoolToPropSimps.lean
Normal file
12
src/Lean/Elab/Tactic/BoolToPropSimps.lean
Normal file
@@ -0,0 +1,12 @@
|
||||
/-
|
||||
Copyright (c) 2024 University of Cambridge. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Tobias Grosser
|
||||
-/
|
||||
|
||||
prelude
|
||||
import Lean.Meta.Tactic.Simp.Attr
|
||||
|
||||
builtin_initialize boolToPropSimps : Lean.Meta.SimpExtension ←
|
||||
Lean.Meta.registerSimpAttr `boolToPropSimps
|
||||
"simp lemmas converting boolean expressions in terms of `decide` into propositional statements"
|
||||
@@ -458,6 +458,8 @@ def renameInaccessibles (mvarId : MVarId) (hs : TSyntaxArray ``binderIdent) : Ta
|
||||
match lctx.getAt? j with
|
||||
| none => pure ()
|
||||
| some localDecl =>
|
||||
if localDecl.isImplementationDetail then
|
||||
continue
|
||||
let inaccessible := !(extractMacroScopes localDecl.userName |>.equalScope callerScopes)
|
||||
let shadowed := found.contains localDecl.userName
|
||||
if inaccessible || shadowed then
|
||||
|
||||
@@ -75,20 +75,28 @@ def congr (mvarId : MVarId) (addImplicitArgs := false) (nameSubgoals := true) :
|
||||
@[builtin_tactic Lean.Parser.Tactic.Conv.congr] def evalCongr : Tactic := fun _ => do
|
||||
replaceMainGoal <| List.filterMap id (← congr (← getMainGoal))
|
||||
|
||||
-- mvarIds is the list of goals produced by congr. We only want to change the one at position `i`
|
||||
-- so this closes all other equality goals with `rfl.`. There are non-equality goals produced
|
||||
-- by `congr` (e.g. dependent instances), thes are kept as goals.
|
||||
private def selectIdx (tacticName : String) (mvarIds : List (Option MVarId)) (i : Int) :
|
||||
TacticM Unit := do
|
||||
if i >= 0 then
|
||||
let i := i.toNat
|
||||
if h : i < mvarIds.length then
|
||||
let mut otherGoals := #[]
|
||||
for mvarId? in mvarIds, j in [:mvarIds.length] do
|
||||
match mvarId? with
|
||||
| none => pure ()
|
||||
| some mvarId =>
|
||||
if i != j then
|
||||
mvarId.refl
|
||||
if (← mvarId.getType').isEq then
|
||||
mvarId.refl
|
||||
else
|
||||
-- If its not an equality, it's likely a class constraint, to be left open
|
||||
otherGoals := otherGoals.push mvarId
|
||||
match mvarIds[i] with
|
||||
| none => throwError "cannot select argument"
|
||||
| some mvarId => replaceMainGoal [mvarId]
|
||||
| some mvarId => replaceMainGoal (mvarId :: otherGoals.toList)
|
||||
return ()
|
||||
throwError "invalid '{tacticName}' conv tactic, application has only {mvarIds.length} (nondependent) argument(s)"
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import Lean.Elab.Tactic.RCases
|
||||
import Lean.Elab.Tactic.Repeat
|
||||
import Lean.Elab.Tactic.BuiltinTactic
|
||||
import Lean.Elab.Command
|
||||
import Lean.Linter.Util
|
||||
import Lean.Linter.Basic
|
||||
|
||||
/-!
|
||||
# Implementation of the `@[ext]` attribute
|
||||
|
||||
@@ -31,16 +31,16 @@ open Lean Meta Elab Tactic
|
||||
-- but fall back to a classical instance. When it is `some true`, we always use the classical instance.
|
||||
-- When it is `some false`, if there is no `Decidable` instance we don't introduce the double negation,
|
||||
-- and fall back to `False.elim`.
|
||||
partial def falseOrByContra (g : MVarId) (useClassical : Option Bool := none) : MetaM MVarId := do
|
||||
partial def falseOrByContra (g : MVarId) (useClassical : Option Bool := none) : MetaM (Option MVarId) := do
|
||||
let ty ← whnfR (← g.getType)
|
||||
match ty with
|
||||
| .const ``False _ => pure g
|
||||
| .forallE _ _ _ _
|
||||
| .const ``False _ => return g
|
||||
| .forallE ..
|
||||
| .app (.const ``Not _) _ =>
|
||||
-- We set the transparency back to default; otherwise this breaks when run by a `simp` discharger.
|
||||
falseOrByContra (← withTransparency default g.intro1P).2 useClassical
|
||||
| _ =>
|
||||
let gs ← if ← isProp ty then
|
||||
let gs ← if (← isProp ty) then
|
||||
match useClassical with
|
||||
| some true => some <$> g.applyConst ``Classical.byContradiction
|
||||
| some false =>
|
||||
@@ -51,12 +51,15 @@ partial def falseOrByContra (g : MVarId) (useClassical : Option Bool := none) :
|
||||
catch _ => some <$> g.applyConst ``Classical.byContradiction
|
||||
else
|
||||
pure none
|
||||
if let some gs := gs then
|
||||
let [g] := gs | panic! "expected one subgoal"
|
||||
pure (← g.intro1).2
|
||||
else
|
||||
let [g] ← g.applyConst ``False.elim | panic! "expected one sugoal"
|
||||
pure g
|
||||
match gs with
|
||||
| some [] => return none
|
||||
| some [g] => return some (← g.intro1).2
|
||||
| some _ => panic! "expected at most one sugoal"
|
||||
| none =>
|
||||
match (← g.applyConst ``False.elim) with
|
||||
| [] => return none
|
||||
| [g] => return some g
|
||||
| _ => panic! "expected at most one sugoal"
|
||||
|
||||
@[builtin_tactic Lean.Parser.Tactic.falseOrByContra]
|
||||
def elabFalseOrByContra : Tactic
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user