Compare commits

..

241 Commits

Author SHA1 Message Date
Leonardo de Moura
933055e1d1 test: more tests 2025-05-07 10:04:56 +02:00
Leonardo de Moura
810a4b31b5 fix: unintended inlining for Repr 2025-05-07 09:56:34 +02:00
Leonardo de Moura
1733be8649 fix: namespaces 2025-05-07 07:00:08 +01:00
Leonardo de Moura
f7f745ab83 fix: unintended inlining in FromJson and ToJson instances 2025-05-07 06:52:10 +01:00
Leonardo de Moura
764078bffb test: exponential compilation time
The option instance is being inlined.
2025-05-07 06:52:10 +01:00
Sebastian Ullrich
af51e3e4b1 fix: make sure all kernel constants are persisted eventually (#8238)
This PR avoids an issue where, through other potential bugs, constants
that are tracked by `Kernel.Environment` but not `Environment` are not
persisted.
2025-05-05 17:20:55 +00:00
Sebastian Ullrich
9c7cb147b9 fix: extern_lib and precompileModules on macOS (#8236)
This PR fixes an issue where the combination of `extern_lib` and
`precompileModules` would lead to "symbol not found" errors.
2025-05-05 14:59:50 +00:00
Kim Morrison
9576e48e1a chore: update release_checklist.py to check new release notes page (#8235) 2025-05-05 13:29:53 +00:00
Kim Morrison
77b9e510fc fix: apply? produces a non-synthetic sorry (#8231)
This PR changes the behaviour of `apply?` so that the `sorry` it uses to
close the goal is non-synthetic. (Recall that correct use of synthetic
sorries requires that the tactic also generates an error message, which
we don't want to do in this situation.) Either this PR or #8230 are
sufficient to defend against the problem reported in #8212.
2025-05-05 12:31:08 +00:00
Sebastian Ullrich
cdb18f48cd fix: ld.so linking on Linux (#8228)
This PR fixes an issue where, depending on the host glibc version,
Lean-built executables fail with an assertion in `ld.so`.
2025-05-05 11:50:59 +00:00
Kim Morrison
208ff3e2b3 feat: upgrades to release_checklist.py script (#8192)
This PR includes upgrades to the `release_checklist.py` script prepared
while releasing v4.20.0-rc1.
2025-05-05 09:03:57 +00:00
Leonardo de Moura
ef603cf37d fix: simplifyBasis (#8226)
This PR fixes the `simplifyBasis` procedure in the commutative ring
procedure in `grind`.
2025-05-05 02:35:52 +00:00
Leonardo de Moura
8cc4505bb1 feat: diagnostics for comm ring procedure in grind (#8224)
This PR adds diagnostic information for the commutative ring procedure
in `grind`.
2025-05-04 22:55:40 +00:00
Mac Malone
70917fac9f feat: lean --setup (#8024)
This PR adds the `--setup` option to the `lean` CLI. It takes a path to
a JSON file containing information about a module's imports and
configuration, superseding that in the module's own file header. This
will be used by Lake to specify paths to module artifacts (e.g., oleans
and ileans) separate from the `LEAN_PATH` schema.

To facilitate JSON serialization of the header data structure, `NameMap`
JSON instances have been added to core, and `LeanOptions` now makes use
of them.
2025-05-03 23:57:37 +00:00
Kim Morrison
132c608ebc chore: more @[grind] annotations for List/Array/Vector (#8218)
This PR continues adding `@[grind]` attributes for List/Array/Vector,
particularly to the lemmas involving the `toList`/`toArray` functions.
2025-05-03 19:28:54 +00:00
Kim Morrison
d005a306f9 chore: cleanup of @[grind] lemmas for Option (#8217) 2025-05-03 18:59:30 +00:00
Kim Morrison
80349ac77b feat: complete addition of @[grind] annotations for Option (#8216)
This PR completes adding `@[grind]` annotations for `Option` lemmas, and
incidentally fills in some `Option` API gaps/defects.
2025-05-03 17:14:25 +00:00
Kim Morrison
6e2e1a4f89 chore: consistently add @[simp] to getKey_eq map lemmas (#8186)
These lemmas were inconsistently marked as `@[simp]`, but they seem
generally useful, so this uniformly marks this lemmas as `@[simp]` for
all map variants.
2025-05-03 16:12:33 +00:00
Cameron Zwarich
afab374305 feat: LCNF -> IR translation (#8211)
This PR adds support for generating IR from the LCNF representation of
the new compiler.
2025-05-03 05:34:37 +00:00
Lean stage0 autoupdater
bc1d30de38 chore: update stage0 2025-05-03 00:16:43 +00:00
Leonardo de Moura
14d647f219 fix: nondeterminism in grind (#8209)
This PR fixes a nondeterminism issue in the `grind` tactic. It was a bug
in the model-based theory combination module.
2025-05-02 20:01:38 +00:00
Henrik Böving
daf7a579ed perf: use less defeq in frequently applied bv_decide simp rules (#8208)
This PR reduces the need for defeq in frequently used bv_decide rewrite
by turning them into simprocs that work on structural equality instead.
As the intended meaning of these rewrites is to simply work with
structural equality anyways this should not change the proving power of
`bv_decide`'s rewriter but just make it faster on certain very large
problems.
2025-05-02 19:15:34 +00:00
Sebastian Ullrich
9f48af3edd fix: cadical distribution on Linux (#8201)
Compile it with the same flags as other executables
2025-05-02 18:25:16 +00:00
Kim Morrison
63cf1052f4 chore: remove grind ext lemmas for List/Array/Vector (#8207) 2025-05-02 17:41:02 +00:00
Kim Morrison
0fd516a1df feat: add simpler getElem_map statements given LawfulBEq for all HashMap variants (#8188)
This PR takes the existing `getElem_map` statements for `HashMap`
variants (also `getElem?`, `getElem!`, and `getD` statements), adds a
prime to their name and an explanatory comment, and replaces the
unprimed statement with a simpler statement that is only true with
`LawfulBEq` present. The original statements which were simp lemmas are
now low priority simp lemmas, so the nicer statements should fire when
`LawfulBEq` is available.
2025-05-02 17:16:35 +00:00
Kim Morrison
34d944c4a9 feat: add ofList_eq_insertMany_empty lemmas for map types (#8182)
This PR adds `ofList_eq_insertMany_empty` lemmas for all the hash/tree
map types, with the exception of
`Std.HashSet.Raw.ofList_eq_insertMany_empty`.
2025-05-02 17:16:23 +00:00
David Thrane Christiansen
7f4f6b3457 doc: add documentation style guide (#8199)
This PR adds a style guide for documentation, including both general
principles and docstring-specific concerns.
2025-05-02 13:05:18 +00:00
Siddharth
43e8288e3f feat: Bitvector 0 equals bitvector 1 iff width is zero (#8202)
This PR adds an inference that was repeatedly needed when proving
`BitVec.msb_sdiv`, and is the symmetric version of
`BitVec.one_eq_zero_iff`
2025-05-02 10:32:01 +00:00
Leonardo de Moura
d26d7973ad fix: theory propagation in grind (#8198)
This PR fixes an issue in the theory propagation used in `grind`. When
two equivalence classes are merged, the core may need to push additional
equalities or disequalities down to the satellite theory solvers (e.g.,
`cutsat`, `comm ring`, etc). Some solvers (e.g. `cutsat`) assume that
all of the core’s invariants hold before they receive those facts.
Propagating immediately therefore risks violating a solver’s
pre-conditions midway through the merge. To decouple the merge operation
from propagation and to keep the core solver-agnostic, this PR adds the
helper type `PendingTheoryPropagation`.
2025-05-02 02:19:56 +00:00
Leonardo de Moura
1143b4766c chore: remove dead code (#8197) 2025-05-02 01:33:41 +00:00
Leonardo de Moura
af4c693030 feat: improve E-matching pattern inference in grind (#8196)
This PR improves the E-matching pattern inference procedure in `grind`.
Consider the following theorem:
```lean
@[grind →]
theorem eq_empty_of_append_eq_empty {xs ys : Array α} (h : xs ++ ys = #[]) : xs = #[] ∧ ys = #[] :=
  append_eq_empty_iff.mp h
```
Before this PR, `grind` inferred the following pattern:
```lean
@HAppend.hAppend _ _ _ _ #2 #1
```
Note that this pattern would match any `++` application, even if it had
nothing to do with arrays. With this PR, the inferred pattern becomes:
```lean
@HAppend.hAppend (Array #3) (Array _) (Array _) _ #2 #1
```
With the new pattern, the theorem will not be considered by `grind` for
goals that do not involve `Array`s.
2025-05-01 23:48:32 +00:00
Sebastian Ullrich
92775557d9 fix: go to import (#8193)
This silently broke with the import syntax changes.

TODO: figure out a way to test
2025-05-01 15:55:04 +00:00
Sebastian Ullrich
29fc6a46a8 chore: CI: exclude test not compatible with Lake CI 2025-05-01 12:58:44 +02:00
Kim Morrison
f634bfe0fc chore: update stage0 2025-05-01 12:42:44 +02:00
James Sully
2b80f801f6 doc: Fix typo in Tactics.lean: fun_cass -> fun_cases (#8191) 2025-05-01 06:38:39 +00:00
Mac Malone
18a9a694b3 doc: lake: add needs and native library options to README (#8190)
This PR adds documentation for native library options (e.g., `dynlibs`,
`plugins`, `moreLinkObjs`, `moreLinkLibs`) and `needs` to the Lake
README. It is also includes information about specifying targets on the
Lake CLI and in Lean and TOML configuration files.
2025-05-01 02:16:07 +00:00
Mac Malone
05153d66b1 chore: more verbose tests & related fixes (#8183)
This PR makes Lake tests much more verbose in output. It also fixes some
bugs that had been missed due to disabled tests. Most significantly, the
target specifier `@pkg` (e.g., in `lake build`) is now always
interpreted as a package. It was previously ambiguously interpreted due
to changes in #7909.
2025-05-01 01:20:50 +00:00
Leonardo de Moura
ae5fe802ce feat: stepwise proof terms for the commutative ring procedure in grind (#8189)
This PR implements **stepwise proof terms** in the commutative ring
procedure used by `grind`. These terms serve as an alternative
representation to the traditional Nullstellensatz certificates, aiming
to address the **exponential worst-case complexity** often associated
with certificate construction.

While various compression techniques for Nullstellensatz certificates
exist, they are not implemented in our procedure. Moreover, many of
these techniques rely on additional properties not available in
arbitrary commutative rings. In contrast, the stepwise proof terms
encode the **actual derivation** used during simplification, offering
significantly better scalability in practice.
Here is a motivating example:
```lean
example {α} [CommRing α] [IsCharP α 0] (d t c : α) (d_inv PSO3_inv : α)
  (Δ40 : d^2 * (d + t - d * t - 2) * (d + t + d * t) = 0)
  (Δ41 : -d^4 * (d + t - d * t - 2) *
         (2 * d + 2 * d * t - 4 * d * t^2 + 2 * d * t^4 + 2 * d^2 * t^4 - c * (d + t + d * t)) = 0)
  (_ : d * d_inv = 1)
  (_ : (d + t - d * t - 2) * PSO3_inv = 1) :
  t^2 = t + 1 := by grind +ring
```
In this case, the Nullstellensatz certificate generated by our procedure
contains **over 20,000 terms**, which overwhelms the Lean kernel during
verification. @kim-em also computed certificates using Mathematica with
various variable orderings, producing results between **500 and 2,000
terms**: still quite large.

By switching to stepwise derivations:
- `grind` completes the goal in **under 10 ms**
- The Lean kernel checks the resulting proof term in **under 1 second**

This change dramatically improves both the performance and robustness of
`grind` for nontrivial algebraic goals.
2025-04-30 18:45:29 +00:00
Kim Morrison
1e9864363f chore: fix statement of Std.HashMap.Equiv.getElem?_eq (#8185) 2025-04-30 17:12:47 +00:00
Kim Morrison
1d5110e140 feat: insertMany_append lemmas for map variants (#8184)
This PR adds the `insertMany_append` lemma for all map variants.
2025-04-30 17:09:51 +00:00
Kim Morrison
670158345a feat: unconditional lemmas for HashMap/TreeMap.getElem?_insertMany_list (#8154)
This PR adds unconditional lemmas for
`HashMap.getElem?_insertMany_list`, alongside the existing ones that
have quite strong preconditions. Also for TreeMap (and
dependent/extensional variants).
2025-04-30 16:20:41 +00:00
Wojciech Rozowski
96fcc94acb feat: add support for lattice-theoretic (co)inductive predicates (#8097)
This PR adds support for inductive and coinductive predicates defined
using lattice theoretic structures on `Prop`. These are syntactically
defined using `greatest_fixpoint` or `least_fixpoint` termination
clauses for recursive `Prop`-valued functions. The functionality relies
on `partial_fixpoint` machinery and requires function definitions to be
monotone. For non-mutually recursive predicates, an appropriate
(co)induction proof principle (given by Park induction) is generated.

Summary of changes:
- `Interal.Order.Basic` now contains `CompleteLattice` class, as well as
version of Knaster-Tarski fixpoint theorem (with an associated Park
induction principle) for the internal use for defining (co)inductive
predicates. `Prop` is shown to have two complete lattice structures (one
given by implication order for defining inductive predicates, and one
given by reverse implication for defining coinductive predicates).
Additionally, proofs that lattices are closed under products and
function spaces are included.
- Partial fixpoint's `EqnInfo` now additionally carries an information
whether something is defined as a lattice-theoretic fixpoint or via
CCPOs.
- When constructing a (co)inductive predicate,`PartialFixpoint/Main`
builds an appropriate lattice structure on the type of the predicate
using product lattice, function space lattice and an appropriate lattice
instance on `Prop`.
- `PartialFixpoint/Eqns` is modified to be able to perform rewrite under
lattice-theoretic fixpoint construction
- `PartialFixpoint/Induction`contains a case split for handling of the
(co)inductive predicates. In the case of lattice-theoretic fixpoints, it
appropriately desugars the Park induction principle.
2025-04-30 15:48:58 +00:00
Kim Morrison
86db67c444 chore: add failing grind test (#8179) 2025-04-30 14:54:51 +00:00
Kim Morrison
a9f4170372 feat: lemmas about List/Array/Vector.contains (#8175)
This PR adds simp/grind lemmas about `List`/`Array`/`Vector.contains`.
In the presence of `LawfulBEq` these effectively already held, via
simplifying `contains` to `mem`, but now these also fire without
`LawfulBEq`.
2025-04-30 14:38:56 +00:00
Kim Morrison
7ffeacf967 chore: move Array.qsort to Basic file (#8177)
No change to content, just moving into a subdirectory, to ease keeping a
branch adding theorems in sync.
2025-04-30 13:32:05 +00:00
Kim Morrison
8a8b9e4556 chore: further cleanup of the if-normalization example (#8176) 2025-04-30 13:02:08 +00:00
Sebastian Ullrich
4c497eaa32 chore: disable #print axioms under the module system (#8174)
No need for extra tracking to enable it considering how easy it is to
opt out
2025-04-30 12:00:09 +00:00
Marc Huisinga
98b864d25b fix: broken import completion (#8164)
This PR fixes import completion being broken by the recent changes to
import syntax for the module system.

Fixes #8162.
2025-04-30 11:31:45 +00:00
Sebastian Ullrich
e2f757d5a7 feat: private import and import all (#8159)
This PR adds support for the following import variants to the
experimental module system:

* `private import`: Makes the imported constants available only in
non-exported contexts such as proofs. In particular, the import will not
be loaded, or required to exist at all, when the current module is
imported into other modules.
* `import all`: Makes non-exported information such as proofs of the
imported module available in non-exported contexts in the current
module. Main purpose is to allow for reasoning about imported
definitions when they would otherwise be opaque. TODO: adjust name
resolution so that imported `private` decls are accessible through
syntax.

They can be combined into `private import all`, which will likely be the
most common usage of `import all`.
2025-04-30 10:06:54 +00:00
Joachim Breitner
d16862fd33 feat: induction: allow complex arguments to motive in conclusion of eliminator (#8096)
This PR lets `induction` accept eliminator where the motive application
in the conclusion has complex arguments; these are abstracted over using
`kabstract` if possible. This feature will go well with unfolding
induction principles (#8088).
2025-04-30 08:56:17 +00:00
Siddharth
0f7eb710e2 feat: add bv-concat-extract normalization simprocs (#8077)
This PR adds simprocs to simplify appends of non-overlapping Bitvector
adds. We add a simproc instead of just a `simp` lemma to ensure that we
correctly rewrite bitvector appends. Since bitvector appends lead to
computation at the bitvector width level, it seems to be more stable to
write a simproc.

As I write this, I realize that I can maybe write the `simp` lemma using
`no_index` to recover the same behaviour, so I'll try that too.
2025-04-30 08:31:38 +00:00
Leonardo de Moura
a1989c2387 feat: infrastructure for creating stepwise proof terms in the commutative ring procedure in grind (#8170)
This PR adds the infrastructure for creating stepwise proof terms in the
commutative procedure used in `grind`.
2025-04-30 05:01:02 +00:00
Lean stage0 autoupdater
9168840e2b chore: update stage0 2025-04-30 04:03:29 +00:00
Mac Malone
de0187ab8b fix: lake: extern_lib loading in non-precompiled module builds (#8152)
This PR fixes a regression where non-precompiled module builds would
`--load-dynlib` package `extern_lib` targets.

A reappearance of #4565. Thanks to Daniil [on
Zulip](https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/Multiple.20extern_lib/near/514772675)
for the report! This was not caught by the old test due to the removal
of `extern_lib` from the FFI example.
2025-04-30 01:04:59 +00:00
Leonardo de Moura
0eb9671787 fix: proof term for Nullstellensatz certificate (#8168)
This PR fixes a bug when constructing the proof term for a
Nullstellensatz certificate produced by the new commutative ring
procedure in `grind`. The kernel was rejecting the proof term.
2025-04-30 01:03:57 +00:00
Leonardo de Moura
e0230d8377 perf: improve heuristics for commutative ring procedure in grind (#8167)
This PR improves the heuristics used to compute the basis and simplify
polynomials in the commutative procedure used in `grind`.
2025-04-29 22:35:36 +00:00
Markus Himmel
925e53fcba fix: include libuv outside of namespace (#8166)
This PR makes sure we never `#include <uv.h>` while inside a namespace,
which recent GCC versions don't seem to like.
2025-04-29 22:19:17 +00:00
Kim Morrison
a4f2c51049 chore: add failing grind +ring tests (#8163)
This PR adds some currently failing tests for `grind +ring`, resulting
in either kernel type mismatches (bugs) or a kernel deep recursion
(perhaps just a too-large problem).
2025-04-29 21:30:43 +00:00
Henrik Böving
7b6c16a44b feat: implement a Selector for async UDP (#8139)
This PR is a follow up to #8055 and implements a `Selector` for async
UDP in order to allow IO multiplexing using UDP sockets.

The technical approach taken for this PR is basically a copy of #8078
but adjusted for UDP. The libuv API gives the same guarantee that was
used in that PR.
2025-04-29 21:01:14 +00:00
Kim Morrison
febf6c10f0 fix: update Grind.CommRing to avoid constructing non-defeq NatCast instance (#8161)
This PR changes `Lean.Grind.CommRing` to inline the `NatCast` instance
(i.e. to be provided by the user) rather than constructing one from the
existing data. Without this change we can't construct instances in
Mathlib that `grind` can use.
2025-04-29 16:50:54 +00:00
Joachim Breitner
3d1d8fc1de feat: unfolding functional induction principles (#8088)
This PR adds the “unfolding” variant of the functional induction and
functional cases principles, under the name `foo.induct_unfolding` resp.
`foo.fun_cases_unfolding`. These theorems combine induction over the
structure of a recursive function with the unfolding of that function,
and should be more reliable, easier to use and more efficient than just
case-splitting and then rewriting with equational theorems.

For example  instead of
```
ackermann.induct
  (motive : Nat → Nat → Prop)
  (case1 : ∀ (m : Nat), motive 0 m)
  (case2 : ∀ (n : Nat), motive n 1 → motive (Nat.succ n) 0)
  (case3 : ∀ (n m : Nat), motive (n + 1) m → motive n (ackermann (n + 1) m) → motive (Nat.succ n) (Nat.succ m))
  (x x : Nat) : motive x x
```
one gets
```
ackermann.fun_cases_unfolding
  (motive : Nat → Nat → Nat → Prop)
  (case1 : ∀ (m : Nat), motive 0 m (m + 1))
  (case2 : ∀ (n : Nat), motive n.succ 0 (ackermann n 1))
  (case3 : ∀ (n m : Nat), motive n.succ m.succ (ackermann n (ackermann (n + 1) m)))
  (x✝ x✝¹ : Nat) : motive x✝ x✝¹ (ackermann x✝ x✝¹)
```
2025-04-29 16:43:06 +00:00
Rob23oba
b5cfd86a89 fix: Substring.isNat for empty string (#8067)
This PR fixes the behavior of `Substring.isNat` to disallow empty
strings.

Closes #8005
2025-04-29 15:54:29 +00:00
Henrik Böving
eaa5d3498c feat: implement a Selector for channels (#8150)
This PR is a follow up to #8055 and implements a Selector for
`Std.Channel` in order to allow
 multiplexing using channels.

There is one subtlety to the implementation: Suppose we are in a
situation where we run `select` in a loop on two channels. One of the
channels is always quiet while the other has data available occasionally
(however not always as this would trigger the `tryFn` fast path and hide
the issue). In this situation the select receivers that are enqueued on
the silent channel would usually just remain there indefinitely as
nothing ever happens, causing a memleak. To avoid this we want to make a
channel select clean up after itself, even if it fails.

In an imperative programming language we could implement the receive
queue as a doubly linked list and simply make each receive select
maintain a pointer to its element in the queue and then remove itself in
`O(1)` upon failure. As that is not possible in Lean trivially we
decided to go for another approach for now: simply filter the queue for
selects that have failed in `unregisterFn`. While this approach is
`O(n)` we expect the amount of receivers enqueued on a channel to not be
terribly large and thus this to be a reasonably fast operation compared
to the remaining overhead. If it ever ends up becoming an issue, we
could switch to an approach that uses a `TreeMap` with numbered
receivers instead at a certain wait queue size and go to `O(log(n))`.
2025-04-29 15:15:38 +00:00
Sebastian Ullrich
db35bbb1a0 test: disable flaky test 2025-04-29 17:34:10 +02:00
Tom Levy
877d51bb15 doc: fix time complexity of List.merge (#8116)
This PR fixes a mistake in documented time complexity of List.merge.

The running time would only be `O(min |l| |r|)` in the very specific
best case where all the elements in the shorter list are less than all
the elements in the longer list. The worst-case (and average-case) time
complexity is `O(|l| + |r|)`.

Also update the variables in the time complexity to match the names of
the parameters.
2025-04-29 11:02:44 +00:00
Sebastian Ullrich
b677702b02 chore: update stage0 2025-04-29 11:01:57 +02:00
Sebastian Ullrich
d544ca5174 chore: fix default of Import.isExported 2025-04-29 10:58:27 +02:00
Rob23oba
9f06aff834 feat: optimized division without remainder for Int and Nat (#8089)
This PR adds optimized division functions for `Int` and `Nat` when the
arguments are known to be divisible (such as when normalizing
rationals). These are backed by the gmp functions `mpz_divexact` and
`mpz_divexact_ui`. See also leanprover-community/batteries#1202.
2025-04-29 07:23:35 +00:00
Cameron Zwarich
2929d547dc fix: make the lcnf expr cache depend on the value of root, not just… (#8156)
This PR fixes a bug where the old compiler's lcnf conversion expr cache
was not including all of the relevant information in the key, leading to
terms inadvertently being erased. The `root` variable is used to
determine whether lambda arguments to applications should get let
bindings or not, which in turn affects later decisions about type
erasure (erase_irrelevant assumes that any non-atomic argument is
irrelevant).
2025-04-29 00:37:52 +00:00
Leonardo de Moura
245ed056a3 fix: grind +splitImp, arrow propagator, missing normalization rule (#8158)
This PR fixes the `grind +splitImp` and the arrow propagator. Given `p :
Prop`, the propagator was incorrectly assuming `A` was always a
proposition in an arrow `A -> p`. This PR also adds a missing
normalization rule to `grind`.
2025-04-28 22:59:43 +00:00
Sebastian Ullrich
eaf1c6b4e1 fix: replayConst with native_decide (#8157)
This PR fixes an incompatibility of `replayConst` as used by e.g.
`aesop` with `native_decide`-using tactics such as `bv_decide`
2025-04-28 20:35:15 +00:00
Cameron Zwarich
d1ed57e92a fix: support borrowed params in the new compiler (#8127)
This PR adds support for borrowed params in the new compiler, which
requires adding support for .mdata expressions to LCNF type handling.
2025-04-28 17:02:47 +00:00
Sebastian Ullrich
0afcda8654 chore: robustify Nix shell (#8141)
* use tarballs directly from releases.nixos.org instead of GitHub
zipballs
* build cadical from source like everyone else since it's so small
2025-04-28 15:08:32 +00:00
Markus Himmel
290507396a chore: Option.guard accepts Bool predicate instead of Prop predicate (#8144)
This PR changes the predicate for `Option.guard` to be `p : α → Bool`
instead of `p : α → Prop`. This brings it in line with other comparable
functions like `Option.filter`.
2025-04-28 13:57:07 +00:00
Kim Morrison
8b3d70d2ab chore: fix statements of HashMap.getKey_insert (#8146) 2025-04-28 13:56:39 +00:00
Kim Morrison
b2ea6b6a02 feat: initial @[grind] attributes for List/Array/Vector (#8136)
This PR adds an initial set of `@[grind]` annotations for
`List`/`Array`/`Vector`, enough to set up some regression tests using
`grind` in proofs about `List`. More annotations to follow.
2025-04-28 13:48:20 +00:00
Kim Morrison
d10d17ce03 chore: add HashMap/TreeMap.isSome_X simp lemmas (#8143)
These lemmas were previously only stated the other way round, but in
this direction they are both good simp lemmas, and good grind lemmas.
2025-04-28 13:48:06 +00:00
Joachim Breitner
bca36b2eba refactor: realizeConst: do not set declPrefix (#8107)
This PR makes `realizeConst` to not set a `declPrefix`. This allows the
realization of both `foo.eq_def` and `bar.eq_def`, where `foo` and `bar`
are mutually recursive, all attached to the same function's environment.
2025-04-28 13:43:52 +00:00
Kim Morrison
573d824b81 feat: add List.eraseDupsBy and basic lemmas (#8148)
This PR generalises `List.eraseDups` to allow for an arbitrary
comparison relation. Further, it proves `eraseDups_append : (as ++
bs).eraseDups = as.eraseDups ++ (bs.removeAll as).eraseDups`.
2025-04-28 11:12:41 +00:00
Kim Morrison
436ebdad78 feat: add List.findRev? and findSomeRev?, and simp lemmas (#8147)
This PR adds `List.findRev?` and `List.findSomeRev?`, for parity with
the existing Array API, and simp lemmas converting these into existing
operations.
2025-04-28 11:09:51 +00:00
Lean stage0 autoupdater
6c9158b5b7 chore: update stage0 2025-04-28 11:29:55 +00:00
Kim Morrison
ecf690f1f1 chore: failing test for grind (#8065)
This PR adds a (failing) test case for an obstacle I've been running
into setting up `grind` for `HashMap`.
2025-04-28 10:46:19 +00:00
Sebastian Ullrich
eb559d58a8 refactor: introduce VisibilityMap in Lean.Environment, use it to split base in preparation for private import (#8145) 2025-04-28 10:17:18 +00:00
Kim Morrison
0b634e59f0 chore: add @[simp] to HashMap.get_getKey? (#8140) 2025-04-28 09:07:21 +00:00
Rob23oba
747ea853b5 feat: extensional hash maps (#8004)
This PR adds extensional hash maps and hash sets under the names
`Std.ExtDHashMap`, `Std.ExtHashMap` and `Std.ExtHashSet`. Extensional
hash maps work like regular hash maps, except that they have
extensionality lemmas which make them easier to use in proofs. This
however makes it also impossible to regularly iterate over its entries.
2025-04-28 06:48:25 +00:00
Leonardo de Moura
2ba021ecc2 fix: equality propagation and simplification in the comm ring procedure (#8137)
This PR improves equality propagation (also known as theory combination)
and polynomial simplification for rings that do not implement the
`NoZeroNatDivisors` class. With these fixes, `grind` can now solve:
```lean
example [CommRing α] (a b c : α) (f : α → Nat)
  : a + b + c = 3 →
    a^2 + b^2 + c^2 = 5 →
    a^3 + b^3 + c^3 = 7 →
    f (a^4 + b^4) + f (9 - c^4) ≠ 1 := by
  grind +ring
```
This example uses the commutative ring procedure, the linear integer
arithmetic solver, and congruence closure.
For rings that implement `NoZeroNatDivisors`, a polynomial is now also
divided by the greatest common divisor (gcd) of its coefficients when it
is inserted into the basis.
2025-04-28 00:55:18 +00:00
Leonardo de Moura
b77e9edd44 feat: add checkInvariants to CommRing (#8135)
This PR implements the sanity check function `CommRing.checkInvariants`.
2025-04-27 21:43:10 +00:00
Sebastian Ullrich
1b1c05916f chore: refine module imports (#8120)
* bump whole imported module closure to private if necessary
* disallow import of non-`module` from `module`
2025-04-27 20:45:31 +00:00
Leonardo de Moura
9a5d961c5e fix: grind.debug true when using grind +ring (#8134)
This PR ensures that `set_option grind.debug true` works properly when
using `grind +ring`. It also adds the helper functions `mkPropEq` and
`mkExpectedPropHint`.
2025-04-27 20:28:08 +00:00
Leonardo de Moura
d6ad3e1a85 fix: monomial order in the CommRing module (#8133)
This PR fixes the monomial order used by the commutative ring procedure
in `grind`. The following new test now terminates quickly.
```lean
example [CommRing α] (a b c : α)
  : a + b + c = 3 →
    a^2 + b^2 + c^2 = 5 →
    a^3 + b^3 + c^3 = 7 →
    a^4 + b^4 + c^4 = 9 := by
  grind +ring
```
2025-04-27 19:05:12 +00:00
Leonardo de Moura
d73557321b feat: add grind (ringSteps := <num>) (#8131)
This PR adds a configuration option that controls the maximum number of
steps the commutative-ring procedure in `grind` performs.
2025-04-27 17:46:02 +00:00
Cameron Zwarich
36ed58351d fix: add support for builtin casesOn recursors to the new compiler (#8132)
This PR adds support for lowering `casesOn` for builtin types in the new
compiler.
2025-04-27 17:11:36 +00:00
Leonardo de Moura
26138a5362 feat: equality propagation for comm ring procedure in grind (#8128)
This PR implements equality propagation in the new commutative ring
procedure in `grind`. The idea is to propagate implied equalities back
to the `grind` core module that does congruence closure. In the
following example, the equalities: `x^2*y = 1` and `x*y^2 - y = 0` imply
that `y*x` is equal to `y*x*y`, which implies by congruence that `f
(y*x) = f (y*x*y)`.
```lean
example [CommRing α] (x y : α) (f : α → Nat) : x^2*y = 1 → x*y^2 - y = 0 → f (y*x) = f (y*x*y) := by
  grind +ring
```
2025-04-27 15:05:56 +00:00
Joachim Breitner
f9d191d7b8 fix: allow ascii <- in if let clauses (#8102)
This PR allows ASCII `<-` in `if let` clauses, for consistency with
bind, where both are allowed. Fixes #8098.
2025-04-27 13:17:58 +00:00
Kim Morrison
cf35e13c60 feat: use fun_induction in if-normalization example (#8129)
This PR updates the If-Normalization example, to separately give an
implementation and subsequently prove the spec (using fun_induction),
instead of previously building a term in the subtype directly. At the
same time, adds a (failing) `grind` test case illustrating a problem
with unused match witnesses.
2025-04-27 12:27:17 +00:00
Sebastian Ullrich
b6259e61f2 chore: update stage0 2025-04-27 07:41:07 +02:00
Sebastian Ullrich
965dca1625 feat: import private 2025-04-27 07:41:07 +02:00
Leonardo de Moura
c3a1669398 feat: process comm ring module todo-queue in grind (#8126)
This PR implements the main loop of the new commutative ring procedure
in `grind`. In the main loop, for each polynomial `p` in the todo queue,
the procedure:
- Simplifies it using the current basis.
- Computes critical pairs with polynomials already in the basis and adds
them to the queue.

After the queue is empty, the disequalities are re-simplified using the
new basis. `grind` can now solve examples such as:
```lean
example [CommRing α] (x y : α) : x*y*x = 1 → x*y*y = y → y = 1 := by
  grind +ring

example [CommRing α] (x y : α) : x^2*y = 1 → x*y^2 = y → y*x = 1 := by
  grind +ring

example (x y : BitVec 16) : x^2*y = 1 → x*y^2 = y → y*x = 1 := by
  grind +ring
```
2025-04-27 01:04:45 +00:00
Cameron Zwarich
c633725b3e fix: add support for the init attribute to the new compiler (#8125)
This PR adds support for the `init` attribute to the new compiler.
2025-04-27 01:01:44 +00:00
Cameron Zwarich
763a43c241 fix: correctly handle escaping functions in LCNF's elimDeadBranches pass (#8124)
This PR correctly handles escaping functions in the LCNF
elimDeadBranches pass, by setting all params to top instead of
potentially leaving them at their default bottom value.
2025-04-26 23:56:01 +00:00
Leonardo de Moura
d64ae32965 feat: generate Nullstellensatz proof terms in grind (#8122)
This PR implements the generation of compact proof terms for
Nullstellensatz certificates in the new commutative ring procedure in
`grind`. Some examples:
```lean
example [CommRing α] (x y : α) : x = 1 → y = 2 → 2*x + y = 4 := by
  grind +ring

example [CommRing α] [IsCharP α 7] (x y : α) : 3*x = 1 → 3*y = 2 → x + y = 1 := by
  grind +ring

example [CommRing α] [NoZeroNatDivisors α] (x y : α) : 3*x = 1 → 3*y = 2 → x + y = 1 := by
  grind +ring

example (x y z : BitVec 8) : z = y → (x + 1)*(x - 1)*y + y = z*x^2 + 1 → False := by
  grind +ring
```
2025-04-26 22:52:00 +00:00
Lean stage0 autoupdater
685aa9b359 chore: update stage0 2025-04-26 17:01:41 +00:00
Sebastian Ullrich
f285867137 perf: no need to register axioms outside of the module system (#8121) 2025-04-26 16:14:00 +00:00
Sebastian Ullrich
87dccb9d1b fix: restore what simp theorems are recorded as rfl (#8114)
#8090 accidentally affected `dsimp` applications even outside the module
system, restore previous extension data.
2025-04-26 16:09:20 +00:00
Sebastian Ullrich
82723489c9 fix: linter should have access to all messages, really (#8117)
Continuation of #8101
2025-04-26 15:23:07 +00:00
Leonardo de Moura
d81a922a20 feat: NoZeroNatDivisors helper class for grind (#8111)
This PR adds the helper type class `NoZeroNatDivisors` for the
commutative ring procedure in `grind`. Core only implements it for
`Int`. It can be instantiated in Mathlib for any type `A` that
implements `NoZeroSMulDivisors Nat A`.
See `findSimp?` and `PolyDerivation` for details on how this instance
impacts the commutative ring procedure.
2025-04-26 15:14:27 +00:00
Kim Morrison
18f8a18bfc chore: fix TreeMap deprecations (#8100)
This PR fixes some incorrect deprecations in TreeMap.
2025-04-26 13:10:05 +00:00
Sebastian Ullrich
4323507b91 fix: linter should have access to complete command message log (#8101)
This PR fixes a parallelism regression where linters that e.g. check for
errors in the command would no longer find such messages.

---------

Co-authored-by: damiano <adomani@gmail.com>
2025-04-26 11:36:21 +00:00
Sebastian Ullrich
20a9db6357 chore: CI: run Linux Lake in all configurations
Otherwise master never has a cache for it
2025-04-26 13:25:29 +02:00
Sebastian Ullrich
c268602795 fix: wf preprocess of ite (#8112)
`[wf_preprocess]` expects a dsimp theorem, which in `Init` temporarily
have a simplistic syntactic representation until a more robust solution
is implemented.
2025-04-26 07:30:45 +00:00
Leonardo de Moura
60ee8c2f76 chore: broken test after update stage0 (#8110)
This is a temporary fix for `master` after update stage0 breakage.

cc @Kha @nomeata
2025-04-26 00:02:23 +00:00
Lean stage0 autoupdater
882d1ab812 chore: update stage0 2025-04-25 21:29:05 +00:00
Sebastian Ullrich
62c6edffef feat: do not export theorem bodies (#8090)
This PR adjusts the experimental module system to elide theorem bodies
(i.e. proofs) from being imported into other modules.
2025-04-25 20:22:32 +00:00
Markus Himmel
6cdabf58c6 chore: deprecate some Int.ofNat_* lemmas (#8000)
This PR deprecates some `Int.ofNat_*` lemmas in favor of
`Int.natCast_*`.
2025-04-25 16:16:58 +00:00
Marc Huisinga
8195f70502 chore: revert "fix: trace nodes collapsing while file is elaborating (#8056)" (#8095)
This PR reverts #8056 because the implementation there has a bug that is
best fixed with a different approach, and which we should preferably
only merge next release cycle.
2025-04-25 09:59:41 +00:00
Joachim Breitner
3fe195a4a9 fix: FunInd with nested well-founded recurison and late fixed parameters (#8094)
This PR fixes the generation of functional induction principles for
functions with nested nested well-founded recursion and late fixed
parameters. This is a follow-up for #7166. Fixes #8093.
2025-04-25 09:20:27 +00:00
Rob23oba
416e07a68e fix: handle surrogate pairs correctly in Json.parse (#8080)
This PR fixes `Json.parse` to handle surrogate pairs correctly.

Closes #5445
2025-04-24 19:07:46 +00:00
Henrik Böving
406bda8807 feat: implement a Selector for async TCP (#8078)
This PR is a follow up to #8055 and implements a `Selector` for async
TCP in order to allow IO multiplexing using TCP sockets.

As we must not commit to actually fetching data from the socket buffer
this cannot be implemented by just racing on `recv?`. Instead we perform
a call to `uv_read_start` and pass an `alloc_cb` that allocates no
memory at all. According to the docs of
[`uv_alloc_cb`](https://docs.libuv.org/en/v1.x/handle.html#c.uv_alloc_cb)
this is guaranteed to give us a `UV_ENOBUFS` in the relevant callback.
Thus we can first run this "zero read" and then go into one of three
cases:
1. We get cancelled before the zero read completes, in this case just
cancel the zero read and give up.
2. The zero read completes and we loose the race for completing the
`select`, in this case just don't do anything anymore
3. The zero read completes and we win the race for completing the
`select`, in this case we perform the actual read on the socket. As we
know that data is available already (since the read callback of the zero
read is only triggered if data actually is available) we know that the
subsequent actual read should complete right away.

In this way we avoid any data loss if we loose the race.
2025-04-24 16:05:35 +00:00
Luisa Cicolini
bc032eec8d feat: add BitVec.sdivOverflow definition and lemmas for overflow in signed and unsigned division (#7671)
This PR contains the theorem proving that signed division x.toInt /
y.toInt only overflows when `x = intMin w` and `y = allOnes w` (for `0 <
w`).
To show that this is the *only* case in which overflow happens, we refer
to overflow for negation
(`BitVec.sdivOverflow_eq_negOverflow_of_neg_one`): in fact,
`x.toInt/(allOnes w).toInt = - x.toInt`, i.e., the overflow conditions
are the same as `negOverflow` for `x`, and then reason about the signs
of the operands with the respective theorems.
These BitVec theorems themselves rely on numerous `Int.ediv_*` theorems,
that carefully set the bounds of signed division for integers.

co-authored by @bollu, @tobiasgrosser
2025-04-24 15:27:18 +00:00
Rob23oba
e2b3daf1dd fix: simp?! and variants to do auto-unfolding (#8076)
This PR fixes `simp?!`, `simp_all?!` and `dsimp?!` to do auto-unfolding.

Closes #7927
2025-04-24 14:04:39 +00:00
Lean stage0 autoupdater
7344bcffd8 chore: update stage0 2025-04-24 14:21:10 +00:00
Markus Himmel
68d9d14d44 chore: do not use the coercion α → Option α in Init and Std (#8085)
This PR moves the coercion `α → Option α` to the new file
`Init.Data.Option.Coe`. This file may not be imported anywhere in `Init`
or `Std`.
2025-04-24 13:35:01 +00:00
Joachim Breitner
9fbdf847bd fix: FunInd: properly split mutual structural recursion with extra parameters (#8086)
This PR makes sure that the functional induction priciples for mutually
recursive structural functions with extra parameters are split deeply,
as expected.
2025-04-24 13:32:53 +00:00
Sebastian Ullrich
66c00d33d4 feat: environment constant data can be split into .olean.private (#8079)
This PR lays the `Environment` groundwork for not exporting (parts of)
declarations.
2025-04-24 13:04:31 +00:00
Sebastian Ullrich
96cda3f498 chore: CI: revert accidentally disabling Lake cache 2025-04-24 15:01:09 +02:00
Joachim Breitner
d38d9400d8 fix: avoid panic in functional induction principle for structural recursion (#8083)
This PR fixes #8081.
2025-04-24 11:58:29 +00:00
Markus Himmel
781c94f2cf chore: test that there are no orphaned modules (#8082)
This PR adds a test that makes sure that there are no orphaned modules.
2025-04-24 11:55:07 +00:00
Lean stage0 autoupdater
e00a2f63ec chore: update stage0 2025-04-24 10:54:10 +00:00
Paul Reichert
be66157583 fix: import all raw tree map modules into Std.Data (#8044)
This PR introduces the modules `Std.Data.DTreeMap.Raw`,
`Std.Data.TreeMap.Raw` and `Std.Data.TreeSet.Raw` and imports them into
`Std.Data`. All modules related to the raw tree maps are imported into
these new modules so that they are now a transitive dependency of `Std`.
2025-04-24 10:06:32 +00:00
Joachim Breitner
b2ed6ac939 refactor: WF: add eq_def theorem for ._unary (#8063)
This PR adds an `foo._unary.eq_def` theorem, so that unfolding
`foo._unary` works as expected. This will help with #8019.
2025-04-24 09:59:08 +00:00
Sebastian Ullrich
51defe5935 chore: disable nondeterministic test 2025-04-24 11:30:26 +02:00
Sebastian Ullrich
c8cdb57c4b feat: move non-essential metadata into .olean.server (#8068)
This PR ensures that for modules opted into the experimental module
system, we do not import module docstrings or declaration ranges.

Excluding declaration docstrings as well would require some more work to
make `[inherit_doc]` leave a mere reference to the other declaration
instead of copying its docstring eagerly.
2025-04-24 08:12:26 +00:00
Henrik Böving
58c7e5da94 feat: async IO multiplexing framework + implementation for timers (#8055)
This PR adds an implementation of an async IO multiplexing framework as
well as an implementation of it for the `Timer` API in order to
demonstrate it.

The main motivation is to have fair and data loss free multiplexing of
event sources.
To illustrate two situations where just naively racing two tasks that
read from an event source might be the wrong thing:
1. Suppose we are waiting on two channel reads that are continuously
being filled up. As the first channel will always be ready when we start
its receive function it will instantly resolve the race before the
second one can even try. Thus the path where we receive data from the
second channel gets starved. For this reason we want to try in random
order (for fairness) if the event sources already have data available
for us.
2. Suppose we are waiting on two socket reads and both happen to finish
at the same time. As we are now only going to select one of them to
execute further, we are going to loose data on the second one (unless
there is a user written buffering mechanism involved) as we are going to
disregard the buffer it received and do a new receive next time. For
this reason it is important to wait for an event source to be available
without committing to actually fetching some data until we know that
this particular event source is going to win the select race.

The implementation is inspired by the Oslo framework written by
@haesbaert as well as Go's
[`select`](https://go.dev/src/runtime/select.go) implementation. Given a
list of event sources to select one from it is going to:
1. Randomly shuffle them
2. Attempt to fetch data from them (in their new random order) without
blocking (for fairness). If any of them succeeds return right away.
3. If none has data available right away set all of them up to resolve a
promise. They will then race to win the right to resolve that promise.
Only the data source that wins the race is allowed to then actually
fetch data, ensuring that no other event source actually fetches data
and then fails to deliver it to the consumer.


Follow up PRs are going to add implementations of `Selector` for
`Std.Channel` as well as TCP and UDP sockets.

---------

Co-authored-by: Markus Himmel <markus@lean-fro.org>
2025-04-24 07:55:39 +00:00
Sebastian Ullrich
d5494a306c chore: CI: extend list of virtual merge checkout files 2025-04-24 08:43:42 +02:00
Kyle Miller
42ab5dfab0 fix: have runTermElabM reset local context when types of autobound implicits contain metavariables (#7952)
This PR makes two improvements to the local context when there are
autobound implicits in `variable`s. First, the local context no longer
has two copies of every variable (the local context is rebuilt if the
types of autobound implicits have metavariables). Second, these
metavariables get names using the same algorithm used by binders that
appear in declarations (with `mkForallFVars'` instead of
`mkForallFVars`).

This removes the last use of `Term.addAutoBoundImplicits'`, which
inherently has this variable duplication issue.
2025-04-24 03:29:10 +00:00
Max Carr
3d31b1f608 doc: fix typo in MetavarDecl docstring (#8069)
This PR fixes a typo (metavarible -> metavariable) in the docstring for
`MetavarDecl.type`
2025-04-23 22:00:13 +00:00
Leonardo de Moura
146df5ac74 feat: EqCnstr.mkNullCertExt (#8071)
This PR implements `EqCnstr.mkNullCertExt`. Given an implied polynomial
equation `p = 0`, it generates the certificate:
```
q₁ * h₁ + … + qₙ * hₙ
```  
for `d * p = 0`, where each `qᵢ`s are polynomials and each `hᵢ` is an
equational hypothesis of the form `lhsᵢ = rhsᵢ`. `d` is a numeral.
2025-04-23 19:41:46 +00:00
Sebastian Ullrich
7feb583b9e feat: enable experimental module system in Init (#8047) 2025-04-23 17:21:33 +00:00
Kim Morrison
50d18cdd75 chore: adding failing grind test (#8064)
This PR adds a failing `grind` test, showing a bug where grind is trying
to assign a metavariable incorrectly.
2025-04-23 14:47:38 +00:00
Lean stage0 autoupdater
92927cb4df chore: update stage0 2025-04-23 14:54:30 +00:00
Paul Reichert
57915af218 fix: reducing Nat.pow, kernel interprets constant as Nat literal (#8060)
This PR fixes a bug in the Lean kernel. During reduction of `Nat.pow`,
the kernel did not validate that the WHNF of the first argument is a
`Nat` literal before interpreting it as an `mpz` number. This PR adds
the missing check.

### Explanation

In `type_checker::reduce_pow`, an expression was interpreted as a `Nat`
literal without previously validating that it actually was a `Nat`
literal.

We (@TwoFX and me) noticed this while fuzzing the Lean kernel with GMP
and Mimalloc disabled. Until now, the fuzzer found one crash, leading us
to this issue.

What are the consequences? If GMP is disabled, the Lean kernel will
crash on some inputs after the memory allocator returns `null`. (MPZ
tries to clone the `.const` expression in disguise of a `Nat` literal
which accidentally has a size field indicating that the number has 88
trillion `mpz` digits. This is too much for every allocator.) If GMP is
enabled, it is possible to [prove
`False`](https://live.lean-lang.org/#codez=JYWwDg9gTgLgBAGQKYEMB2AoDATJAzOAcwDoBvAVwF84AuOABQFU1gYyq5AkwjgDkV4aAXjh5yaOAH04ggHxwIYJOIDCAGxQBnDcADGKVXGDjgBACoBPRdLgWrMABZK4ABjhJVGpC6wBaH3AApcg14AHcoViNCOAADPjZIULgACnjiRLgARlcADgBKOABWZwAmGLhQiHJVbDh9DQgK6ABrAG5YtIzU/nSIJOy4fKLnAGZyyursNAByGBx8G1pefi4GKAVaYVFxAA9pORM4PeFXBycAMXqvd08bKHIkX39TRzhmpCg0dzgoJGxyHRIDRwBzAYEwRooOBocggABGHzgCJgoSQTmyAD0cqMSnU0LVMdiACw5eYEegAeQA6gBCTbLBJ9FIkUjOaiAC/JAJfkBUyWHcKDhcAARABiIwAKyQOhgEjhKGwEjA6wgeCFSx0EBAIHQtVkcGwEAwcDgqiQ8FwOgMdGQ6GIABEpeooPxgBBxEI4MRcHg0A7LXBSEbjcG0CgQF4PTEQOYHCAADRB4NwexGGDAj3EX6EaooKAuBNJ40aFB4M3menEYulguFmCWCPCZLEFBgMApYgatAhWKmOAAbQAugUm53uzFKbT+0O8jWk6aAG7uei5sPp4SD2fB+f6B70kduseme5IYip9ZTvKJuCUIM2tDEACi6jhxGUmu1+OIqhMMDfvwAsikd7Ntg2B+gYFqqJeGB+HAyjOhojjAocADi/70IYwLYGCAqmtgGBimgkrSrK8qKsqeBYGc0BICARASEgACOEgAF4fI0dAshw4gnPScLmEGYh4BANREEGGhgN+8AADytHIUB4KoVGODRdGIX0Eh4FcSyXB4DZIgJxo6PY6CEF4vbdIySSuJkl7GlASR9oACYT0UxrHsQOQZIDsKDSnA0axhgQA)
because the kernel doesn't crash on a memory allocation and instead just
happily interprets the `.const` expression as a GMP number.

Importantly, this is _not_ a flaw in Lean's type theory. It is an
implementation bug in the built-in kernel, related to the efficient
reduction of `Nat.pow`, that will be fixed with this PR; see the test
file. Because Lean's kernel is relatively small, there are third-party
kernel implementations such as `lean4lean` and `nanoda`. `lean4lean`
catches the bogus proof, and looking at its code `nanoda` will, too, but
I haven't tried it yet.
2025-04-23 13:55:20 +00:00
Sebastian Ullrich
521a37f796 chore: rework module system import (#8062)
Ensure .server and .private are inspected only for `module`s and that
`module`s and non-`module`s interact correctly
2025-04-23 13:48:53 +00:00
Rob23oba
70bf2db056 fix: use one-field structures for Array.Perm and Vector.Perm (#7999)
This PR replaces `Array.Perm` and `Vector.Perm` with one-field
structures. This avoids dot notation for `List` to work like e.g.
`h.cons 3` where `h` is an `Array.Perm`.
2025-04-23 13:32:30 +00:00
Sebastian Ullrich
5c7a7d6406 chore: CI: reset reldebug check level 2025-04-23 14:31:59 +02:00
Marc Huisinga
55cb65c0fe fix: trace nodes collapsing while file is elaborating (#8056)
This PR fixes a bug where the trace nodes in the InfoView would close
while the file was still being elaborated.

Closes #8053.

The cause of this bug was that we didn't memorize interactive
diagnostics correctly, so the server would generate new RPC pointers in
every single `getInteractiveDiagnostics` RPC request, which lead to the
client resetting the UI.
2025-04-23 10:08:48 +00:00
Sebastian Ullrich
fbbf42e82f chore: fix reldebug preset (#8051)
Build with assertions, but without debug info
2025-04-23 10:05:11 +00:00
Sebastian Ullrich
8c8df274cf chore: fix enabling module system (#8057) 2025-04-23 09:31:08 +00:00
Sebastian Ullrich
2e42013555 chore: clarify m_cs_sz use with mimalloc (#8058)
We didn't feed correct data to `mi_free_size`, but it turns out it
discards it anyway.
2025-04-23 07:39:01 +00:00
Leonardo de Moura
dfa72d6c04 feat: infrastructure for computing Nullstellensatz certificates (#8059)
This PR adds infrastructure for computing Nullstellensatz certificates
in the comm ring procedure in `grind`.
2025-04-23 04:25:38 +00:00
Sebastian Ullrich
ad3ac150bc chore: remove lakefile copy in root and tests/ (#8054)
As we use a different Lean in these directories, using those copies
always results in a full rebuild
2025-04-22 16:03:12 +00:00
David Thrane Christiansen
5d82927d10 chore: move Lake DSL syntax into dedicated module (#8048)
This PR moves the Lake DSL syntax into a dedicated module with minimal
imports.

This allows modules outside of Lake/Lean to import Lake.DSL.Syntax
without crashing, because it reduces the transitive closure of these
modules' imports. This is needed for the reference manual to be able to
document the DSL syntax.

Additionally, the imports of `Lake.Build.Fetch` are decreased, which
reduces its import closure sufficiently to include docs for `FetchM` in
the reference manual.
2025-04-22 14:35:54 +00:00
Henrik Böving
8e1b9abb7a fix: missing wakeup in bounded channel try receive (#8052)
This PR fixes a small oversight in the wakeup mechanism of blocked
bounded channel senders that occurs when calling `tryRecv`.

Marked as `changelog-no` as this isn't released yet.
2025-04-22 14:32:59 +00:00
Sebastian Ullrich
be117c4738 fix: missing traces from realizeConst (#8050)
This PR fixes missing trace messages when produced inside `realizeConst`

Fixes #8049
2025-04-22 12:23:54 +00:00
Lean stage0 autoupdater
46526cc8fb chore: update stage0 2025-04-22 11:08:24 +00:00
Sebastian Ullrich
3ae41cb181 feat: allow use of experimental module system in Init (#7919) 2025-04-22 09:09:27 +00:00
Kim Morrison
2c6d634127 fix: make IntCast a field of Grind.CommRing (#8042)
This PR makes `IntCast` a field of `Lean.Grind.CommRing`, along with
additional axioms relating it to negation of `OfNat`. This allows use to
use existing instances which are not definitionally equal to the
previously given construction.

---------

Co-authored-by: Leonardo de Moura <leomoura@amazon.com>
2025-04-22 02:43:27 +00:00
Leonardo de Moura
ff336fb63c feat: Nullstellensatz certificates for the comm ring procedure in grind (#8043)
This PR adds `NullCert` type for representing Nullstellensatz
certificates that will be produced by the new commutative ring procedure
in `grind`.
2025-04-22 00:40:11 +00:00
Leonardo de Moura
9bdd11465c feat: improve denoteNum (#8040)
This PR modifies `denoteNum` to avoid `intCast`. It is too verbose in
pretty printing messages.
2025-04-21 18:29:23 +00:00
Sebastian Ullrich
791bba0091 feat: LLVM 15 -> 19 (#6063)
This PR updates the version of LLVM and clang used by and shipped with
Lean to 19.1.2

Fixes #5649
2025-04-21 17:18:18 +00:00
Sebastian Ullrich
d6c30a8a0a chore: disable build of old manual 2025-04-21 18:40:11 +02:00
Sebastian Ullrich
f86b192ec2 chore: fix Nix build 2025-04-21 18:40:11 +02:00
Sebastian Ullrich
e6771d7524 chore: update stage0 2025-04-21 18:40:11 +02:00
Sebastian Ullrich
da82cbd3d1 feat: module header keyword for enabling module system 2025-04-21 18:40:11 +02:00
Joachim Breitner
2386a3d7c7 chore: add RISC-V ast benchmark (#8035)
This PR adds a realistic large-inductive benchmark, taken from
https://github.com/opencompl/sail-riscv-lean
2025-04-21 15:46:38 +00:00
Henrik Böving
39f7380663 perf: fix linearity issue in bv_decide (#8036)
This PR fixes a linearity issue in `bv_decide`'s bitblaster, caused by
the fact that the higher order combinators `AIG.RefVec.zip` and
`AIG.RefVec.fold` were not being properly specialised.

Example benchmark `QF_BV/sage/app1/bench_1967.smt2`:
- before: https://share.firefox.dev/4cE86It
- after: https://share.firefox.dev/42L9chd
2025-04-21 13:51:21 +00:00
Kyle Miller
517899da7b feat: extract_lets and lift_lets tactics (#6432)
This PR implements tactics called `extract_lets` and `lift_lets` that
manipulate `let`/`let_fun` expressions. The `extract_lets` tactic
creates new local declarations extracted from any `let` and `let_fun`
expressions in the main goal. For top-level lets in the target, it is
like the `intros` tactic, but in general it can extract lets from deeper
subexpressions as well. The `lift_lets` tactic moves `let` and `let_fun`
expressions as far out of an expression as possible, but it does not
extract any new local declarations. The option `extract_lets +lift`
combines these behaviors.

This is a re-implementation of `extract_lets` and `lift_lets` from
mathlib. The new `extract_lets` is like doing `lift_lets; extract_lets`,
but it does not lift unextractable lets like `lift_lets`. The
`lift_lets; extract_lets` behavior is now handled by `extract_lets
+lift`. The new `lift_lets` tactic is a frontend to `extract_lets +lift`
machinery, which rather than creating new local definitions instead
represents the accumulated local declarations as top-level lets.

There are also conv tactics for both of these. The `extract_lets` has a
limitation due to the conv architecture; it can extract lets for a given
conv goal, but the local declarations don't survive outside conv. They
get zeta reduced immediately upon leaving conv.
2025-04-21 08:57:01 +00:00
Cameron Zwarich
02f7a1dd41 fix: correctly handle duplicate projections in the IR expand_reset_reuse pass (#8023)
This PR fixes the IR expand_reset_reuse pass to correctly handle
duplicate projections from the same base/index. This does not occur (at
least easily) with the old compiler, but it occurs when bootstrapping
Lean with the new compiler.
2025-04-21 03:27:32 +00:00
Leonardo de Moura
568a1b1a81 refactor: comm ring procedure in grind (#8034)
This PR makes the following modifications to the new comm ring procedure
in `grind`
1. Adds data-structures for representing equations (and their
justifications), basis, and queue of equations to be processed.
2. Adds `RingM` helper monad.
3. Adds equation simplification main loop
2025-04-21 02:53:43 +00:00
Leonardo de Moura
63cf571553 feat: add functions for converting ring reified terms back into Expr (#8033)
This PR adds functions for converting `CommRing` reified terms back into
Lean expressions.
2025-04-20 21:49:14 +00:00
Sebastian Ullrich
11f6326102 chore: un-orphan file (#8031)
This file is used in a test, which thus fails using `-DUSE_LAKE=ON`
2025-04-20 18:16:51 +00:00
Sebastian Ullrich
b5f191724d chore: stop taking constants from kernel env in synchronous case as well (#7915)
Makes the elaborator constant map truly independent of the kernel's in
preparation for the module system where declarations in the elab env may
in fact differ from the kernel env.
2025-04-20 17:56:14 +00:00
Leonardo de Moura
a49ad77754 feat: unsat comm ring equations in grind (#8032)
This PR adds support to `grind` for detecting unsatisfiable commutative
ring equations when the ring characteristic is known. Examples:
```lean
example (x : Int) : (x + 1)*(x - 1) = x^2 → False := by
  grind +ring

example (x y : Int) : (x + 1)*(x - 1)*y + y = y*x^2 + 1 → False := by
  grind +ring

example (x : UInt8) : (x + 1)*(x - 1) = x^2 → False := by
  grind +ring

example (x y : BitVec 8) : (x + 1)*(x - 1)*y + y = y*x^2 + 1 → False := by
  grind +ring
```
2025-04-20 17:26:46 +00:00
Kim Morrison
2cd874bd30 feat: additional List.findX lemmas (#8030)
This PR adds some missing lemmas about
`List/Array/Vector.findIdx?/findFinIdx?/findSome?/idxOf?`.
2025-04-20 08:08:53 +00:00
Leonardo de Moura
de27872f3f feat: basic CommRing support in grind (#8029)
This PR implements basic support for `CommRing` in `grind`. Terms are
already being reified and normalized. We still need to process the
equations, but `grind` can already prove simple examples such as:
```lean
open Lean.Grind in
example [CommRing α] (x : α) : (x + 1)*(x - 1) = x^2 - 1 := by
  grind +ring

open Lean.Grind in
example [CommRing α] [IsCharP α 256] (x : α) : (x + 16)*(x - 16) = x^2 := by
  grind +ring

example (x : Int) : (x + 1)*(x - 1) = x^2 - 1 := by
  grind +ring

example (x : UInt8) : (x + 16)*(x - 16) = x^2 := by
  grind +ring

example (x : Int) : (x + 1)^2 - 1 = x^2 + 2*x := by
  grind +ring

example (x : BitVec 8) : (x + 16)*(x - 16) = x^2 := by
  grind +ring

example (x : BitVec 8) : (x + 1)^2 - 1 = x^2 + 2*x := by
  grind +ring
```
2025-04-20 05:12:09 +00:00
Mac Malone
72e4f699c6 fix: lake: import-related bugs (#8026)
This PR fixes bugs in #7809 and #7909 that were not caught partially
because the `badImport` test had been disabled.

**Bugs Fixed:**

* Building by path no longer drops top-level logs.
* "bad import" errors are once again printed.
* Transitively imported precompiled modules are once again loaded during
elaboration.
2025-04-19 21:02:38 +00:00
Leonardo de Moura
876680001b feat: add Poly.simp? (#8027)
This PR adds `Poly.simp?` and improves the function for computing
S-polynomials.
2025-04-19 20:10:00 +00:00
JovanGerb
87930f59c3 fix: don't reset localInstances in delaboration (#8022)
This PR fixes a bug where pretty printing is done in a context with
cleared local instances. These were cleared since the local context is
updated during a name sanitization step, but preserving local instances
is valid since the modification to the local context only affects user
names.

This showed up when writing the mathlib delaborator for `max` and `min`
(https://github.com/leanprover-community/mathlib4/pull/23558#discussion_r2050787403)
2025-04-19 15:39:16 +00:00
Leonardo de Moura
f463b62ac3 feat: S-polynomials and cleanup (#8025)
This PR simplifies the `CommRing` monomials, and adds 
1. Monomial `lcm`
2. Monomial division
3. S-polynomials
2025-04-19 04:21:04 +00:00
Cameron Zwarich
9bb1e4f277 fix: correctly handle extern functions in the IR elim_dead_branches pass (#8017)
This PR makes the IR elim_dead_branches pass correctly handle extern
functions by considering them as having a top return value. This fix is
required to bootstrap the Init/ directory with the new compiler.
2025-04-18 17:28:32 +00:00
Sebastian Ullrich
a52e0c5ba5 chore: CI: bring back Lake build job (#8020)
Thanks to recent fixes
2025-04-18 13:42:27 +00:00
Joachim Breitner
02b206af9b fix: mkAppM to typecheck at .default transparency (#7957)
This PR ensures that `mkAppM` can be used to construct terms that are
only type-correct at at default transparency, even if we are in
`withReducible` (e.g. in simp), so that simp does not stumble over
simplifying `let` expression with simplifiable type.reliable.

Here is a reproducer of the issue this solves:
```
example (a b : Nat) (h : a = b):
  (let _ : id Bool := true; a) = (let _ : Bool := true; b) := by
  simp -zeta -zetaDelta [h]
```

This fixes #7826.
2025-04-18 09:23:51 +00:00
Joachim Breitner
e6343497a7 doc: RArray is now universe-polymorphic (#8018)
This PR adjusts the RArray docstring to the new reality from #8014.
2025-04-18 09:23:05 +00:00
Leonardo de Moura
27a7a0a2bd fix: CommRing multivariate polynomials (#8016)
This PR fixes several issues in the `CommRing` multivariate polynomial
library:
1. Replaces the previous array type with the universe polymorphic
`RArray`.
2. Properly eliminates cancelled monomials.
3. Sorts monomials in decreasing order.
4. Marks the parameter `p` of the `IsCharP` class as an output
parameter.
5. Adds `LawfulBEq` instances for the types `Power`, `Mon`, and `Poly`.
2025-04-18 04:34:05 +00:00
Cameron Zwarich
f163758bcf fix: correctly handle join points with no params in the IR elim_dead_branches pass (#8015)
This PR fixes the IR elim_dead_branches pass to correctly handle join
points with no params, which currently get considered unreachable. I was
not able to find an easy repro of this with the old compiler, but it
occurs when bootstrapping Lean with the new compiler.
2025-04-18 03:52:19 +00:00
Leonardo de Moura
32fe2391b9 feat: universe polymorphic RArray (#8014)
This PR makes `RArray` universe polymorphic.
2025-04-18 02:18:10 +00:00
Lean stage0 autoupdater
3cbffee94b chore: update stage0 2025-04-18 01:52:46 +00:00
Leonardo de Moura
807182d63e chore: allow RArray to be universe polymorphic (#8013)
This PR ensures that `RArray` can be made universe polymorphic. We need
an update-stage0 before finalizing this modification.
2025-04-18 01:10:44 +00:00
Lean stage0 autoupdater
a21377b9ec chore: update stage0 2025-04-18 00:52:57 +00:00
Leonardo de Moura
96fd2f195c feat: add debug.terminalTacticsAsSorry (#8012)
This PR adds the option `debug.terminalTacticsAsSorry`. When enabled,
terminal tactics such as `grind` and `omega` are replaced with `sorry`.
Useful for debugging and fixing bootstrapping issues.
2025-04-18 00:10:59 +00:00
Leonardo de Moura
5823d03283 feat: add IsCharP support to multivariate polynomial library (#8011)
This PR adds `IsCharP` support to the multivariate‑polynomial library in
`CommRing`.
2025-04-17 23:55:21 +00:00
Cameron Zwarich
d981fa0faf fix: make implemented_by of casesOn work correctly with hash consing (#8010)
This PR fixes caseOn expressions with an implemented_by to work
correctly with hash consing, even when the elaborator produces terms
that reconstruct the discriminant rather than just reusing a variable.
2025-04-17 23:32:59 +00:00
Cameron Zwarich
7b292090ce fix: restrict lifting outside of cases expressions on Decidable (#8009)
This PR restricts lifting outside of cases expressions on values of a
Decidable type, since we can't correctly represent the dependency on the
erased proposition in the later stages of the compiler.
2025-04-17 23:01:56 +00:00
Cameron Zwarich
f0033cd15e fix: consider params to be ground variables in specialization (#8008)
This PR changes specialization in the new code generator to consider
callee params to be ground variables, which improves the specialization
of polymorphic functions.
2025-04-17 22:34:16 +00:00
Cameron Zwarich
7bbcfdf712 fix: modify eager lambda lifting heuristics to match the old compiler (#8007)
This PR changes eager lambda lifting heuristics in the new compiler to
match the old compiler, which ensures that inlining/specializing monadic
code does not accidentally create mutual tail recursion that the code
generator can't handle.
2025-04-17 21:46:51 +00:00
Cameron Zwarich
130e2d93a5 fix: change inlining heuristics to match old code generator (#8006)
This PR changes the inlining heuristics of the new code generator to
match the old one, which ensures that monadic folds get sufficiently
inlined for their tail recursion to be exposed to the code generator.
2025-04-17 20:47:40 +00:00
Mac Malone
5b16ea98f5 fix: lake: extern_lib linking (#7987)
This PR fixes a bug in #7967 that broke external library linking.

This is slipped through because the FFI example no longer uses
`extern_lib`. As such, a separate `extern_lib` test has been added.
2025-04-17 19:33:22 +00:00
Rob23oba
acfc9c50d5 feat: hash map lemmas for filter, map and filterMap (#7400)
This PR adds lemmas for the `filter`, `map` and `filterMap` functions of
the hash map.

---------

Co-authored-by: jt0202 <johannes.tantow@gmail.com>
Co-authored-by: Johannes Tantow <44068763+jt0202@users.noreply.github.com>
Co-authored-by: Markus Himmel <markus@himmel-villmar.de>
2025-04-17 10:15:52 +00:00
Markus Himmel
5af99cc840 chore: fix typo in simp docstring (#7998)
This PR fixes a typo in the `simp` hover.
2025-04-17 08:46:41 +00:00
Joachim Breitner
85f5a81f17 feat: FunInd: consume all type annotaions (#7997)
This PR removes all type annotations (optional paramters, auto
parameters, out params, semi-out params, not just optional parameters as
before) from the type of functional induction principles.
2025-04-17 07:52:17 +00:00
Cameron Zwarich
a81169bbe4 fix: don't eliminate fun decls in CSE in the base phase (#7996)
This PR disables CSE of local function declarations in the base phase of
the new compiler. This was introducing sharing between lambdas to bind
calls w/ `do` notation, which caused them to later no longer be inlined.
2025-04-17 04:57:21 +00:00
Kim Morrison
fdc62faa0f feat: reproduce Array.Perm API for Vector.Perm (#7994)
This PR reproduces the `Array.Perm` API for `Vector`. Both are still
significantly less developed than the API for `List.Perm`.
2025-04-17 02:39:48 +00:00
Leonardo de Moura
eaf46dfab1 feat: add Expr.toPoly (#7992)
This PR add a function for converting `CommRing` expressions into
multivariate polynomials.

Co-authored-by: Leonardo de Moura <leonardodemoura@Leonardos-MacBook-Pro.local>
2025-04-17 01:48:03 +00:00
Cameron Zwarich
d52b8e3cc1 fix: use lcAny in more cases of type erasure (#7990)
This PR adopts lcAny in more cases of type erasure in the new code
generator.
2025-04-16 22:53:18 +00:00
Kim Morrison
2a5373258f chore: add grind non-determinism repro (#7978)
This PR adds a repro for a non-determinism problem in `grind`.
2025-04-16 22:36:22 +00:00
Leonardo de Moura
d71e9cb96b feat: CommRing.Poly functions and theorems (#7989)
This PR adds functions and theorems for `CommRing` multivariate
polynomials.
2025-04-16 22:09:50 +00:00
Leonardo de Moura
a3a11ffaf9 feat: revlex and grevlex monomial orders (#7986)
This PR implements reverse lexicographical and graded reverse
lexicographical orders for `CommRing` monomials.
2025-04-16 18:03:53 +00:00
Markus Himmel
9d57ed83a9 chore: upstream Int lemmas from mathlib (#7983)
This PR upstreams many of the results from `Mathlib/Data/Int/Init.lean`.

Notably, we upstream the `simp` tag on `Int.natCast_pow`. While this is
desirable as a `simp` lemma, it is non-confluent with other good `simp`
lemmas like `Int.emod_bmod_congr`, and this will need to be addressed in
the future.
2025-04-16 17:45:08 +00:00
Rob23oba
7cca594a4a chore: adjust BEq classes (#7855)
This PR moves `ReflBEq` to `Init.Core` and changes `LawfulBEq` to extend
`ReflBEq`.

**BREAKING CHANGES:**
- The `refl` field of `ReflBEq` has been renamed to `rfl` to match
`LawfulBEq`
- `LawfulBEq` extends `ReflBEq`, so in particular `LawfulBEq.rfl` is no
longer valid
2025-04-16 13:24:23 +00:00
Kim Morrison
eed8a4828b chore: updates to List API before installing grind attributes (#7982) 2025-04-16 08:06:53 +00:00
Kim Morrison
4bea52c48e chore: failing grind test (#7981)
`propagateForallPropDown` is assuming the domain is a `Prop`
2025-04-16 07:24:53 +00:00
Markus Himmel
5a34ffb9b0 chore: upstream Nat material from mathlib (#7971)
This PR upstreams much of the material from `Mathlib/Data/Nat/Init.lean`
and `Mathlib/Data/Nat/Basic.lean`.
2025-04-16 06:55:32 +00:00
Leonardo de Moura
020b8834c3 feat: monomials for CommRing (#7980)
This PR adds a simple type for representing monomials in a `CommRing`.
This is going to be used in `grind`.
2025-04-16 02:39:31 +00:00
Mac Malone
7423e570f4 chore: lake: temporarily disable tests in tests (#7979)
These tests are currently flaky in `merge-ci` and nightly releases, so
they are being temporarily disabled. Whatever the issue is will be
debugged in a separate PR.
2025-04-16 02:29:53 +00:00
Mac Malone
b51115dac5 feat: IO.Process.SpawnArgs.inheritEnv (#6081)
This PR adds an `inheritEnv` field to `IO.Process.SpawnArgs`. If
`false`, the spawned process does not inherit its parent's environment.

For example, Lake will make use of this to ensure that build processes
do not use environment variables that Lake is not properly tracking with
its traces.
2025-04-16 00:25:32 +00:00
Mac Malone
46769b64c9 chore: lake: bootstrap Lean include directory (#7967)
This PR adds a `bootstrap` option to Lake which is used to identify the
core Lean package. This enables Lake to use the current stage's include
directory rather than the Lean toolchains when compiling Lean with Lean
in core.

**Breaking change:** The Lean library directory is no longer part of
`getLeanLinkSharedFlags`. FFI users should provide this option
separately when linking to Lean (e.g.. via `s!"-L{(←
getLeanLibDir).toString}"`). See the FFI example for a demonstration.
2025-04-15 23:15:53 +00:00
Mac Malone
7d26c7c4f3 feat: lake: build by source path (#7909)
This PR adds Lake support for building modules given their source file
path. This is made use of in both the CLI and the sever.

As a target specifier, `lake build Foo/Bar.lean` will now look for a
module in the workspace whose source file is `Foo/Bar.lean` and build
it. Facets are support via `lake build Foo/Bar.lean:o`. As such, `:` is
an illegal character in such file names (which is reasonable considering
its use in search paths like `PATH` on Linux).

In the server, `lake setup-file Foo/Bar.lean` will now try to lookup a
module for the source and and build its dependencies, ignoring the
imports specified. This allows Lake to return more specific
configuration for the module requested (e.g., library-specific dynlibs
and plugins). If the path cannot be found in the workspace, Lake will
fallback to its previous behavior.

Finally, like `setup-file`, `lake lean Foo/Bar.lean` will try to lookup
a module for the source path and use its more specific configuration if
possible.

Closes #2756.
2025-04-15 23:12:36 +00:00
Kyle Miller
dd84829282 feat: allow omission of => ?_ in induction/cases tactics (#7830)
This PR modifies the syntax of `induction`, `cases`, and other tactics
that use `Lean.Parser.Tactic.inductionAlts`. If a case omits `=> ...`
then it is assumed to be `=> ?_`. Example:
```lean
example (p : Nat × Nat) : p.1 = p.1 := by
  cases p with | _ p1 p2
  /-
  case mk
  p1 p2 : Nat
  ⊢ (p1, p2).fst = (p1, p2).fst
  -/
```
This works with multiple cases as well. Example:
```lean
example (n : Nat) : n + 1 = 1 + n := by
  induction n with | zero | succ n ih
  /-
  case zero
  ⊢ 0 + 1 = 1 + 0
  
  case succ
  n : Nat
  ih : n + 1 = 1 + n
  ⊢ n + 1 + 1 = 1 + (n + 1)
  -/
```
The `induction n with | zero | succ n ih` is short for `induction n with
| zero | succ n ih => ?_`, which is short for `induction n with | zero
=> ?_ | succ n ih => ?_`. Note that a consequence of parsing is that
only the last alternative can omit `=>`. Any `=>`-free alternatives
before an alternative with `=>` will be a part of that alternative.

Rationale:
- In the future we may require `tacticSeq` to be indented. For
one-constructor types, this lets the rest of the tactic sequence not
need indentation.
- This is a semi-structured alternative to the `cases'`/`induction'`
tactics in mathlib.
2025-04-15 22:03:46 +00:00
Mac Malone
17d3daca8a feat: lake: track trace inputs & related fixes (#7906)
This PR changes Lake build traces to track their mixed inputs. The
tracked inputs are saved as part of the `.trace` file, which can
significantly assist in debugging trace issues. In addition, this PR
tweaks some existing Lake traces. Most significant, module olean traces
no longer incorporate their module's source trace.
2025-04-15 19:23:02 +00:00
Henrik Böving
712bb070f9 feat: make bv_decide work on simp normal forms of shifts (#7976)
This PR ensure that `bv_decide` can handle the simp normal form of a
shift.

Consider:
```lean
theorem test1 (b s : BitVec 5) (hb : b = 0) (hs : s ≠ 0)
  : b <<< s = 0 := by
  bv_decide
```
This works out, however:
```lean
theorem test2 (b s : BitVec 5) (hb : b = 0) (hs : s ≠ 0)
  : b <<< s = 0 := by
  simp
  bv_decide
```
this fails because the `simp` normal form adds `toNat` to the right hand
argument of the `<<<` and `bv_decide` cannot deal with shifts by
non-constant `Nat`.

Discovered by @spdskatr
2025-04-15 17:26:19 +00:00
Kim Morrison
525fd2697c fix: reduce priorities of CommRing parent projections (#7975)
This PR reduces the priority of the parent projections of
`Lean.Grind.CommRing`, to avoid these being used in typeclass inference
in Mathlib.
2025-04-15 13:45:53 +00:00
Markus Himmel
c82159e09b feat: Int.bmod lemmas (#7933)
This PR adds lemmas about `Int.bmod` to achieve parity between
`Int.bmod` and `Int.emod`/`Int.fmod`/`Int.tmod`. Furthermore, it adds
missing lemmas for `emod`/`fmod`/`tmod` and performs cleanup on names
and statements for all four operations, also with a view towards
increasing consistency with the corresponding `Nat.mod` lemmas.
2025-04-15 12:26:49 +00:00
Kim Morrison
c3996aadb8 feat: Array.count_erase lemma (#7939)
This PR adds `Array.count_erase` and specializations.
2025-04-15 04:02:29 +00:00
Eric Wieser
bb2f51a230 feat: link Lake.EStateT with EStateM (#7963)
This PR adds helper functions to convert between `Lake.EStateT` and
`EStateM`.

In the longer run the two types could just be merged.
2025-04-15 01:05:47 +00:00
Mac Malone
d5027c1a29 chore: lake: rm unused import in DSL.DeclUtil (#7964) 2025-04-15 00:01:02 +00:00
Henrik Böving
bfb02be281 fix: bv_decide default match with as many arms as constructors (#7961)
This PR fixes a bug in bv_decide where if it was presented with a match
on an enum with as many arms as constructors but the last arm being a
default match it would (wrongly) give up on the match.
2025-04-14 14:58:13 +00:00
Sebastian Ullrich
0076ba03d4 fix: race condition in IO.getTaskState (#7945)
This PR fixes a potential race between `IO.getTaskState` and the task in
question finishing, resulting in undefined behavior.

All task state must be accessed under the respective lock.
2025-04-14 14:08:36 +00:00
Henrik Böving
8e9da7a1bc feat: wait on dedicated tasks after main is finished (#7958)
This PR ensures that after `main` is finished we still wait on dedicated
tasks instead of exiting forcefully. If users wish to violently kill
their dedicated tasks at the end of main instead they can run
`IO.Process.exit` at the end of `main` instead.
2025-04-14 11:53:54 +00:00
Henrik Böving
ac738a8e81 perf: use mimalloc in compactor hashmaps (#7929)
This PR changes the compactor hashmap to use mimalloc which speeds up
olean serialization.
2025-04-14 09:11:34 +00:00
Lean stage0 autoupdater
689acab1d3 chore: update stage0 2025-04-14 07:03:16 +00:00
Kyle Miller
de25524dd6 feat: preparation for #7830 (#7955)
This PR adds the tactic implementation for #7830, before changing the
syntax after a stage0 update. It will allow optional RHSs in induction
cases.
2025-04-14 06:22:04 +00:00
Kyle Miller
48a9bfb73d doc: add docstrings to mkFreshUserName etc (#7947)
This PR adds some docstrings to clarify the functions of
`Lean.mkFreshId`, `Lean.Core.mkFreshUserName`,
`Lean.Elab.Term.mkFreshBinderName`, and
`Lean.Meta.mkFreshBinderNameForTactic`.
2025-04-14 04:17:45 +00:00
Kyle Miller
7c9519e60c fix: make sure all_goals restores state on failure (#7950)
This PR modifies `all_goals` so that in recovery mode it commits changes
to the state only for those goals for which the tactic succeeds (while
preserving the new message log state). Before, we were trusting that
failing tactics left things in a reasonable state, but now we roll back
and admit the goal. The changes also fixes a bug where we were rolling
back only the metacontext state and not the tactic state, leading to an
inconsistent state (a goal list with metavariables not in the
metacontext). Closes #7883

Alternatively we could stop on the first error, however it is helpful to
see what the tactic did to each goal while interactively writing a
tactic script. There is some non-monotonicity here though since tactics
can solve for metavariables that appear in successive goals, and
conceivably a later goal succeeds only if a previous one does. Given
that the non-monotonicity is limited to recovery mode (which is for
example the RHS and not the LHS of the `<;>` combinator), we think this
is acceptable.

Another justification for the change to roll back the state on each
failure is that we need to admit goals in the failing cases. When a
tactic throws an error, we cannot assume the goal list is meaningful.
Rolling back lets us admit just the goal the tactic started with,
without needing to try to work out which new metavariables should be
admitted in the error state, allowing the tactic to continue trying the
tactic on the next goal.
2025-04-14 04:16:28 +00:00
Leonardo de Moura
4e1dbe1ae8 chore: add [grind ext] funext (#7951)
Co-authored-by: Kim Morrison <kim@tqft.net>
2025-04-14 02:52:44 +00:00
Kim Morrison
a0b63deb04 feat: updates to List/Array.Perm API (#7953)
This PR generalizes some typeclass hypotheses in the `List.Perm` API
(away from `DecidableEq`), and reproduces `List.Perm.mem_iff` for
`Array`, and fixes a mistake in the statement of `Array.Perm.extract`.
2025-04-14 01:17:02 +00:00
Lean stage0 autoupdater
c5e20c980c chore: update stage0 2025-04-13 23:32:03 +00:00
Leonardo de Moura
cd5b495573 feat: add [grind ext] attribute (#7949)
This PR adds the attribute `[grind ext]`. It is used to select which
`[ext]` theorems should be used by `grind`. The option `grind +extAll`
instructs `grind` to use all `[ext]` theorems available in the
environment.
After update stage0, we need to add the builtin `[grind ext]`
annotations to key theorems such as `funext`.
2025-04-13 22:08:36 +00:00
Leonardo de Moura
2337b95676 feat: improve case split heuristics in grind (#7946)
This PR improves the case split heuristics in `grind`.
2025-04-13 17:57:56 +00:00
Sebastian Ullrich
973f521c46 chore: fix cmake install exclude patterns (#7941) 2025-04-13 12:32:55 +00:00
Sebastian Ullrich
069456ea9c chore: disable flaky test 2025-04-13 13:18:05 +02:00
Kim Morrison
aa2cae8801 feat: List/Array/Vector.count_replace lemmas (#7938)
This PR adds lemmas about `List/Array/Vector.countP/count` interacting
with `replace`. (Specializing to `_self` and `_ne` lemmas doesn't seem
useful, as there will still be an `if` on the RHS.)
2025-04-13 03:10:19 +00:00
Leonardo de Moura
f513c35742 feat: lookahead in grind (#7937)
This PR implements a lookahead feature to reduce the size of the search
space in `grind`. It is currently effective only for arithmetic atoms.
2025-04-13 03:01:47 +00:00
1366 changed files with 41251 additions and 6314 deletions

View File

@@ -82,7 +82,7 @@ jobs:
- name: CI Merge Checkout
run: |
git fetch --depth=1 origin ${{ github.sha }}
git checkout FETCH_HEAD flake.nix flake.lock
git checkout FETCH_HEAD flake.nix flake.lock script/prepare-*
if: github.event_name == 'pull_request'
# (needs to be after "Checkout" so files don't get overridden)
- name: Setup emsdk
@@ -99,7 +99,6 @@ jobs:
if: matrix.cmultilib
- name: Cache
id: restore-cache
if: matrix.name != 'Linux Lake'
uses: actions/cache/restore@v4
with:
# NOTE: must be in sync with `save` below

View File

@@ -139,20 +139,21 @@ jobs:
let large = ${{ github.repository == 'leanprover/lean4' }};
const isPr = "${{ github.event_name }}" == "pull_request";
let matrix = [
/* TODO: to be updated to new LLVM
{
"name": "Linux LLVM",
"os": "ubuntu-latest",
"release": false,
"check-level": 2,
"shell": "nix develop .#oldGlibc -c bash -euxo pipefail {0}",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-x86_64-linux-gnu.tar.zst",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/19.1.2/lean-llvm-x86_64-linux-gnu.tar.zst",
"prepare-llvm": "../script/prepare-llvm-linux.sh lean-llvm*",
"binary-check": "ldd -v",
// foreign code may be linked against more recent glibc
// reverse-ffi needs to be updated to link to LLVM libraries
"CTEST_OPTIONS": "-E 'foreign|leanlaketest_reverse-ffi'",
"CMAKE_OPTIONS": "-DLLVM=ON -DLLVM_CONFIG=${GITHUB_WORKSPACE}/build/llvm-host/bin/llvm-config"
},
}, */
{
// portable release build: use channel with older glibc (2.26)
"name": "Linux release",
@@ -160,25 +161,23 @@ jobs:
"release": true,
"check-level": 0,
"shell": "nix develop .#oldGlibc -c bash -euxo pipefail {0}",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-x86_64-linux-gnu.tar.zst",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/19.1.2/lean-llvm-x86_64-linux-gnu.tar.zst",
"prepare-llvm": "../script/prepare-llvm-linux.sh lean-llvm*",
"binary-check": "ldd -v",
// foreign code may be linked against more recent glibc
"CTEST_OPTIONS": "-E 'foreign'"
},
// deactivated due to bugs
/*
{
"name": "Linux Lake",
"os": large ? "nscloud-ubuntu-22.04-amd64-4x8" : "ubuntu-latest",
// just a secondary PR build job for now
"check-level": isPr ? 0 : 3,
"check-level": 0,
// just a secondary build job for now until false positives can be excluded
"secondary": true,
"CMAKE_OPTIONS": "-DUSE_LAKE=ON",
// TODO: why does this fail?
"CTEST_OPTIONS": "-E 'scopedMacros'"
// TODO: importStructure is not compatible with .olean caching
// TODO: why does scopedMacros fail?
"CTEST_OPTIONS": "-E 'scopedMacros|importStructure'"
},
*/
{
"name": "Linux",
"os": large ? "nscloud-ubuntu-22.04-amd64-4x8" : "ubuntu-latest",
@@ -210,7 +209,7 @@ jobs:
"release": true,
"check-level": 2,
"shell": "bash -euxo pipefail {0}",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-x86_64-apple-darwin.tar.zst",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/19.1.2/lean-llvm-x86_64-apple-darwin.tar.zst",
"prepare-llvm": "../script/prepare-llvm-macos.sh lean-llvm*",
"binary-check": "otool -L",
"tar": "gtar" // https://github.com/actions/runner-images/issues/2619
@@ -221,7 +220,7 @@ jobs:
"CMAKE_OPTIONS": "-DLEAN_INSTALL_SUFFIX=-darwin_aarch64",
"release": true,
"shell": "bash -euxo pipefail {0}",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-aarch64-apple-darwin.tar.zst",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/19.1.2/lean-llvm-aarch64-apple-darwin.tar.zst",
"prepare-llvm": "../script/prepare-llvm-macos.sh lean-llvm*",
"binary-check": "otool -L",
"tar": "gtar", // https://github.com/actions/runner-images/issues/2619
@@ -242,7 +241,7 @@ jobs:
"CMAKE_OPTIONS": "-G \"Unix Makefiles\"",
// for reasons unknown, interactivetests are flaky on Windows
"CTEST_OPTIONS": "--repeat until-pass:2",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-x86_64-w64-windows-gnu.tar.zst",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/19.1.2/lean-llvm-x86_64-w64-windows-gnu.tar.zst",
"prepare-llvm": "../script/prepare-llvm-mingw.sh lean-llvm*",
"binary-check": "ldd"
},
@@ -253,7 +252,7 @@ jobs:
"release": true,
"check-level": 2,
"shell": "nix develop .#oldGlibcAArch -c bash -euxo pipefail {0}",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-aarch64-linux-gnu.tar.zst",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/19.1.2/lean-llvm-aarch64-linux-gnu.tar.zst",
"prepare-llvm": "../script/prepare-llvm-linux.sh lean-llvm*"
},
// Started running out of memory building expensive modules, a 2GB heap is just not that much even before fragmentation

View File

@@ -73,7 +73,7 @@ jobs:
with:
extra-conf: |
extra-sandbox-paths = /nix/var/cache/ccache?
substituters = file://${{ github.workspace }}/nix-store-cache-copy?priority=10&trusted=true https://cache.nixos.org
substituters = file://${{ github.workspace }}/nix-store-cache-copy?priority=10&trusted=true https://cache.nixos.org
- name: Prepare CCache Cache
run: |
sudo mkdir -m0770 -p /nix/var/cache/ccache
@@ -103,40 +103,10 @@ jobs:
paths: push-test/test-results.xml
if: always()
continue-on-error: true
- name: Build manual
run: |
nix build $NIX_BUILD_ARGS --update-input lean --no-write-lock-file ./doc#{lean-mdbook,leanInk,alectryon,inked} -o push-doc
nix build $NIX_BUILD_ARGS --update-input lean --no-write-lock-file ./doc
# https://github.com/netlify/cli/issues/1809
cp -r --dereference ./result ./dist
if: matrix.name == 'Nix Linux'
- name: Rebuild Nix Store Cache
run: |
rm -rf nix-store-cache || true
nix copy ./push-* --to file://$PWD/nix-store-cache?compression=none
- id: deploy-info
name: Compute Deployment Metadata
run: |
set -e
python3 -c 'import base64; print("alias="+base64.urlsafe_b64encode(bytes.fromhex("${{github.sha}}")).decode("utf-8").rstrip("="))' >> "$GITHUB_OUTPUT"
echo "message=`git log -1 --pretty=format:"%s"`" >> "$GITHUB_OUTPUT"
- name: Publish manual to Netlify
uses: nwtgck/actions-netlify@v3.0
id: publish-manual
with:
publish-dir: ./dist
production-branch: master
github-token: ${{ secrets.GITHUB_TOKEN }}
deploy-message: |
${{ github.event_name == 'pull_request' && format('pr#{0}: {1}', github.event.number, github.event.pull_request.title) || format('ref/{0}: {1}', github.ref_name, steps.deploy-info.outputs.message) }}
alias: ${{ steps.deploy-info.outputs.alias }}
enable-commit-comment: false
enable-pull-request-comment: false
github-deployment-environment: "lean-lang.org/lean4/doc"
fails-without-credentials: false
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: "b8e805d2-7e9b-4f80-91fb-a84d72fc4a68"
- name: Fixup CCache Cache
run: |
sudo chown -R $USER /nix/var/cache

View File

@@ -5,13 +5,15 @@ option(USE_MIMALLOC "use mimalloc" ON)
# store all variables passed on the command line into CL_ARGS so we can pass them to the stage builds
# https://stackoverflow.com/a/48555098/161659
# MUST be done before call to 'project'
# Use standard release build (discarding LEAN_CXX_EXTRA_FLAGS etc.) for stage0 by default since it is assumed to be "good", but still pass through CMake platform arguments (compiler, toolchain file, ..).
# Use standard release build (discarding LEAN_EXTRA_CXX_FLAGS etc.) for stage0 by default since it is assumed to be "good", but still pass through CMake platform arguments (compiler, toolchain file, ..).
# Use `STAGE0_` prefix to pass variables to stage0 explicitly.
get_cmake_property(vars CACHE_VARIABLES)
foreach(var ${vars})
get_property(currentHelpString CACHE "${var}" PROPERTY HELPSTRING)
if("${var}" MATCHES "STAGE0_(.*)")
list(APPEND STAGE0_ARGS "-D${CMAKE_MATCH_1}=${${var}}")
elseif("${var}" MATCHES "STAGE1_(.*)")
list(APPEND STAGE1_ARGS "-D${CMAKE_MATCH_1}=${${var}}")
elseif("${currentHelpString}" MATCHES "No help, variable specified on the command line." OR "${currentHelpString}" STREQUAL "")
list(APPEND CL_ARGS "-D${var}=${${var}}")
if("${var}" MATCHES "USE_GMP|CHECK_OLEAN_VERSION")
@@ -37,10 +39,14 @@ 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++)
if (CADICAL_USE_CUSTOM_CXX)
set(CADICAL_CXX ${CMAKE_CXX_COMPILER})
set(CADICAL_CXXFLAGS "${LEAN_EXTRA_CXX_FLAGS}")
set(CADICAL_LDFLAGS "-Wl,-rpath=\\$$ORIGIN/../lib")
endif()
find_program(CCACHE ccache)
if(CCACHE)
set(CADICAL_CXX "${CCACHE} ${CADICAL_CXX}")
@@ -55,8 +61,11 @@ if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
GIT_REPOSITORY https://github.com/arminbiere/cadical
GIT_TAG rel-2.1.2
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_COMMAND $(MAKE) -f ${CMAKE_SOURCE_DIR}/src/cadical.mk
CMAKE_EXECUTABLE_SUFFIX=${CMAKE_EXECUTABLE_SUFFIX}
CXX=${CADICAL_CXX}
CXXFLAGS=${CADICAL_CXXFLAGS}
LDFLAGS=${CADICAL_LDFLAGS}
BUILD_IN_SOURCE ON
INSTALL_COMMAND "")
set(CADICAL ${CMAKE_BINARY_DIR}/cadical/cadical${CMAKE_EXECUTABLE_SUFFIX} CACHE FILEPATH "path to cadical binary" FORCE)
@@ -77,26 +86,29 @@ if (USE_MIMALLOC)
list(APPEND EXTRA_DEPENDS mimalloc)
endif()
ExternalProject_add(stage0
SOURCE_DIR "${LEAN_SOURCE_DIR}/stage0"
SOURCE_SUBDIR src
BINARY_DIR stage0
# do not rebuild stage0 when git hash changes; it's not from this commit anyway
# (however, CI will override this as we need to embed the githash into the stage 1 library built
# by stage 0)
CMAKE_ARGS -DSTAGE=0 -DUSE_GITHASH=OFF ${PLATFORM_ARGS} ${STAGE0_ARGS}
BUILD_ALWAYS ON # cmake doesn't auto-detect changes without a download method
INSTALL_COMMAND "" # skip install
DEPENDS ${EXTRA_DEPENDS}
)
if (NOT STAGE1_PREV_STAGE)
ExternalProject_add(stage0
SOURCE_DIR "${LEAN_SOURCE_DIR}/stage0"
SOURCE_SUBDIR src
BINARY_DIR stage0
# do not rebuild stage0 when git hash changes; it's not from this commit anyway
# (however, CI will override this as we need to embed the githash into the stage 1 library built
# by stage 0)
CMAKE_ARGS -DSTAGE=0 -DUSE_GITHASH=OFF ${PLATFORM_ARGS} ${STAGE0_ARGS}
BUILD_ALWAYS ON # cmake doesn't auto-detect changes without a download method
INSTALL_COMMAND "" # skip install
DEPENDS ${EXTRA_DEPENDS}
)
list(APPEND EXTRA_DEPENDS stage0)
endif()
ExternalProject_add(stage1
SOURCE_DIR "${LEAN_SOURCE_DIR}"
SOURCE_SUBDIR src
BINARY_DIR stage1
CMAKE_ARGS -DSTAGE=1 -DPREV_STAGE=${CMAKE_BINARY_DIR}/stage0 -DPREV_STAGE_CMAKE_EXECUTABLE_SUFFIX=${STAGE0_CMAKE_EXECUTABLE_SUFFIX} ${CL_ARGS}
CMAKE_ARGS -DSTAGE=1 -DPREV_STAGE=${CMAKE_BINARY_DIR}/stage0 -DPREV_STAGE_CMAKE_EXECUTABLE_SUFFIX=${STAGE0_CMAKE_EXECUTABLE_SUFFIX} ${CL_ARGS} ${STAGE1_ARGS}
BUILD_ALWAYS ON
INSTALL_COMMAND ""
DEPENDS stage0
DEPENDS ${EXTRA_DEPENDS}
STEP_TARGETS configure
)
ExternalProject_add(stage2
@@ -108,6 +120,7 @@ ExternalProject_add(stage2
INSTALL_COMMAND ""
DEPENDS stage1
EXCLUDE_FROM_ALL ON
STEP_TARGETS configure
)
ExternalProject_add(stage3
SOURCE_DIR "${LEAN_SOURCE_DIR}"

View File

@@ -24,9 +24,9 @@
},
{
"name": "reldebug",
"displayName": "Release with debug info build config",
"displayName": "Release with assertions enabled",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "RelWithDebInfo"
"CMAKE_BUILD_TYPE": "RelWithAssert"
},
"generator": "Unix Makefiles",
"binaryDir": "${sourceDir}/build/reldebug"

View File

@@ -144,6 +144,10 @@ We'll use `v4.7.0-rc1` as the intended release version in this example.
- Run `script/release_steps.py v4.7.0-rc1 <repo>` (e.g. replacing `<repo>` with `batteries`), which will walk you through the following steps:
- Create a new branch off `master`/`main` (as specified in the `branch` field), called `bump_to_v4.7.0-rc1`.
- Merge `origin/bump/v4.7.0` if relevant (i.e. `bump-branch: true` appears in `release_repos.yml`).
- Otherwise, you *may* need to merge `origin/nightly-testing`.
- Note that for `verso` and `reference-manual` development happens on `nightly-testing`, so
we will merge that branch into `bump_to_v4.7.0-rc1`, but it is essential in the GitHub interface that we do a rebase merge,
in order to preserve the history.
- Update the contents of `lean-toolchain` to `leanprover/lean4:v4.7.0-rc1`.
- In the `lakefile.toml` or `lakefile.lean`, if there are dependencies on `nightly-testing`, `bump/v4.7.0`, or specific version tags, update them to the new tag.
If they depend on `main` or `master`, don't change this; you've just updated the dependency, so `lake update` will take care of modifying the manifest.
@@ -151,7 +155,7 @@ We'll use `v4.7.0-rc1` as the intended release version in this example.
- Run `lake build && if lake check-test; then lake test; fi` to check things are working.
- Commit the changes as `chore: bump toolchain to v4.7.0-rc1` and push.
- Create a PR with title "chore: bump toolchain to v4.7.0-rc1".
- Merge the PR once CI completes.
- Merge the PR once CI completes. (Recall: for `verso` and `reference-manual` you will need to do a rebase merge.)
- Re-running `script/release_checklist.py` will then create the tag `v4.7.0-rc1` from `master`/`main` and push it (unless `toolchain-tag: false` in the `release_repos.yml` file)
- We do this for the same list of repositories as for stable releases, see above for notes about special cases.
As above, there are dependencies between these, and so the process above is iterative.

1146
doc/style.md Normal file

File diff suppressed because it is too large Load Diff

100
flake.lock generated
View File

@@ -1,112 +1,50 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1710146030,
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1710889954,
"narHash": "sha256-Pr6F5Pmd7JnNEMHHmspZ0qVqIBVxyZ13ik1pJtm2QXk=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "7872526e9c5332274ea5932a0c3270d6e4724f3b",
"type": "github"
"lastModified": 1745636243,
"narHash": "sha256-kbNvlQZf8wwok3d2X1kM/TlXH/MZ+03ZNv+IPPBx+DM=",
"rev": "f771eb401a46846c1aebd20552521b233dd7e18b",
"type": "tarball",
"url": "https://releases.nixos.org/nixos/unstable/nixos-25.05pre789333.f771eb401a46/nixexprs.tar.xz"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-cadical": {
"locked": {
"lastModified": 1740791350,
"narHash": "sha256-igS2Z4tVw5W/x3lCZeeadt0vcU9fxtetZ/RyrqsCRQ0=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "199169a2135e6b864a888e89a2ace345703c025d",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "199169a2135e6b864a888e89a2ace345703c025d",
"type": "github"
"type": "tarball",
"url": "https://channels.nixos.org/nixos-unstable/nixexprs.tar.xz"
}
},
"nixpkgs-old": {
"flake": false,
"locked": {
"lastModified": 1581379743,
"narHash": "sha256-i1XCn9rKuLjvCdu2UeXKzGLF6IuQePQKFt4hEKRU5oc=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "34c7eb7545d155cc5b6f499b23a7cb1c96ab4d59",
"type": "github"
"lastModified": 1582018169,
"narHash": "sha256-qv1iK1IchpZKSeWL3NEs4U5Jl5QVyNHDdiMJvLOI4Yc=",
"type": "tarball",
"url": "https://releases.nixos.org/nixos/19.03/nixos-19.03.173691.34c7eb7545d/nixexprs.tar.xz"
},
"original": {
"owner": "NixOS",
"ref": "nixos-19.03",
"repo": "nixpkgs",
"type": "github"
"type": "tarball",
"url": "https://channels.nixos.org/nixos-19.03/nixexprs.tar.xz"
}
},
"nixpkgs-older": {
"flake": false,
"locked": {
"lastModified": 1523316493,
"narHash": "sha256-5qJS+i5ECICPAKA6FhPLIWkhPKDnOZsZbh2PHYF1Kbs=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "0b307aa73804bbd7a7172899e59ae0b8c347a62d",
"type": "github"
"lastModified": 1550657948,
"narHash": "sha256-BE0XqzNfzvhhtTXv37LyySyq4moL7z1i1hMvwbFNL/I=",
"type": "tarball",
"url": "https://releases.nixos.org/nixos/18.03/nixos-18.03.133402.cb0e20d6db9/nixexprs.tar.xz"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "0b307aa73804bbd7a7172899e59ae0b8c347a62d",
"type": "github"
"type": "tarball",
"url": "https://channels.nixos.org/nixos-18.03/nixexprs.tar.xz"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs",
"nixpkgs-cadical": "nixpkgs-cadical",
"nixpkgs-old": "nixpkgs-old",
"nixpkgs-older": "nixpkgs-older"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",

View File

@@ -1,29 +1,22 @@
{
description = "Lean development flake. Not intended for end users.";
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
# We use channels so we're not affected by GitHub's rate limits
inputs.nixpkgs.url = "https://channels.nixos.org/nixos-unstable/nixexprs.tar.xz";
# old nixpkgs used for portable release with older glibc (2.27)
inputs.nixpkgs-old.url = "github:NixOS/nixpkgs/nixos-19.03";
inputs.nixpkgs-old.url = "https://channels.nixos.org/nixos-19.03/nixexprs.tar.xz";
inputs.nixpkgs-old.flake = false;
# old nixpkgs used for portable release with older glibc (2.26)
inputs.nixpkgs-older.url = "github:NixOS/nixpkgs/0b307aa73804bbd7a7172899e59ae0b8c347a62d";
inputs.nixpkgs-older.url = "https://channels.nixos.org/nixos-18.03/nixexprs.tar.xz";
inputs.nixpkgs-older.flake = false;
# for cadical 2.1.2; sync with CMakeLists.txt by taking commit from https://www.nixhub.io/packages/cadical
inputs.nixpkgs-cadical.url = "github:NixOS/nixpkgs/199169a2135e6b864a888e89a2ace345703c025d";
inputs.flake-utils.url = "github:numtide/flake-utils";
outputs = inputs: inputs.flake-utils.lib.eachDefaultSystem (system:
outputs = inputs: builtins.foldl' inputs.nixpkgs.lib.attrsets.recursiveUpdate {} (builtins.map (system:
let
pkgs = import inputs.nixpkgs { inherit system; };
# An old nixpkgs for creating releases with an old glibc
pkgsDist-old = import inputs.nixpkgs-older { inherit system; };
# An old nixpkgs for creating releases with an old glibc
pkgsDist-old-aarch = import inputs.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 = ./.; };
@@ -31,7 +24,7 @@
stdenv = pkgs.overrideCC pkgs.stdenv lean-packages.llvmPackages.clang;
} ({
buildInputs = with pkgs; [
cmake gmp libuv ccache cadical pkg-config
cmake gmp libuv ccache pkg-config
lean-packages.llvmPackages.llvm # llvm-symbolizer for asan/lsan
gdb
tree # for CI
@@ -67,15 +60,17 @@
GDB = pkgsDist.gdb;
});
in {
packages = {
packages.${system} = {
# to be removed when Nix CI is not needed anymore
inherit (lean-packages) cacheRoots test update-stage0-commit ciShell;
deprecated = lean-packages;
};
# The default development shell for working on lean itself
devShells.default = devShellWithDist pkgs;
devShells.oldGlibc = devShellWithDist pkgsDist-old;
devShells.oldGlibcAArch = devShellWithDist pkgsDist-old-aarch;
});
devShells.${system} = {
# The default development shell for working on lean itself
default = devShellWithDist pkgs;
oldGlibc = devShellWithDist pkgsDist-old;
oldGlibcAArch = devShellWithDist pkgsDist-old-aarch;
};
}) ["x86_64-linux" "aarch64-linux"]);
}

View File

@@ -194,7 +194,7 @@ with builtins; let
modCandidates = mapAttrs (mod: header:
let
deps = if header.errors == []
then map (m: m.module) header.imports
then map (m: m.module) header.result.imports
else abort "errors while parsing imports of ${mod}:\n${lib.concatStringsSep "\n" header.errors}";
in mkMod mod (map (dep: if modDepsMap ? ${dep} then modCandidates.${dep} else externalModMap.${dep}) deps)) modDepsMap;
expandGlob = g:
@@ -206,7 +206,7 @@ with builtins; let
# subset of `modCandidates` that is transitively reachable from `roots`
mods' = listToAttrs (map (e: { name = e.key; value = modCandidates.${e.key}; }) (genericClosure {
startSet = map (m: { key = m; }) (concatMap expandGlob roots);
operator = e: if modDepsMap ? ${e.key} then map (m: { key = m.module; }) (filter (m: modCandidates ? ${m.module}) modDepsMap.${e.key}.imports) else [];
operator = e: if modDepsMap ? ${e.key} then map (m: { key = m.module; }) (filter (m: modCandidates ? ${m.module}) modDepsMap.${e.key}.result.imports) else [];
}));
allLinkFlags = lib.foldr (shared: acc: acc ++ [ "-L${shared}" "-l${shared.linkName or shared.name}" ]) linkFlags allNativeSharedLibs;

View File

@@ -1,4 +1,5 @@
import Lean.Data.Lsp
import Lean.Elab.Import
open Lean
open Lean.Lsp
open Lean.JsonRpc
@@ -7,9 +8,7 @@ open Lean.JsonRpc
Tests language server memory use by repeatedly re-elaborate a given file.
NOTE: only works on Linux for now.
HACK: The line that is to be prepended with a space is hard-coded below to be sufficiently far down
not to touch the imports for usual files.
ot to touch the imports for usual files.
-/
def main (args : List String) : IO Unit := do
@@ -33,6 +32,8 @@ def main (args : List String) : IO Unit := do
Ipc.writeRequest 0, "initialize", { capabilities : InitializeParams }
let text IO.FS.readFile file
let (_, headerEndPos, _) Elab.parseImports text
let headerEndPos := FileMap.ofString text |>.leanPosToLspPos headerEndPos
let mut requestNo : Nat := 1
let mut versionNo : Nat := 1
Ipc.writeNotification "textDocument/didOpen", {
@@ -40,15 +41,14 @@ def main (args : List String) : IO Unit := do
for i in [0:iters.toNat!] do
if i > 0 then
versionNo := versionNo + 1
let pos := { line := 19, character := 0 }
let params : DidChangeTextDocumentParams := {
textDocument := {
uri := uri
version? := versionNo
}
contentChanges := #[TextDocumentContentChangeEvent.rangeChange {
start := pos
«end» := pos
start := headerEndPos
«end» := headerEndPos
} " "]
}
let params := toJson params

View File

@@ -47,10 +47,10 @@ def run_command(command, check=True, capture_output=True):
def clone_repo(repo, temp_dir):
"""Clone the repository to a temporary directory using shallow clone."""
print(f"Shallow cloning {repo}...")
# Keep the shallow clone for efficiency
clone_result = run_command(f"gh repo clone {repo} {temp_dir} -- --depth=1", check=False)
"""Clone the repository to a temporary directory."""
print(f"Cloning {repo}...")
# Remove shallow clone for better merge detection
clone_result = run_command(f"gh repo clone {repo} {temp_dir}", check=False)
if clone_result.returncode != 0:
print(f"Failed to clone repository {repo}.")
print(f"Error: {clone_result.stderr}")
@@ -95,26 +95,16 @@ def check_and_merge(repo, branch, tag, temp_dir):
if checkout_result.returncode != 0:
return False
# Try merging the tag in a dry-run to check if it can be merged cleanly
print(f"Checking if {tag} can be merged cleanly into {branch}...")
merge_check = run_command(f"git merge --no-commit --no-ff {tag}", check=False)
# Try merging the tag directly
print(f"Merging {tag} into {branch}...")
merge_result = run_command(f"git merge {tag} --no-edit", check=False)
if merge_check.returncode != 0:
if merge_result.returncode != 0:
print(f"Cannot merge {tag} cleanly into {branch}.")
print("Merge conflicts would occur. Aborting merge.")
run_command("git merge --abort")
return False
# Abort the test merge
run_command("git reset --hard HEAD")
# Now perform the actual merge and push to remote
print(f"Merging {tag} into {branch}...")
merge_result = run_command(f"git merge {tag} --no-edit")
if merge_result.returncode != 0:
print(f"Failed to merge {tag} into {branch}.")
return False
print(f"Pushing changes to remote...")
push_result = run_command(f"git push origin {branch}")
if push_result.returncode != 0:

View File

@@ -1,5 +1,5 @@
#!/usr/bin/env bash
set -uo pipefail
set -euxo pipefail
# run from root build directory (from inside nix-shell or otherwise defining GLIBC/ZLIB/GMP) as in
# ```
@@ -14,6 +14,7 @@ set -uo pipefail
else
ln -s llvm llvm-host
fi
mkdir -p stage0/lib
mkdir -p stage1/{bin,lib,lib/glibc,include/clang}
CP="cp -d" # preserve symlinks
# a C compiler!
@@ -25,6 +26,8 @@ cp -L llvm/bin/llvm-ar stage1/bin/
# dependencies of the above
$CP llvm/lib/lib{clang-cpp,LLVM}*.so* stage1/lib/
$CP $ZLIB/lib/libz.so* stage1/lib/
# also copy USE_LLVM deps into stage 0
$CP llvm/lib/libLLVM*.so* $ZLIB/lib/libz.so* stage0/lib/
# general clang++ dependency, breaks cross-library C++ exceptions if linked statically
$CP $GCC_LIB/lib/libgcc_s.so* stage1/lib/
# bundle libatomic (referenced by LLVM >= 15, and required by the lean executable to run)
@@ -39,20 +42,21 @@ $CP $GLIBC/lib/*crt* stage1/lib/
# runtime
(cd llvm; $CP --parents lib/clang/*/lib/*/{clang_rt.*.o,libclang_rt.builtins*} ../stage1)
$CP llvm/lib/*/lib{c++,c++abi,unwind}.* $GMP/lib/libgmp.a $LIBUV/lib/libuv.a stage1/lib/
# LLVM 15 appears to ship the dependencies in 'llvm/lib/<target-triple>/' and 'llvm/include/<target-triple>/'
# but clang-15 that we use to compile is linked against 'llvm/lib/' and 'llvm/include'
# LLVM 19 appears to ship the dependencies in 'llvm/lib/<target-triple>/' and 'llvm/include/<target-triple>/'
# but clang-19 that we use to compile is linked against 'llvm/lib/' and 'llvm/include'
# https://github.com/llvm/llvm-project/issues/54955
$CP llvm/lib/*/lib{c++,c++abi,unwind}.* llvm/lib/
$CP llvm-host/lib/*/lib{c++,c++abi,unwind}.* llvm-host/lib/
# libc++ headers are looked up in the host compiler's root, so copy over target-specific includes
$CP -r llvm/include/*-*-* llvm-host/include/
$CP -r llvm/include/*-*-* llvm-host/include/ || true
# glibc: use for linking (so Lean programs don't embed newer symbol versions), but not for running (because libc.so, librt.so, and ld.so must be compatible)!
$CP $GLIBC/lib/libc_nonshared.a stage1/lib/glibc
# libpthread_nonshared.a must be linked in order to be able to use `pthread_atfork(3)`. LibUV uses this function.
$CP $GLIBC/lib/libpthread_nonshared.a stage1/lib/glibc
for f in $GLIBC/lib/lib{c,dl,m,rt,pthread}-*; do b=$(basename $f); cp $f stage1/lib/glibc/${b%-*}.so; done
for f in $GLIBC/lib/{ld,lib{c,dl,m,rt,pthread}}-*; do b=$(basename $f); cp $f stage1/lib/glibc/${b%-*}.so; done
OPTIONS=()
echo -n " -DLEAN_STANDALONE=ON"
# We build cadical using the custom toolchain on Linux to avoid glibc versioning issues
echo -n " -DLEAN_STANDALONE=ON -DCADICAL_USE_CUSTOM_CXX=ON"
echo -n " -DCMAKE_CXX_COMPILER=$PWD/llvm-host/bin/clang++ -DLEAN_CXX_STDLIB='-Wl,-Bstatic -lc++ -lc++abi -Wl,-Bdynamic'"
echo -n " -DLEAN_EXTRA_CXX_FLAGS='--sysroot $PWD/llvm -idirafter $GLIBC_DEV/include ${EXTRA_FLAGS:-}'"
# use target compiler directly when not cross-compiling
@@ -64,7 +68,9 @@ fi
# use `-nostdinc` to make sure headers are not visible by default (in particular, not to `#include_next` in the clang headers),
# but do not change sysroot so users can still link against system libs
echo -n " -DLEANC_INTERNAL_FLAGS='--sysroot ROOT -nostdinc -isystem ROOT/include/clang' -DLEANC_CC=ROOT/bin/clang"
echo -n " -DLEANC_INTERNAL_LINKER_FLAGS='--sysroot ROOT -L ROOT/lib -L ROOT/lib/glibc ROOT/lib/glibc/libc_nonshared.a ROOT/lib/glibc/libpthread_nonshared.a -Wl,--as-needed -Wl,-Bstatic -lgmp -lunwind -luv -Wl,-Bdynamic -Wl,--no-as-needed -fuse-ld=lld'"
# ld.so is usually included by the libc.so linker script but we discard those. Make sure it is linked to only after `libc.so` like in the original
# linker script so that no libc symbols are bound to it instead.
echo -n " -DLEANC_INTERNAL_LINKER_FLAGS='--sysroot ROOT -L ROOT/lib -L ROOT/lib/glibc -lc -lc_nonshared -Wl,--as-needed -l:ld.so -Wl,--no-as-needed -lpthread_nonshared -Wl,--as-needed -Wl,-Bstatic -lgmp -lunwind -luv -Wl,-Bdynamic -Wl,--no-as-needed -fuse-ld=lld'"
# when not using the above flags, link GMP dynamically/as usual
echo -n " -DLEAN_EXTRA_LINKER_FLAGS='-Wl,--as-needed -lgmp -luv -lpthread -ldl -lrt -Wl,--no-as-needed'"
# do not set `LEAN_CC` for tests

View File

@@ -7,6 +7,7 @@ import base64
import subprocess
import sys
import os
import re # Import re module
# Import run_command from merge_remote.py
from merge_remote import run_command
@@ -58,13 +59,29 @@ def release_page_exists(repo_url, tag_name, github_token):
response = requests.get(api_url, headers=headers)
return response.status_code == 200
def get_release_notes(repo_url, tag_name, github_token):
api_url = repo_url.replace("https://github.com/", "https://api.github.com/repos/") + f"/releases/tags/{tag_name}"
headers = {'Authorization': f'token {github_token}'} if github_token else {}
response = requests.get(api_url, headers=headers)
if response.status_code == 200:
return response.json().get("body", "").strip()
return None
def get_release_notes(tag_name):
"""Fetch release notes page title from lean-lang.org."""
# Strip -rcX suffix if present for the URL
base_tag = tag_name.split('-')[0]
reference_url = f"https://lean-lang.org/doc/reference/latest/releases/{base_tag}/"
try:
response = requests.get(reference_url)
response.raise_for_status() # Raise HTTPError for bad responses (4xx or 5xx)
# Extract title using regex
match = re.search(r"<title>(.*?)</title>", response.text, re.IGNORECASE | re.DOTALL)
if match:
return match.group(1).strip()
else:
print(f" ⚠️ Could not find <title> tag in {reference_url}")
return None
except requests.exceptions.RequestException as e:
print(f" ❌ Error fetching release notes from {reference_url}: {e}")
return None
except Exception as e:
print(f" ❌ An unexpected error occurred while processing release notes: {e}")
return None
def get_branch_content(repo_url, branch, file_path, github_token):
api_url = repo_url.replace("https://github.com/", "https://api.github.com/repos/") + f"/contents/{file_path}?ref={branch}"
@@ -255,6 +272,7 @@ def main():
branch_name = f"releases/v{version_major}.{version_minor}.0"
if not branch_exists(lean_repo_url, branch_name, github_token):
print(f" ❌ Branch {branch_name} does not exist")
print(f" 🟡 After creating the branch, we'll need to check CMake version settings.")
lean4_success = False
else:
print(f" ✅ Branch {branch_name} exists")
@@ -274,14 +292,22 @@ def main():
lean4_success = False
else:
print(f" ✅ Release page for {toolchain} exists")
release_notes = get_release_notes(lean_repo_url, toolchain, github_token)
if not (release_notes and toolchain in release_notes.splitlines()[0].strip()):
previous_minor_version = version_minor - 1
previous_release = f"v{version_major}.{previous_minor_version}.0"
print(f" ❌ Release notes not published. Please run `script/release_notes.py --since {previous_release}` on branch `{branch_name}`.")
lean4_success = False
else:
print(f" ✅ Release notes look good.")
# Check the actual release notes page title
actual_title = get_release_notes(toolchain)
expected_title_prefix = f"Lean {toolchain.lstrip('v')}" # e.g., "Lean 4.19.0" or "Lean 4.19.0-rc1"
if actual_title is None:
# Error already printed by get_release_notes
lean4_success = False
elif not actual_title.startswith(expected_title_prefix):
# Construct URL for the error message (using the base tag)
base_tag = toolchain.split('-')[0]
check_url = f"https://lean-lang.org/doc/reference/latest/releases/{base_tag}/"
print(f" ❌ Release notes page title mismatch. Expected prefix '{expected_title_prefix}', got '{actual_title}'. Check {check_url}")
lean4_success = False
else:
print(f" ✅ Release notes page title looks good ('{actual_title}').")
repo_status["lean4"] = lean4_success
@@ -360,10 +386,24 @@ def main():
if check_stable and not is_release_candidate(toolchain):
if not is_merged_into_stable(url, toolchain, "stable", github_token, verbose):
org_repo = extract_org_repo_from_url(url)
print(f" ❌ Tag {toolchain} is not merged into stable")
print(f" Run `script/merge_remote.py {org_repo} stable {toolchain}` to merge it")
repo_status[name] = False
continue
if args.dry_run:
print(f" ❌ Tag {toolchain} is not merged into stable")
print(f" Run `script/merge_remote.py {org_repo} stable {toolchain}` to merge it")
repo_status[name] = False
continue
else:
print(f" … Tag {toolchain} is not merged into stable. Running `script/merge_remote.py {org_repo} stable {toolchain}`...")
# Run the script to merge the tag
subprocess.run(["script/merge_remote.py", org_repo, "stable", toolchain])
# Check again if the tag is merged now
if not is_merged_into_stable(url, toolchain, "stable", github_token, verbose):
print(f" ❌ Manual intervention required.")
repo_status[name] = False
continue
# This will print in all successful cases - whether tag was merged initially or was merged successfully
print(f" ✅ Tag {toolchain} is merged into stable")
if check_bump:

View File

@@ -21,12 +21,19 @@ repositories:
branch: master
dependencies: []
- name: lean4-cli
url: https://github.com/leanprover/lean4-cli
toolchain-tag: true
stable-branch: false
branch: main
dependencies: []
- name: doc-gen4
url: https://github.com/leanprover/doc-gen4
toolchain-tag: true
stable-branch: false
branch: main
dependencies: []
dependencies: [lean4-cli]
- name: verso
url: https://github.com/leanprover/verso
@@ -42,20 +49,13 @@ repositories:
branch: main
dependencies: [verso]
- name: lean4-cli
url: https://github.com/leanprover/lean4-cli
toolchain-tag: true
stable-branch: false
branch: main
dependencies: []
- name: ProofWidgets4
url: https://github.com/leanprover-community/ProofWidgets4
toolchain-tag: false
stable-branch: false
branch: main
dependencies:
- Batteries
- batteries
- name: aesop
url: https://github.com/leanprover-community/aesop
@@ -63,7 +63,7 @@ repositories:
stable-branch: true
branch: master
dependencies:
- Batteries
- batteries
- name: import-graph
url: https://github.com/leanprover-community/import-graph
@@ -71,8 +71,8 @@ repositories:
stable-branch: false
branch: main
dependencies:
- Cli
- Batteries
- lean4-cli
- batteries
- name: plausible
url: https://github.com/leanprover-community/plausible
@@ -88,10 +88,11 @@ repositories:
branch: master
bump-branch: true
dependencies:
- Aesop
- aesop
- ProofWidgets4
- lean4checker
- Batteries
- batteries
- lean4-cli
- doc-gen4
- import-graph
- plausible
@@ -102,4 +103,4 @@ repositories:
stable-branch: true
branch: master
dependencies:
- Mathlib
- mathlib4

View File

@@ -68,7 +68,7 @@ def generate_script(repo, version, config):
]
# Special cases for specific repositories
if repo_name == "REPL":
if repo_name == "repl":
script_lines.extend([
"lake update",
"cd test/Mathlib",
@@ -79,7 +79,7 @@ def generate_script(repo, version, config):
"./test.sh"
])
elif dependencies:
script_lines.append('echo "Please update the dependencies in lakefile.{lean,toml}"')
script_lines.append('perl -pi -e \'s/"v4\\.[0-9]+(\\.[0-9]+)?(-rc[0-9]+)?"/"' + version + '"/g\' lakefile.*')
script_lines.append("lake update")
script_lines.append("")
@@ -89,13 +89,20 @@ def generate_script(repo, version, config):
""
])
if re.search(r'rc\d+$', version) and repo_name in ["Batteries", "Mathlib"]:
if re.search(r'rc\d+$', version) and repo_name in ["batteries", "mathlib4"]:
script_lines.extend([
"echo 'This repo has nightly-testing infrastructure'",
f"git merge origin/bump/{version.split('-rc')[0]}",
"echo 'Please resolve any conflicts.'",
""
])
if re.search(r'rc\d+$', version) and repo_name in ["verso", "reference-manual"]:
script_lines.extend([
"echo 'This repo does development on nightly-testing: remember to rebase merge the PR.'",
f"git merge origin/nightly-testing",
"echo 'Please resolve any conflicts.'",
""
])
if repo_name != "Mathlib":
script_lines.extend([
"lake build && if lake check-test; then lake test; fi",
@@ -104,7 +111,7 @@ def generate_script(repo, version, config):
script_lines.extend([
'gh pr create --title "chore: bump toolchain to ' + version + '" --body ""',
"echo 'Please review the PR and merge it.'",
"echo 'Please review the PR and merge or rebase it.'",
""
])

View File

@@ -191,10 +191,11 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules")
# Initialize CXXFLAGS.
set(CMAKE_CXX_FLAGS "${LEAN_EXTRA_CXX_FLAGS} -DLEAN_BUILD_TYPE=\"${CMAKE_BUILD_TYPE}\" -DLEAN_EXPORTING")
set(CMAKE_CXX_FLAGS_DEBUG "-DLEAN_DEBUG -DLEAN_TRACE")
set(CMAKE_CXX_FLAGS_DEBUG "-DLEAN_DEBUG")
set(CMAKE_CXX_FLAGS_MINSIZEREL "-DNDEBUG")
set(CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-DNDEBUG")
set(CMAKE_CXX_FLAGS_RELWITHASSERT "-DLEAN_DEBUG")
# SPLIT_STACK
if (SPLIT_STACK)
@@ -221,6 +222,7 @@ elseif (MSVC)
set(CMAKE_CXX_FLAGS_DEBUG "/Od /Zi ${CMAKE_CXX_FLAGS_DEBUG}")
set(CMAKE_CXX_FLAGS_MINSIZEREL "/Os /Zc:inline ${CMAKE_CXX_FLAGS_MINSIZEREL}")
set(CMAKE_CXX_FLAGS_RELEASE "/O2 /Oi /Oy /Zc:inline ${CMAKE_CXX_FLAGS_RELEASE}")
set(CMAKE_CXX_FLAGS_RELWITHASSERT "/O2 /Oi /Oy /Zc:inline ${CMAKE_CXX_FLAGS_RELWITHASSERT}")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "/O2 /Oi /Zi ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
set(LEAN_EXTRA_LINKER_FLAGS "/LTCG:INCREMENTAL ${LEAN_EXTRA_LINKER_FLAGS}")
set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} ${LEAN_EXTRA_LINKER_FLAGS}")
@@ -240,11 +242,13 @@ if (NOT MSVC)
set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os ${CMAKE_CXX_FLAGS_MINSIZEREL}")
endif ()
set(CMAKE_CXX_FLAGS_RELEASE "-O3 ${CMAKE_CXX_FLAGS_RELEASE}")
set(CMAKE_CXX_FLAGS_RELWITHASSERT "-O3 ${CMAKE_CXX_FLAGS_RELWITHASSERT}")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g3 -fno-omit-frame-pointer ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
elseif (MULTI_THREAD)
set(CMAKE_CXX_FLAGS_DEBUG "/MTd ${CMAKE_CXX_FLAGS_DEBUG}")
set(CMAKE_CXX_FLAGS_MINSIZEREL "/MT ${CMAKE_CXX_FLAGS_MINSIZEREL}")
set(CMAKE_CXX_FLAGS_RELEASE "/MT ${CMAKE_CXX_FLAGS_RELEASE}")
set(CMAKE_CXX_FLAGS_RELWITHASSERT "/MT ${CMAKE_CXX_FLAGS_RELWITHASSERT}")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "/MT ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
endif ()
@@ -365,8 +369,8 @@ if(LLVM)
execute_process(COMMAND ${LLVM_CONFIG} --version COMMAND_ERROR_IS_FATAL ANY OUTPUT_VARIABLE LLVM_CONFIG_VERSION ECHO_OUTPUT_VARIABLE OUTPUT_STRIP_TRAILING_WHITESPACE)
string(REGEX MATCH "^[0-9]*" LLVM_CONFIG_MAJOR_VERSION ${LLVM_CONFIG_VERSION})
message(STATUS "Found 'llvm-config' at '${LLVM_CONFIG}' with version '${LLVM_CONFIG_VERSION}', major version '${LLVM_CONFIG_MAJOR_VERSION}'")
if (NOT LLVM_CONFIG_MAJOR_VERSION STREQUAL "15")
message(FATAL_ERROR "Unable to find llvm-config version 15. Found invalid version '${LLVM_CONFIG_MAJOR_VERSION}'")
if (NOT LLVM_CONFIG_MAJOR_VERSION STREQUAL "19")
message(FATAL_ERROR "Unable to find llvm-config version 19. Found invalid version '${LLVM_CONFIG_MAJOR_VERSION}'")
endif()
# -DLEAN_LLVM is used to conditionally compile Lean features that depend on LLVM
string(APPEND CMAKE_CXX_FLAGS " -D LEAN_LLVM")
@@ -507,7 +511,10 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
# import libraries created by the stdlib.make targets
string(APPEND LEANC_SHARED_LINKER_FLAGS " -lInit_shared -lleanshared_1 -lleanshared")
elseif("${CMAKE_SYSTEM_NAME}" MATCHES "Darwin")
string(APPEND LEANC_SHARED_LINKER_FLAGS " -Wl,-undefined,dynamic_lookup")
# The second flag is necessary to even *load* dylibs without resolved symbols, as can happen
# if a Lake `extern_lib` depends on a symbols defined by the Lean library but is loaded even
# before definition.
string(APPEND LEANC_SHARED_LINKER_FLAGS " -Wl,-undefined,dynamic_lookup -Wl,-no_fixup_chains")
endif()
# Linux ignores undefined symbols in shared libraries by default
@@ -780,12 +787,11 @@ add_custom_target(clean-olean
DEPENDS clean-stdlib)
install(DIRECTORY "${CMAKE_BINARY_DIR}/lib/" DESTINATION lib
PATTERN temp
PATTERN "*.export"
PATTERN "*.hash"
PATTERN "*.trace"
PATTERN "*.rsp"
EXCLUDE)
PATTERN temp EXCLUDE
PATTERN "*.export" EXCLUDE
PATTERN "*.hash" EXCLUDE
PATTERN "*.trace" EXCLUDE
PATTERN "*.rsp" EXCLUDE)
# symlink source into expected installation location for go-to-definition, if file system allows it
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/src)
@@ -844,6 +850,4 @@ endif()
if(USE_LAKE AND STAGE EQUAL 1)
configure_file(${LEAN_SOURCE_DIR}/lakefile.toml.in ${LEAN_SOURCE_DIR}/lakefile.toml)
configure_file(${LEAN_SOURCE_DIR}/lakefile.toml.in ${LEAN_SOURCE_DIR}/../tests/lakefile.toml)
configure_file(${LEAN_SOURCE_DIR}/lakefile.toml.in ${LEAN_SOURCE_DIR}/../lakefile.toml)
endif()

View File

@@ -3,6 +3,8 @@ Copyright (c) 2014 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
module
prelude
import Init.Prelude
import Init.Notation

View File

@@ -4,6 +4,8 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Joachim Breitner
-/
module
prelude
import Init.Prelude
import Init.Tactics

View File

@@ -3,6 +3,8 @@ Copyright (c) 2021 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Gabriel Ebner
-/
module
prelude
import Init.NotationExtra

View File

@@ -3,6 +3,8 @@ Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura, Mario Carneiro
-/
module
prelude
import Init.Classical

View File

@@ -3,6 +3,8 @@ Copyright (c) 2020 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura, Mario Carneiro
-/
module
prelude
import Init.PropLemmas

View File

@@ -3,6 +3,8 @@ Copyright (c) 2020 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura, Mario Carneiro
-/
module
prelude
import Init.Prelude
set_option linter.missingDocs true -- keep it documented
@@ -307,9 +309,6 @@ instance boolToSort : CoeSort Bool Prop where
instance decPropToBool (p : Prop) [Decidable p] : CoeDep Prop p Bool where
coe := decide p
instance optionCoe {α : Type u} : Coe α (Option α) where
coe := some
instance subtypeCoe {α : Sort u} {p : α Prop} : CoeOut (Subtype p) α where
coe v := v.val

View File

@@ -3,6 +3,8 @@ Copyright (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
module
prelude
import Init.Control.Basic
import Init.Control.State

View File

@@ -3,6 +3,8 @@ Copyright (c) 2020 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura, Sebastian Ullrich
-/
module
prelude
import Init.Core
import Init.BinderNameHint

View File

@@ -3,6 +3,8 @@ Copyright (c) 2019 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
module
prelude
import Init.Control.State
import Init.Control.Except

View File

@@ -5,6 +5,8 @@ Authors: Jared Roesch, Sebastian Ullrich
The Except monad transformer.
-/
module
prelude
import Init.Control.Basic
import Init.Control.Id

View File

@@ -3,6 +3,8 @@ Copyright (c) 2021 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
module
prelude
import Init.Control.Lawful.Basic

View File

@@ -5,6 +5,8 @@ Authors: Sebastian Ullrich
The identity Monad.
-/
module
prelude
import Init.Core

View File

@@ -3,6 +3,8 @@ Copyright (c) 2021 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sebastian Ullrich, Leonardo de Moura, Mario Carneiro
-/
module
prelude
import Init.Control.Lawful.Basic
import Init.Control.Lawful.Instances

View File

@@ -3,6 +3,8 @@ Copyright (c) 2021 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sebastian Ullrich, Leonardo de Moura, Mario Carneiro
-/
module
prelude
import Init.SimpLemmas
import Init.Meta
@@ -142,6 +144,7 @@ class LawfulMonad (m : Type u → Type v) [Monad m] : Prop extends LawfulApplica
export LawfulMonad (bind_pure_comp bind_map pure_bind bind_assoc)
attribute [simp] pure_bind bind_assoc bind_pure_comp
attribute [grind] pure_bind
@[simp] theorem bind_pure [Monad m] [LawfulMonad m] (x : m α) : x >>= pure = x := by
show x >>= (fun a => pure (id a)) = x

View File

@@ -3,6 +3,8 @@ Copyright (c) 2021 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sebastian Ullrich, Leonardo de Moura, Mario Carneiro
-/
module
prelude
import Init.Control.Lawful.Basic
import Init.Control.Except

View File

@@ -3,6 +3,8 @@ Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
module
prelude
import Init.Control.Lawful.Basic
import Init.RCases

View File

@@ -3,6 +3,8 @@ Copyright (c) 2017 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura, Sebastian Ullrich
-/
module
prelude
import Init.Data.Option.Basic
import Init.Control.Basic
@@ -98,7 +100,7 @@ Handles failures by treating them as exceptions of type `Unit`.
-/
@[always_inline, inline] protected def tryCatch (x : OptionT m α) (handle : Unit OptionT m α) : OptionT m α := OptionT.mk do
let some a x | handle ()
pure a
pure <| some a
instance : MonadExceptOf Unit (OptionT m) where
throw := fun _ => OptionT.fail

View File

@@ -5,6 +5,8 @@ Authors: Sebastian Ullrich
The Reader monad transformer for passing immutable State.
-/
module
prelude
import Init.Control.Basic
import Init.Control.Id

View File

@@ -5,6 +5,8 @@ Authors: Leonardo de Moura, Sebastian Ullrich
The State monad transformer.
-/
module
prelude
import Init.Control.Basic
import Init.Control.Id

View File

@@ -3,6 +3,8 @@ Copyright (c) 2021 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
module
prelude
import Init.Control.Lawful.Basic

View File

@@ -5,6 +5,8 @@ Authors: Leonardo de Moura, Sebastian Ullrich
The State monad transformer using IO references.
-/
module
prelude
import Init.System.ST

View File

@@ -5,6 +5,8 @@ Authors: Leonardo de Moura
Notation for operators defined at Prelude.lean
-/
module
prelude
import Init.Tactics
import Init.Meta
@@ -318,6 +320,25 @@ syntax "repeat " convSeq : conv
macro_rules
| `(conv| repeat $seq) => `(conv| first | ($seq); repeat $seq | skip)
/--
Extracts `let` and `let_fun` expressions from within the target expression.
This is the conv mode version of the `extract_lets` tactic.
- `extract_lets` extracts all the lets from the target.
- `extract_lets x y z` extracts all the lets from the target and uses `x`, `y`, and `z` for the first names.
Using `_` for a name leaves it unnamed.
Limitation: the extracted local declarations do not persist outside of the `conv` goal.
See also `lift_lets`, which does not extract lets as local declarations.
-/
syntax (name := extractLets) "extract_lets " optConfig (ppSpace colGt (ident <|> hole))* : conv
/--
Lifts `let` and `let_fun` expressions within the target expression as far out as possible.
This is the conv mode version of the `lift_lets` tactic.
-/
syntax (name := liftLets) "lift_lets " optConfig : conv
/--
`conv => ...` allows the user to perform targeted rewriting on a goal or hypothesis,
by focusing on particular subexpressions.

View File

@@ -5,6 +5,8 @@ Authors: Leonardo de Moura
notation, basic datatypes and type classes
-/
module
prelude
import Init.Prelude
import Init.SizeOf
@@ -21,6 +23,8 @@ which applies to all applications of the function).
theorem id_def {α : Sort u} (a : α) : id a = a := rfl
attribute [grind] id
/--
`flip f a b` is `f b a`. It is useful for "point-free" programming,
since it can sometimes be used to avoid introducing variables.
@@ -738,6 +742,20 @@ Unlike `x ≠ y` (which is notation for `Ne x y`), this is `Bool` valued instead
recommended_spelling "bne" for "!=" in [bne, «term_!=_»]
/-- `ReflBEq α` says that the `BEq` implementation is reflexive. -/
class ReflBEq (α) [BEq α] : Prop where
/-- `==` is reflexive, that is, `(a == a) = true`. -/
protected rfl {a : α} : a == a
@[simp] theorem BEq.rfl [BEq α] [ReflBEq α] {a : α} : a == a := ReflBEq.rfl
theorem BEq.refl [BEq α] [ReflBEq α] (a : α) : a == a := BEq.rfl
theorem beq_of_eq [BEq α] [ReflBEq α] {a b : α} : a = b a == b
| rfl => BEq.rfl
theorem not_eq_of_beq_eq_false [BEq α] [ReflBEq α] {a b : α} (h : (a == b) = false) : ¬a = b := by
intro h'; subst h'; have : true = false := BEq.rfl.symm.trans h; contradiction
/--
A Boolean equality test coincides with propositional equality.
@@ -745,11 +763,9 @@ In other words:
* `a == b` implies `a = b`.
* `a == a` is true.
-/
class LawfulBEq (α : Type u) [BEq α] : Prop where
class LawfulBEq (α : Type u) [BEq α] : Prop extends ReflBEq α where
/-- If `a == b` evaluates to `true`, then `a` and `b` are equal in the logic. -/
eq_of_beq : {a b : α} a == b a = b
/-- `==` is reflexive, that is, `(a == a) = true`. -/
protected rfl : {a : α} a == a
export LawfulBEq (eq_of_beq)
@@ -761,6 +777,15 @@ instance [DecidableEq α] : LawfulBEq α where
eq_of_beq := of_decide_eq_true
rfl := of_decide_eq_self_eq_true _
/--
Non-instance for `DecidableEq` from `LawfulBEq`.
To use this, add `attribute [local instance 5] instDecidableEqOfLawfulBEq` at the top of a file.
-/
def instDecidableEqOfLawfulBEq [BEq α] [LawfulBEq α] : DecidableEq α := fun x y =>
match h : x == y with
| false => .isFalse (not_eq_of_beq_eq_false h)
| true => .isTrue (eq_of_beq h)
instance : LawfulBEq Char := inferInstance
instance : LawfulBEq String := inferInstance
@@ -855,8 +880,8 @@ theorem Bool.of_not_eq_false : {b : Bool} → ¬ (b = false) → b = true
| true, _ => rfl
| false, h => absurd rfl h
theorem ne_of_beq_false [BEq α] [LawfulBEq α] {a b : α} (h : (a == b) = false) : a b := by
intro h'; subst h'; have : true = false := Eq.trans LawfulBEq.rfl.symm h; contradiction
theorem ne_of_beq_false [BEq α] [ReflBEq α] {a b : α} (h : (a == b) = false) : a b :=
not_eq_of_beq_eq_false h
theorem beq_false_of_ne [BEq α] [LawfulBEq α] {a b : α} (h : a b) : (a == b) = false :=
have : ¬ (a == b) = true := by
@@ -915,6 +940,34 @@ term.
theorem eqRec_heq {α : Sort u} {φ : α Sort v} {a a' : α} : (h : a = a') (p : φ a) HEq (Eq.recOn (motive := fun x _ => φ x) h p) p
| rfl, p => HEq.refl p
/--
Heterogenous equality with an `Eq.rec` application on the left is equivalent to a heterogenous
equality on the original term.
-/
theorem eqRec_heq_iff {α : Sort u} {a : α} {motive : (b : α) a = b Sort v}
{b : α} {refl : motive a (Eq.refl a)} {h : a = b} {c : motive b h} :
HEq (@Eq.rec α a motive refl b h) c HEq refl c :=
h.rec (fun _ => id, id) c
/--
Heterogenous equality with an `Eq.rec` application on the right is equivalent to a heterogenous
equality on the original term.
-/
theorem heq_eqRec_iff {α : Sort u} {a : α} {motive : (b : α) a = b Sort v}
{b : α} {refl : motive a (Eq.refl a)} {h : a = b} {c : motive b h} :
HEq c (@Eq.rec α a motive refl b h) HEq c refl :=
h.rec (fun _ => id, id) c
/--
Moves an cast using `Eq.rec` from the function to the argument.
Note: because the motive isn't reliably detected by unification,
it needs to be provided as an explicit parameter.
-/
theorem apply_eqRec {α : Sort u} {a : α} (motive : (b : α) a = b Sort v)
{b : α} {h : a = b} {c : motive a (Eq.refl a) β} {d : motive b h} :
@Eq.rec α a (fun b h => motive b h β) c b h d = c (h.symm d) := by
cases h; rfl
/--
If casting a term with `Eq.rec` to another type makes it equal to some other term, then the two
terms are heterogeneously equal.
@@ -960,7 +1013,7 @@ theorem HEq.comm {a : α} {b : β} : HEq a b ↔ HEq b a := Iff.intro HEq.symm H
theorem heq_comm {a : α} {b : β} : HEq a b HEq b a := HEq.comm
@[symm] theorem Iff.symm (h : a b) : b a := Iff.intro h.mpr h.mp
theorem Iff.comm: (a b) (b a) := Iff.intro Iff.symm Iff.symm
theorem Iff.comm : (a b) (b a) := Iff.intro Iff.symm Iff.symm
theorem iff_comm : (a b) (b a) := Iff.comm
@[symm] theorem And.symm : a b b a := fun ha, hb => hb, ha
@@ -1125,12 +1178,12 @@ theorem dif_eq_if (c : Prop) {h : Decidable c} {α : Sort u} (t : α) (e : α) :
| isTrue _ => rfl
| isFalse _ => rfl
instance {c t e : Prop} [dC : Decidable c] [dT : Decidable t] [dE : Decidable e] : Decidable (if c then t else e) :=
instance {c t e : Prop} [dC : Decidable c] [dT : Decidable t] [dE : Decidable e] : Decidable (if c then t else e) :=
match dC with
| isTrue _ => dT
| isFalse _ => dE
instance {c : Prop} {t : c Prop} {e : ¬c Prop} [dC : Decidable c] [dT : h, Decidable (t h)] [dE : h, Decidable (e h)] : Decidable (if h : c then t h else e h) :=
instance {c : Prop} {t : c Prop} {e : ¬c Prop} [dC : Decidable c] [dT : h, Decidable (t h)] [dE : h, Decidable (e h)] : Decidable (if h : c then t h else e h) :=
match dC with
| isTrue hc => dT hc
| isFalse hc => dE hc
@@ -1296,6 +1349,15 @@ theorem eta (a : {x // p x}) (h : p (val a)) : mk (val a) h = a := by
cases a
exact rfl
instance {α : Type u} {p : α Prop} [BEq α] : BEq {x : α // p x} :=
fun x y => x.1 == y.1
instance {α : Type u} {p : α Prop} [BEq α] [ReflBEq α] : ReflBEq {x : α // p x} where
rfl {x} := BEq.refl x.1
instance {α : Type u} {p : α Prop} [BEq α] [LawfulBEq α] : LawfulBEq {x : α // p x} where
eq_of_beq h := Subtype.eq (eq_of_beq h)
instance {α : Type u} {p : α Prop} [DecidableEq α] : DecidableEq {x : α // p x} :=
fun a, h₁ b, h₂ =>
if h : a = b then isTrue (by subst h; exact rfl)
@@ -1564,7 +1626,7 @@ theorem Nat.succ.injEq (u v : Nat) : (u.succ = v.succ) = (u = v) :=
Eq.propIntro Nat.succ.inj (congrArg Nat.succ)
@[simp] theorem beq_iff_eq [BEq α] [LawfulBEq α] {a b : α} : a == b a = b :=
eq_of_beq, by intro h; subst h; exact LawfulBEq.rfl
eq_of_beq, beq_of_eq
/-! # Prop lemmas -/
@@ -1837,9 +1899,7 @@ protected abbrev hrecOn
(f : (a : α) motive (Quot.mk r a))
(c : (a b : α) (p : r a b) HEq (f a) (f b))
: motive q :=
Quot.recOn q f fun a b p => eq_of_heq <|
have p₁ : HEq (Eq.ndrec (f a) (sound p)) (f a) := eqRec_heq (sound p) (f a)
HEq.trans p₁ (c a b p)
Quot.recOn q f fun a b p => eq_of_heq (eqRec_heq_iff.mpr (c a b p))
end
end Quot
@@ -2202,6 +2262,27 @@ theorem funext {α : Sort u} {β : α → Sort v} {f g : (x : α) → β x}
show extfunApp (Quot.mk eqv f) = extfunApp (Quot.mk eqv g)
exact congrArg extfunApp (Quot.sound h)
/--
Like `Quot.liftOn q f h` but allows `f a` to "know" that `q = Quot.mk r a`.
-/
protected abbrev Quot.pliftOn {α : Sort u} {r : α α Prop}
(q : Quot r)
(f : (a : α) q = Quot.mk r a β)
(h : (a b : α) (h h'), r a b f a h = f b h') : β :=
q.rec (motive := fun q' => q = q' β) f
(fun a b p => funext fun h' =>
(apply_eqRec (motive := fun b _ => q = b)).trans
(@h a b (h'.trans (sound p).symm) h' p)) rfl
/--
Like `Quotient.liftOn q f h` but allows `f a` to "know" that `q = Quotient.mk s a`.
-/
protected abbrev Quotient.pliftOn {α : Sort u} {s : Setoid α}
(q : Quotient s)
(f : (a : α) q = Quotient.mk s a β)
(h : (a b : α) (h h'), a b f a h = f b h') : β :=
Quot.pliftOn q f h
instance Pi.instSubsingleton {α : Sort u} {β : α Sort v} [ a, Subsingleton (β a)] :
Subsingleton ( a, β a) where
allEq f g := funext fun a => Subsingleton.elim (f a) (g a)

View File

@@ -3,6 +3,8 @@ Copyright (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
module
prelude
import Init.Data.Basic
import Init.Data.Nat

View File

@@ -4,6 +4,8 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Dany Fabian
-/
module
prelude
import Init.Classical
import Init.ByCases

View File

@@ -3,6 +3,8 @@ Copyright (c) 2017 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Gabriel Ebner
-/
module
prelude
import Init.Data.Array.Basic
import Init.Data.Array.QSort

View File

@@ -3,6 +3,8 @@ Copyright (c) 2021 Floris van Doorn. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Joachim Breitner, Mario Carneiro
-/
module
prelude
import Init.Data.Array.Mem
import Init.Data.Array.Lemmas
@@ -54,15 +56,15 @@ well-founded recursion mechanism to prove that the function terminates.
-/
@[inline] def attach (xs : Array α) : Array {x // x xs} := xs.attachWith _ fun _ => id
@[simp] theorem _root_.List.attachWith_toArray {l : List α} {P : α Prop} {H : x l.toArray, P x} :
@[simp, grind =] theorem _root_.List.attachWith_toArray {l : List α} {P : α Prop} {H : x l.toArray, P x} :
l.toArray.attachWith P H = (l.attachWith P (by simpa using H)).toArray := by
simp [attachWith]
@[simp] theorem _root_.List.attach_toArray {l : List α} :
@[simp, grind =] theorem _root_.List.attach_toArray {l : List α} :
l.toArray.attach = (l.attachWith (· l.toArray) (by simp)).toArray := by
simp [attach]
@[simp] theorem _root_.List.pmap_toArray {l : List α} {P : α Prop} {f : a, P a β} {H : a l.toArray, P a} :
@[simp, grind =] theorem _root_.List.pmap_toArray {l : List α} {P : α Prop} {f : a, P a β} {H : a l.toArray, P a} :
l.toArray.pmap f H = (l.pmap f (by simpa using H)).toArray := by
simp [pmap]
@@ -525,7 +527,7 @@ theorem countP_attachWith {p : α → Prop} {q : α → Bool} {xs : Array α} {H
simp
@[simp]
theorem count_attach [DecidableEq α] {xs : Array α} {a : {x // x xs}} :
theorem count_attach [BEq α] {xs : Array α} {a : {x // x xs}} :
xs.attach.count a = xs.count a := by
rcases xs with xs
simp only [List.attach_toArray, List.attachWith_mem_toArray, List.count_toArray]
@@ -534,7 +536,7 @@ theorem count_attach [DecidableEq α] {xs : Array α} {a : {x // x ∈ xs}} :
rw [List.countP_pmap, List.countP_attach (p := (fun x => x == a.1)), List.count]
@[simp]
theorem count_attachWith [DecidableEq α] {p : α Prop} {xs : Array α} (H : a xs, p a) {a : {x // p x}} :
theorem count_attachWith [BEq α] {p : α Prop} {xs : Array α} (H : a xs, p a) {a : {x // p x}} :
(xs.attachWith p H).count a = xs.count a := by
cases xs
simp
@@ -588,7 +590,7 @@ def unattach {α : Type _} {p : α → Prop} (xs : Array { x // p x }) : Array
unfold unattach
simp
@[simp] theorem _root_.List.unattach_toArray {p : α Prop} {xs : List { x // p x }} :
@[simp, grind =] theorem _root_.List.unattach_toArray {p : α Prop} {xs : List { x // p x }} :
xs.toArray.unattach = xs.unattach.toArray := by
simp only [unattach, List.map_toArray, List.unattach]

View File

@@ -3,6 +3,8 @@ Copyright (c) 2018 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
module
prelude
import Init.WFTactics
import Init.Data.Nat.Basic
@@ -38,11 +40,11 @@ namespace Array
/-! ### Preliminary theorems -/
@[simp] theorem size_set {xs : Array α} {i : Nat} {v : α} (h : i < xs.size) :
@[simp, grind] theorem size_set {xs : Array α} {i : Nat} {v : α} (h : i < xs.size) :
(set xs i v h).size = xs.size :=
List.length_set ..
@[simp] theorem size_push {xs : Array α} (v : α) : (push xs v).size = xs.size + 1 :=
@[simp, grind] theorem size_push {xs : Array α} (v : α) : (push xs v).size = xs.size + 1 :=
List.length_concat ..
theorem ext {xs ys : Array α}
@@ -86,11 +88,11 @@ theorem ext' {xs ys : Array α} (h : xs.toList = ys.toList) : xs = ys := by
@[simp] theorem toArrayAux_eq {as : List α} {acc : Array α} : (as.toArrayAux acc).toList = acc.toList ++ as := by
induction as generalizing acc <;> simp [*, List.toArrayAux, Array.push, List.append_assoc, List.concat_eq_append]
@[simp] theorem toArray_toList {xs : Array α} : xs.toList.toArray = xs := rfl
@[simp, grind =] theorem toArray_toList {xs : Array α} : xs.toList.toArray = xs := rfl
@[simp] theorem getElem_toList {xs : Array α} {i : Nat} (h : i < xs.size) : xs.toList[i] = xs[i] := rfl
@[simp, grind =] theorem getElem_toList {xs : Array α} {i : Nat} (h : i < xs.size) : xs.toList[i] = xs[i] := rfl
@[simp] theorem getElem?_toList {xs : Array α} {i : Nat} : xs.toList[i]? = xs[i]? := by
@[simp, grind =] theorem getElem?_toList {xs : Array α} {i : Nat} : xs.toList[i]? = xs[i]? := by
simp [getElem?_def]
/-- `a ∈ as` is a predicate which asserts that `a` is in the array `as`. -/
@@ -105,10 +107,10 @@ instance : Membership α (Array α) where
theorem mem_def {a : α} {as : Array α} : a as a as.toList :=
fun | .mk h => h, Array.Mem.mk
@[simp] theorem mem_toArray {a : α} {l : List α} : a l.toArray a l := by
@[simp, grind =] theorem mem_toArray {a : α} {l : List α} : a l.toArray a l := by
simp [mem_def]
@[simp] theorem getElem_mem {xs : Array α} {i : Nat} (h : i < xs.size) : xs[i] xs := by
@[simp, grind] theorem getElem_mem {xs : Array α} {i : Nat} (h : i < xs.size) : xs[i] xs := by
rw [Array.mem_def, getElem_toList]
apply List.getElem_mem
@@ -125,18 +127,18 @@ theorem toList_toArray {as : List α} : as.toArray.toList = as := rfl
@[deprecated toList_toArray (since := "2025-02-17")]
abbrev _root_.Array.toList_toArray := @List.toList_toArray
@[simp] theorem size_toArray {as : List α} : as.toArray.size = as.length := by simp [Array.size]
@[simp, grind] theorem size_toArray {as : List α} : as.toArray.size = as.length := by simp [Array.size]
@[deprecated size_toArray (since := "2025-02-17")]
abbrev _root_.Array.size_toArray := @List.size_toArray
@[simp] theorem getElem_toArray {xs : List α} {i : Nat} (h : i < xs.toArray.size) :
@[simp, grind =] theorem getElem_toArray {xs : List α} {i : Nat} (h : i < xs.toArray.size) :
xs.toArray[i] = xs[i]'(by simpa using h) := rfl
@[simp] theorem getElem?_toArray {xs : List α} {i : Nat} : xs.toArray[i]? = xs[i]? := by
@[simp, grind =] theorem getElem?_toArray {xs : List α} {i : Nat} : xs.toArray[i]? = xs[i]? := by
simp [getElem?_def]
@[simp] theorem getElem!_toArray [Inhabited α] {xs : List α} {i : Nat} :
@[simp, grind =] theorem getElem!_toArray [Inhabited α] {xs : List α} {i : Nat} :
xs.toArray[i]! = xs[i]! := by
simp [getElem!_def]
@@ -192,7 +194,7 @@ Examples:
def pop (xs : Array α) : Array α where
toList := xs.toList.dropLast
@[simp] theorem size_pop {xs : Array α} : xs.pop.size = xs.size - 1 := by
@[simp, grind] theorem size_pop {xs : Array α} : xs.pop.size = xs.size - 1 := by
match xs with
| [] => rfl
| a::as => simp [pop, Nat.succ_sub_succ_eq_sub, size]
@@ -834,7 +836,7 @@ some 10
def findSomeM? {α : Type u} {β : Type v} {m : Type v Type w} [Monad m] (f : α m (Option β)) (as : Array α) : m (Option β) := do
for a in as do
match ( f a) with
| some b => return b
| some b => return some b
| _ => pure
return none
@@ -865,7 +867,7 @@ some 1
def findM? {α : Type} [Monad m] (p : α m Bool) (as : Array α) : m (Option α) := do
for a in as do
if ( p a) then
return a
return some a
return none
/--
@@ -1173,7 +1175,7 @@ def find? {α : Type u} (p : α → Bool) (as : Array α) : Option α :=
Id.run do
for a in as do
if p a then
return a
return some a
return none
/--
@@ -2156,13 +2158,15 @@ Examples:
/-! ### Repr and ToString -/
protected def Array.repr {α : Type u} [Repr α] (xs : Array α) : Std.Format :=
let _ : Std.ToFormat α := repr
if xs.size == 0 then
"#[]"
else
Std.Format.bracketFill "#[" (Std.Format.joinSep (toList xs) ("," ++ Std.Format.line)) "]"
instance {α : Type u} [Repr α] : Repr (Array α) where
reprPrec xs _ :=
let _ : Std.ToFormat α := repr
if xs.size == 0 then
"#[]"
else
Std.Format.bracketFill "#[" (Std.Format.joinSep (toList xs) ("," ++ Std.Format.line)) "]"
reprPrec xs _ := Array.repr xs
instance [ToString α] : ToString (Array α) where
toString xs := "#" ++ toString xs.toList

View File

@@ -3,6 +3,8 @@ Copyright (c) 2022 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
module
prelude
import Init.Data.Array.Basic
import Init.Data.Nat.Linear

View File

@@ -3,6 +3,8 @@ Copyright (c) 2019 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
module
prelude
import Init.Data.Array.Basic
import Init.Data.Int.DivMod.Lemmas

View File

@@ -4,6 +4,8 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro
-/
module
prelude
import Init.Data.List.TakeDrop
@@ -53,12 +55,12 @@ theorem foldlM_toList.aux [Monad m]
rfl
· rw [List.drop_of_length_le (Nat.ge_of_not_lt _)]; rfl
@[simp] theorem foldlM_toList [Monad m]
@[simp, grind =] theorem foldlM_toList [Monad m]
{f : β α m β} {init : β} {xs : Array α} :
xs.toList.foldlM f init = xs.foldlM f init := by
simp [foldlM, foldlM_toList.aux]
@[simp] theorem foldl_toList (f : β α β) {init : β} {xs : Array α} :
@[simp, grind =] theorem foldl_toList (f : β α β) {init : β} {xs : Array α} :
xs.toList.foldl f init = xs.foldl f init :=
List.foldl_eq_foldlM .. foldlM_toList ..
@@ -77,32 +79,32 @@ theorem foldrM_eq_reverse_foldlM_toList [Monad m] {f : α → β → m β} {init
match xs, this with | _, .inl rfl => rfl | xs, .inr h => ?_
simp [foldrM, h, foldrM_eq_reverse_foldlM_toList.aux, List.take_length]
@[simp] theorem foldrM_toList [Monad m]
@[simp, grind =] theorem foldrM_toList [Monad m]
{f : α β m β} {init : β} {xs : Array α} :
xs.toList.foldrM f init = xs.foldrM f init := by
rw [foldrM_eq_reverse_foldlM_toList, List.foldlM_reverse]
@[simp] theorem foldr_toList (f : α β β) {init : β} {xs : Array α} :
@[simp, grind =] theorem foldr_toList (f : α β β) {init : β} {xs : Array α} :
xs.toList.foldr f init = xs.foldr f init :=
List.foldr_eq_foldrM .. foldrM_toList ..
@[simp] theorem push_toList {xs : Array α} {a : α} : (xs.push a).toList = xs.toList ++ [a] := by
@[simp, grind =] theorem push_toList {xs : Array α} {a : α} : (xs.push a).toList = xs.toList ++ [a] := by
simp [push, List.concat_eq_append]
@[simp] theorem toListAppend_eq {xs : Array α} {l : List α} : xs.toListAppend l = xs.toList ++ l := by
@[simp, grind =] theorem toListAppend_eq {xs : Array α} {l : List α} : xs.toListAppend l = xs.toList ++ l := by
simp [toListAppend, foldr_toList]
@[simp] theorem toListImpl_eq {xs : Array α} : xs.toListImpl = xs.toList := by
@[simp, grind =] theorem toListImpl_eq {xs : Array α} : xs.toListImpl = xs.toList := by
simp [toListImpl, foldr_toList]
@[simp] theorem toList_pop {xs : Array α} : xs.pop.toList = xs.toList.dropLast := rfl
@[simp, grind =] theorem toList_pop {xs : Array α} : xs.pop.toList = xs.toList.dropLast := rfl
@[deprecated toList_pop (since := "2025-02-17")]
abbrev pop_toList := @Array.toList_pop
@[simp] theorem append_eq_append {xs ys : Array α} : xs.append ys = xs ++ ys := rfl
@[simp] theorem toList_append {xs ys : Array α} :
@[simp, grind =] theorem toList_append {xs ys : Array α} :
(xs ++ ys).toList = xs.toList ++ ys.toList := by
rw [ append_eq_append]; unfold Array.append
rw [ foldl_toList]
@@ -110,24 +112,24 @@ abbrev pop_toList := @Array.toList_pop
@[simp] theorem toList_empty : (#[] : Array α).toList = [] := rfl
@[simp] theorem append_empty {xs : Array α} : xs ++ #[] = xs := by
@[simp, grind =] theorem append_empty {xs : Array α} : xs ++ #[] = xs := by
apply ext'; simp only [toList_append, toList_empty, List.append_nil]
@[deprecated append_empty (since := "2025-01-13")]
abbrev append_nil := @append_empty
@[simp] theorem empty_append {xs : Array α} : #[] ++ xs = xs := by
@[simp, grind =] theorem empty_append {xs : Array α} : #[] ++ xs = xs := by
apply ext'; simp only [toList_append, toList_empty, List.nil_append]
@[deprecated empty_append (since := "2025-01-13")]
abbrev nil_append := @empty_append
@[simp] theorem append_assoc {xs ys zs : Array α} : xs ++ ys ++ zs = xs ++ (ys ++ zs) := by
@[simp, grind _=_] theorem append_assoc {xs ys zs : Array α} : xs ++ ys ++ zs = xs ++ (ys ++ zs) := by
apply ext'; simp only [toList_append, List.append_assoc]
@[simp] theorem appendList_eq_append {xs : Array α} {l : List α} : xs.appendList l = xs ++ l := rfl
@[simp] theorem toList_appendList {xs : Array α} {l : List α} :
@[simp, grind =] theorem toList_appendList {xs : Array α} {l : List α} :
(xs ++ l).toList = xs.toList ++ l := by
rw [ appendList_eq_append]; unfold Array.appendList
induction l generalizing xs <;> simp [*]

View File

@@ -3,6 +3,8 @@ Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
module
prelude
import Init.Data.Array.Lemmas
import Init.Data.List.Nat.Count
@@ -23,7 +25,7 @@ section countP
variable {p q : α Bool}
@[simp] theorem _root_.List.countP_toArray {l : List α} : countP p l.toArray = l.countP p := by
@[simp, grind =] theorem _root_.List.countP_toArray {l : List α} : countP p l.toArray = l.countP p := by
simp [countP]
induction l with
| nil => rfl
@@ -31,7 +33,7 @@ variable {p q : α → Bool}
simp only [List.foldr_cons, ih, List.countP_cons]
split <;> simp_all
@[simp] theorem countP_toList {xs : Array α} : xs.toList.countP p = countP p xs := by
@[simp, grind =] theorem countP_toList {xs : Array α} : xs.toList.countP p = countP p xs := by
cases xs
simp
@@ -162,10 +164,10 @@ section count
variable [BEq α]
@[simp] theorem _root_.List.count_toArray {l : List α} {a : α} : count a l.toArray = l.count a := by
@[simp, grind =] theorem _root_.List.count_toArray {l : List α} {a : α} : count a l.toArray = l.count a := by
simp [count, List.count_eq_countP]
@[simp] theorem count_toList {xs : Array α} {a : α} : xs.toList.count a = xs.count a := by
@[simp, grind =] theorem count_toList {xs : Array α} {a : α} : xs.toList.count a = xs.count a := by
cases xs
simp
@@ -257,8 +259,8 @@ theorem filter_beq {xs : Array α} (a : α) : xs.filter (· == a) = replicate (c
rcases xs with xs
simp [List.filter_beq]
theorem filter_eq {α} [DecidableEq α] {xs : Array α} (a : α) : xs.filter (· = a) = replicate (count a xs) a :=
filter_beq a
theorem filter_eq {α} [BEq α] [LawfulBEq α] [DecidableEq α] {xs : Array α} (a : α) : xs.filter (· = a) = replicate (count a xs) a :=
funext (Bool.beq_eq_decide_eq · a) filter_beq a
theorem replicate_count_eq_of_count_eq_size {xs : Array α} (h : count a xs = xs.size) :
replicate (count a xs) a = xs := by
@@ -273,7 +275,7 @@ abbrev mkArray_count_eq_of_count_eq_size := @replicate_count_eq_of_count_eq_size
rcases xs with xs
simp [List.count_filter, h]
theorem count_le_count_map [DecidableEq β] {xs : Array α} {f : α β} {x : α} :
theorem count_le_count_map [BEq β] [LawfulBEq β] {xs : Array α} {f : α β} {x : α} :
count x xs count (f x) (map f xs) := by
rcases xs with xs
simp [List.count_le_count_map, countP_map]
@@ -299,15 +301,14 @@ theorem count_replace {a b c : α} {xs : Array α} :
if xs.contains a then xs.count c + (if b == c then 1 else 0) - (if a == c then 1 else 0) else xs.count c := by
simp [count_eq_countP, countP_replace]
-- FIXME these theorems can be restored once `List.erase` and `Array.erase` have been related.
theorem count_erase (a b : α) (xs : Array α) : count a (xs.erase b) = count a xs - if b == a then 1 else 0 := by
rcases xs with l
simp [List.count_erase]
-- theorem count_erase (a b : α) (l : Array α) : count a (l.erase b) = count a l - if b == a then 1 else 0 := by
-- sorry
@[simp] theorem count_erase_self (a : α) (xs : Array α) :
count a (xs.erase a) = count a xs - 1 := by rw [count_erase, if_pos (by simp)]
-- @[simp] theorem count_erase_self (a : α) (l : Array α) :
-- count a (l.erase a) = count a l - 1 := by rw [count_erase, if_pos (by simp)]
-- @[simp] theorem count_erase_of_ne (ab : a ≠ b) (l : Array α) : count a (l.erase b) = count a l := by
-- rw [count_erase, if_neg (by simpa using ab.symm), Nat.sub_zero]
@[simp] theorem count_erase_of_ne (ab : a b) (xs : Array α) : count a (xs.erase b) = count a xs := by
rw [count_erase, if_neg (by simpa using ab.symm), Nat.sub_zero]
end count

View File

@@ -3,6 +3,8 @@ Copyright (c) 2022 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
module
prelude
import Init.Data.Array.Basic
import Init.Data.BEq
@@ -66,7 +68,7 @@ theorem isEqv_eq_decide (xs ys : Array α) (r) :
Bool.not_eq_true]
simpa [isEqv_iff_rel] using h'
@[simp] theorem isEqv_toList [BEq α] (xs ys : Array α) : (xs.toList.isEqv ys.toList r) = (xs.isEqv ys r) := by
@[simp, grind =] theorem isEqv_toList [BEq α] (xs ys : Array α) : (xs.toList.isEqv ys.toList r) = (xs.isEqv ys r) := by
simp [isEqv_eq_decide, List.isEqv_eq_decide]
theorem eq_of_isEqv [DecidableEq α] (xs ys : Array α) (h : Array.isEqv xs ys (fun x y => x = y)) : xs = ys := by
@@ -97,25 +99,27 @@ theorem beq_eq_decide [BEq α] (xs ys : Array α) :
decide ( (i : Nat) (h' : i < xs.size), xs[i] == ys[i]'(h h')) else false := by
simp [BEq.beq, isEqv_eq_decide]
@[simp] theorem beq_toList [BEq α] (xs ys : Array α) : (xs.toList == ys.toList) = (xs == ys) := by
@[simp, grind =] theorem beq_toList [BEq α] (xs ys : Array α) : (xs.toList == ys.toList) = (xs == ys) := by
simp [beq_eq_decide, List.beq_eq_decide]
end Array
namespace List
@[simp] theorem isEqv_toArray [BEq α] (as bs : List α) : (as.toArray.isEqv bs.toArray r) = (as.isEqv bs r) := by
@[simp, grind =] theorem isEqv_toArray [BEq α] (as bs : List α) : (as.toArray.isEqv bs.toArray r) = (as.isEqv bs r) := by
simp [isEqv_eq_decide, Array.isEqv_eq_decide]
@[simp] theorem beq_toArray [BEq α] (as bs : List α) : (as.toArray == bs.toArray) = (as == bs) := by
@[simp, grind =] theorem beq_toArray [BEq α] (as bs : List α) : (as.toArray == bs.toArray) = (as == bs) := by
simp [beq_eq_decide, Array.beq_eq_decide]
end List
namespace Array
instance [BEq α] [LawfulBEq α] : LawfulBEq (Array α) where
instance [BEq α] [ReflBEq α] : ReflBEq (Array α) where
rfl := by simp [BEq.beq, isEqv_self_beq]
instance [BEq α] [LawfulBEq α] : LawfulBEq (Array α) where
eq_of_beq := by
rintro _ _ h
simpa using h

View File

@@ -3,6 +3,8 @@ Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
module
prelude
import Init.Data.Array.Lemmas
import Init.Data.List.Nat.Erase
@@ -59,7 +61,7 @@ theorem exists_or_eq_self_of_eraseP (p) (xs : Array α) :
@[simp] theorem size_eraseP_of_mem {xs : Array α} (al : a xs) (pa : p a) :
(xs.eraseP p).size = xs.size - 1 := by
let _, ys, zs, _, _, e₁, e₂ := exists_of_eraseP al pa
rw [e₂]; simp [size_append, e₁]; omega
rw [e₂]; simp [size_append, e₁]
theorem size_eraseP {xs : Array α} : (xs.eraseP p).size = if xs.any p then xs.size - 1 else xs.size := by
split <;> rename_i h

View File

@@ -3,6 +3,8 @@ Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
module
prelude
import Init.Data.Array.Lemmas
import Init.Data.List.Nat.TakeDrop

View File

@@ -3,6 +3,8 @@ Copyright (c) 2024 François G. Dorais. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: François G. Dorais
-/
module
prelude
import Init.Data.List.FinRange
import Init.Data.Array.OfFn

View File

@@ -3,6 +3,8 @@ Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
module
prelude
import Init.Data.List.Nat.Find
import Init.Data.Array.Lemmas
@@ -21,6 +23,14 @@ open Nat
/-! ### findSome? -/
@[simp, grind] theorem findSome?_empty : (#[] : Array α).findSome? f = none := rfl
@[simp, grind] theorem findSome?_push {xs : Array α} : (xs.push a).findSome? f = (xs.findSome? f).or (f a) := by
cases xs; simp [List.findSome?_append]
@[grind]
theorem findSome?_singleton {a : α} {f : α Option β} : #[a].findSome? f = f a := by
simp
@[simp] theorem findSomeRev?_push_of_isSome {xs : Array α} (h : (f a).isSome) : (xs.push a).findSomeRev? f = f a := by
cases xs; simp_all
@@ -28,7 +38,7 @@ open Nat
cases xs; simp_all
theorem exists_of_findSome?_eq_some {f : α Option β} {xs : Array α} (w : xs.findSome? f = some b) :
a, a xs f a = b := by
a, a xs f a = some b := by
cases xs; simp_all [List.exists_of_findSome?_eq_some]
@[simp] theorem findSome?_eq_none_iff : findSome? p xs = none x xs, p x = none := by
@@ -129,6 +139,8 @@ abbrev findSome?_mkArray_of_isNone := @findSome?_replicate_of_isNone
/-! ### find? -/
@[simp] theorem find?_empty : find? p #[] = none := rfl
@[simp] theorem find?_singleton {a : α} {p : α Bool} :
#[a].find? p = if p a then some a else none := by
simp [singleton_eq_toArray_singleton]
@@ -157,6 +169,9 @@ theorem find?_eq_some_iff_append {xs : Array α} :
exact as.toList, l, by simpa using congrArg Array.toList h',
by simpa using h
theorem find?_push {xs : Array α} : (xs.push a).find? p = (xs.find? p).or (if p a then some a else none) := by
cases xs; simp
@[simp]
theorem find?_push_eq_some {xs : Array α} :
(xs.push a).find? p = some b xs.find? p = some b (xs.find? p = none (p a a = b)) := by
@@ -331,6 +346,11 @@ theorem find?_eq_some_iff_getElem {xs : Array α} {p : α → Bool} {b : α} :
/-! ### findIdx -/
@[simp] theorem findIdx_empty : findIdx p #[] = 0 := rfl
theorem findIdx_singleton {a : α} {p : α Bool} :
#[a].findIdx p = if p a then 0 else 1 := by
simp
theorem findIdx_of_getElem?_eq_some {xs : Array α} (w : xs[xs.findIdx p]? = some y) : p y := by
rcases xs with xs
exact List.findIdx_of_getElem?_eq_some (by simpa using w)
@@ -411,6 +431,13 @@ theorem findIdx_append {p : α → Bool} {xs ys : Array α} :
rcases ys with ys
simp [List.findIdx_append]
theorem findIdx_push {xs : Array α} {a : α} {p : α Bool} :
(xs.push a).findIdx p = if xs.findIdx p < xs.size then xs.findIdx p else xs.size + if p a then 0 else 1 := by
simp only [push_eq_append, findIdx_append]
split <;> rename_i h
· rfl
· simp [findIdx_singleton, Nat.add_comm]
theorem findIdx_le_findIdx {xs : Array α} {p q : α Bool} (h : x xs, p x q x) : xs.findIdx q xs.findIdx p := by
rcases xs with xs
simp_all [List.findIdx_le_findIdx]
@@ -439,6 +466,9 @@ theorem false_of_mem_extract_findIdx {xs : Array α} {p : α → Bool} (h : x
/-! ### findIdx? -/
@[simp] theorem findIdx?_empty : (#[] : Array α).findIdx? p = none := by simp
theorem findIdx?_singleton {a : α} {p : α Bool} :
#[a].findIdx? p = if p a then some 0 else none := by
simp
@[simp]
theorem findIdx?_eq_none_iff {xs : Array α} {p : α Bool} :
@@ -506,6 +536,13 @@ theorem of_findIdx?_eq_none {xs : Array α} {p : α → Bool} (w : xs.findIdx? p
rcases ys with ys
simp [List.findIdx?_append]
theorem findIdx?_push {xs : Array α} {a : α} {p : α Bool} :
(xs.push a).findIdx? p = (xs.findIdx? p).or (if p a then some xs.size else none) := by
simp only [push_eq_append, findIdx?_append]
split <;> rename_i h
· simp only [findIdx?_singleton, if_pos h, Option.map_some, Nat.zero_add]
· simp only [findIdx?_singleton, if_neg h, Option.map_none]
theorem findIdx?_flatten {xss : Array (Array α)} {p : α Bool} :
xss.flatten.findIdx? p =
(xss.findIdx? (·.any p)).map
@@ -563,6 +600,9 @@ theorem findIdx?_eq_some_le_of_findIdx?_eq_some {xs : Array α} {p q : α → Bo
/-! ### findFinIdx? -/
@[simp] theorem findFinIdx?_empty {p : α Bool} : findFinIdx? p #[] = none := by simp
theorem findFinIdx?_singleton {a : α} {p : α Bool} :
#[a].findFinIdx? p = if p a then some 0, by simp else none := by
simp
-- We can't mark this as a `@[congr]` lemma since the head of the RHS is not `findFinIdx?`.
theorem findFinIdx?_congr {p : α Bool} {xs ys : Array α} (w : xs = ys) :
@@ -593,6 +633,21 @@ theorem findFinIdx?_eq_some_iff {xs : Array α} {p : α → Bool} {i : Fin xs.si
· rintro h, w
exact i, i.2, h, fun j hji => w j, by omega hji, rfl
theorem findFinIdx?_push {xs : Array α} {a : α} {p : α Bool} :
(xs.push a).findFinIdx? p =
((xs.findFinIdx? p).map (Fin.castLE (by simp))).or (if p a then some xs.size, by simp else none) := by
simp only [findFinIdx?_eq_pmap_findIdx?, findIdx?_push, Option.pmap_or]
split <;> rename_i h _ <;> split <;> simp [h]
theorem findFinIdx?_append {xs ys : Array α} {p : α Bool} :
(xs ++ ys).findFinIdx? p =
((xs.findFinIdx? p).map (Fin.castLE (by simp))).or
((ys.findFinIdx? p).map (Fin.natAdd xs.size) |>.map (Fin.cast (by simp))) := by
simp only [findFinIdx?_eq_pmap_findIdx?, findIdx?_append, Option.pmap_or]
split <;> rename_i h _
· simp [h, Option.pmap_map, Option.map_pmap, Nat.add_comm]
· simp [h]
@[simp]
theorem isSome_findFinIdx? {xs : Array α} {p : α Bool} :
(xs.findFinIdx? p).isSome = xs.any p := by

View File

@@ -4,6 +4,8 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
module
prelude
import Init.Data.Array.Basic

View File

@@ -3,6 +3,8 @@ Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
module
prelude
import Init.Data.Array.Lemmas
import Init.Data.List.Nat.InsertIdx

View File

@@ -3,6 +3,8 @@ Copyright (c) 2018 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
module
prelude
import Init.Data.Array.Basic

File diff suppressed because it is too large Load Diff

View File

@@ -3,6 +3,8 @@ Copyright (c) 2024 Lean FRO. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Kim Morrison
-/
module
prelude
import Init.Data.Array.Lex.Basic
import Init.Data.Array.Lex.Lemmas

View File

@@ -3,6 +3,8 @@ Copyright (c) 2024 Lean FRO. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Kim Morrison
-/
module
prelude
import Init.Data.Array.Basic
import Init.Data.Nat.Lemmas

View File

@@ -3,6 +3,8 @@ Copyright (c) 2024 Lean FRO. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Kim Morrison
-/
module
prelude
import Init.Data.Array.Lemmas
import Init.Data.List.Lex
@@ -14,11 +16,11 @@ namespace Array
/-! ### Lexicographic ordering -/
@[simp] theorem _root_.List.lt_toArray [LT α] {l₁ l₂ : List α} : l₁.toArray < l₂.toArray l₁ < l₂ := Iff.rfl
@[simp] theorem _root_.List.le_toArray [LT α] {l₁ l₂ : List α} : l₁.toArray l₂.toArray l₁ l₂ := Iff.rfl
@[simp, grind =] theorem _root_.List.lt_toArray [LT α] {l₁ l₂ : List α} : l₁.toArray < l₂.toArray l₁ < l₂ := Iff.rfl
@[simp, grind =] theorem _root_.List.le_toArray [LT α] {l₁ l₂ : List α} : l₁.toArray l₂.toArray l₁ l₂ := Iff.rfl
@[simp] theorem lt_toList [LT α] {xs ys : Array α} : xs.toList < ys.toList xs < ys := Iff.rfl
@[simp] theorem le_toList [LT α] {xs ys : Array α} : xs.toList ys.toList xs ys := Iff.rfl
@[simp, grind =] theorem lt_toList [LT α] {xs ys : Array α} : xs.toList < ys.toList xs < ys := Iff.rfl
@[simp, grind =] theorem le_toList [LT α] {xs ys : Array α} : xs.toList ys.toList xs ys := Iff.rfl
protected theorem not_lt_iff_ge [LT α] {l₁ l₂ : List α} : ¬ l₁ < l₂ l₂ l₁ := Iff.rfl
protected theorem not_le_iff_gt [DecidableEq α] [LT α] [DecidableLT α] {l₁ l₂ : List α} :
@@ -45,7 +47,7 @@ private theorem cons_lex_cons [BEq α] {lt : αα → Bool} {a b : α} {xs
cases a == b <;> simp
· simp
@[simp] theorem _root_.List.lex_toArray [BEq α] {lt : α α Bool} {l₁ l₂ : List α} :
@[simp, grind =] theorem _root_.List.lex_toArray [BEq α] {lt : α α Bool} {l₁ l₂ : List α} :
l₁.toArray.lex l₂.toArray lt = l₁.lex l₂ lt := by
induction l₁ generalizing l₂ with
| nil => cases l₂ <;> simp [lex, Id.run]
@@ -55,7 +57,7 @@ private theorem cons_lex_cons [BEq α] {lt : αα → Bool} {a b : α} {xs
| cons y l₂ =>
rw [List.toArray_cons, List.toArray_cons y, cons_lex_cons, List.lex, ih]
@[simp] theorem lex_toList [BEq α] {lt : α α Bool} {xs ys : Array α} :
@[simp, grind =] theorem lex_toList [BEq α] {lt : α α Bool} {xs ys : Array α} :
xs.toList.lex ys.toList lt = xs.lex ys lt := by
cases xs <;> cases ys <;> simp
@@ -150,13 +152,13 @@ instance [DecidableEq α] [LT α] [DecidableLT α]
Std.Total (· · : Array α Array α Prop) where
total := Array.le_total
@[simp] theorem lex_eq_true_iff_lt [DecidableEq α] [LT α] [DecidableLT α]
@[simp] theorem lex_eq_true_iff_lt [BEq α] [LawfulBEq α] [LT α] [DecidableLT α]
{xs ys : Array α} : lex xs ys = true xs < ys := by
cases xs
cases ys
simp
@[simp] theorem lex_eq_false_iff_ge [DecidableEq α] [LT α] [DecidableLT α]
@[simp] theorem lex_eq_false_iff_ge [BEq α] [LawfulBEq α] [LT α] [DecidableLT α]
{xs ys : Array α} : lex xs ys = false ys xs := by
cases xs
cases ys

View File

@@ -3,6 +3,8 @@ Copyright (c) 2022 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro, Kim Morrison
-/
module
prelude
import Init.Data.Array.Lemmas
import Init.Data.Array.Attach
@@ -64,7 +66,7 @@ theorem mapFinIdx_spec {xs : Array α} {f : (i : Nat) → α → (h : i < xs.siz
@[simp] theorem getElem?_mapFinIdx {xs : Array α} {f : (i : Nat) α (h : i < xs.size) β} {i : Nat} :
(xs.mapFinIdx f)[i]? =
xs[i]?.pbind fun b h => f i b (getElem?_eq_some_iff.1 h).1 := by
xs[i]?.pbind fun b h => some <| f i b (getElem?_eq_some_iff.1 h).1 := by
simp only [getElem?_def, size_mapFinIdx, getElem_mapFinIdx]
split <;> simp_all
@@ -109,11 +111,11 @@ end Array
namespace List
@[simp] theorem mapFinIdx_toArray {l : List α} {f : (i : Nat) α (h : i < l.length) β} :
@[simp, grind =] theorem mapFinIdx_toArray {l : List α} {f : (i : Nat) α (h : i < l.length) β} :
l.toArray.mapFinIdx f = (l.mapFinIdx f).toArray := by
ext <;> simp
@[simp] theorem mapIdx_toArray {f : Nat α β} {l : List α} :
@[simp, grind =] theorem mapIdx_toArray {f : Nat α β} {l : List α} :
l.toArray.mapIdx f = (l.mapIdx f).toArray := by
ext <;> simp
@@ -130,7 +132,7 @@ namespace Array
@[deprecated getElem_zipIdx (since := "2025-01-21")]
abbrev getElem_zipWithIndex := @getElem_zipIdx
@[simp] theorem zipIdx_toArray {l : List α} {k : Nat} :
@[simp, grind =] theorem zipIdx_toArray {l : List α} {k : Nat} :
l.toArray.zipIdx k = (l.zipIdx k).toArray := by
ext i hi₁ hi₂ <;> simp [Nat.add_comm]
@@ -153,7 +155,7 @@ theorem mk_mem_zipIdx_iff_le_and_getElem?_sub {k i : Nat} {x : α} {xs : Array
/-- Variant of `mk_mem_zipIdx_iff_le_and_getElem?_sub` specialized at `k = 0`,
to avoid the inequality and the subtraction. -/
theorem mk_mem_zipIdx_iff_getElem? {x : α} {i : Nat} {xs : Array α} :
(x, i) xs.zipIdx xs[i]? = x := by
(x, i) xs.zipIdx xs[i]? = some x := by
rw [mk_mem_zipIdx_iff_le_and_getElem?_sub]
simp
@@ -452,7 +454,7 @@ end Array
namespace List
theorem mapFinIdxM_toArray [Monad m] [LawfulMonad m] {l : List α}
@[grind] theorem mapFinIdxM_toArray [Monad m] [LawfulMonad m] {l : List α}
{f : (i : Nat) α (h : i < l.length) m β} :
l.toArray.mapFinIdxM f = toArray <$> l.mapFinIdxM f := by
let rec go (i : Nat) (acc : Array β) (inv : i + acc.size = l.length) :
@@ -473,7 +475,7 @@ theorem mapFinIdxM_toArray [Monad m] [LawfulMonad m] {l : List α}
simp only [Array.mapFinIdxM, mapFinIdxM]
exact go _ #[] _
theorem mapIdxM_toArray [Monad m] [LawfulMonad m] {l : List α}
@[grind] theorem mapIdxM_toArray [Monad m] [LawfulMonad m] {l : List α}
{f : Nat α m β} :
l.toArray.mapIdxM f = toArray <$> l.mapIdxM f := by
let rec go (bs : List α) (acc : Array β) (inv : bs.length + acc.size = l.length) :

View File

@@ -3,6 +3,8 @@ 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
-/
module
prelude
import Init.Data.Array.Basic
import Init.Data.Nat.Linear

View File

@@ -3,6 +3,8 @@ Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
module
prelude
import Init.Data.Array.Lemmas
import Init.Data.Array.Attach
@@ -262,7 +264,7 @@ end Array
namespace List
theorem filterM_toArray [Monad m] [LawfulMonad m] {l : List α} {p : α m Bool} :
@[grind =] theorem filterM_toArray [Monad m] [LawfulMonad m] {l : List α} {p : α m Bool} :
l.toArray.filterM p = toArray <$> l.filterM p := by
simp only [Array.filterM, filterM, foldlM_toArray, bind_pure_comp, Functor.map_map]
conv => lhs; rw [ reverse_nil]
@@ -282,7 +284,7 @@ theorem filterM_toArray [Monad m] [LawfulMonad m] {l : List α} {p : α → m Bo
subst w
rw [filterM_toArray]
theorem filterRevM_toArray [Monad m] [LawfulMonad m] {l : List α} {p : α m Bool} :
@[grind =] theorem filterRevM_toArray [Monad m] [LawfulMonad m] {l : List α} {p : α m Bool} :
l.toArray.filterRevM p = toArray <$> l.filterRevM p := by
simp [Array.filterRevM, filterRevM]
rw [ foldlM_reverse, foldlM_toArray, Array.filterM, filterM_toArray]
@@ -294,7 +296,7 @@ theorem filterRevM_toArray [Monad m] [LawfulMonad m] {l : List α} {p : α → m
subst w
rw [filterRevM_toArray]
theorem filterMapM_toArray [Monad m] [LawfulMonad m] {l : List α} {f : α m (Option β)} :
@[grind =] theorem filterMapM_toArray [Monad m] [LawfulMonad m] {l : List α} {f : α m (Option β)} :
l.toArray.filterMapM f = toArray <$> l.filterMapM f := by
simp [Array.filterMapM, filterMapM]
conv => lhs; rw [ reverse_nil]
@@ -312,7 +314,7 @@ theorem filterMapM_toArray [Monad m] [LawfulMonad m] {l : List α} {f : α → m
subst w
rw [filterMapM_toArray]
@[simp] theorem flatMapM_toArray [Monad m] [LawfulMonad m] {l : List α} {f : α m (Array β)} :
@[simp, grind =] theorem flatMapM_toArray [Monad m] [LawfulMonad m] {l : List α} {f : α m (Array β)} :
l.toArray.flatMapM f = toArray <$> l.flatMapM (fun a => Array.toList <$> f a) := by
simp only [Array.flatMapM, bind_pure_comp, foldlM_toArray, flatMapM]
conv => lhs; arg 2; change [].reverse.flatten.toArray

View File

@@ -3,6 +3,8 @@ Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
module
prelude
import Init.Data.Array.Lemmas
import Init.Data.List.OfFn

View File

@@ -3,6 +3,8 @@ Copyright (c) 2024 Lean FRO. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
module
prelude
import Init.Data.List.Nat.Perm
import Init.Data.Array.Lemmas
@@ -20,45 +22,91 @@ open List
This is a wrapper around `List.Perm`, and for now has much less API.
For more complicated verification, use `perm_iff_toList_perm` and the `List` API.
-/
def Perm (as bs : Array α) : Prop :=
as.toList ~ bs.toList
structure Perm (as bs : Array α) : Prop where
of_toList_perm ::
toList : as.toList ~ bs.toList
@[inherit_doc] scoped infixl:50 " ~ " => Perm
theorem perm_iff_toList_perm {as bs : Array α} : as ~ bs as.toList ~ bs.toList := Iff.rfl
theorem perm_iff_toList_perm {as bs : Array α} : as ~ bs as.toList ~ bs.toList :=
Perm.toList, Perm.of_toList_perm
@[simp] theorem perm_toArray (as bs : List α) : as.toArray ~ bs.toArray as ~ bs := by
end Array
namespace List
open Array
theorem perm_iff_toArray_perm {as bs : List α} : as ~ bs as.toArray ~ bs.toArray := by
simp [perm_iff_toList_perm]
theorem Perm.of_toArray_perm {as bs : List α} : as.toArray ~ bs.toArray as ~ bs :=
perm_iff_toArray_perm.mpr
theorem Perm.toArray {as bs : List α} : as ~ bs as.toArray ~ bs.toArray :=
perm_iff_toArray_perm.mp
end List
namespace Array
open List
@[simp, refl] protected theorem Perm.refl (xs : Array α) : xs ~ xs := by
cases xs
simp
simp [perm_iff_toList_perm]
protected theorem Perm.rfl {xs : List α} : xs ~ xs := .refl _
protected theorem Perm.rfl {xs : Array α} : xs ~ xs := .refl _
theorem Perm.of_eq {xs ys : Array α} (h : xs = ys) : xs ~ ys := h .rfl
@[symm]
protected theorem Perm.symm {xs ys : Array α} (h : xs ~ ys) : ys ~ xs := by
cases xs; cases ys
simp only [perm_toArray] at h
simpa using h.symm
simp only [perm_iff_toList_perm] at h
simpa [perm_iff_toList_perm] using h.symm
protected theorem Perm.trans {xs ys zs : Array α} (h₁ : xs ~ ys) (h₂ : ys ~ zs) : xs ~ zs := by
cases xs; cases ys; cases zs
simp only [perm_toArray] at h₁ h₂
simpa using h₁.trans h₂
simp only [perm_iff_toList_perm] at h₁ h₂
simpa [perm_iff_toList_perm] using h₁.trans h₂
instance : Trans (Perm (α := α)) (Perm (α := α)) (Perm (α := α)) where
trans h₁ h₂ := Perm.trans h₁ h₂
theorem perm_comm {xs ys : Array α} : xs ~ ys ys ~ xs := Perm.symm, Perm.symm
theorem Perm.push (x y : α) {xs ys : Array α} (p : xs ~ ys) :
theorem Perm.size_eq {xs ys : Array α} (p : xs ~ ys) : xs.size = ys.size := by
cases xs; cases ys
simp only [perm_iff_toList_perm] at p
simpa using p.length_eq
@[deprecated Perm.size_eq (since := "2025-04-17")]
abbrev Perm.length_eq := @Perm.size_eq
theorem Perm.mem_iff {a : α} {xs ys : Array α} (p : xs ~ ys) : a xs a ys := by
rcases xs with xs
rcases ys with ys
simp only [perm_iff_toList_perm] at p
simpa using p.mem_iff
theorem Perm.append {xs ys as bs : Array α} (p₁ : xs ~ ys) (p₂ : as ~ bs) :
xs ++ as ~ ys ++ bs := by
cases xs; cases ys; cases as; cases bs
simp only [append_toArray, perm_iff_toList_perm] at p₁ p₂
exact p₁.append p₂
theorem Perm.push (x : α) {xs ys : Array α} (p : xs ~ ys) :
xs.push x ~ ys.push x := by
rw [push_eq_append_singleton]
exact p.append .rfl
theorem Perm.push_comm (x y : α) {xs ys : Array α} (p : xs ~ ys) :
(xs.push x).push y ~ (ys.push y).push x := by
cases xs; cases ys
simp only [perm_toArray] at p
simp only [push_toArray, List.append_assoc, singleton_append, perm_toArray]
exact p.append (Perm.swap' _ _ Perm.nil)
simp only [perm_iff_toList_perm] at p
simp only [push_toArray, List.append_assoc, singleton_append, perm_iff_toList_perm]
exact p.append (Perm.swap ..)
theorem swap_perm {xs : Array α} {i j : Nat} (h₁ : i < xs.size) (h₂ : j < xs.size) :
xs.swap i j ~ xs := by
@@ -70,10 +118,10 @@ namespace Perm
set_option linter.indexVariables false in
theorem extract {xs ys : Array α} (h : xs ~ ys) {lo hi : Nat}
(wlo : i, i < lo xs[i]? = ys[i]?) (whi : i, hi i xs[i]? = ys[i]?) :
(xs.extract lo (hi + 1)) ~ (ys.extract lo (hi + 1)) := by
xs.extract lo hi ~ ys.extract lo hi := by
rcases xs with xs
rcases ys with ys
simp_all only [perm_toArray, List.getElem?_toArray, List.extract_toArray,
simp_all only [perm_iff_toList_perm, List.getElem?_toArray, List.extract_toArray,
List.extract_eq_drop_take]
apply List.Perm.take_of_getElem? (w := fun i h => by simpa using whi (lo + i) (by omega))
apply List.Perm.drop_of_getElem? (w := wlo)

View File

@@ -1,68 +1,9 @@
/-
Copyright (c) 2019 Microsoft Corporation. All rights reserved.
Copyright (c) 2024 Lean FRO. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
Authors: Kim Morrison
-/
module
prelude
import Init.Data.Vector.Basic
import Init.Data.Ord
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
-- We do not enable `linter.indexVariables` because it is helpful to name index variables `lo`, `mid`, `hi`, etc.
namespace Array
private def qpartition {n} (as : Vector α n) (lt : α α Bool) (lo hi : Nat)
(hlo : lo < n := by omega) (hhi : hi < n := by omega) : {n : Nat // lo n} × Vector α n :=
let mid := (lo + hi) / 2
let as := if lt as[mid] as[lo] then as.swap lo mid else as
let as := if lt as[hi] as[lo] then as.swap lo hi else as
let as := if lt as[mid] as[hi] then as.swap mid hi else as
let pivot := as[hi]
let rec loop (as : Vector α n) (i j : Nat)
(ilo : lo i := by omega) (jh : j < n := by omega) (w : i j := by omega) :=
if h : j < hi then
if lt as[j] pivot then
loop (as.swap i j) (i+1) (j+1)
else
loop as i (j+1)
else
(i, ilo, as.swap i hi)
loop as lo lo
/--
Sorts an array using the Quicksort algorithm.
The optional parameter `lt` specifies an ordering predicate. It defaults to `LT.lt`, which must be
decidable to be used for sorting. Use `Array.qsortOrd` to sort the array according to the `Ord α`
instance.
The optional parameters `low` and `high` delimit the region of the array that is sorted. Both are
inclusive, and default to sorting the entire array.
-/
@[inline] def qsort (as : Array α) (lt : α α Bool := by exact (· < ·))
(low := 0) (high := as.size - 1) : Array α :=
let rec @[specialize] sort {n} (as : Vector α n) (lo hi : Nat)
(hlo : lo < n := by omega) (hhi : hi < n := by omega) :=
if h₁ : lo < hi then
let mid, hmid, as := qpartition as lt lo hi
if h₂ : mid hi then
as
else
sort (sort as lo mid) (mid+1) hi
else as
if h : as.size = 0 then
as
else
let low := min low (as.size - 1)
let high := min high (as.size - 1)
sort as, rfl low high |>.toArray
set_option linter.unusedVariables.funArgs false in
/--
Sorts an array using the Quicksort algorithm, using `Ord.compare` to compare elements.
-/
def qsortOrd [ord : Ord α] (xs : Array α) : Array α :=
xs.qsort fun x y => compare x y |>.isLT
end Array
import Init.Data.Array.QSort.Basic

View File

@@ -0,0 +1,81 @@
/-
Copyright (c) 2019 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
module
prelude
import Init.Data.Vector.Basic
import Init.Data.Ord
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
-- We do not enable `linter.indexVariables` because it is helpful to name index variables `lo`, `mid`, `hi`, etc.
namespace Array
/--
Internal implementation of `Array.qsort`.
`qpartition as lt lo hi hlo hhi` returns a pair `(⟨m, h₁, h₂⟩, as')` where
`as'` is a permutation of `as` and `m` is a number such that:
- `lo ≤ m`
- `m < n`
- `∀ i, lo ≤ i → i < m → lt as[i] as[m]`
- `∀ j, m < j → j < hi → !lt as[j] as[m]`
It does so by first swapping the elements at indices `lo`, `mid := (lo + hi) / 2`, and `hi`
if necessary so that the middle (pivot) element is at index `hi`.
We then iterate from `j = lo` to `j = hi`, with a pointer `i` starting at `lo`, and
swapping each element which is less than the pivot to position `i`, and then incrementing `i`.
-/
def qpartition {n} (as : Vector α n) (lt : α α Bool) (lo hi : Nat)
(hlo : lo < n := by omega) (hhi : hi < n := by omega) : {m : Nat // lo m m < n} × Vector α n :=
let mid := (lo + hi) / 2
let as := if lt as[mid] as[lo] then as.swap lo mid else as
let as := if lt as[hi] as[lo] then as.swap lo hi else as
let as := if lt as[mid] as[hi] then as.swap mid hi else as
let pivot := as[hi]
let rec loop (as : Vector α n) (i j : Nat)
(ilo : lo i := by omega) (jh : j < n := by omega) (w : i j := by omega) :=
if h : j < hi then
if lt as[j] pivot then
loop (as.swap i j) (i+1) (j+1)
else
loop as i (j+1)
else
(i, ilo, by omega, as.swap i hi)
loop as lo lo
/--
In-place quicksort.
`qsort as lt low high` sorts the subarray `as[low:high+1]` in-place using `lt` to compare elements.
-/
@[inline] def qsort (as : Array α) (lt : α α Bool := by exact (· < ·))
(low := 0) (high := as.size - 1) : Array α :=
let rec @[specialize] sort {n} (as : Vector α n) (lo hi : Nat)
(hlo : lo < n := by omega) (hhi : hi < n := by omega) :=
if h₁ : lo < hi then
let mid, hmid, as := qpartition as lt lo hi
if h₂ : mid hi then
as
else
sort (sort as lo mid) (mid+1) hi
else as
if h : as.size = 0 then
as
else
let low := min low (as.size - 1)
let high := min high (as.size - 1)
sort as.toVector low high |>.toArray
set_option linter.unusedVariables.funArgs false in
/--
Sort an array using `compare` to compare elements.
-/
def qsortOrd [ord : Ord α] (xs : Array α) : Array α :=
xs.qsort fun x y => compare x y |>.isLT
end Array

View File

@@ -3,6 +3,8 @@ Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
module
prelude
import Init.Data.Array.Lemmas
import Init.Data.Array.OfFn

View File

@@ -3,6 +3,8 @@ Copyright (c) 2020 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura, Mario Carneiro
-/
module
prelude
import Init.Tactics

View File

@@ -3,6 +3,8 @@ Copyright (c) 2020 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
module
prelude
import Init.Data.Array.Basic
@@ -462,8 +464,12 @@ instance : Append (Subarray α) where
let a := x.toArray ++ y.toArray
a.toSubarray 0 a.size
/-- `Subarray` representation. -/
protected def Subarray.repr [Repr α] (s : Subarray α) : Std.Format :=
repr s.toArray ++ ".toSubarray"
instance [Repr α] : Repr (Subarray α) where
reprPrec s _ := repr s.toArray ++ ".toSubarray"
reprPrec s _ := Subarray.repr s
instance [ToString α] : ToString (Subarray α) where
toString s := toString s.toArray

View File

@@ -4,6 +4,8 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: David Thrane Christiansen
-/
module
prelude
import Init.Data.Array.Basic
import Init.Data.Array.Subarray

View File

@@ -3,6 +3,8 @@ Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel
-/
module
prelude
import Init.Data.Array.Lemmas
import Init.Data.List.Nat.TakeDrop

View File

@@ -3,6 +3,8 @@ Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
module
prelude
import Init.Data.Array.TakeDrop
import Init.Data.List.Zip
@@ -323,7 +325,7 @@ theorem map_zipWithAll {δ : Type _} {f : α → β} {g : Option γ → Option
simp [List.map_zipWithAll]
@[simp] theorem zipWithAll_replicate {a : α} {b : β} {n : Nat} :
zipWithAll f (replicate n a) (replicate n b) = replicate n (f a b) := by
zipWithAll f (replicate n a) (replicate n b) = replicate n (f (some a) (some b)) := by
simp [ List.toArray_replicate]
@[deprecated zipWithAll_replicate (since := "2025-03-18")]

View File

@@ -3,6 +3,8 @@ Copyright (c) 2022 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro, Markus Himmel
-/
module
prelude
import Init.Data.Bool
@@ -19,21 +21,9 @@ class PartialEquivBEq (α) [BEq α] : Prop where
/-- Transitivity for `BEq`. If `a == b` and `b == c` then `a == c`. -/
trans : (a : α) == b b == c a == c
/-- `ReflBEq α` says that the `BEq` implementation is reflexive. -/
class ReflBEq (α) [BEq α] : Prop where
/-- Reflexivity for `BEq`. -/
refl : (a : α) == a
/-- `EquivBEq` says that the `BEq` implementation is an equivalence relation. -/
class EquivBEq (α) [BEq α] : Prop extends PartialEquivBEq α, ReflBEq α
@[simp]
theorem BEq.refl [BEq α] [ReflBEq α] {a : α} : a == a :=
ReflBEq.refl
theorem beq_of_eq [BEq α] [ReflBEq α] {a b : α} : a = b a == b
| rfl => BEq.refl
theorem BEq.symm [BEq α] [PartialEquivBEq α] {a b : α} : a == b b == a :=
PartialEquivBEq.symm
@@ -66,6 +56,5 @@ theorem BEq.neq_of_beq_of_neq [BEq α] [PartialEquivBEq α] {a b c : α} :
fun h₁ h₂ => Bool.eq_false_iff.2 fun h₃ => Bool.eq_false_iff.1 h₂ (BEq.trans (BEq.symm h₁) h₃)
instance (priority := low) [BEq α] [LawfulBEq α] : EquivBEq α where
refl := LawfulBEq.rfl
symm h := beq_iff_eq.2 <| Eq.symm <| beq_iff_eq.1 h
trans hab hbc := beq_iff_eq.2 <| (beq_iff_eq.1 hab).trans <| beq_iff_eq.1 hbc

View File

@@ -3,6 +3,8 @@ Copyright (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
module
prelude
import Init.Data.Nat.Basic
import Init.Data.Fin.Basic

View File

@@ -3,6 +3,8 @@ Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
module
prelude
import Init.Data.BitVec.Basic
import Init.Data.BitVec.Bitblast

View File

@@ -3,6 +3,8 @@ Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Joe Hendrix, Wojciech Nawrocki, Leonardo de Moura, Mario Carneiro, Alex Keizer, Harun Khan, Abdalrhman M Mohamed, Siddharth Bhat
-/
module
prelude
import Init.Data.Fin.Basic
import Init.Data.Nat.Bitwise.Lemmas
@@ -197,7 +199,13 @@ protected def toHex {n : Nat} (x : BitVec n) : String :=
let t := (List.replicate ((n+3) / 4 - s.length) '0').asString
t ++ s
instance : Repr (BitVec n) where reprPrec a _ := "0x" ++ (a.toHex : Std.Format) ++ "#" ++ repr n
/-- `BitVec` representation. -/
protected def BitVec.repr (a : BitVec n) : Std.Format :=
"0x" ++ (a.toHex : Std.Format) ++ "#" ++ repr n
instance : Repr (BitVec n) where
reprPrec a _ := BitVec.repr a
instance : ToString (BitVec n) where toString a := toString (repr a)
end repr_toString
@@ -767,6 +775,15 @@ SMT-Lib name: `bvnego`.
def negOverflow {w : Nat} (x : BitVec w) : Bool :=
x.toInt == - 2 ^ (w - 1)
/--
Checks whether the signed division of `x` by `y` results in overflow.
For BitVecs `x` and `y` with nonzero width, this only happens if `x = intMin` and `y = allOnes w`.
SMT-LIB name: `bvsdivo`.
-/
def sdivOverflow {w : Nat} (x y : BitVec w) : Bool :=
(2 ^ (w - 1) x.toInt / y.toInt) || (x.toInt / y.toInt < - 2 ^ (w - 1))
/- ### reverse -/
/-- Reverses the bits in a bitvector. -/

View File

@@ -3,6 +3,8 @@ Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Joe Hendrix, Wojciech Nawrocki, Leonardo de Moura, Mario Carneiro, Alex Keizer, Harun Khan, Abdalrhman M Mohamed
-/
module
prelude
import Init.Data.Fin.Basic

View File

@@ -3,6 +3,8 @@ Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Harun Khan, Abdalrhman M Mohamed, Joe Hendrix, Siddharth Bhat
-/
module
prelude
import Init.Data.BitVec.Folds
import Init.Data.Nat.Mod
@@ -566,7 +568,7 @@ theorem ult_eq_msb_of_msb_neq {x y : BitVec w} (h : x.msb ≠ y.msb) :
theorem slt_eq_not_ult_of_msb_neq {x y : BitVec w} (h : x.msb y.msb) :
x.slt y = !x.ult y := by
simp only [BitVec.slt, toInt_eq_msb_cond, Bool.eq_not_of_ne h, ult_eq_msb_of_msb_neq h]
cases y.msb <;> (simp; omega)
cases y.msb <;> (simp [-Int.natCast_pow]; omega)
theorem slt_eq_ult {x y : BitVec w} :
x.slt y = (x.msb != y.msb).xor (x.ult y) := by
@@ -1332,7 +1334,7 @@ theorem saddOverflow_eq {w : Nat} (x y : BitVec w) :
simp only [ decide_or, msb_eq_toInt, decide_beq_decide, toInt_add, decide_not, decide_and,
decide_eq_decide]
rw_mod_cast [Int.bmod_neg_iff (by omega) (by omega)]
simp
simp only [Nat.add_one_sub_one, ge_iff_le]
omega
theorem usubOverflow_eq {w : Nat} (x y : BitVec w) :
@@ -1356,9 +1358,41 @@ theorem negOverflow_eq {w : Nat} (x : BitVec w) :
rcases w with _|w
· simp [toInt_of_zero_length, Int.min_eq_right]
· suffices - 2 ^ w = (intMin (w + 1)).toInt by simp [beq_eq_decide_eq, toInt_inj, this]
simp only [toInt_intMin, Nat.add_one_sub_one, Int.ofNat_emod, Int.neg_inj]
simp only [toInt_intMin, Nat.add_one_sub_one, Int.natCast_emod, Int.neg_inj]
rw_mod_cast [Nat.mod_eq_of_lt (by simp [Nat.pow_lt_pow_succ])]
/--
Prove that signed division `x.toInt / y.toInt` only overflows when `x = intMin w` and `y = allOnes w` (for `0 < w`).
-/
theorem sdivOverflow_eq {w : Nat} (x y : BitVec w) :
(sdivOverflow x y) = (decide (0 < w) && (x = intMin w) && (y = allOnes w)) := by
rcases w with _|w
· simp [sdivOverflow, of_length_zero]
· have yle := le_two_mul_toInt (x := y)
have ylt := two_mul_toInt_lt (x := y)
-- if y = allOnes (w + 1), thus y.toInt = -1,
-- the division overflows iff x = intMin (w + 1), as for negation
by_cases hy : y = allOnes (w + 1)
· simp [sdivOverflow_eq_negOverflow_of_eq_allOnes, negOverflow_eq, hy, beq_eq_decide_eq]
· simp only [sdivOverflow, hy, bool_to_prop]
have := BitVec.neg_two_pow_le_toInt_ediv (x := x) (y := y)
simp only [Nat.add_one_sub_one] at this
by_cases hx : 0 x.toInt
· by_cases hy' : 0 y.toInt
· have := BitVec.toInt_ediv_toInt_lt_of_nonneg_of_nonneg
(x := x) (y := y) (by omega) (by omega)
simp only [Nat.add_one_sub_one] at this; simp; omega
· have := BitVec.toInt_ediv_toInt_nonpos_of_nonneg_of_nonpos
(x := x) (y := y) (by omega) (by omega)
simp; omega
· by_cases hy' : 0 y.toInt
· have := BitVec.toInt_ediv_toInt_nonpos_of_nonpos_of_nonneg
(x := x) (y := y) (by omega) (by omega)
simp; omega
· have := BitVec.toInt_ediv_toInt_lt_of_nonpos_of_lt_neg_one
(x := x) (y := y) (by omega) (by rw [ toInt_inj, toInt_allOnes] at hy; omega)
simp only [Nat.add_one_sub_one] at this; simp; omega
theorem umulOverflow_eq {w : Nat} (x y : BitVec w) :
umulOverflow x y =
(0 < w && BitVec.twoPow (w * 2) w x.zeroExtend (w * 2) * y.zeroExtend (w * 2)) := by
@@ -1456,7 +1490,6 @@ theorem udiv_intMin_of_msb_false {x : BitVec w} (h : x.msb = false) :
have wpos : 0 < w := by omega
have := Nat.two_pow_pos (w-1)
simp [toNat_eq, wpos]
rw [Nat.div_eq_zero_iff_lt (by omega)]
exact toNat_lt_of_msb_false h
theorem sdiv_intMin {x : BitVec w} :
@@ -1468,7 +1501,6 @@ theorem sdiv_intMin {x : BitVec w} :
by_cases h : x = intMin w
· subst h
simp
omega
· simp only [sdiv_eq, msb_intMin, show 0 < w by omega, h]
have := Nat.two_pow_pos (w-1)
by_cases hx : x.msb
@@ -1541,7 +1573,7 @@ theorem sdiv_ne_intMin_of_ne_intMin {x y : BitVec w} (h : x ≠ intMin w) :
theorem toInt_eq_neg_toNat_neg_of_msb_true {x : BitVec w} (h : x.msb = true) :
x.toInt = -((-x).toNat) := by
simp only [toInt_eq_msb_cond, h, reduceIte, toNat_neg, Int.ofNat_emod]
simp only [toInt_eq_msb_cond, h, reduceIte, toNat_neg, Int.natCast_emod]
norm_cast
rw [Nat.mod_eq_of_lt]
· omega
@@ -1571,7 +1603,8 @@ theorem intMin_udiv_eq_intMin_iff (x : BitVec w) :
· intro h
rw [ toInt_inj, toInt_eq_msb_cond] at h
have : (intMin w / x).msb = false := by simp [msb_udiv, msb_intMin, wpos, hx]
simp [this, wpos, toInt_intMin] at h
simp only [this, false_eq_true, reduceIte, toNat_udiv, toNat_intMin, wpos,
Nat.two_pow_pred_mod_two_pow, Int.natCast_ediv, toInt_intMin] at h
omega
· intro h
subst h
@@ -1612,11 +1645,11 @@ theorem toInt_sdiv_of_ne_or_ne (a b : BitVec w) (h : a ≠ intMin w b ≠ -1
Nat.two_pow_pred_mod_two_pow, Int.neg_tdiv, Int.neg_neg]
rw [Int.tdiv_self (by omega)]
· by_cases ha_nonneg : 0 a.toInt
· simp [Int.tdiv_eq_zero_of_lt ha_nonneg (by norm_cast at *), ha_intMin]
· simp [Int.tdiv_eq_zero_of_lt ha_nonneg (by norm_cast at *), ha_intMin, -Int.natCast_pow]
· simp only [ne_eq, toInt_inj, toInt_intMin, wpos, Nat.two_pow_pred_mod_two_pow] at h
rw [ Int.neg_tdiv, Int.tdiv_eq_zero_of_lt (by omega)]
· simp [ha_intMin]
· simp [wpos, toInt_ne, toInt_intMin] at ha_intMin
· simp [wpos, toInt_ne, toInt_intMin, -Int.natCast_pow] at ha_intMin
omega
· by_cases ha : a.msb <;> by_cases hb : b.msb
@@ -1635,7 +1668,7 @@ theorem toInt_sdiv_of_ne_or_ne (a b : BitVec w) (h : a ≠ intMin w b ≠ -1
(a.sdiv b).toInt = -((-a).toNat / b.toNat) := by
simp only [sdiv_eq, ha, hb, udiv_eq]
rw [toInt_eq_neg_toNat_neg_of_nonpos]
· rw [neg_neg, toNat_udiv, toNat_neg, Int.ofNat_emod, Int.neg_inj]
· rw [neg_neg, toNat_udiv, toNat_neg, Int.natCast_emod, Int.neg_inj]
norm_cast
· rw [neg_eq_zero_iff]
by_cases h' : -a / b = 0#w
@@ -1778,9 +1811,23 @@ theorem extractLsb'_mul {w len} {x y : BitVec w} (hlen : len ≤ w) :
(x * y).extractLsb' 0 len = (x.extractLsb' 0 len) * (y.extractLsb' 0 len) := by
simp [ setWidth_eq_extractLsb' hlen, setWidth_mul _ _ hlen]
/-- Adding bitvectors that are zero in complementary positions equals concatenation.
We add a `no_index` annotation to `HAppend.hAppend` such that the width `v + w`
does not act as a key in the discrimination tree.
This is important to allow matching, when the type of the result of append
`x : BitVec 3` and `y : BitVec 4` has been reduced to `x ++ y : BitVec 7`.
-/
theorem append_zero_add_zero_append {v w : Nat} {x : BitVec v} {y : BitVec w} :
(HAppend.hAppend (γ := BitVec (no_index _)) x 0#w) +
(HAppend.hAppend (γ := BitVec (no_index _)) 0#v y)
= x ++ y := by
rw [add_eq_or_of_and_eq_zero] <;> ext i <;> simp
/-- Adding bitvectors that are zero in complementary positions equals concatenation. -/
theorem append_add_append_eq_append {v w : Nat} {x : BitVec v} {y : BitVec w} :
(x ++ 0#w) + (0#v ++ y) = x ++ y := by
theorem zero_append_add_append_zero {v w : Nat} {x : BitVec v} {y : BitVec w} :
(HAppend.hAppend (γ := BitVec (no_index _)) 0#v y) +
(HAppend.hAppend (γ := BitVec (no_index _)) x 0#w)
= x ++ y := by
rw [add_eq_or_of_and_eq_zero] <;> ext i <;> simp
/-- Heuristically, `y <<< x` is much larger than `x`,
@@ -1796,4 +1843,10 @@ theorem add_shiftLeft_eq_or_shiftLeft {x y : BitVec w} :
have : i < 2^i := by exact Nat.lt_two_pow_self
omega
/-- Heuristically, `y <<< x` is much larger than `x`,
and hence low bits of `y <<< x`. Thus, `(y <<< x) + x = (y <<< x) ||| x.` -/
theorem shiftLeft_add_eq_shiftLeft_or {x y : BitVec w} :
(y <<< x) + x = (y <<< x) ||| x := by
rw [BitVec.add_comm, add_shiftLeft_eq_or_shiftLeft, or_comm]
end BitVec

View File

@@ -3,6 +3,8 @@ Copyright (c) 2023 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Joe Hendrix, Harun Khan
-/
module
prelude
import Init.Data.BitVec.Lemmas
import Init.Data.Nat.Lemmas

View File

@@ -4,6 +4,8 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Joe Hendrix, Harun Khan, Alex Keizer, Abdalrhman M Mohamed, Siddharth Bhat
-/
module
prelude
import Init.Data.Bool
import Init.Data.BitVec.Basic
@@ -21,6 +23,9 @@ set_option linter.missingDocs true
namespace BitVec
@[simp] theorem mk_zero : BitVec.ofFin (w := w) 0, h = 0#w := rfl
@[simp] theorem ofNatLT_zero : BitVec.ofNatLT (w := w) 0 h = 0#w := rfl
@[simp] theorem getLsbD_ofFin (x : Fin (2^n)) (i : Nat) :
getLsbD (BitVec.ofFin x) i = x.val.testBit i := rfl
@@ -68,6 +73,9 @@ theorem getElem?_eq_some_iff {l : BitVec w} : l[n]? = some a ↔ ∃ h : n < w,
· simp_all
· simp; omega
theorem getElem_of_getElem? {l : BitVec w} : l[n]? = some a h : n < w, l[n] = a :=
getElem?_eq_some_iff.mp
set_option linter.missingDocs false in
@[deprecated getElem?_eq_some_iff (since := "2025-02-17")]
abbrev getElem?_eq_some := @getElem?_eq_some_iff
@@ -133,6 +141,8 @@ theorem toNat_ne_iff_ne {n} {x y : BitVec n} : x.toNat ≠ y.toNat ↔ x ≠ y :
@[bitvec_to_nat] theorem toNat_eq {x y : BitVec n} : x = y x.toNat = y.toNat :=
Iff.intro (congrArg BitVec.toNat) eq_of_toNat_eq
theorem toNat_inj {x y : BitVec n} : x.toNat = y.toNat x = y := toNat_eq.symm
@[bitvec_to_nat] theorem toNat_ne {x y : BitVec n} : x y x.toNat y.toNat := by
rw [Ne, toNat_eq]
@@ -449,7 +459,7 @@ theorem getLsbD_ofNat (n : Nat) (x : Nat) (i : Nat) :
@[simp] theorem sub_add_bmod_cancel {x y : BitVec w} :
((((2 ^ w : Nat) - y.toNat) : Int) + x.toNat).bmod (2 ^ w) =
((x.toNat : Int) - y.toNat).bmod (2 ^ w) := by
rw [Int.sub_eq_add_neg, Int.add_assoc, Int.add_comm, Int.bmod_add_cancel, Int.add_comm,
rw [Int.sub_eq_add_neg, Int.add_assoc, Int.add_comm, Int.add_bmod_right, Int.add_comm,
Int.sub_eq_add_neg]
private theorem lt_two_pow_of_le {x m n : Nat} (lt : x < 2 ^ m) (le : m n) : x < 2 ^ n :=
@@ -508,6 +518,10 @@ theorem getElem_ofBool {b : Bool} {h : i < 1}: (ofBool b)[i] = b := by
· rintro rfl
simp
/-- `0#w = 1#w` iff the width is zero. -/
@[simp] theorem zero_eq_one_iff (w : Nat) : (0#w = 1#w) (w = 0) := by
rw [ one_eq_zero_iff, eq_comm]
/-! ### msb -/
@[simp] theorem msb_zero : (0#w).msb = false := by simp [BitVec.msb, getMsbD]
@@ -610,10 +624,10 @@ theorem toInt_eq_toNat_bmod (x : BitVec n) : x.toInt = Int.bmod x.toNat (2^n) :=
simp only [toInt_eq_toNat_cond]
split
next g =>
rw [Int.bmod_pos] <;> simp only [Int.ofNat_emod, toNat_mod_cancel]
rw [Int.bmod_pos] <;> simp only [Int.natCast_emod, toNat_mod_cancel]
omega
next g =>
rw [Int.bmod_neg] <;> simp only [Int.ofNat_emod, toNat_mod_cancel]
rw [Int.bmod_neg] <;> simp only [Int.natCast_emod, toNat_mod_cancel]
omega
theorem toInt_neg_of_msb_true {x : BitVec w} (h : x.msb = true) : x.toInt < 0 := by
@@ -629,12 +643,12 @@ theorem toInt_nonneg_of_msb_false {x : BitVec w} (h : x.msb = false) : 0 ≤ x.t
@[simp] theorem toInt_one_of_lt {w : Nat} (h : 1 < w) : (1#w).toInt = 1 := by
rw [toInt_eq_msb_cond]
simp only [msb_one, show w 1 by omega, decide_false, Bool.false_eq_true, reduceIte,
toNat_ofNat, Int.ofNat_emod]
toNat_ofNat, Int.natCast_emod]
norm_cast
apply Nat.mod_eq_of_lt
apply Nat.one_lt_two_pow (by omega)
/-- Prove equality of bitvectors in terms of nat operations. -/
/-- Prove equality of bitvectors in terms of integer operations. -/
theorem eq_of_toInt_eq {x y : BitVec n} : x.toInt = y.toInt x = y := by
intro eq
simp only [toInt_eq_toNat_cond] at eq
@@ -655,20 +669,19 @@ theorem toInt_ne {x y : BitVec n} : x.toInt ≠ y.toInt ↔ x ≠ y := by
unfold BitVec.ofInt
simp
theorem toInt_ofNat {n : Nat} (x : Nat) :
(BitVec.ofNat n x).toInt = (x : Int).bmod (2^n) := by
simp [toInt_eq_toNat_bmod]
theorem toInt_ofNat {n : Nat} (x : Nat) : (BitVec.ofNat n x).toInt = (x : Int).bmod (2^n) := by
simp [toInt_eq_toNat_bmod, -Int.natCast_pow]
@[simp] theorem toInt_ofInt {n : Nat} (i : Int) :
(BitVec.ofInt n i).toInt = i.bmod (2^n) := by
have _ := Nat.two_pow_pos n
have p : 0 i % (2^n : Nat) := by omega
simp [toInt_eq_toNat_bmod, Int.toNat_of_nonneg p]
simp [toInt_eq_toNat_bmod, Int.toNat_of_nonneg p, -Int.natCast_pow]
theorem toInt_ofInt_eq_self {w : Nat} (hw : 0 < w) {n : Int}
(h : -2 ^ (w - 1) n) (h' : n < 2 ^ (w - 1)) : (BitVec.ofInt w n).toInt = n := by
have hw : w = (w - 1) + 1 := by omega
rw [toInt_ofInt, Int.bmod_eq_self_of_le] <;> (rw [hw]; simp [Int.natCast_pow]; omega)
rw [toInt_ofInt, Int.bmod_eq_of_le] <;> (rw [hw]; simp [Int.natCast_pow]; omega)
@[simp] theorem ofInt_natCast (w n : Nat) :
BitVec.ofInt w (n : Int) = BitVec.ofNat w n := rfl
@@ -676,16 +689,13 @@ theorem toInt_ofInt_eq_self {w : Nat} (hw : 0 < w) {n : Int}
@[simp] theorem ofInt_ofNat (w n : Nat) :
BitVec.ofInt w (no_index (OfNat.ofNat n)) = BitVec.ofNat w (OfNat.ofNat n) := rfl
@[simp] theorem ofInt_toInt {x : BitVec w} : BitVec.ofInt w x.toInt = x := by
by_cases h : 2 * x.toNat < 2^w <;> ext <;> simp [getLsbD_eq_getElem, getLsbD, h, BitVec.toInt]
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
simp only [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
simp only [toInt_eq_toNat_cond]; omega
theorem eq_zero_or_eq_one (a : BitVec 1) : a = 0#1 a = 1#1 := by
obtain a, ha := a
@@ -777,6 +787,12 @@ theorem le_toInt {w : Nat} (x : BitVec w) : -2 ^ (w - 1) ≤ x.toInt := by
rw [(show w = w - 1 + 1 by omega), Int.pow_succ] at this
omega
@[simp] theorem ofInt_toInt {x : BitVec w} : BitVec.ofInt w x.toInt = x := by
apply eq_of_toInt_eq
rw [toInt_ofInt, Int.bmod_eq_of_le_mul_two]
· simpa [Int.mul_comm _ 2] using le_two_mul_toInt
· simpa [Int.mul_comm _ 2] using two_mul_toInt_lt
/-! ### sle/slt -/
/--
@@ -795,7 +811,7 @@ theorem slt_zero_iff_msb_cond {x : BitVec w} : x.slt 0#w ↔ x.msb = true := by
omega /- Can't have `x.toInt` which is equal to `x.toNat` be strictly less than zero -/
· intros h
simp only [h, reduceIte] at this
simp [BitVec.slt, this]
simp only [BitVec.slt, this, toInt_zero, decide_eq_true_eq]
omega
theorem slt_zero_eq_msb {w : Nat} {x : BitVec w} : x.slt 0#w = x.msb := by
@@ -866,7 +882,7 @@ theorem zeroExtend_eq_setWidth {v : Nat} {x : BitVec w} :
@[simp] theorem toInt_setWidth (x : BitVec w) :
(x.setWidth v).toInt = Int.bmod x.toNat (2^v) := by
simp [toInt_eq_toNat_bmod, toNat_setWidth, Int.emod_bmod]
simp [toInt_eq_toNat_bmod, toNat_setWidth, Int.emod_bmod, -Int.natCast_pow]
@[simp] theorem toFin_setWidth {x : BitVec w} :
(x.setWidth v).toFin = Fin.ofNat' (2^v) x.toNat := by
@@ -1059,7 +1075,7 @@ and the second `setWidth` is a non-trivial extension.
theorem toInt_setWidth' {m n : Nat} (p : m n) {x : BitVec m} :
(setWidth' p x).toInt = if m = n then x.toInt else x.toNat := by
split
case isTrue h => simp [h, toInt_eq_toNat_bmod]
case isTrue h => simp [h, toInt_eq_toNat_bmod, -Int.natCast_pow]
case isFalse h => rw [toInt_setWidth'_of_lt (by omega)]
@[simp] theorem toFin_setWidth' {m n : Nat} (p : m n) (x : BitVec m) :
@@ -1267,7 +1283,7 @@ theorem extractLsb'_eq_zero {x : BitVec w} {start : Nat} :
· subst h
simp
· have : 1 < 2 ^ w := by simp [h]
simp [BitVec.toInt]
simp [BitVec.toInt, -Int.natCast_pow]
omega
@[simp] theorem toFin_allOnes : (allOnes w).toFin = Fin.ofNat' (2^w) (2^w - 1) := by
@@ -1627,7 +1643,8 @@ theorem not_def {x : BitVec v} : ~~~x = allOnes v ^^^ x := rfl
@[simp] theorem toInt_not {x : BitVec w} :
(~~~x).toInt = Int.bmod (2^w - 1 - x.toNat) (2^w) := by
rw_mod_cast [BitVec.toInt, BitVec.toNat_not, Int.bmod_def]
simp [show ((2^w : Nat) : Int) - 1 - x.toNat = ((2^w - 1 - x.toNat) : Nat) by omega]
simp [show ((2^w : Nat) : Int) - 1 - x.toNat = ((2^w - 1 - x.toNat) : Nat) by omega,
-Int.natCast_pow]
rw_mod_cast [Nat.mod_eq_of_lt (by omega)]
omega
@@ -1803,7 +1820,7 @@ theorem not_xor_right {x y : BitVec w} : ~~~ (x ^^^ y) = x ^^^ ~~~ y := by
@[simp] theorem toInt_shiftLeft {x : BitVec w} :
(x <<< n).toInt = (x.toNat <<< n : Int).bmod (2^w) := by
rw [toInt_eq_toNat_bmod, toNat_shiftLeft, Nat.shiftLeft_eq]
simp
simp [-Int.natCast_pow]
@[simp] theorem toFin_shiftLeft {n : Nat} (x : BitVec w) :
(x <<< n).toFin = Fin.ofNat' (2^w) (x.toNat <<< n) := rfl
@@ -2032,7 +2049,6 @@ theorem toInt_ushiftRight_of_lt {x : BitVec w} {n : Nat} (hn : 0 < n) :
have : 2^w 2^n := Nat.pow_le_pow_of_le (by decide) (by omega)
omega
simp [this] at h
omega
/--
Unsigned shift right by at least one bit makes the interpretations of the bitvector as an `Int` or `Nat` agree,
@@ -2137,8 +2153,8 @@ theorem sshiftRight_eq_of_msb_true {x : BitVec w} {s : Nat} (h : x.msb = true) :
simp only [hxbound, reduceIte, toNat_ofInt, toNat_not, toNat_ushiftRight]
rw [ Int.subNatNat_eq_coe, Int.subNatNat_of_lt (by omega),
Nat.pred_eq_sub_one, Int.negSucc_shiftRight,
Int.emod_negSucc, Int.natAbs_ofNat, Nat.succ_eq_add_one,
Int.subNatNat_of_le (by omega), Int.toNat_ofNat, Nat.mod_eq_of_lt,
Int.emod_negSucc, Int.natAbs_natCast, Nat.succ_eq_add_one,
Int.subNatNat_of_le (by omega), Int.toNat_natCast, Nat.mod_eq_of_lt,
Nat.sub_right_comm]
omega
· rw [Nat.shiftRight_eq_div_pow]
@@ -2334,7 +2350,7 @@ theorem toInt_sshiftRight {x : BitVec w} {n : Nat} :
have := @toInt_shiftRight_lt w x n
have := @le_toInt_shiftRight w x n
norm_cast at *
exact Int.bmod_eq_self_of_le (by omega) (by omega)
exact Int.bmod_eq_of_le (by omega) (by omega)
/-! ### sshiftRight reductions from BitVec to Nat -/
@@ -2427,9 +2443,9 @@ theorem signExtend_eq_not_setWidth_not_of_msb_true {x : BitVec w} {v : Nat} (hms
toNat_setWidth, hmsb, reduceIte]
norm_cast
rw [Int.ofNat_sub_ofNat_of_lt, Int.negSucc_emod]
simp only [Int.natAbs_ofNat, Nat.succ_eq_add_one]
simp only [Int.natAbs_natCast, Nat.succ_eq_add_one]
rw [Int.subNatNat_of_le]
· rw [Int.toNat_ofNat, Nat.add_comm, Nat.sub_add_eq]
· rw [Int.toNat_natCast, Nat.add_comm, Nat.sub_add_eq]
· apply Nat.le_trans
· apply Nat.succ_le_of_lt
apply Nat.mod_lt
@@ -2536,10 +2552,10 @@ where
simp [getElem_signExtend, Nat.le_sub_one_of_lt hv]
omega
have H : 2^w 2^v := Nat.pow_le_pow_right (by omega) (by omega)
simp only [this, toNat_setWidth, Int.natCast_add, Int.ofNat_emod, Int.natCast_mul]
simp only [this, toNat_setWidth, Int.natCast_add, Int.natCast_emod, Int.natCast_mul]
by_cases h : x.msb
<;> norm_cast
<;> simp [h, Nat.mod_eq_of_lt (Nat.lt_of_lt_of_le x.isLt H)]
<;> simp [h, Nat.mod_eq_of_lt (Nat.lt_of_lt_of_le x.isLt H), -Int.natCast_pow]
omega
/--
@@ -3314,7 +3330,7 @@ theorem setWidth_add (x y : BitVec w) (h : i ≤ w) :
@[simp, bitvec_to_nat] theorem toInt_add (x y : BitVec w) :
(x + y).toInt = (x.toInt + y.toInt).bmod (2^w) := by
simp [toInt_eq_toNat_bmod]
simp [toInt_eq_toNat_bmod, -Int.natCast_pow]
theorem ofInt_add {n} (x y : Int) : BitVec.ofInt n (x + y) =
BitVec.ofInt n x + BitVec.ofInt n y := by
@@ -3344,7 +3360,7 @@ theorem sub_def {n} (x y : BitVec n) : x - y = .ofNat n ((2^n - y.toNat) + x.toN
@[simp, bitvec_to_nat] theorem toInt_sub {x y : BitVec w} :
(x - y).toInt = (x.toInt - y.toInt).bmod (2 ^ w) := by
simp [toInt_eq_toNat_bmod, @Int.ofNat_sub y.toNat (2 ^ w) (by omega)]
simp [toInt_eq_toNat_bmod, @Int.ofNat_sub y.toNat (2 ^ w) (by omega), -Int.natCast_pow]
theorem toInt_sub_toInt_lt_twoPow_iff {x y : BitVec w} :
(x.toInt - y.toInt < - 2 ^ (w - 1))
@@ -3356,7 +3372,7 @@ theorem toInt_sub_toInt_lt_twoPow_iff {x y : BitVec w} :
simp only [Nat.add_one_sub_one]
constructor
· intros h
rw_mod_cast [ Int.bmod_add_cancel, Int.bmod_eq_self_of_le]
rw_mod_cast [ Int.add_bmod_right, Int.bmod_eq_of_le]
<;> omega
· have := Int.bmod_neg_iff (x := x.toInt - y.toInt) (m := 2 ^ (w + 1))
push_cast at this
@@ -3373,7 +3389,7 @@ theorem twoPow_le_toInt_sub_toInt_iff {x y : BitVec w} :
constructor
· intros h
simp only [show 0 x.toInt by omega, show y.toInt < 0 by omega, _root_.true_and]
rw_mod_cast [ Int.bmod_sub_cancel, Int.bmod_eq_self_of_le (by omega) (by omega)]
rw_mod_cast [ Int.sub_bmod_right, Int.bmod_eq_of_le (by omega) (by omega)]
omega
· have := Int.bmod_neg_iff (x := x.toInt - y.toInt) (m := 2 ^ (w + 1))
push_cast at this
@@ -3707,7 +3723,7 @@ theorem two_mul {x : BitVec w} : 2#w * x = x + x := by rw [BitVec.mul_comm, mul_
@[simp, bitvec_to_nat] theorem toInt_mul (x y : BitVec w) :
(x * y).toInt = (x.toInt * y.toInt).bmod (2^w) := by
simp [toInt_eq_toNat_bmod]
simp [toInt_eq_toNat_bmod, -Int.natCast_pow]
theorem ofInt_mul {n} (x y : Int) : BitVec.ofInt n (x * y) =
BitVec.ofInt n x * BitVec.ofInt n y := by
@@ -3859,7 +3875,7 @@ theorem le_zero_iff {x : BitVec w} : x ≤ 0#w ↔ x = 0#w := by
theorem lt_one_iff {x : BitVec w} (h : 0 < w) : x < 1#w x = 0#w := by
constructor
· intro h₂
rw [lt_def, toNat_ofNat, Int.ofNat_lt, Int.ofNat_emod, Int.ofNat_one, Int.natCast_pow,
rw [lt_def, toNat_ofNat, Int.ofNat_lt, Int.natCast_emod, Int.ofNat_one, Int.natCast_pow,
Int.ofNat_two, @Int.emod_eq_of_lt 1 (2^w) (by omega) (by omega)] at h₂
simp [toNat_eq, show x.toNat = 0 by omega]
· simp_all
@@ -3981,7 +3997,6 @@ then `x / y` is nonnegative, thus `toInt` and `toNat` coincide.
theorem toInt_udiv_of_msb {x : BitVec w} (h : x.msb = false) (y : BitVec w) :
(x / y).toInt = x.toNat / y.toNat := by
simp [toInt_eq_msb_cond, msb_udiv_eq_false_of h]
norm_cast
/-! ### umod -/
@@ -4139,6 +4154,110 @@ theorem sdiv_self {x : BitVec w} :
rcases x.msb with msb | msb <;> simp
· rcases x.msb with msb | msb <;> simp [h]
/-- Unsigned division never overflows. -/
theorem toNat_div_toNat_lt {w : Nat} {x y : BitVec w} :
x.toNat / y.toNat < 2 ^ w := by
have hy : y.toNat = 0 y.toNat = 1 1 < y.toNat := by omega
rcases hy with hy|hy|hy
· simp [hy]; omega
· simp [hy]; omega
· rw [Nat.div_lt_iff_lt_mul (k := y.toNat) (x := x.toNat) (y := 2 ^ w) (by omega), show x.toNat = x.toNat * 1 by omega]
apply Nat.mul_lt_mul_of_le_of_lt (by omega) (by omega) (by omega)
/-- Non-overflowing signed division bounds when numerator is nonneg, denominator is nonneg. -/
theorem toInt_ediv_toInt_lt_of_nonneg_of_nonneg {w : Nat} {x y : BitVec w} (hx : 0 x.toInt) (hy : 0 y.toInt) :
x.toInt / y.toInt < 2 ^ (w - 1) := by
rcases w with _|w
· simp [of_length_zero]
· have xle := le_two_mul_toInt (x := x); have xlt := two_mul_toInt_lt (x := x)
by_cases hy' : y.toInt = 1
· simp [hy', Int.ediv_one]; omega
· by_cases hx' : x.toInt = 0
· simp only [hx', Int.zero_ediv, Nat.add_one_sub_one, gt_iff_lt]
norm_cast
exact Nat.two_pow_pos (w := w)
· have := Int.ediv_lt_self_of_pos_of_ne_one (x := x.toInt) (y := y.toInt) (by omega) (by omega)
simp; omega
/-- Non-overflowing signed division bounds when numerator is nonpos, denominator is nonneg. -/
theorem toInt_ediv_toInt_nonpos_of_nonpos_of_nonneg {w : Nat} {x y : BitVec w} (hx : x.toInt 0) (hy : 0 y.toInt) :
x.toInt / y.toInt 0 := by
rcases w with _|w
· simp [of_length_zero]
· by_cases hx' : x.toInt = 0
· simp [hx']
· by_cases hy' : y.toInt = 0
· simp [hy']
· have := Int.ediv_neg_of_neg_of_pos (a := x.toInt) (b := y.toInt) (by omega) (by omega)
simp; omega
/-- Non-overflowing signed division bounds when numerator is nonneg, denominator is nonpos. -/
theorem toInt_ediv_toInt_nonpos_of_nonneg_of_nonpos {w : Nat} {x y : BitVec w} (hx : 0 x.toInt) (hy : y.toInt 0) :
x.toInt / y.toInt 0 := by
rcases w with _|w
· simp [of_length_zero]
· by_cases hy' : y.toInt = -1
· simp [hy']; omega
· have := Int.ediv_nonpos_of_nonneg_of_nonpos (a := x.toInt) (b := y.toInt) (by omega) (by omega)
simp; omega
/-- Given the definition of ediv/emod for signed integer division (https://dl.acm.org/doi/pdf/10.1145/128861.128862)
we have that for two integers `x` and `y`: `x/y = q ↔ x.ediv y = q ↔ r = x.emod y`
and in particular: `-1/y = q ↔ -1.ediv y = q ↔ r = -1.emod y`.
from which it follows that:
(-1)/0 = 0
(-1)/y = -1 when 0 < y
(-1)/(-5) = 1 when y < 0
-/
theorem neg_one_ediv_toInt_eq {w : Nat} {y : BitVec w} :
(-1) / y.toInt = if y.toInt = 0 then 0 else if 0 < y.toInt then -1 else 1 := by
rcases w with _|_|w
· simp [of_length_zero]
· cases eq_zero_or_eq_one y
· case _ h => simp [h]
· case _ h => simp [h]
· by_cases 0 < y.toInt
· simp [Int.sign_eq_one_of_pos (a := y.toInt) (by omega), Int.neg_one_ediv]
omega
· by_cases hy : y.toInt = 0
· simp [hy]
· simp [Int.sign_eq_neg_one_of_neg (a := y.toInt) (by omega), Int.neg_one_ediv]
omega
/-- Non-overflowing signed division bounds when numerator is nonpos, denominator is less than -1. -/
theorem toInt_ediv_toInt_lt_of_nonpos_of_lt_neg_one {w : Nat} {x y : BitVec w} (hx : x.toInt 0) (hy : y.toInt < -1) :
x.toInt / y.toInt < 2 ^ (w - 1) := by
rcases w with _|_|w
· simp [of_length_zero]
· have hy := eq_zero_or_eq_one (a := y)
simp [ toInt_inj, toInt_zero, toInt_one] at hy
omega
· have xle := le_two_mul_toInt (x := x); have xlt := two_mul_toInt_lt (x := x)
have hx' : x.toInt = 0 x.toInt = - 1 x.toInt < - 1 := by omega
rcases hx' with hx'|hx'|hx'
· simp [hx']; omega
· have := BitVec.neg_one_ediv_toInt_eq (y := y)
simp only [toInt_allOnes, Nat.lt_add_left_iff_pos, Nat.zero_lt_succ, reduceIte,
Int.reduceNeg] at this
simp [hx', this]
omega
· have := Int.ediv_lt_natAbs_self_of_lt_neg_one_of_lt_neg_one (x := x.toInt) (y := y.toInt) (by omega) hy
simp; omega
/-- Signed division of (x y : BitVec w) is always -2 ^ w ≤ x.toInt / y.toInt. -/
theorem neg_two_pow_le_toInt_ediv {x y : BitVec w} :
- 2 ^ (w - 1) x.toInt / y.toInt := by
have xlt := @toInt_lt w x; have lex := @le_toInt w x
by_cases hx : 0 x.toInt <;> by_cases hy : 0 y.toInt
· have := Int.ediv_nonneg_of_nonneg_of_nonneg (x := x.toInt) (y := y.toInt) hx hy
omega
· have := Int.neg_self_le_ediv_of_nonneg_of_nonpos (x := x.toInt) (y := y.toInt) hx (by omega)
omega
· have := Int.self_le_ediv_of_nonpos_of_nonneg (x := x.toInt) (y := y.toInt) (by omega) hy
omega
· have := Int.ediv_nonneg_of_nonpos_of_nonpos (a := x.toInt) (b := y.toInt) (by omega) (by omega)
omega
/-! ### smtSDiv -/
theorem smtSDiv_eq (x y : BitVec w) : smtSDiv x y =
@@ -4914,7 +5033,6 @@ theorem intMin_eq_zero_iff {w : Nat} : intMin w = 0#w ↔ w = 0 := by
· constructor
· have := Nat.two_pow_pos (w - 1)
simp [toNat_eq, show 0 < w by omega]
omega
· simp [h]
/--
@@ -4942,7 +5060,7 @@ theorem toInt_intMin_le (x : BitVec w) :
cases w
case zero => simp [toInt_intMin, @of_length_zero x]
case succ w =>
simp only [toInt_intMin, Nat.add_one_sub_one, Int.ofNat_emod]
simp only [toInt_intMin, Nat.add_one_sub_one, Int.natCast_emod]
have : 0 < 2 ^ w := Nat.two_pow_pos w
rw [Int.emod_eq_of_lt (by omega) (by omega)]
rw [BitVec.toInt_eq_toNat_bmod]
@@ -5137,6 +5255,22 @@ theorem sub_le_sub_iff_le {x y z : BitVec w} (hxz : z ≤ x) (hyz : z ≤ y) :
BitVec.toNat_sub_of_le (by rw [BitVec.le_def]; omega)]
omega
theorem sdiv_neg_one {w : Nat} {x : BitVec w} :
x.toInt / -1 = -x.toInt := by
rcases w with _|w
· simp [of_length_zero]
· simp [toInt_allOnes]
theorem sdivOverflow_eq_negOverflow_of_eq_allOnes {w : Nat} {x y : BitVec w} (hy : y = allOnes w) :
sdivOverflow x y = negOverflow x := by
rcases w with _|w
· simp [sdivOverflow, negOverflow, of_length_zero]
· have xle := le_two_mul_toInt (x := x); have xlt := two_mul_toInt_lt (x := x)
simp only [sdivOverflow, hy, show ¬2 ^ w < x.toInt by omega, negOverflow]
by_cases hx : x.toInt = - 2 ^ w
· simp [hx]
· simp [show ¬x.toInt == -2 ^ w by simp only [beq_iff_eq, hx, not_false_eq_true]]; omega
theorem two_pow_le_toInt_mul_toInt_iff {x y : BitVec w} :
2 ^ (w - 1) x.toInt * y.toInt
(signExtend (w * 2) (intMax w)).slt (signExtend (w * 2) x * signExtend (w * 2) y) := by
@@ -5152,7 +5286,7 @@ theorem two_pow_le_toInt_mul_toInt_iff {x y : BitVec w} :
toInt_intMax, Nat.add_one_sub_one]
push_cast
rw [ Nat.two_pow_pred_add_two_pow_pred (by omega),
Int.bmod_eq_self_of_le_mul_two (by rw [ Nat.mul_two]; push_cast; omega)
Int.bmod_eq_of_le_mul_two (by rw [ Nat.mul_two]; push_cast; omega)
(by rw [ Nat.mul_two]; push_cast; omega)]
omega
@@ -5169,14 +5303,14 @@ theorem toInt_mul_toInt_lt_neg_two_pow_iff {x y : BitVec w} :
simp only [toInt_twoPow, show ¬w + 1 w by omega, reduceIte]
push_cast
rw [ Nat.two_pow_pred_add_two_pow_pred (by omega),
Int.bmod_eq_self_of_le_mul_two (by rw [ Nat.mul_two]; push_cast; omega)
Int.bmod_eq_of_le_mul_two (by rw [ Nat.mul_two]; push_cast; omega)
(by rw [ Nat.mul_two]; push_cast; omega)]
/-! ### neg -/
theorem msb_eq_toInt {x : BitVec w}:
x.msb = decide (x.toInt < 0) := by
by_cases h : x.msb <;> simp [h, toInt_eq_msb_cond] <;> omega
by_cases h : x.msb <;> simp [h, toInt_eq_msb_cond, -Int.natCast_pow] <;> omega
theorem msb_eq_toNat {x : BitVec w}:
x.msb = decide (x.toNat 2 ^ (w - 1)) := by

View File

@@ -3,6 +3,8 @@ Copyright (c) 2023 F. G. Dorais. No rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: F. G. Dorais
-/
module
prelude
import Init.NotationExtra
@@ -690,9 +692,5 @@ def boolRelToRel : Coe (αα → Bool) (αα → Prop) where
/-! ### subtypes -/
@[simp] theorem Subtype.beq_iff {α : Type u} [DecidableEq α] {p : α Prop} {x y : {a : α // p a}} :
(x == y) = (x.1 == y.1) := by
cases x
cases y
rw [Bool.eq_iff_iff]
simp [beq_iff_eq]
@[simp] theorem Subtype.beq_iff {α : Type u} [BEq α] {p : α Prop} {x y : {a : α // p a}} :
(x == y) = (x.1 == y.1) := rfl

View File

@@ -3,5 +3,7 @@ Copyright (c) 2019 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
-/
module
prelude
import Init.Data.ByteArray.Basic

View File

@@ -3,6 +3,8 @@ Copyright (c) 2019 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
-/
module
prelude
import Init.Data.Array.Basic
import Init.Data.Array.Subarray

View File

@@ -3,6 +3,8 @@ Copyright (c) 2014 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro, Gabriel Ebner
-/
module
prelude
import Init.Coe

View File

@@ -3,6 +3,8 @@ Copyright (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
module
prelude
import Init.Data.Char.Basic
import Init.Data.Char.Lemmas

View File

@@ -3,6 +3,8 @@ Copyright (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
-/
module
prelude
import Init.Data.UInt.BasicAux

View File

@@ -3,6 +3,8 @@ Copyright (c) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
module
prelude
import Init.Data.Char.Basic
import Init.Data.UInt.Lemmas

View File

@@ -3,6 +3,8 @@ Copyright (c) 2017 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
-/
module
prelude
import Init.Data.Fin.Basic
import Init.Data.Fin.Log2

View File

@@ -3,6 +3,8 @@ Copyright (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura, Robert Y. Lewis, Keeley Hoek, Mario Carneiro
-/
module
prelude
import Init.Data.Nat.Bitwise.Basic

View File

@@ -3,6 +3,8 @@ Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel
-/
module
prelude
import Init.Data.Nat.Bitwise
import Init.Data.Fin.Basic

View File

@@ -3,6 +3,8 @@ Copyright (c) 2023 François G. Dorais. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: François G. Dorais
-/
module
prelude
import Init.Data.Nat.Linear
import Init.Control.Lawful.Basic

View File

@@ -3,6 +3,8 @@ Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Joe Hendrix
-/
module
prelude
import Init.PropLemmas
import Init.Data.Fin.Basic

View File

@@ -3,6 +3,8 @@ Copyright (c) 2022 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro, Leonardo de Moura
-/
module
prelude
import Init.Data.Fin.Basic
import Init.Data.Nat.Lemmas
@@ -410,6 +412,14 @@ theorem succ_succ_ne_one (a : Fin n) : Fin.succ (Fin.succ a) ≠ 1 :=
@[simp] theorem coe_cast (h : n = m) (i : Fin n) : (i.cast h : Nat) = i := rfl
@[simp] theorem cast_castLE {k m n} (km : k m) (mn : m = n) (i : Fin k) :
Fin.cast mn (i.castLE km) = i.castLE (mn km) :=
Fin.ext (by simp)
@[simp] theorem cast_castLT {k m n} (i : Fin k) (h : (i : Nat) < m) (mn : m = n) :
Fin.cast mn (i.castLT h) = i.castLT (mn h) :=
Fin.ext (by simp)
@[simp] theorem cast_zero [NeZero n] [NeZero m] (h : n = m) : Fin.cast h 0 = 0 := rfl
@[simp] theorem cast_last {n' : Nat} {h : n + 1 = n' + 1} : (last n).cast h = last n' :=

View File

@@ -3,6 +3,8 @@ Copyright (c) 2022 Henrik Böving. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Henrik Böving
-/
module
prelude
import Init.Data.Nat.Log2

View File

@@ -3,6 +3,8 @@ Copyright (c) 2020 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
module
prelude
import Init.Core
import Init.Data.Int.Basic
@@ -289,8 +291,11 @@ implementation.
instance : Inhabited Float where
default := UInt64.toFloat 0
protected def Float.repr (n : Float) (prec : Nat) : Std.Format :=
if n < UInt64.toFloat 0 then Repr.addAppParen (toString n) prec else toString n
instance : Repr Float where
reprPrec n prec := if n < UInt64.toFloat 0 then Repr.addAppParen (toString n) prec else toString n
reprPrec := Float.repr
instance : ReprAtom Float :=

View File

@@ -3,6 +3,8 @@ Copyright (c) 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
module
prelude
import Init.Core
import Init.Data.Int.Basic
@@ -290,8 +292,11 @@ implementation.
instance : Inhabited Float32 where
default := UInt64.toFloat32 0
protected def Float32.repr (n : Float32) (prec : Nat) : Std.Format :=
if n < UInt64.toFloat32 0 then Repr.addAppParen (toString n) prec else toString n
instance : Repr Float32 where
reprPrec n prec := if n < UInt64.toFloat32 0 then Repr.addAppParen (toString n) prec else toString n
reprPrec := Float32.repr
instance : ReprAtom Float32 :=

View File

@@ -3,5 +3,7 @@ Copyright (c) 2020 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
-/
module
prelude
import Init.Data.FloatArray.Basic

View File

@@ -3,6 +3,8 @@ Copyright (c) 2020 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
-/
module
prelude
import Init.Data.Array.Basic
import Init.Data.Float

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