Compare commits

...

388 Commits

Author SHA1 Message Date
Kim Morrison
1a61b025fe feat: theorems about Nat/Int/Rat needed for dyadics 2025-08-22 21:34:31 +10:00
Lean stage0 autoupdater
584ed5f33e chore: update stage0 2025-08-22 08:30:43 +00:00
Mac Malone
1523ed1cdb chore: allow module in LakeMain (#10039)
This PR enables core's `LakeMain` to be a `module` when core is built
without `USE_LAKE`.

This was a problem when porting Lake to the module system (#9749).
2025-08-22 07:45:42 +00:00
Jason Yuen
facc356a0a chore: fix spelling errors (#10042)
Typos were found with
```
pip install codespell --upgrade
codespell --summary --ignore-words-list enew,forin,fro,happend,hge,ihs,iterm,spred --skip stage0 --check-filenames
codespell --summary --ignore-words-list enew,forin,fro,happend,hge,ihs,iterm,spred --skip stage0 --check-filenames --regex '[A-Z][a-z]*'
codespell --summary --ignore-words-list enew,forin,fro,happend,hge,ihs,iterm,spred --skip stage0 --check-filenames --regex "\b[a-z']*"
```
2025-08-22 07:23:12 +00:00
Paul Reichert
c4b3f303bb feat: more convenient creation of polymorphic range instances (#10005)
This PR shortens the work necessary to make a type compatible with the
polymorphic range notation. In the concrete case of `Nat`, it reduces
the required lines of code from 150 to 70.
2025-08-22 07:08:33 +00:00
Paul Reichert
1448493489 feat: improvements to Min/Max-related classes (#10024)
This PR adds useful declarations to the `LawfulOrderMin/Max` and
`LawfulOrderLeftLeaningMin/Max` API. In particular, it introduces
`.leftLeaningOfLE` factories for `Min` and `Max`. It also renames
`LawfulOrderMin/Max.of_le` to .of_le_min_iff` and `.of_max_le_iff` and
introduces a second variant with different arguments.
2025-08-22 07:08:00 +00:00
Cameron Zwarich
f7a251b75f chore: set experimental.module=true when running grind benchmarks (#10041) 2025-08-22 03:15:36 +00:00
Cameron Zwarich
5aa706435a fix: incorporate info from _redArg decls for noncomputable check (#10040)
This PR changes the `toMono` pass to replace decls with their `_redArg`
equivalent, which has the consequence of not considering arguments
deemed useless by the `reduceArity` pass for the purposes of the
`noncomputable` check.
2025-08-22 01:02:40 +00:00
Leonardo de Moura
a581433d8b fix: grind error messages (#10038)
This PR ensures `grind` error messages use `{.ofConstName declName}`
when referencing declaration names.
2025-08-21 23:28:26 +00:00
Leonardo de Moura
6683d1eb91 chore: add module keyword to grind tests (#10036)
This PR also fixes missing `@[expose]` in grind support definitions.
2025-08-21 22:02:08 +00:00
Cameron Zwarich
504d71f268 chore: remove unnecessary code handling cases of an erased value (#10035)
This is now all handled in `toLCNF`.
2025-08-21 21:55:10 +00:00
Cameron Zwarich
ca4322ff09 fix: support casesOn for inductive predicates with computations on fields (#10023)
This PR adds support for correctly handling computations on fields in
`casesOn` for inductive predicates that support large elimination. In
any such predicate, the only relevant fields allowed are those that are
also used as an index, in which case we can find the supplied index and
use that term instead.
2025-08-21 18:55:34 +00:00
Cameron Zwarich
d32f04ba21 refactor: split out an isInductivePredicateVal function (#10033)
This is just like `isInductivePredicate?`, but on an existing
`InductiveVal` rather than one that is looked up by name.
2025-08-21 18:05:14 +00:00
Leonardo de Moura
0db795a1dc feat: improve grind cutsat support for Fin n when n is not a numeral (#10022)
This PR improves support for `Fin n` in `grind cutsat` when `n` is not a
numeral. For example, the following goals can now be solved
automatically:

```lean
example (p d : Nat) (n : Fin (p + 1)) 
    : 2 ≤ p → p ≤ d + 1 → d = 1 → n = 0 ∨ n = 1 ∨ n = 2 := by
  grind

example (s : Nat) (i j : Fin (s + 1)) (hn : i ≠ j) (hl : ¬i < j) : j < i := by
  grind

example {n : Nat} (j : Fin (n + 1)) : j ≤ j := by
  grind

example {n : Nat} (x y : Fin ((n + 1) + 1)) (h₂ : ¬x = y) (h : ¬x < y) : y < x := by
  grind
```
2025-08-21 17:25:52 +00:00
Mac Malone
d9a73dd1e3 feat: @[expose] on Lean.ParserState.setPos (#10019)
This PR adds `@[expose]` to `Lean.ParserState.setPos`. This makes it
possible to prove in-boundedness for a state produced by `setPos` for
functions like `next'` and `get'` without needing to `import all`.

This came up while porting Lake to the module system (#9749).
2025-08-21 17:24:10 +00:00
Joachim Breitner
e9f6033467 chore: benchmark for deriving BEq on large inductive (#10028) 2025-08-21 15:50:12 +00:00
Cameron Zwarich
0c9bb4b861 fix: lower overapplied constructors to unreachable (#10032)
This PR changes the handling of overapplied constructors when lowering
LCNF to IR from a (slightly implicit) assertion failure to producing
`unreachable`. Transformations on inlined unreachable code can produce
constructor applications with additional arguments.

In the old compiler, these additional arguments were silently ignored,
but it seems more sensible to replace them with `unreachable`, just in
case they arise due to a compiler error.

Fixes #9937.
2025-08-21 15:05:09 +00:00
Sebastian Ullrich
9b4911f8f6 chore: CI: fix Linux Lake on PR half-merge (#10029) 2025-08-21 13:25:53 +00:00
Sebastian Ullrich
f678b40660 chore: make USE_LAKE the default (#10016) 2025-08-21 11:43:25 +00:00
Joachim Breitner
890722f571 refactor: factor out mkNatLookupTable (#10006)
This PR shares the meta code that creates a binary decision tree on
Nats.
2025-08-21 10:45:21 +00:00
Marc Huisinga
ef1ca99bff chore: simplify require config in lakefile.toml schema (#10001)
This PR simplifies the `require` config of the lakefile.toml schema in
order to present simpler completions for dependency configuration.
2025-08-21 08:30:42 +00:00
Mac Malone
26fdc1e19a feat: deriving BEq, Hashable for Lean.Import (#10018)
This PR derives `BEq` and `Hashable` for `Lean.Import`. Lake already did
this later, but it now done when defining `Import`.

Doing this in Lake became problematic when porting it to the module
system (#9749).
2025-08-21 07:53:30 +00:00
Mac Malone
0b0d183c1d feat: @[expose] on Name.append & friends (#10015)
This PR exposes the bodies of `Name.append`, `Name.appendCore`, and
`Name.hasMacroScopes`. This enables proof by reflection of the
concatenation of name literals when using the module system.

```lean
example : `foo ++ `bar = `foo.bar := rfl
```

This is necessary for Lake as part of the port to using `module`
(#9749).
2025-08-21 07:52:59 +00:00
Kim Morrison
21f5263f2f feat: minor quality of life improvements in script/AnalyzeGrindAnnotations (#10021)
This PR make some minor changes to the grind annotation analysis script,
including sorting results and handling errors. Still need to add an
external UI.
2025-08-21 04:12:21 +00:00
Lean stage0 autoupdater
02edc0bd92 chore: update stage0 2025-08-21 03:44:22 +00:00
Leonardo de Moura
45affb5e09 fix: missing nonlinear / and % in grind cutsat (#10020)
This PR fixes a missing case for PR #10010.
2025-08-21 02:59:52 +00:00
Mac Malone
6a7111ed0e fix: lake: no LEAN_GITHASH for bootstrap (#10012)
This PR changes Lake to not set `LEAN_GITHASH` when in core (i.e.
`bootstrap = true`). This avoids Lake rebuilding modules when the Lake
watchdog is on one build of Lean/Lake and the command line is on a
different one.
2025-08-21 02:44:36 +00:00
Mac Malone
6b4c356c5b chore: lake: fix tests/module (#10013)
This PR fixes an existing breakage in the Lake's module test caused by
Lean's automatic inlining.
2025-08-21 01:28:06 +00:00
Kim Morrison
e3947cbe20 chore: remove bad Option grind annotation (#10000)
This PR removes a `grind` annotation that fired on all `Option.map`s,
causing an avalanche of instantiations.
2025-08-21 01:08:31 +00:00
Cameron Zwarich
d6a43a660f perf: disable reuse when a value's last use is an owned argument (#10017)
The current reuse analysis is greedy in that every function attempts to
reuse a value. However, this means that if the last use is an owned
argument, it will be `inc`'d prior to this last use, in order to prevent
reuse from happening in the callee. In many cases, it makes more sense
to give the callee the chance to reuse it instead. The benchmark results
indicate that this is a much better default.
2025-08-21 00:41:56 +00:00
thorimur
b2330fee2b chore: miscellaneous documentation typos (#10009)
This PR fixes several typos in documentation.
2025-08-20 21:39:03 +00:00
Leonardo de Moura
105879669e chore: remove unnecessary hypothesis in ToInt helper theorems (#10014) 2025-08-20 20:13:15 +00:00
Sebastian Ullrich
679df58329 chore: revert "chore: make USE_LAKE the default" (#10011)
Reverts leanprover/lean4#10003, which broke the merge queue's breakage
check
2025-08-20 19:52:57 +00:00
Leonardo de Moura
d604c16c0e feat: nonlinear / and % support in grind cutsat (#10010)
This PR improves support for nonlinear `/` and `%` in `grind cutsat`.
For example, given `a / b`, if `cutsat` discovers that `b = 2`, it now
propagates that `a / b = b / 2`. This PR is similar to #9996, but for
`/` and `%`. Example:

```lean
example (a b c d : Nat)
    : b > 1 → d = 1 → b ≤ d + 1 → a % b = 1 → a = 2 * c → False := by
  grind
```
2025-08-20 19:31:31 +00:00
Sebastian Ullrich
44891fe0c0 chore: make USE_LAKE the default (#10003) 2025-08-20 19:24:10 +00:00
Kyle Miller
ee699518fa fix: have #eval save the info context (#10008)
This PR fixes a bug in `#eval` where clicking on the evaluated
expression could show errors in the Infoview. This was caused by `#eval`
not saving the temporary environment that is used when elaborating the
expression.
2025-08-20 17:49:09 +00:00
Joachim Breitner
1b213835e6 fix: #print attributes in the right order (#10007)
This PR lets #print print `private` before `protected`, matching the
syntax.
2025-08-20 15:34:55 +00:00
Paul Reichert
22becc78f7 feat: better get-elem tactic for ranges (#9987)
This PR improves the tactic for proving that elements of a `Nat`-based
`PRange` are in-bounds by relying on the `omega` tactic.
2025-08-20 13:42:41 +00:00
Paul Reichert
e083771b81 feat: package factories for order typeclasses based on Ord (#9916)
This PR provides factories that derive order typeclasses in bulk, given
an `Ord` instance. If present, existing instances are preferred over
those derived from `Ord`. It is possible to specify any instance
manually if desired.
2025-08-20 11:14:07 +00:00
Kim Morrison
1a31aa3d2b chore: fewer Nat.bitwise grind attributes for distributivity (#9999)
This PR reduces the number of `Nat.Bitwise` grind annotations we have
the deal with distributivity. The new smaller set encourages `grind` to
rewrite into DNF. The old behaviour just resulted in saturating up to
the instantiation limits.
2025-08-20 05:38:05 +00:00
Leonardo de Moura
86dc07c20d feat: nonlinear monomials in grind cutsat (#9996)
This PR improves support for nonlinear monomials in `grind cutsat`. For
example, given a monomial `a * b`, if `cutsat` discovers that `a = 2`,
it now propagates that `a * b = 2 * b`.
Recall that nonlinear monomials like `a * b` are treated as variables in
`cutsat`, a procedure designed for linear integer arithmetic.

Example:
```lean
example (a : Nat) (ha : a < 8) (b c : Nat) : 2 ≤ b → c = 1 → b ≤ c + 1 → a * b < 8 * b := by
  grind

example (x y z w : Int) : z * x * y = 4 → x = z + w → z = 1 → w = 2 → False := by
  grind
```
2025-08-20 03:16:53 +00:00
Sebastian Ullrich
48365b6052 chore: update stage0 2025-08-19 14:49:12 -07:00
Sebastian Ullrich
d4a5a2c632 fix: local syntax should create private definitions 2025-08-19 14:49:12 -07:00
Sebastian Ullrich
8d34dfe914 chore: CI: make cached Lake primary job (#9401) 2025-08-19 20:43:00 +00:00
Mac Malone
a1cf67edc3 feat: parser alias for visibility (#9974)
This PR registers a parser alias for `Lean.Parser.Command.visibility`.
This avoids having to import `Lean.Parser.Command` in simple command
macros that use visibilities.
2025-08-19 15:20:32 +00:00
Sebastian Ullrich
d0167f7002 chore: show origin module for inaccessible private decls (#9964) 2025-08-19 15:12:09 +00:00
Sebastian Graf
90ef90b462 feat: change extended syntax for mvcgen invariants ... with ... (#9989)
This PR changes the new extended syntax for `mvcgen` to `mvcgen
invariants ... with ...`.
2025-08-19 14:51:19 +00:00
Marc Huisinga
cab46ea3d1 fix: leanOptions in lakefile.toml schema (#9988)
This PR fixes a bug in the `lakefile.toml` schema where it would issue
an invalid validation for multi-layer `leanOptions` .
2025-08-19 14:43:01 +00:00
Paul Reichert
24cafcd65d feat: package factories for order typeclasses (#9797)
This PR provides the means to quickly provide all the order instances
associated with some high-level order structure (preorder, partial
order, linear preorder, linear order). This can be done via the factory
functions `PreorderPackage.ofLE`, `PartialOrderPackage.ofLE`,
`LinearPreorderPackage.ofLE` and `LinearOrderPackage.ofLE`.
2025-08-19 13:43:29 +00:00
nnarek
b75fbe7a40 doc: documentation of p,+ macro should mention that it maps to sepBy1, not sepBy (#9876)
This PR fixes doc issue of p,+ macro,which maps to sepBy1(p, ",") while
doc says that it maps to sepBy(p, ",").

Closes https://github.com/leanprover/lean4/issues/9873
2025-08-19 11:54:47 +00:00
Sebastian Ullrich
cd729660ed chore: allow quoting private names from inside public scope (#9985) 2025-08-19 09:07:48 +00:00
Paul Reichert
f81236185c feat: integrate high-level order typeclasses with BEq and Ord (#9908)
This PR makes `IsPreorder`, `IsPartialOrder`, `IsLinearPreorder` and
`IsLinearOrder` extend `BEq` and `Ord` as appropriate, adds the
`LawfulOrderBEq` and `LawfulOrderOrd` typeclasses relating `BEq` and
`Ord` to `LE`, and adds many lemmas and instances.

Note: This PR contains a refactoring where `Init.Data.Ord` is moved to
`Init.Data.Ord.Basic`. If I added `Init.Data.Ord` simply importing all
submodules, git would not be able to determine that `Init.Data.Ord` was
renamed to `Init.Data.Ord.Basic`. This could lead to unnecessary merge
conflicts in the future. Hence, I chose the name `Init.Data.OrdRoot`
instead of `Init.Data.Ord` temporarily. After this PR, I will rename
this module back to `Init.Data.Ord` in a separate PR.

(This is a copy of #9430: I will not touch that PR because it currently
allows to debug a CI problem and pushing commits might break the
reproducibility.)
2025-08-19 07:54:53 +00:00
Kyle Miller
7fa1a8b114 chore: eliminate uses of intros x y z (#9983)
This PR eliminates uses of `intros x y z` (with arguments) and updates
the `intros` docstring to suggest that `intro x y z` should be used
instead. The `intros` tactic is historical, and can be traced all the
way back to Lean 2, when `intro` could only introduce a single
hypothesis. Since 2020, the `intro` tactic has superceded it. The
`intros` tactic (without arguments) is currently still useful.
2025-08-19 06:09:13 +00:00
Cameron Zwarich
8536fe5aa9 refactor: split handling of normal fvars and join points in toIR (#9981)
This makes the representation of lowered fvar values the IR `Arg` type.
2025-08-19 03:44:15 +00:00
Leonardo de Moura
6b24eb474f fix: variable reordering in grind cutsat (#9980)
This PR fixes a bug in the dynamic variable reordering function used in
`grind cutsat`.

Closes #9948
2025-08-19 02:19:50 +00:00
Kim Morrison
de493d761d feat: upstream definition of Rat from Batteries (#9957)
This PR upstreams the definition of Rat from Batteries, for use in our
planned interval arithmetic tactic.

---------

Co-authored-by: Sebastian Ullrich <sebasti@nullri.ch>
2025-08-19 01:58:24 +00:00
Cameron Zwarich
b68f3455d3 refactor: use a separate getter for fvar values in toIR (#9978) 2025-08-19 01:28:15 +00:00
Anne Baanen
f88d35f6c9 chore: add fixed grind tests for Nat and Int ring structure (#9615)
This PR adds two test cases extracted from Mathlib, that `grind` cannot
solve but `omega` can. Originally the multiplication instance came from
`Nat.instSemiring` and `Int.instSemiring`, in minimizing I found that
`Distrib` is already enough.

---------

Co-authored-by: Kim Morrison <kim@tqft.net>
2025-08-19 01:12:44 +00:00
Cameron Zwarich
89752e2242 fix: support compiling casesOn recursors of subsingleton predicates (#9977)
This PR adds support for compilation of `casesOn` recursors of
subsingleton predicates.

Fixes #9963.
2025-08-19 00:23:24 +00:00
Cameron Zwarich
b8fa6f17ee fix: make lcAny-producing arrow types lower to tobj rather than obj (#9972)
This PR fixes an issue when running Mathlib's `FintypeCat` as code,
where an erased type former is passed to a polymorphic function. We were
lowering the arrow type to`object`, which conflicts with the runtime
representation of an erased value as a tagged scalar.
2025-08-18 22:18:26 +00:00
Henrik Böving
2d4bcf202f chore: even more independent benchmarks (#9970) 2025-08-18 18:36:33 +00:00
Sebastian Graf
1b0d83e7fc fix: remove local Triple notation from SpecLemmas.lean to fix stage2 (#9967)
This PR removes local `Triple` notation from SpecLemmas.lean to work
around a bug that breaks the stage2 build.
2025-08-18 16:41:26 +00:00
Wojciech Rozowski
2d52d44710 feat: fixpoint_induct and partial_correctness lemmas for mutual blocks come in conjunction and projected variants (#9651)
This PR modifies the generation of induction and partial correctness
lemmas for `mutual` blocks defined via `partial_fixpoint`. Additionally,
the generation of lattice-theoretic induction principles of functions
via `mutual` blocks is modified for consistency with `partial_fixpoint`.

The lemmas now come in two variants:
1. A conjunction variant that combines conclusions for all elements of
the mutual block. This is generated only for the first function inside
of the mutual block.
2. Projected variants for each function separately

## Example 1
```lean4
axiom A : Type
axiom B : Type

axiom A.toB : A → B
axiom B.toA : B → A

mutual
noncomputable def f : A := g.toA
partial_fixpoint
noncomputable def g : B := f.toB
partial_fixpoint
end
```

Generated `fixpoint_induct` lemmas:
```lean4
f.fixpoint_induct (motive_1 : A → Prop) (motive_2 : B → Prop) (adm_1 : admissible motive_1)
  (adm_2 : admissible motive_2) (h_1 : ∀ (g : B), motive_2 g → motive_1 g.toA)
  (h_2 : ∀ (f : A), motive_1 f → motive_2 f.toB) : motive_1 f

g.fixpoint_induct (motive_1 : A → Prop) (motive_2 : B → Prop) (adm_1 : admissible motive_1)
  (adm_2 : admissible motive_2) (h_1 : ∀ (g : B), motive_2 g → motive_1 g.toA)
  (h_2 : ∀ (f : A), motive_1 f → motive_2 f.toB) : motive_2 g
```

Mutual (conjunction) variant:
```lean4
f.mutual_fixpoint_induct (motive_1 : A → Prop) (motive_2 : B → Prop) (adm_1 : admissible motive_1) (adm_2 : admissible motive_2)
  (h_1 : ∀ (g : B), motive_2 g → motive_1 g.toA) (h_2 : ∀ (f : A), motive_1 f → motive_2 f.toB) :
  motive_1 f ∧ motive_2 g
```

## Example 2 
```lean4
mutual
  def f (n : Nat) : Option Nat :=
    g (n + 1)
  partial_fixpoint

  def g (n : Nat) : Option Nat :=
    if n = 0 then .none else f (n + 1)
  partial_fixpoint
end
```
Generated `partial_correctness` lemmas (in a projected variant):
```lean4
f.partial_correctness (motive_1 motive_2 : Nat → Nat → Prop)
  (h_1 :
    ∀ (g : Nat → Option Nat),
      (∀ (n r : Nat), g n = some r → motive_2 n r) → ∀ (n r : Nat), g (n + 1) = some r → motive_1 n r)
  (h_2 :
    ∀ (f : Nat → Option Nat),
      (∀ (n r : Nat), f n = some r → motive_1 n r) →
        ∀ (n r : Nat), (if n = 0 then none else f (n + 1)) = some r → motive_2 n r)
  (n r✝ : Nat) : f n = some r✝ → motive_1 n r✝

g.partial_correctness (motive_1 motive_2 : Nat → Nat → Prop)
  (h_1 :
    ∀ (g : Nat → Option Nat),
      (∀ (n r : Nat), g n = some r → motive_2 n r) → ∀ (n r : Nat), g (n + 1) = some r → motive_1 n r)
  (h_2 :
    ∀ (f : Nat → Option Nat),
      (∀ (n r : Nat), f n = some r → motive_1 n r) →
        ∀ (n r : Nat), (if n = 0 then none else f (n + 1)) = some r → motive_2 n r)
  (n r✝ : Nat) : g n = some r✝ → motive_2 n r✝
```

Mutual (conjunction) variant:
```
f.mutual_partial_correctness (motive_1 motive_2 : Nat → Nat → Prop)
  (h_1 :
    ∀ (g : Nat → Option Nat),
      (∀ (n r : Nat), g n = some r → motive_2 n r) → ∀ (n r : Nat), g (n + 1) = some r → motive_1 n r)
  (h_2 :
    ∀ (f : Nat → Option Nat),
      (∀ (n r : Nat), f n = some r → motive_1 n r) →
        ∀ (n r : Nat), (if n = 0 then none else f (n + 1)) = some r → motive_2 n r) :
  (∀ (n r : Nat), f n = some r → motive_1 n r) ∧ ∀ (n r : Nat), g n = some r → motive_2 n r
```
2025-08-18 15:26:30 +00:00
Kyle Miller
af5322c7ef feat: tactic info per intro hypothesis, rfl pattern (#9942)
This PR modifies `intro` to create tactic info localized to each
hypothesis, making it possible to see how `intro` works
variable-by-variable. Additionally:
- The tactic supports `intro rfl` to introduce an equality and
immediately substitute it, like `rintro rfl` (recall: the `rfl` pattern
is like doing `intro h; subst h`). The `rintro` tactic can also now
support `HEq` in `rfl` patterns if `eq_of_heq` applies.
- In `intro (h : t)`, elaboration of `t` is interleaved with unification
with the type of `h`, which prevents default instances from causing
unification to fail.
- Tactics that change types of hypotheses (including `intro (h : t)`,
`delta`, `dsimp`) now update the local instance cache.

In `intro x y z`, tactic info ranges are `intro x`, `y`, and `z`. The
reason for including `intro` with `x` is to make sure the info range is
"monotonic" while adding the first argument to `intro`.
2025-08-18 13:55:06 +00:00
Kyle Miller
3af9cc3f6f doc: extend docstrings for let/have tactics (#9956)
This PR adds additional information to the `let` and `have` tactic
docstrings about opaqueness, when to use each, and associated tactics.
2025-08-18 13:48:08 +00:00
Rob23oba
688b930bad feat: tree map lemmas for filter, map, filterMap (#9632)
This PR adds lemmas for the `TreeMap` operations `filter`, `map` and
`filterMap`. These lemmas existed already for hash maps and are simply
ported over from there.
2025-08-18 12:13:52 +00:00
Tom Levy
04f9baf4d3 fix: remove dependency on LawfulBEq from List.lookup lemmas (#9949)
This PR allows most of the `List.lookup` lemmas to be used when
`LawfulBEq α` is not available.

`LawfulBEq` is very strong. Most of the lemmas don't actually require it
-- some only require `ReflBEq`, and only `List.lookup_eq_some_iff`
actually requires `LawfulBEq`.
2025-08-18 10:16:30 +00:00
Johannes Tantow
19301f83eb feat: verify toArray for hash maps (#9685)
This PR verifies `toArray` and related functions for hashmaps.
2025-08-18 09:39:44 +00:00
Markus Himmel
2e6c1a74e5 chore: move String.Pos operations out of Prelude (#9845)
This PR moves arithmetic of `String.Pos` out of the prelude.

Other `String` declarations are part of the prelude because they are
generated by macros, but this does not seem to be the case for these.
2025-08-18 09:23:02 +00:00
Henrik Böving
e4be2b2cad chore: make perf tests more independent of external factors (#9960) 2025-08-18 08:45:23 +00:00
Henrik Böving
48a8dd4a56 fix: print mathlib toolchain URL properly (#9962)
This PR makes lake print the error message it intended for when fetching
the mathlib toolchain
fails.
2025-08-18 08:11:50 +00:00
Sebastian Ullrich
a805e7e12c chore: avoid turning accesses to private decs from public signatures into auto implicits (#9961) 2025-08-18 08:01:12 +00:00
Jason Yuen
3c702f38ee chore: add a missing backtick (#9959)
This PR adds a backtick and fixes the docs for `section`.
2025-08-18 07:48:05 +00:00
Lean stage0 autoupdater
fe90da5a8d chore: update stage0 2025-08-18 05:25:50 +00:00
Kyle Miller
fd926cc44e feat: clean up type annotations when elaborating declaration bodies (#9674)
This PR cleans up `optParam`/`autoParam`/etc. annotations before
elaborating definition bodies, theorem bodies, `fun` bodies, and `let`
function bodies. Both `variable`s and binders in declaration headers are
supported.

There are no changes to `inductive`/`structure`/`axiom`/etc. processing,
just `def`/`theorem`/`example`/`instance`.
2025-08-18 04:43:20 +00:00
Leonardo de Moura
f5bab3c8ba feat: grind cutsat equations in solved form (#9958)
This PR ensures that equations in the `grind cutsat` module are
maintained in solved form. That is, given an equation `a*x + p = 0` used
to eliminate `x`, the linear polynomial `p` must not contain other
eliminated variables. Before this PR, equations were maintained in
triangular form. We are going to use the solved form to linearize
nonlinear terms.
2025-08-18 01:34:37 +00:00
Leonardo de Moura
973885d087 chore: remove NullCert leftovers (#9955) 2025-08-18 00:07:23 +00:00
Lean stage0 autoupdater
1aa59f5579 chore: update stage0 2025-08-17 23:48:38 +00:00
Leonardo de Moura
a4496a4a6b chore: remove grind +ringNull option (#9954)
This PR removes the option `grind +ringNull`. It provided an alternative
proof term construction for the `grind ring` module, but it was less
effective than the default proof construction mode and had effectively
become dead code.
This PR also optimizes semiring normalization proof terms using the
infrastructure added in #9946.
**Remark:** After updating stage0, we can remove several background
theorems from the `Init/Grind` folder.
2025-08-17 23:04:59 +00:00
Lean stage0 autoupdater
84fecdc042 chore: update stage0 2025-08-17 16:58:21 +00:00
Sebastian Ullrich
81a4b0ca99 chore: fix failing mk*Sorry in bootstrapping contexts (#9950) 2025-08-17 16:14:53 +00:00
Leonardo de Moura
6f7dba167a feat: trim grind linarith proof context (#9947)
This PR optimizes the proof terms produced by `grind linarith`. It is
similar to #9945, but for the `linarith` module in `grind`.
It removes unused entries from the context objects when generating the
final proof, significantly reducing the amount of junk in the resulting
terms.
2025-08-17 05:32:40 +00:00
Leonardo de Moura
0cc0de9e51 feat: trim grind ring proof context (#9946)
This PR optimizes the proof terms produced by `grind ring`. It is
similar to #9945, but for the ring module in `grind`.
It removes unused entries from the context objects when generating the
final proof, significantly reducing the amount of junk in the resulting
terms. Example:
```lean
/--
trace: [grind.debug.proof] fun h h_1 h_2 h_3 =>
      Classical.byContradiction fun h_4 =>
        let ctx := RArray.branch 1 (RArray.leaf x) (RArray.leaf x⁻¹);
        let e_1 := (Expr.var 0).mul (Expr.var 1);
        let e_2 := Expr.num 0;
        let e_3 := Expr.num 1;
        let e_4 := (Expr.var 0).pow 2;
        let m_1 := Mon.mult (Power.mk 1 1) Mon.unit;
        let m_2 := Mon.mult (Power.mk 0 1) Mon.unit;
        let p_1 := Poly.num (-1);
        let p_2 := Poly.add (-1) (Mon.mult (Power.mk 0 1) Mon.unit) (Poly.num 0);
        let p_3 := Poly.add 1 (Mon.mult (Power.mk 0 2) Mon.unit) (Poly.num 0);
        let p_4 := Poly.add 1 (Mon.mult (Power.mk 0 1) (Mon.mult (Power.mk 1 1) Mon.unit)) (Poly.num (-1));
        let p_5 := Poly.add 1 (Mon.mult (Power.mk 0 1) Mon.unit) (Poly.num 0);
        one_eq_zero_unsat ctx p_1 (eagerReduce (Eq.refl true))
          (Stepwise.simp ctx 1 p_4 (-1) m_1 p_5 p_1 (eagerReduce (Eq.refl true))
            (Stepwise.core ctx e_1 e_3 p_4 (eagerReduce (Eq.refl true)) (diseq0_to_eq x h_4))
            (Stepwise.mul ctx p_2 (-1) p_5 (eagerReduce (Eq.refl true))
              (Stepwise.superpose ctx 1 m_2 p_4 (-1) m_1 p_3 p_2 (eagerReduce (Eq.refl true))
                (Stepwise.core ctx e_1 e_3 p_4 (eagerReduce (Eq.refl true)) (diseq0_to_eq x h_4))
                (Stepwise.core ctx e_4 e_2 p_3 (eagerReduce (Eq.refl true)) h))))
-/
#guard_msgs in -- Context should contains only `x` and its inverse.
set_option trace.grind.debug.proof true in
set_option pp.structureInstances false in
open Lean Grind CommRing in
example [Field α] (x y z w : α) :
   x^2 = 0 → y^2 = 0 → z^3 = 0 → w^2 = 0 → x = 0 := by
  grind
```
2025-08-17 04:44:47 +00:00
Leonardo de Moura
010468699f feat: trim grind cutsat proof context (#9945)
This PR optimizes the proof terms produced by `grind cutsat`. It removes
unused entries from the context objects when generating the final proof,
significantly reducing the amount of junk in the resulting terms.
Example:
```lean
/--
trace: [grind.debug.proof] fun h h_1 h_2 h_3 h_4 h_5 h_6 h_7 h_8 =>
      let ctx := RArray.leaf (f 2);
      let p_1 := Poly.add 1 0 (Poly.num 0);
      let p_2 := Poly.add (-1) 0 (Poly.num 1);
      let p_3 := Poly.num 1;
      le_unsat ctx p_3 (eagerReduce (Eq.refl true)) (le_combine ctx p_2 p_1 p_3 (eagerReduce (Eq.refl true)) h_8 h_1)
-/
#guard_msgs in -- Context should contain only `f 2`
open Lean Int Linear in
set_option trace.grind.debug.proof true in
example (f : Nat → Int) :
    f 1 <= 0 → f 2 <= 0 → f 3 <= 0 → f 4 <= 0 → f 5 <= 0 → 
    f 6 <= 0 → f 7 <= 0 → f 8 <= 0 → -1 * f 2 + 1 <= 0 → False := by
  grind
```
2025-08-17 02:53:19 +00:00
Sebastian Ullrich
4a6004b8fa perf: use Lean.realizeValue in getFunInfo (#9810) 2025-08-16 15:02:29 +00:00
Sebastian Graf
c6df4a4a89 fix: delegate to exact in mvcgen using invariants to avoid MVar mishaps (#9939)
This PR expands `mvcgen using invariants | $n => $t` to `mvcgen; case
inv<$n> => exact $t` to avoid MVar instantiation mishaps observable in
the test case for #9581.

Closes #9581.
2025-08-16 09:40:42 +00:00
Sebastian Graf
ee4cbbeb14 fix: remove duplicate mpure_intro tactic definition (#9938)
This PR removes a duplicate `mpure_intro` tactic definition.
2025-08-16 09:19:07 +00:00
Cameron Zwarich
0e968f010a chore: fix indentation (#9936) 2025-08-16 05:30:36 +00:00
Lean stage0 autoupdater
b0d42e6ac9 chore: update stage0 2025-08-16 02:17:49 +00:00
Sebastian Graf
df898a5c87 chore: make test mvcgenUsingWith deterministic (#9933) 2025-08-15 17:57:55 +00:00
Sebastian Graf
bdc9124228 feat: implement Std.Do.Triple.mp (#9931)
This PR implements `Std.Do.Triple.mp`, enabling users to compose two
specifications for the same program.
2025-08-15 17:44:15 +00:00
Leonardo de Moura
aad98fe749 fix: revert Nat.sub embedding into Int (#9930)
This PR reverts the way `grind cutsat` embeds `Nat.sub` into `Int`. It
fixes a regression reported by David Renshaw on Zulip.


https://leanprover.zulipchat.com/#narrow/channel/113488-general/topic/v4.2E23.2E0/near/534646557
2025-08-15 16:06:31 +00:00
Sebastian Ullrich
506d16a603 chore: complete riscv_ast benchmark (#9928) 2025-08-15 14:39:25 +00:00
Sebastian Graf
9e1d97c261 feat: extended using invariants and with syntax for mvcgen (#9927)
This PR implements extended `induction`-inspired syntax for `mvcgen`,
allowing optional `using invariants` and `with` sections.

```lean
  mvcgen
  using invariants
  | 1 => Invariant.withEarlyReturn
      (onReturn := fun ret seen => ⌜ret = false ∧ ¬l.Nodup⌝)
      (onContinue := fun traversalState seen =>
        ⌜(∀ x, x ∈ seen ↔ x ∈ traversalState.prefix) ∧ traversalState.prefix.Nodup⌝)
  with mleave -- mleave is a no-op here, but we are just testing the grammar
  | vc1 => grind
  | vc2 => grind
  | vc3 => grind
  | vc4 => grind
  | vc5 => grind
```
2025-08-15 12:25:01 +00:00
Lean stage0 autoupdater
4c562fc1a3 chore: update stage0 2025-08-15 12:21:02 +00:00
Sebastian Ullrich
415a58f9fb chore: warn on [expose] on private definition (#9917) 2025-08-15 11:31:33 +00:00
Sebastian Graf
85ba133df0 fix: better ProofMode/Delab (#9926)
This PR guards the `Std.Tactic.Do.MGoalEntails` delaborator by a check
ensuring that there are at least 3 arguments present, preventing
potential panics.
2025-08-15 08:36:34 +00:00
Kim Morrison
3ee8d35031 chore: begin dev cycle for v4.24.0 (#9925) 2025-08-15 08:02:04 +00:00
Sebastian Graf
45fbe4a73d fix: documentated examples for PostCond, move around tests (#9924)
This PR fixes examples in the documentation for `PostCond`.
2025-08-15 07:59:33 +00:00
Sofia Rodrigues
287b173844 fix: background function and forIn (#9560)
This PR fixes the `forIn` function, that previously caused the resulting
Promise to be dropped without a value when an exception was thrown
inside of it. It also corrects the parameter order of the `background`
function.
2025-08-15 02:39:57 +00:00
Cameron Zwarich
05c1ba291d fix: erase dependencies on let-bound fvars in internalizeCode (#9922)
This PR changes `internalizeCode` to replace all substitutions with
non-param-bound fvars in `Expr`s (which are all types) with `lcAny`,
preserving the invariant that there are no such dependencies. The
violation of this invariant across files caused test failures in a
pending PR, but it is difficult to write a direct test for it. In the
future, we should probably change the LCNF checker to detect this.

This change also speeds up some compilation-heavy benchmarks much more
than I would've expected, which is a pleasant surprise. This indicates
we might get more speedups from reducing the amount of type information
we preserve in LCNF.
2025-08-15 01:52:47 +00:00
Sebastian Ullrich
15a065d14d fix: panic in delabPRange (#9920)
This PR fixes a panic in the delaborator for `Std.PRange`. It also
modifies the delaborators for both `Std.Range` and `Std.PRange` to not
use `let_expr`, which cleans up annotations and metadata, since
delaborators must follow the structures of expressions. It adds support
for `pp.notation` and `pp.explicit` options. It also adds tests for
these delaborators.

---------

Co-authored-by: Kim Morrison <kim@tqft.net>
Co-authored-by: Kyle Miller <kmill31415@gmail.com>
2025-08-15 01:50:23 +00:00
Kim Morrison
35a753dc98 fix: panic in ProofMode/Delab (#9923)
This PR adds a guard for a delaborator that is causing panics in
doc-gen4. This is a band-aid solution for now, and @sgraf812 will take a
look when they're back from leave.
2025-08-15 01:01:12 +00:00
Leonardo de Moura
06d05d1f46 feat: missing grind annotations (#9921)
This PR marks `List.drop_length` and `List.take_length` with `[grind
=]`.
2025-08-14 22:47:42 +00:00
Leonardo de Moura
fe7e0859d5 fix: div/norm normalization assumptions in grind (#9919)
This PR ensures `grind cutsat` does not rely on div/mod terms to have
been normalized. The `grind` preprocessor has normalizers for them, but
sometimes they cannot be applied because of type dependencies.

Closes #9907
2025-08-14 22:28:25 +00:00
Lean stage0 autoupdater
76971a88ff chore: update stage0 2025-08-14 16:21:50 +00:00
Sebastian Ullrich
ddfeca1b1b fix: do not allow access to private primitives in public scope (#9890)
This PR addresses a missing check in the module system where private
names that remain in the public environment map for technical reasons
(e.g. inductive constructors generated by the kernel and relied on by
the code generator) accidentally were accessible in the public scope.
2025-08-14 15:34:54 +00:00
Sebastian Ullrich
0ab29c7420 fix: do not show progress bar for checking/compiling helper decls (#9786)
This PR ensures we only show progress bars for computations directly
relevant to users

---------

Co-authored-by: Marc Huisinga <mhuisi@protonmail.com>
2025-08-14 14:46:38 +00:00
Sebastian Ullrich
1ba1424ac3 perf: local metaExt (#9822) 2025-08-14 14:26:12 +00:00
Kim Morrison
c8dae31ba5 feat: review of grind annotations for Option (#9863)
This PR reviews `grind` annotations for `Option`, preferring to use
`@[grind =]` instead of `@[grind]` (and fixing a few problems revealed
by this), and making sure `@[grind =]` theorems are "fully applied".
2025-08-14 11:08:05 +00:00
Lean stage0 autoupdater
49cd03bc29 chore: update stage0 2025-08-14 10:47:52 +00:00
Sebastian Ullrich
6e1451dbd8 fix: duplicate private instance name avoidance under the module system (#9914) 2025-08-14 10:03:41 +00:00
Joachim Breitner
6b3aed29b9 feat: unused simp argument linter to explain false positives around (#9912)
This PR lets the unused simp argument linter explain that the given hint
of removing `←` arguments may be too strong, and that replacing them
with `-` arguments can be needed. Fixes #9909.
2025-08-14 09:54:21 +00:00
Sebastian Graf
34fe6b460c chore: fix docs of mspec (#9913)
Just docs.
2025-08-14 09:49:11 +00:00
Joachim Breitner
62f9de5edf fix: fun_induction to instantiateMVars (#9877)
This PR makes `fun_induction foo` instantiate the MVars in the goal
before searching for suitable applications of foo. Fixes #9844.
2025-08-14 09:42:26 +00:00
Sebastian Graf
0c39a50337 feat: Rename Std.List.Zipper to List.Cursor (#9911)
This PR renames `Std.List.Zipper` to `List.Cursor`, with slight changes
to the implementation (no `reverse`) and use in loop specification
lemmas.
2025-08-14 09:17:54 +00:00
Sebastian Ullrich
535435955b chore: remove broken Nix build (#9910) 2025-08-14 08:31:39 +00:00
Marc Huisinga
93e35dc3da feat: add lakefile.toml json schema (#9871)
This PR adds a JSON schema for `lakefile.toml`. Importantly, this schema
is *not* intended for validating `lakefile.toml`, but is instead
optimized for auto-completion and hovers using the [Even Better
TOML](https://marketplace.visualstudio.com/items?itemName=tamasfe.even-better-toml)
VS Code extension.

Once merged, I will attempt to contribute a link to this schema to the
[JSON Schema store](https://github.com/SchemaStore/schemastore). When
that is done, we can integrate the Lean 4 VS Code extension with Even
Better TOML, providing us with language server support in
`lakefile.toml`.

The schema contributed by this PR has the following known deficiencies:
- Superfluous properties do not produce an error.
- The structure of complicated structures (e.g. path or version
patterns) is deliberately not accurately reflected in the schema. Even
Better TOML doesn't seem to handle these structures well in
auto-completion.
- Due to the lack of an accurate declarative spec of the lakefile.toml
format and several deviations from the format to provide better
auto-completions, this schema will have to be kept in sync manually with
the code in Lake, at least for now.
2025-08-14 07:24:40 +00:00
Leonardo de Moura
05e8c856fa fix: reset decision stack in grind linarith (#9904)
This PR ensures the decision stack is reset after an assignment is found
in `grind linarith`.

Closes #9897
2025-08-14 02:53:01 +00:00
Leonardo de Moura
2e991d3b10 fix: panic at invalid pattern in grind (#9902)
This PR fixes a panic when an invalid pattern is provided to `grind`.

closes #9899
2025-08-14 02:25:37 +00:00
Kim Morrison
f60f946e11 chore: missing doc-strings for grind typeclasses (#9900)
This PR adds some missing doc-strings for grind typeclasses.
2025-08-14 02:15:13 +00:00
Leonardo de Moura
253c10c398 fix: normalize Nat.cast and Int.cast of numerals in grind (#9901)
This PR ensures that `Nat.cast` and `Int.cast` of numerals are
normalized by `grind`.
It also adds a `simp` flag for controlling how bitvector literals are
represented. By default, the bitvector simprocs use `BitVec.ofNat`. This
representation is problematic for the `grind ring` and `grind cutsat`
modules. The new flag allows the use of `OfNat.ofNat` and `Neg.neg` to
represent literals, consistent with how they are represented for other
commutative rings.

Closes #9321
2025-08-14 02:04:55 +00:00
Leonardo de Moura
f8c743e37d feat: consider all singleton patterns in local forall expressions in grind (#9896)
This PR improves the heuristic used to select patterns for local
`forall` expressions occurring in the goal being solved by `grind`. It
now considers all singleton patterns in addition to the selected
multi-patterns. Example:
```lean
example (p : Nat → Prop) (h₁ : x < n) (h₂ : ¬ p x) : ∃ i, i < n ∧ ¬ p i := by
  grind
```
2025-08-13 18:45:29 +00:00
Sebastian Graf
f80274be6b fix: Rename M.by_wp lemmas according to naming convention (#9894)
This PR renames `M.by_wp` lemmas to `M.of_wp_*`.
2025-08-13 16:56:07 +00:00
Sebastian Graf
d93cdde938 feat: Aggressively eta expand before applying a spec in mvcgen (#9888)
This PR makes `mvcgen` aggressively eta-expand before trying to apply a
spec. This ensures that `mspec` will be able to frame hypotheses
involving uninstantiated loop invariants in goals for the inductive step
of a loop instead of losing them in a destructive world update.
2025-08-13 15:53:48 +00:00
Sebastian Ullrich
640337e0a0 chore: error on [macro_inline] without [expose] (#9891) 2025-08-13 10:57:48 +00:00
Sebastian Graf
55f9dfad7d feat: More grind annotations for List.range' (#9766)
This PR moves `List.range'_elim` to `List.eq_of_range'_eq_append_cons`
and adds a couple of `grind` annotations for `List.range'`. This will
make it more convenient to work with proof obligations produced by
`mvcgen`.
2025-08-13 09:27:48 +00:00
Sebastian Graf
b9a8dd8f0d feat: simp and grind rules for ExceptConds (#9889)
This PR adds `simp` and `grind` rules for
`ExceptCond.{const,true,false}`.
2025-08-13 08:11:22 +00:00
Sebastian Graf
f973e855e0 feat: Make mrefine reduce applications of SPred.and (#9887)
This PR makes `mrefine` reduce applications of `SPred.and`.
2025-08-13 07:50:17 +00:00
Kim Morrison
93e0ebf25c feat: make Lean.Grind.Preorder a mixin (#9885)
This PR is initially motivated by noticing `Lean.Grind.Preorder.toLE`
appearing in long Mathlib typeclass searches; this change will prevent
these searches. These changes are also helpful preparation for
potentially dropping the custom `Lean.Grind.*` typeclasses, and unifying
with the new typeclasses introduced in #9729.
2025-08-13 05:02:39 +00:00
Leonardo de Moura
21fa5d10f4 chore: move tests that are working (#9884) 2025-08-13 00:46:54 +00:00
Leonardo de Moura
0046b8b4bb feat: warning based on patterns for grind (#9883)
This PR refines the warning message for redundant `grind` arguments. It
is not based on the actual inferred pattern instead provided kind.
2025-08-13 00:42:09 +00:00
Cameron Zwarich
639baaaa03 refactor: adopt do notation (#9882) 2025-08-12 22:12:59 +00:00
Cameron Zwarich
6f7ca5e5d3 refactor: take more advantage of anonymous constructors (#9881) 2025-08-12 21:19:40 +00:00
Lean stage0 autoupdater
5210cdf43f chore: update stage0 2025-08-12 21:07:52 +00:00
Leonardo de Moura
072e3e89e3 fix: local forall activation in grind (#9880)
This PR ensures a local forall is activated at most once per pattern in
`grind`.
2025-08-12 19:49:05 +00:00
Leonardo de Moura
6e18afac8c feat: kernel hint for proof-by-reflection (#9865)
This PR adds improved support for proof-by-reflection to the kernel type
checker. It addresses the performance issue exposed by #9854. With this
PR, whenever the kernel type-checks an argument of the form `eagerReduce
_`, it enters "eager-reduction" mode. In this mode, the kernel is more
eager to reduce terms. The new `eagerReduce _` hint is often used to
wrap `Eq.refl true`. The new hint should not negatively impact any
existing Lean package.

---------

Co-authored-by: Joachim Breitner <mail@joachim-breitner.de>
2025-08-12 19:24:47 +00:00
Sebastian Ullrich
a9145d3312 fix: do not block in snapshot reporter when creating ilean update (#9784)
This PR ensures the editor progress bar better reflects the actual
progress of parallel elaboration.
2025-08-12 16:08:59 +00:00
Leonardo de Moura
5801dff9ea chore: Eq.refl (#9878) 2025-08-12 15:34:29 +00:00
Leonardo de Moura
54dce214d1 fix: nondeterminism in grind ring (#9867)
This PR fixes a nondeterministic behavior in `grind ring`.

Closes #9825
2025-08-12 15:27:39 +00:00
Sebastian Graf
e5bb854748 feat: Add delaborator for Std.PRange notation (#9850)
This PR add a delaborator for `Std.PRange` notation.
2025-08-12 08:51:27 +00:00
Cameron Zwarich
e9df183e87 perf: avoid ref count increments for borrowed array accesses (#9866) 2025-08-12 05:27:35 +00:00
Lean stage0 autoupdater
954957c456 chore: update stage0 2025-08-12 05:06:58 +00:00
Cameron Zwarich
dfc8e38a21 feat: add array access functions that return a borrowed result (#9864)
This PR adds new variants of `Array.getInternal` and
`Array.get!Internal` that return their argument borrowed, i.e. without a
reference count increment. These are intended for use by the compiler in
cases where it can determine that the array will continue to hold a
valid reference to the element for the returned value's lifetime.

In the future, this will likely be replaced by a return value borrow
annotation, in which case the special variant of the functions could be
removed, with the compiler inserting an extra `inc` in the non-borrow
cases.
2025-08-12 04:25:14 +00:00
Cameron Zwarich
bf348ae60f refactor: use more helper functions (#9862) 2025-08-11 23:56:50 +00:00
Leonardo de Moura
4df4968538 fix: grind theorem activation (#9860)
This PR fixes E-matching theorem activation in `grind`.

Fixes #9856
2025-08-11 22:59:35 +00:00
Cameron Zwarich
ca05569cd5 refactor: rename VarProjInfo to DerivedValInfo (#9859)
We want to use this for non-projections in the near future.
2025-08-11 22:02:28 +00:00
Leonardo de Moura
a157abbbc9 fix: E-matching patterns containing ground universe polymorphic patterns in grind (#9857)
This PR ensures `grind` can E-match patterns containing universe
polymorphic ground sub-patterns. For example, given
```
set_option pp.universes true in
attribute [grind?] Id.run_pure
```
the pattern
```
Id.run_pure.{u_1}: [@Id.run.{u_1} #1 (@pure.{u_1, u_1} `[Id.{u_1}] `[Applicative.toPure.{u_1, u_1}] _ #0)]
```
contains two nested universe polymorphic ground patterns
- `Id.{u_1}`
- `Applicative.toPure.{u_1, u_1}`

This kind of pattern is not common, but it occurs in core.
2025-08-11 21:12:57 +00:00
Leonardo de Moura
5abf4bb651 fix: additional numeral normalization in grind (#9853)
This PR adds `Nat` and `Int` numeral normalizers in `grind`.

closes #9828
2025-08-11 19:13:17 +00:00
Leonardo de Moura
7ea711e043 fix: remove inShareCommon filter used in grind (#9852)
This PR removes the `inShareCommon` quick filter used in `grind`
preprocessing steps. `shareCommon` is no longer used only for fully
preprocessed terms.

closes #9830
2025-08-11 18:24:13 +00:00
Sebastian Graf
b853166575 feat: Deterministic case labels in mvcgen (#9843)
This PR makes `mvcgen` produce deterministic case labels for the
generated VCs. Invariants will be named `inv<n>` and every other VC will
be named `vc<n>.*`, where the `*` part serves as a loose indication of
provenance.
2025-08-11 14:57:59 +00:00
Paul Reichert
0725349bbd feat: high-level order typeclasses (#9729)
This PR introduces a canonical way to endow a type with an order
structure. The basic operations (`LE`, `LT`, `Min`, `Max`, and in later
PRs `BEq`, `Ord`, ...) and any higher-level property (a preorder, a
partial order, a linear order etc.) are then put in relation to `LE` as
necessary. The PR provides `IsLinearOrder` instances for many core types
and updates the signatures of some lemmas.

**BREAKING CHANGES:**

* The requirements of the `lt_of_le_of_lt`/`le_trans` lemmas for
`Vector`, `List` and `Array` are simplified. They now require an
`IsLinearOrder` instance. The new requirements are logically equivalent
to the old ones, but the `IsLinearOrder` instance is not automatically
inferred from the smaller typeclasses.
* Hypotheses of type `Std.Total (¬ · < · : α → α → Prop)` are replaced
with the equivalent class `Std.Asymm (· < · : α → α → Prop)`. Breakage
should be limited because there is now an instance that derives the
latter from the former.
* In `Init.Data.List.MinMax`, multiple theorem signatures are modified,
replacing explicit parameters for antisymmetry, totality, `min_ex_or`
etc. with corresponding instance parameters.
2025-08-11 14:55:17 +00:00
Sebastian Graf
264e451d3c feat: Add @[spec] lemmas for forIn at Std.PRange (#9848)
This PR adds `@[spec]` lemmas for `forIn` and `forIn'` at `Std.PRange`.
2025-08-11 14:34:34 +00:00
Cameron Zwarich
5b5bb5174b fix: check for recursive decls before instance proj inlining (#9847)
This PR adds a check for reursive decls in this bespoke inlining path,
which fixes a regression from the old compiler.

Fixes #9624.
2025-08-11 13:50:26 +00:00
Sofia Rodrigues
14120a519c fix: replace 'D' with 'd' for day representation in long date format (#9799)
This PR fixes the #9410 issue.
2025-08-11 13:17:34 +00:00
Sebastian Graf
2875e8f277 chore: Add Nodup and Fresh tests to doLogicTests.lean (#9837)
Two test cases that will be added to the reference manual
2025-08-11 09:12:38 +00:00
Sebastian Graf
9a0c1ab2d0 feat: Simpler first-order implementation for pure SPreds (#9841)
This PR migrates the ⌜p⌝ notation for embedding pure p : Prop into SPred
σs to expand into a simple, first-order expression SPred.pure p that can
be supported by e-matching in grind.

Doing so deprives ⌜p⌝ notation of its idiom-bracket-like support for
#selector and ‹Nat›ₛ syntax which is thus removed.
2025-08-11 08:32:16 +00:00
Paul Reichert
f15d531acb refactor: reduce omega's dependency on fvar IDs (#9723)
This PR replaces some `HashSet Expr`-typed collections of facts in
`omega`'s implementation with plain lists. This change makes some
`omega` calls faster, some slower, but the advantage is that `omega`'s
performance is more independent the state of the name generator that
produces fvar IDs.

I've created this PR for discussion and am happy to hear opinions on
whether this should be merged or not. A good reason *not* to merge is
that it causes regressions in some places and `grind` is expected to
supersede `omega` either way. A good reason to merge is that `omega` is
used all over the place and its flaky performance increases the noise in
future benchmarks.
2025-08-11 07:17:24 +00:00
Sebastian Graf
e0fcaf5e7d chore: Naming in Invariant.withEarlyReturn (#9835)
Just a small renaming leftover.
2025-08-11 06:43:30 +00:00
Sebastian Graf
1b78d8f0a3 fix: Rewriting in mvcgen when there are excess arguments to wp (#9834)
This PR fixes a bug in `mvcgen` triggered by excess state arguments to
the `wp` application, a situation which arises when working with
`StateT` primitives.
2025-08-11 06:42:08 +00:00
Sebastian Graf
66772d77fc fix: Work around a DefEq bug in mspec involving delayed assignments (#9833)
This PR works around a DefEq bug in `mspec` involving delayed
assignments.
2025-08-11 06:40:19 +00:00
Sebastian Graf
d64637e8c7 fix: Add simp lemmas SPred.entails_<n> to replace SPred.entails_cons (#9832)
This PR adds simp lemmas `SPred.entails_<n>` to replace
`SPred.entails_cons` which was disfunctional as a simp lemma due to
#8074.
2025-08-11 06:38:33 +00:00
Sebastian Graf
02fa9641fd feat: Add delaborator for Std.Range (#9831)
This PR adds a delaborator for `Std.Range` notation.
2025-08-11 06:36:26 +00:00
Cameron Zwarich
4506173a27 fix: support overapplication of Quot.lift in the compiler (#9827)
This PR changes the lowering of `Quot.lcInv` (the compiler-internal form
of `Quot.lift`) in `toMono` to support overapplication.

Fixes #9806.
2025-08-11 01:51:54 +00:00
Kyle Miller
20eea7372f feat: make delta deriving more robust and handle binders (#9800)
This PR improves the delta deriving handler, giving it the ability to
process definitions with binders, as well as the ability to recursively
unfold definitions. Furthermore, delta deriving now tries all explicit
non-out-param arguments to a class, and it can handle "mixin" instance
arguments. The `deriving` syntax has been changed to accept general
terms, which makes it possible to derive specific instances with for
example `deriving OfNat _ 1` or `deriving Module R`. The class is
allowed to be a pi type, to add additional hypotheses; here is a Mathlib
example:
```lean
def Sym (α : Type*) (n : ℕ) :=
  { s : Multiset α // Multiset.card s = n }
deriving [DecidableEq α] → DecidableEq _
```
This underscore stands for where `Sym α n` may be inserted, which is
necessary when `→` is used. The `deriving instance` command can refer to
scoped variables when delta deriving as well. Breaking change: the
derived instance's name uses the `instance` command's name generator,
and the new instance is added to the current namespace.

This closes
[mathlib4#380](https://github.com/leanprover-community/mathlib4/issues/380).
2025-08-10 21:21:54 +00:00
Mac Malone
79f6bb6f54 refactor: lake: reorganize tests/module (#9824)
This PR reorganizes the directory structure of Lake's module test and
renames some of the files to be more descriptive.

Originally, this was meant to be combined with a fix, but that fix
appears to be incorrect, so this is just a refactor.
2025-08-10 19:16:55 +00:00
Kyle Miller
fc076c5acc fix: get DecidableEq deriving handler to work for enumerations in higher universes (#9818)
This PR fixes a bug where the `DecidableEq` deriving handler did not
take universe levels into account for enumerations (inductive types
whose constructors all have no fields). Closes #9541.
2025-08-10 16:29:02 +00:00
Henrik Böving
44d3cfb3dc chore: stabilize benchmark output (#9820) 2025-08-10 10:53:38 +00:00
Sebastian Ullrich
0985326b2e chore: remove unnecessary withoutExporting use (#9821) 2025-08-10 10:20:31 +00:00
Kyle Miller
cbeef963a9 fix: have unsafe term produce an opaqueDecl (#9819)
This PR makes the `unsafe t` term create an auxiliary opaque
declaration, rather than an auxiliary definition with opaque
reducibility hints.
2025-08-10 09:30:55 +00:00
Cameron Zwarich
544f9912b7 chore: add separate profiling entries for base, mono, and IR phases (#9817) 2025-08-10 05:00:49 +00:00
Cameron Zwarich
361ca788a7 refactor: split the LCNF pass list into separate base/mono lists (#9816)
This will make it easier to run the two phases in parallel.
2025-08-10 04:23:19 +00:00
Leonardo de Moura
68a249d23d perf: normalizeLevels in grind (#9814)
This PR skips the `normalizeLevels` preprocessing step in `grind` when
it is not needed.
2025-08-10 00:51:20 +00:00
Leonardo de Moura
95c8f1f866 fix: unfoldReducible in grind (#9813)
This PR fixes an unexpected bound variable panic in `unfoldReducible`
used in `grind`.
2025-08-10 00:02:05 +00:00
Leonardo de Moura
fa17ea2715 chore: include generation in grind.internalize trace message (#9812) 2025-08-09 23:48:43 +00:00
Sebastian Ullrich
c970c74d66 feat: introduce Lean.realizeValue for sharing computation results between compatible environment branches (#9798)
This PR introduces `Lean.realizeValue`, a new metaprogramming API for
parallelism-aware caching of `MetaM` computations
2025-08-09 17:19:29 +00:00
Leonardo de Moura
479da83f57 feat: grind annotation analyzer (#9809)
This PR adds a script for analyzing `grind` E-matching annotations. The
script is useful for detecting matching loops. We plan to add
user-facing commands for running the script in the future.
2025-08-09 17:14:57 +00:00
Yaël Dillies
feca9e8103 fix: allow trailing comma in the arg list of simp?, dsimp?, simpa, etc (#9804)
This PR allows trailing comma in the argument list of `simp?`, `dsimp?`,
`simpa`, etc... Previously, it was only allowed in the non `?` variants
of `simp`, `dsimp`, `simp_all`.

Closes #7383.
2025-08-09 16:37:30 +00:00
Leonardo de Moura
a041ffa702 chore: remove leftover (#9808) 2025-08-09 15:58:50 +00:00
Sebastian Graf
5eafc080e1 feat: Simplify Std.List.Zipper.pref using mleave (#9807)
This PR adds `Std.List.Zipper.pref` to the simp set of `mleave`.
2025-08-09 15:57:47 +00:00
Sebastian Graf
8558b2d278 feat: Improved API for invariants and postconditions (#9805)
This PR improves the API for invariants and postconditions and as such
introduces a few breaking changes to the existing pre-release API around
`Std.Do`. It also adds Markus Himmel's `pairsSumToZero` example as a
test case.
2025-08-09 14:42:37 +00:00
Cameron Zwarich
756f837f82 perf: reduce redundant inc/dec using "implied borrows" from projections and liveness (#9801)
This PR changes the IR RC pass to take "implied borrows" from
projections into account. If a projected value's lifetime is contained
in that of its parent (or any projection ancestor), then it does not
need its reference count incremented (or later decremented).

I believe that this same technique should generalize to both the
reset/reuse and borrow signature inference passes.
2025-08-09 14:13:50 +00:00
Sebastian Ullrich
0b838ff2c9 chore: update stage0 2025-08-09 12:35:07 +02:00
Sebastian Ullrich
ca43608aa0 feat: allow combining private/public and protected 2025-08-09 12:35:07 +02:00
Rob23oba
ad471b46b8 fix: Inhabited instance of StdGen (#9782)
This PR corrects the `Inhabited` instance of `StdGen` to use a valid
initial state for the pseudorandom number generator. Previously, the
`default` generator had the property that `Prod.snd (stdNext default) =
default`, so it would produce only constant sequences.

[Zulip
discussion](https://leanprover.zulipchat.com/#narrow/channel/113489-new-members/topic/inhabited.20instance.20for.20StdGen.20isn't.20very.20random/with/533247146)
2025-08-08 06:23:48 +00:00
Kim Morrison
e6b357e87a chore: @[expose] List.mapIdxM (#9794) 2025-08-08 04:55:50 +00:00
Kim Morrison
b676fb1164 fix: @[expose] String.firstDiffPos and String.extract (#9792)
This PR adds `@[expose]` to two definitions with `where` clauses that
Batteries proves theorems about.
2025-08-08 04:55:45 +00:00
Kim Morrison
ca68b84623 chore: @[expose] List.filterMapTR (#9793)
This PR adds `@[expose]`, as Batteries wants access to the `where`
clause.
2025-08-08 04:55:38 +00:00
Kim Morrison
d6bc78dcb8 feat: split out Expr.getMVarDependencies from MVarId.getMVarDependencies (#9785)
This PR splits out an implementation detail of
MVarId.getMVarDependencies into a top-level function. Aesop was relying
on the function defined in the where clause, which is no longer possible
after #9759.
2025-08-08 00:28:30 +00:00
Cameron Zwarich
2104fd7da9 chore: remove unused default (#9791) 2025-08-07 16:27:23 +00:00
Kyle Miller
c801a9e8cf feat: use the metavariable index when pretty printing (#9778)
This PR modifies the pretty printing of anonymous metavariables to use
the index rather than the internal name. This leads to smaller numerical
suffixes in `?m.123` since the indices are numbered within a given
metavariable context rather than across an entire file, hence each
command gets its own numbering. This does not yet affect pretty printing
of universe level metavariables.

For debugging purposes, metavariables that are not defined now pretty
print as `?_mvar.123` rather than cause pretty printing to fail.
2025-08-07 15:58:51 +00:00
Sebastian Ullrich
c9a6446041 chore: CI: include tests in rebootstrap check (#9788) 2025-08-07 15:37:36 +00:00
Cameron Zwarich
a2f24fac65 chore: use unreachable! for unreachable cases, not silent fallback (#9790) 2025-08-07 15:23:01 +00:00
Cameron Zwarich
eaec888dc3 refactor: add isPossibleRef/isDefiniteRef fields to RC VarInfo (#9789)
These are the only uses of the existing `type` field, so we might as
well compute them up-front and store them.
2025-08-07 14:21:19 +00:00
Sebastian Graf
69d8cca38a feat: Add a simp lemma for PostCond.const (#9787)
This PR adds a simp lemma `PostCond.const_apply`.
2025-08-07 13:15:22 +00:00
Sebastian Graf
04a3968206 chore: Move withFreshUserNames to Lean/Meta/Basic.lean (#9783)
This PR generalizes and moves `withFreshUserNames` to
Lean/Meta/Basic.lean where it can be reused.
2025-08-07 10:27:52 +00:00
Sebastian Graf
ae699a6b13 fix: proper hygiene for goals generated by mvcgen (#9781)
This PR ensures that `mvcgen` is hygienic. The goals it generates should
now introduce all locals inaccessibly.
2025-08-07 09:33:06 +00:00
Kim Morrison
9257ef42ba feat: extend grind category theory tests (#9780)
This PR extends the test suite for `grind` working category theory, to
help debug outstanding problems in Mathlib.
2025-08-07 05:56:42 +00:00
Kim Morrison
63f899a407 chore: cleanup tests/lean/run/grind_cat (#9779)
Just tidying up and organising into sections, in preparation for
extending to capture problems in Mathlib.
2025-08-07 04:20:39 +00:00
Leonardo de Moura
690cf16aa5 fix: merge simplification and unfolding steps in grind (#9776)
This PR combines the simplification and unfold-reducible-constants steps
in `grind` to ensure that no potential normalization steps are missed.

Closes #9610
2025-08-07 04:15:52 +00:00
Cameron Zwarich
aaf831cd93 perf: don't mark params stored in tagged pointers as borrowed (#9775) 2025-08-07 03:49:23 +00:00
Cameron Zwarich
472a0b4954 refactor: invert and rename RC VarInfo.mustBeConsumed to .inheritsBorrowFromParam (#9777) 2025-08-07 02:04:12 +00:00
Cameron Zwarich
c04323a7d5 perf: use a slightly more refined borrowed param test in RC pass (#9774) 2025-08-07 01:08:32 +00:00
Cameron Zwarich
fac4905e89 chore: fix function body indentation (#9773) 2025-08-07 00:44:43 +00:00
Leonardo de Moura
65e55ac094 fix: projection propagation in grind (#9772)
This PR fixes a bug in the projection over constructor propagator used
in `grind`. It may construct type incorrect terms when a equivalence
class contains heterogeneous equalities.

closes #9769
2025-08-06 21:05:45 +00:00
Cameron Zwarich
f23d24ec7c perf: avoid computing liveness twice for cases in RC pass (#9770) 2025-08-06 18:54:58 +00:00
Cameron Zwarich
e332adf3d5 perf: avoid computing liveness twice for join point decls in RC pass (#9768)
We compute the liveness information for the join point body, so the only
thing that updateJPLiveVarMap should be adding is the binding of the
params, which we can easily do ourselves.

If we supported recursive join points, I believe this would actually be
a correctness issue, but as-is it doesn't affect the output.
2025-08-06 17:34:29 +00:00
Leonardo de Moura
13f00ea8ed fix: equality congruence proofs in grind (#9767)
This PR fixes equality congruence proof terms contructed by `grind`.
2025-08-06 16:40:27 +00:00
Lean stage0 autoupdater
a14e542ecb chore: update stage0 2025-08-06 16:54:50 +00:00
Sébastien Boisgérault
6065f08528 doc: fix "the same as the same as" in the description of |> (#9765)
This PR fixes the documentation of the pipe operator |>, which is
currently (emphasis mine):

> Haskell-like pipe operator `|>`. `x |> f` means **the same as the same
as** `f x`,
> and it chains such that `x |> f |> g` is interpreted as `g (f x)`.
2025-08-06 16:11:08 +00:00
Sebastian Ullrich
d455b05619 fix: panic on duplicate private def in public section (#9761) 2025-08-06 16:09:18 +00:00
Sebastian Ullrich
d49b941ea9 feat: default let rec and where decls to private under the module system (#9759)
Re-lands #9666
2025-08-06 15:53:51 +00:00
Sebastian Graf
478be16fc5 feat: Implement mvcgen +jp to prevent exponential VC blowup (#9736)
This PR implements the option `mvcgen +jp` to employ a slightly lossy VC
encoding for join points that prevents exponential VC blowup incurred by
naïve splitting on control flow.

```lean
def ifs_pure (n : Nat) : Id Nat := do
  let mut x := 0
  if n > 0 then x := x + 1 else x := x + 2
  if n > 1 then x := x + 3 else x := x + 4
  if n > 2 then x := x + 1 else x := x + 2
  if n > 3 then x := x + 1 else x := x + 2
  if n > 4 then x := x + 1 else x := x + 2
  if n > 5 then x := x + 1 else x := x + 2
  return x

theorem ifs_pure_triple : ⦃⌜True⌝⦄ ifs_pure n ⦃⇓ r => ⌜r > 0⌝⦄ := by
  unfold ifs_pure
  mvcgen +jp
  /-
  ...
  h✝⁵ : if n > 0 then x✝⁵ = 0 + 1 else x✝⁵ = 0 + 2
  h✝⁴ : if n > 1 then x✝⁴ = x✝⁵ + 3 else x✝⁴ = x✝⁵ + 4
  h✝³ : if n > 2 then x✝³ = x✝⁴ + 1 else x✝³ = x✝⁴ + 2
  h✝² : if n > 3 then x✝² = x✝³ + 1 else x✝² = x✝³ + 2
  h✝¹ : if n > 4 then x✝¹ = x✝² + 1 else x✝¹ = x✝² + 2
  h✝ : if n > 5 then x✝ = x✝¹ + 1 else x✝ = x✝¹ + 2
  ⊢ x✝ > 0
  -/
  grind
```
2025-08-06 15:21:08 +00:00
Sebastian Graf
26c1ddf104 feat: Add grind annotations for key SPred lemmas (#9757)
This PR adds `grind` annotations for key `Std.Do.SPred` lemmas.
2025-08-06 14:36:34 +00:00
Cameron Zwarich
f759d5dbc1 perf: erase all constructor params in the mono phase (#9764) 2025-08-06 14:23:28 +00:00
Paul Reichert
ea09ffc8ce refactor: restore Subarray.foldl and Subarray.forIn signatures (#9762)
This PR does what #9234 regrettably failed to do: actually reintroduce
the signatures of some `Subarray` functions that are now implemented via
slices (see #9017) in order to ensure backward compatibility and
consistency. With this PR, the old interface is restored. As an added
benefit, `Subarray.forIn` is no longer opaque.
2025-08-06 14:15:54 +00:00
Henrik Böving
6d5ce9b87f refactor: implement IO.waitAny using Lean (#9732)
This PR re-implements `IO.waitAny` using Lean instead of C++. This is to
reduce the size and
complexity of `task_manager` in order to ease future refactorings.

There is an import behavioral change of `IO.waitAny` in this PR.
Consider a situation where we have
two promises `p1`, `p2` and call `IO.waitAny [p1.result!, p2.result!]`
and `p1` resolves instantly.
Previously this would just return the result of `p1` and require nothing
else. With the new
implementation if `p2` is released before being resolved this can cause
a panic, even if
`IO.waitAny` has already finished. I argue that this is reasonable
behavior, given that an
invocation of `result!` promises that the promise will eventually be
resolved.
2025-08-06 13:09:15 +00:00
Lean stage0 autoupdater
24d4353ab2 chore: update stage0 2025-08-06 12:37:09 +00:00
Sebastian Ullrich
822f9e0a80 chore: deriving Hashable under the module system (#9760) 2025-08-06 11:55:53 +00:00
Sebastian Ullrich
09600f2ca4 chore: add lakeprof benchmarks (#9709) 2025-08-06 11:25:45 +00:00
Sebastian Ullrich
42e472ff3f refactor: simplify AddConstAsyncResult.commitCheckEnv use (#9715)
Also gets rid of some artifical `blocked (untracked)` time
2025-08-06 11:24:11 +00:00
Sebastian Ullrich
285d271505 doc: more careful Promise.result! docstring (#9734) 2025-08-06 11:23:43 +00:00
Kim Morrison
dcba6dfa7e chore: failing grind test cases for linarith on ordered fields (#9756) 2025-08-06 09:31:09 +00:00
Sebastian Graf
953a1eefbb feat: Implement mrevert ∀ (#9755)
This PR implements a `mrevert ∀n` tactic that "eta-reduces" the stateful
goal and is adjoint to `mintro ∀x1 ... ∀xn`.
2025-08-06 08:53:54 +00:00
Sebastian Graf
d5331d4150 feat: Make mleave apply at * and improve its simp set (#9581) (#9754)
This PR make `mleave` apply `at *` and improve its simp set in order to
discharge some more trivialities (#9581).

It also improves some documentation.
2025-08-06 08:34:45 +00:00
Sebastian Graf
61ea403bfa fix: Make mvcgen mintro let/have bindings (#9474) (#9507)
This PR makes `mvcgen` `mintro` let/have bindings.

Closes #9474.
2025-08-06 07:30:09 +00:00
Parth Shastri
d5e19f9b28 fix: remove accidental instance for lexOrd (#9739)
This PR removes the `instance` attribute from `lexOrd` that was
accidentally applied in `Std.Classes.Ord.Basic`.
2025-08-06 06:16:57 +00:00
Cameron Zwarich
d8c7c9fdb5 refactor: reduce code duplication (#9753) 2025-08-06 05:38:10 +00:00
Kim Morrison
ed1ca47199 chore: add failing grind cutsat tests (#9751)
Further `grind` cutsat failures relative to `omega`, found using Anne's
tactic analysis tool in Mathlib.
2025-08-06 04:15:34 +00:00
Cameron Zwarich
885b8bcc60 chore: inline a function into its only caller (#9750) 2025-08-06 03:47:38 +00:00
Cameron Zwarich
31e05cd2bd chore: fix typos (#9747) 2025-08-06 00:49:49 +00:00
Cameron Zwarich
7fb72a0081 refactor: rename RC VarInfo.consume field to .mustBeConsumed (#9746) 2025-08-06 00:48:10 +00:00
Mac Malone
f3e3ebba81 refactor: move import validation to parser & Lake (#9716)
This PR moves the validation of cross-package `import all` to Lake and
the syntax validation of import keywords (`public`, `meta`, and `all`)
to the two import parsers.

It also fixes the error reporting of the fast import parser
(`Lean.parseImports`) and adds positions to its errors.
2025-08-05 22:36:54 +00:00
Cameron Zwarich
51b780cd9f chore: rewrite LiveVars in a monadic style (#9745) 2025-08-05 21:49:43 +00:00
Cameron Zwarich
25b0c5af34 chore: don't match on Context (#9744) 2025-08-05 21:32:34 +00:00
Sebastian Ullrich
6ab20e7f03 chore: revert "feat: default let rec and where decls to private under the module system" (#9743)
Stage 2 tests broke, to be fixed tomorrow 

Reverts leanprover/lean4#9666
2025-08-05 21:28:08 +00:00
Cameron Zwarich
6846a5179b chore: reduce code duplication (#9742) 2025-08-05 20:42:56 +00:00
Cameron Zwarich
eb5399445a chore: clean up parens (#9740) 2025-08-05 18:21:38 +00:00
Cameron Zwarich
83c08880a6 chore: use dotted constructor names (#9738) 2025-08-05 17:17:06 +00:00
Cameron Zwarich
172a02557e chore: clean up uses of getters (#9737) 2025-08-05 16:48:35 +00:00
Leonardo de Moura
2d3501be61 feat: constant functions in grind (#9735)
This PR extends the propagation rule implemented in #9699 to constant
functions.
2025-08-05 16:19:51 +00:00
Sebastian Ullrich
d07ec9a19f chore: show @[expose] attribute in #print (#9722) 2025-08-05 15:59:49 +00:00
Cameron Zwarich
ed860dfa23 chore: use better struct literal syntax (#9731) 2025-08-05 14:07:39 +00:00
Henrik Böving
09e8079ea3 fix: U/SIntX BEq handling in bv_decide (#9728)
This PR fixes #9724
2025-08-05 11:43:43 +00:00
Sebastian Ullrich
b42a7780e2 feat: default let rec and where decls to private under the module system (#9666)
This PR addresses an outstanding feature in the module system to
automatically mark `let rec` and `where` helper declarations as private
unless they are defined in a public context such as under `@[expose]`.
2025-08-05 11:41:28 +00:00
Henrik Böving
4ee90bd82f fix: tag S/UInt conversions with int_toBitVec (#9721)
This PR tags more `SInt` and `UInt` lemmas with `int_toBitVec` so
`bv_decide`
can handle casts between them and negation.

This is based on a bug report from
https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/open.20scoped.20UInt64.2ECommRing/near/532485974
2025-08-05 08:30:33 +00:00
Cameron Zwarich
12cd4ca742 fix: remove incorrect error in LCNF's check (#9720)
This PR removes an error which implicitly assumes that the sort of type
dependency between erased types present in the test being added can not
occur. It would be difficult to refine the error using only the
information present in LCNF types, and it is of very little ongoing
value (I don't recall it ever finding an actual problem), so it makes
more sense to delete it.

Fixes #9692.
2025-08-05 04:36:57 +00:00
Cameron Zwarich
713a46cd75 chore: adopt <||> to reduce code duplication (#9719) 2025-08-05 04:13:02 +00:00
Cameron Zwarich
f236328bc3 chore: don't check type of erased arguments in FixedParams analysis (#9602) 2025-08-05 02:41:36 +00:00
Kim Morrison
6e06978961 chore: remove >6 month old deprecations (#9640) 2025-08-05 02:29:15 +00:00
Cameron Zwarich
8edcfbe776 fix: correctly handle non-Nat literal types in LCNF elimDeadBranches (#9703)
This PR changes the LCNF `elimDeadBranches` pass so that it considers
all non-`Nat` literal types to be `⊤`. It turns out that fixing this to
correctly handle all of these types with the current abstract value
representation is surprisingly nontrivial, and it's better to just land
the fix first.
2025-08-05 02:14:07 +00:00
Wojciech Nawrocki
1c60173b69 fix: mark __x patterns as impl details in match and intro (#9702)
This PR fixes an issue in the `match` elaborator where pattern variables
like `__x` would not have the kind `implDetail` in the local context.
Now `kindOfBinderName` is `LocalDeclKind.ofBinderName`.

Zulip discussion
[here](https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/Bad.20interaction.20of.20Qq.20with.20grind).

---------

Co-authored-by: Kyle Miller <kmill31415@gmail.com>
2025-08-04 22:54:39 +00:00
Cameron Zwarich
59579bfc3e refactor: remove goBig case from UnreachableBranches.ofNat (#9717)
This case can't meaningfully contribute to the result, because there are
no uses of `Nat` constructors in the `mono` phase.
2025-08-04 19:47:40 +00:00
Leonardo de Moura
7f22c0883b perf: Expr.toPoly in grind (#9714)
This PR adds a version of `CommRing.Expr.toPoly` optimized for kernel
reduction. We use this function not only to implement `grind ring`, but
also to interface the ring module with `grind cutsat`.
2025-08-04 15:30:10 +00:00
Cameron Zwarich
78b941019b refactor: use a state monad rather than combinators for computing free indices (#9711) 2025-08-04 13:55:41 +00:00
Leonardo de Moura
ae728d84f0 perf: proof terms for grind ring and grind cutsat (#9710)
This PR improves some of the proof terms produced by `grind ring` and
`grind cutsat`.
2025-08-04 12:27:11 +00:00
Markus Himmel
3eab35ef22 chore: minor improvements (#9708)
This PR stylistically improves an internal hash map proof and fixes a
typo in the docsting of `String.join`.
2025-08-04 07:12:05 +00:00
Cameron Zwarich
c260435913 refactor: use a state monad rather than combinators for computing max indices (#9707) 2025-08-04 05:15:22 +00:00
Leonardo de Moura
a5351b5c47 perf: add Poy.combine_mul_k (#9706)
This PR combines `Poly.combine_k` and `Poly.mul_k` steps used in the
`grind cutsat` proof terms.
2025-08-04 03:49:08 +00:00
Kim Morrison
718d8acc76 chore: update release_repos.yml (#9705)
This PR updates `release_repos.yml` to reflect that `import-graph` no
longer depends on `batteries`, and reorders the repositories to better
reflect dependencies.
2025-08-04 02:51:41 +00:00
Leonardo de Moura
e8c3c7b5eb perf: grind cutsat proof terms (#9704)
This PR optimizes the proof terms produced by `grind cutsat`. Additional
performance improvements will be merged later.
2025-08-04 02:35:34 +00:00
Sebastian Graf
8f575bf986 fix: Use non-overloading Std.Do.Triple notation in SpecLemmas.lean (#9701)
This PR switches to a non-verloading local `Std.Do.Triple` notation in
SpecLemmas.lean to work around a stage2 build failure.
2025-08-03 16:00:24 +00:00
Leonardo de Moura
cf48c6004d fix: assertion violations at grind checkInvariants (#9700)
This PR fixes assertion violations when `checkInvariants` is enabled in
`grind`
2025-08-03 12:27:26 +00:00
Leonardo de Moura
d0dc5dfd3d feat: propagation for functions with singleton domain in grind (#9699)
This PR adds propagation rules for functions that take singleton types.
This feature is useful for discharging verification conditions produced
by `mvcgen`. For example:

```lean
example (h : (fun (_ : Unit) => x + 1) = (fun _ => 1 + y)) : x = y := by
  grind
```
2025-08-03 12:00:29 +00:00
Joachim Breitner
af473b085a perf: MatchEqs.simpH: do not use contradiction (#9643)
This removes the early call to `contradiction` from `simpH`, and
replaces it with a quick check if the pattern start with different
constructors.

We already call `simpH` quadratically often (unavoidable), so we want it
to be quick. Most common contradictions are found later on, so maybe we
don't want to the expensive `contradiction` tactic to be run early.

May help with #9598.
2025-08-03 06:41:52 +00:00
Mac Malone
9e778f3a61 feat: lake: improve query formatting (#9698)
This PR adjusts the formatting type classes for `lake query` to no
longer require both a text and JSON form and instead work with any
combination of the two. The classes have also been renamed. In addition,
the query formatting of a text module header has been improved to only
produce valid headers.
2025-08-03 05:51:13 +00:00
Mac Malone
52855ce1c1 fix: lake: module lookup by source w/ multiple . (#9697)
This PR fixes the handling in `lake lean` and `lake setup-file` of a
library source file with multiple dots (e.g., `src/Foo.Bar.lean`).
2025-08-03 05:18:59 +00:00
Lean stage0 autoupdater
22000a703a chore: update stage0 2025-08-03 04:34:00 +00:00
Mac Malone
f6f54955fe fix: lake: thin archives for Windows bootstrap only (#9604)
This PR restricts Lake's production of thin archives to only the Windows
core build (i.e., `bootstrap = true`). The unbundled `ar` usually used
for core builds on macOS does not support `--thin`, so we avoid using it
unless necessary.
2025-08-03 03:54:33 +00:00
Cameron Zwarich
1459d17bfd chore: lift redundant markSimplified (#9691) 2025-08-02 23:21:21 +00:00
Sebastian Ullrich
77646f7149 chore: avoid calling into lake twice from CMake build (#9688)
Keeps the output cleaner and save a little time and potential
re-rebuilds
2025-08-02 20:48:45 +00:00
Sebastian Ullrich
1e83f62d31 perf: clarify and granularize access to async env ext state (#9587)
* Have asynchronous environment extensions specify whether they are
manipulate data for declarations from the "outside"/main branch (e.g.
attributes) or from the "inside"/async branch (e.g. data collected from
body elaboration) in order to avoid unnecessary waiting.
* Merge `findStateAsync?` into `getState` via a new, optional
`asyncDecl` parameter.
* Make `mayContainAsync` check an automatic part of `modifyState`.
2025-08-02 17:01:08 +00:00
Joachim Breitner
df9ca20339 perf: create unfolding theorem for wf-rec in one go (#9646)
This PR uses a more simple approach to proving the unfolding theorem for
a function defined by well-founded recursion. Instead of looping a bunch
of tactics, it uses simp in single-pass mode to (try to) exactly undo
the changes done in `WF.Fix`, using a dedicated theorem that pushes the
extra argument in for each matcher (or `casesOn`).

Improves performance for recursive functions with large `match`
statements, as in #9598.
2025-08-02 15:26:02 +00:00
Cameron Zwarich
b60f97cc19 chore: remove unused code in comment (#9687) 2025-08-02 15:18:41 +00:00
Leonardo de Moura
ab946fdf2c feat: clear implDetail local declarations in grind (#9686)
This PR applies `clear` to implementation detail local declarations
during the `grind` preprocessing steps.
2025-08-02 14:28:15 +00:00
Sebastian Ullrich
d9956a9d05 perf: properly specialize through PrefixTree (#9681)
The previous specialize annotations weren't doing anything
2025-08-02 13:59:56 +00:00
Leonardo de Moura
2713c846f1 chore: update tests/lean/grind todo folder (#9683)
Remove examples that we have already moved to `tests/lean/run`, and add
notes for possible fixes.
2025-08-02 13:10:34 +00:00
Leonardo de Moura
3056848819 fix: unfoldReducible' optimization regression in grind (#9682)
This PR fixes a regression introduced by an optimization in the
`unfoldReducible` step used by the `grind` normalizer. It also ensures
that projection functions are not reduced, as they are folded in a later
step.
2025-08-02 12:57:25 +00:00
Leonardo de Moura
08c3f3c236 feat: warn grind redundant parameters (#9679)
This PR produces a warning for redundant `grind` arguments.
2025-08-02 05:37:07 +00:00
Mac Malone
a01eda79e8 feat: lake: build times & --no-build jobs (#9677)
This PR adds build times to each build step of the build monitor (under
`-v` or in CI) and delays exiting on a `--no-build` until after the
build monitor finishes. Thus, a `--no-build` failure will now report
which targets blocked Lake by needing a rebuild.
2025-08-02 04:28:02 +00:00
Leonardo de Moura
f6e19f1f93 fix: nonstandard Nat and Int instances (#9676)
This PR adds normalizers for nonstandard arithmetic instances. The types
`Nat` and `Int` have built-in support in `grind`, which uses the
standard instances for these types and assumes they are the ones in use.
However, users may define their own alternative instances that are
definitionally equal to the standard ones. This PR normalizes such
instances using simprocs. This situation actually occurs in Mathlib.
Example:

```lean
class Distrib (R : Type _) extends Mul R where

namespace Nat

instance instDistrib : Distrib Nat where
  mul := (· * ·)

theorem odd_iff.extracted_1_4 {n : Nat} (m : Nat)
  (hm : n =
    @HMul.hMul _ _ _ (@instHMul Nat instDistrib.toMul)
      2 m + 1) :
    n % 2 = 1 := by
  grind

end Nat
```
2025-08-01 23:48:57 +00:00
Leonardo de Moura
bad582ed45 feat: Fin.val support in grind cutsat (#9675)
This PR adds support for `Fin.val` in `grind cutsat`. Examples:
```lean
example (a b : Fin 2) (n : Nat) : n = 1 → ↑(a + b) ≠ n → a ≠ 0 → b = 0 → False := by
  grind

example (m n : Nat) (i : Fin (m + n)) (hi : m ≤ ↑i) : ↑i - m < n := by
  grind

example {n : Nat} (m : Nat) (i : Fin n) ⦃j : Fin (n + m)⦄
    (this : ↑i + m ≤ ↑j) : ↑j - m < n := by
  grind

example {n : Nat} (i : Fin n) (j : Nat) (hj : j < ↑i) : j < n := by
  grind
```
2025-08-01 22:29:30 +00:00
Leonardo de Moura
18e1cdb7bb fix: user provided ToInt.toInt applications (#9673)
This PR ensures that `grind cutsat` processes `ToInt.toInt` applications
provided by the user. Example:

```lean
open Lean Grind
example (x : Fin 3) : ToInt.toInt x ≠ 0 → ToInt.toInt x ≠ 1 → ToInt.toInt x ≠ 2 → False := by
  grind -ring

example (x y z : Fin 5) : ToInt.toInt (x + z) = ToInt.toInt y → z = 0 → x = y := by
  grind -ring
```
2025-08-01 21:30:54 +00:00
Kyle Miller
aa3e50ee76 chore: revert reversion (#9672)
This PR reverts the test that was re-added #9669, since it remains
flaky.
2025-08-01 20:16:55 +00:00
Leonardo de Moura
eb6cede35d fix: normalize SMul.smul for Semiring and Ring (#9671)
This PR fixes support for `SMul.smul` in `grind ring`. `SMul.smul`
applications are now normalized. Example:
```lean
example (x : BitVec 2) : x - 2 • x + x = 0 := by
  grind
```
2025-08-01 20:16:03 +00:00
Leonardo de Moura
f8cdb03352 fix: add CommRing.Expr.intCast k and CommRing.Expr.natCast k (#9670)
This PR add constructors `.intCast k` and `.natCast k` to
`CommRing.Expr`. We need them because terms such as `Nat.cast (R := α)
1` and `(1 : α)` are not definitionally equal. This is pervaise in
Mathlib for the numerals `0` and `1`.

```lean
import Mathlib

example {α : Type} [AddMonoidWithOne α] : Nat.cast (R := α) 0 = (0 : α) := rfl -- not defeq
example {α : Type} [AddMonoidWithOne α] : Nat.cast (R := α) 1 = (1 : α) := rfl -- not defeq
example {α : Type} [AddMonoidWithOne α] : Nat.cast (R := α) 2 = (2 : α) := rfl -- defeq from here
-- Similarly for everything past `AddMonoidWithOne` in the Mathlib hierarchy, e.g. `Ring`.
```
2025-08-01 19:35:13 +00:00
Kyle Miller
08ff19d973 chore: add code action test back in (#9669)
This PR re-adds the code action test that was reverted in
5b18ea1545, now with more robustness.
2025-08-01 18:41:41 +00:00
Cameron Zwarich
2ea6b5068c chore: make Compiler.findJoinPoints trace messages more useful (#9668) 2025-08-01 17:42:32 +00:00
Paul Reichert
4cd917aa65 chore: make datokrat code owner for iterators, ranges and slices (#9667)
This PR adds some lines for `datokrat` to the `CODEOWNERS` file.
2025-08-01 17:34:43 +00:00
Joachim Breitner
417031fc17 chore: large match statement benchmark (#9665)
This PR adds a benchmark with a large, two-level, not-overlapping match
statement, including the splitter generation.
2025-08-01 15:25:07 +00:00
Sebastian Ullrich
416a8372cd perf: shorten rebuild critical path by 19% (#9626)
This PR adjusts the import graph, primarily of `Lean`, such that the
worst case rebuild time of core (`lean` only) is below 3 minutes on the
speedcenter machine (not captured by benchmark yet).
2025-08-01 11:18:21 +00:00
Henrik Böving
6eaf406305 chore: bump stack limit in benchmark (#9660) 2025-08-01 09:33:39 +00:00
Henrik Böving
009bcf1a27 perf: optimize fuzzyMatching (#9563)
This PR performs some micro optimizations on fuzzy matching for a `~20%`
instructions win.

The three key changes are:
- try to remove some unnecessary allocations of things such as tuples
- change `containsInOrderLower` to use the efficient `get'` and `next'`
primitives. I hope that we can replace these with iterators on strings
in the second half of this quarter
- Do the same thing as clangd and use `Int16` with the `minValue` being
used for "worst score" while this does have the potential to
over/underflow, if the user is working with a score in the 10000s
something weird is certainly going on already (the score usually seems
to be in the 2 digit area based on some).

As an additional bonus, once we finally have unboxed arrays we will get
some additional cache wins on the 16 bit arrays!
2025-08-01 09:11:15 +00:00
Sebastian Ullrich
335c5ca5c8 fix: trace.profiler.output with newer Firefox Profiler (#9659)
This PR fixes compatibility of the `trace.profiler.output` option with
newer versions of Firefox Profiler

Fixes #9473
2025-08-01 09:00:15 +00:00
Sebastian Ullrich
5b18ea1545 chore: remove flaky code action tests (#9658) 2025-08-01 07:58:13 +00:00
Rob23oba
d817fb0ef3 fix: handle NUL bytes in IO functions (#9616)
This PR introduces checks to make sure that the IO functions produce
errors when inputs contain NUL bytes (instead of ignoring everything
after the first NUL byte).
2025-08-01 06:12:53 +00:00
Lean stage0 autoupdater
37bf79b0e2 chore: update stage0 2025-08-01 06:29:07 +00:00
Mario Carneiro
7cdd65d5fb fix: build with libuv pre-1.45.0 (part 2) (#9652)
This PR continues #9644 , fixing the core build when using an older
system libuv.

This only affected users building Lean from scratch, since the lean
binaries we ship as part of toolchains statically link their own copy of
libuv 1.50+.

---------

Co-authored-by: Markus Himmel <markus@lean-fro.org>
2025-08-01 05:53:55 +00:00
Mac Malone
1901e2ecfd fix: lake: use server header for workspace modules (#9559)
This PR changes `lake setup-file` to use the server-provided header for
workspace modules.

This also reverts #9163 as the underlying issue is now fixed.
2025-08-01 05:08:44 +00:00
Kyle Miller
76051ab1fe feat: use name resolution for dot identifier notation (#9634)
This PR modifies dot identifier notation so that `(.a : T)` resolves
`T.a` with respect to the root namespace, like for generalized field
notation. This lets the notation refer to private names, follow aliases,
and also use open namespaces. The LSP completions are improved to follow
how dot ident notation is resolved, but it doesn't yet take into account
aliases or open namespaces.

Closes #9629
2025-08-01 02:27:40 +00:00
Kim Morrison
062ac89c34 chore: failing test cases for grind regressions vs omega (#9656) 2025-08-01 02:19:16 +00:00
jrr6
0c686e09db feat: add explanations for large elimination errors (#9653)
This PR adds error explanations for two common errors caused by large
elimination from `Prop`. To support this functionality, "nested" named
errors thrown by sub-tactics are now able to display their error code
and explanation.
2025-07-31 23:33:38 +00:00
Kyle Miller
4575799f8e chore: library style cleanup (#9654)
This PR cleans up the style of the library in anticipation of a future
PR that requires strict indentation for tactic sequences.
2025-07-31 21:28:59 +00:00
Sebastian Ullrich
271c8ab9cb fix: macros unfolding to multiple commands inside mutual (#9649)
This PR fixes an issue where a macro unfolding to multiple commands
would not be accepted inside `mutual`
2025-07-31 21:00:53 +00:00
jrr6
ee1854a607 feat: note potential discrepancies in deprecation warning (#9606)
This PR adds notes to the deprecation warning when the replacement
constant has a different type, visibility, and/or namespace.

Closes #7993
2025-07-31 16:41:14 +00:00
jrr6
9b186297c7 feat: add conversion-mode clear tactic (#6732)
This PR adds support for the `clear` tactic in conversion mode.

Closes #5734
2025-07-31 16:39:57 +00:00
Joachim Breitner
c8ef2fae1a chore: add #9598 as benchmark (#9642)
This PR adds the example from #9598 as a benchmark.
2025-07-31 15:32:54 +00:00
Sebastian Ullrich
0aba471758 perf: do not export LCNF/IR function summaries under the module system (#9645)
to avoid unexpected rebuilds
2025-07-31 15:23:04 +00:00
Sebastian Ullrich
467d905709 fix: more deriving handlers under the module system (#9647)
This PR fixes further deriving handlers to apply visibilities correctly
2025-07-31 15:00:58 +00:00
jrr6
62f14514da refactor: update built-in tactic error messages (#9633)
This PR updates various error messages produced by or associated with
built-in tactics and adapts their formatting to current conventions.
2025-07-31 14:16:57 +00:00
Lean stage0 autoupdater
5ece18cede chore: update stage0 2025-07-31 14:06:09 +00:00
Markus Himmel
33eac4497b fix: build with libuv pre-1.45.0 (#9644)
This PR fixes the core build when using an older system libuv.

This only affected users building Lean from scratch, since the `lean`
binaries we ship as part of toolchains statically link their own copy of
libuv 1.50+.
2025-07-31 13:18:41 +00:00
Wojciech Rozowski
fa449aab14 feat: add mutual_induct for (co)inductive predicates in mutual blocks (#9628)
This PR introduces a `mutual_induct` variant of the generated
(co)induction proof principle for mutually defined (co)inductive
predicates. Unlike the standard (co)induction principle (which projects
conclusions separately for each predicate), `mutual_induct` produces a
conjunction of all conclusions.

## Example

Given the following mutual definition:

```lean4
mutual
  def f : Prop := g
  coinductive_fixpoint

  def g : Prop := f
  coinductive_fixpoint
end
```

Standard coinduction principles:
```lean4 
f.coind : ∀ (pred_1 pred_2 : Prop), (pred_1 → pred_2) → (pred_2 → pred_1) → pred_1 → f
g.coind : ∀ (pred_1 pred_2 : Prop), (pred_1 → pred_2) → (pred_2 → pred_1) → pred_2 → g
```

New `mutual_induct`principle:
```lean4
f.mutual_induct: ∀ (pred_1 pred_2 : Prop), (pred_1 → pred_2) → (pred_2 → pred_1) → (pred_1 → f) ∧ (pred_2 → g)
```

---------

Co-authored-by: Joachim Breitner <mail@joachim-breitner.de>
2025-07-31 12:39:52 +00:00
Sebastian Ullrich
5f20213876 refactor: minimize Lean.Meta.Tactic.TryThis imports (#9539) 2025-07-31 12:21:48 +00:00
Sebastian Ullrich
28f64e57ae chore: [match_pattern] should enforce [expose] (#9534) 2025-07-31 11:51:47 +00:00
Sebastian Ullrich
5e7c4557f8 refactor: minimize Lean.Meta.Diagnostics imports (#9546) 2025-07-31 08:23:13 +00:00
Joachim Breitner
c517f8fc9e chore: resurrect #8978, #8992, #8973 from bad merge (#9641)
This PR resurrects the changes from #8978, #8992, #8973 which were
accidentally removed by #8996.

Fixes #8962.

---------

Co-authored-by: Wojciech Rozowski <wojciech@lean-fro.org>
2025-07-31 08:04:40 +00:00
jrr6
3a3c816a27 chore: break up universe level error message (#9637)
This PR improves the readability of the "maximum universe level offset
exceeded" error message.
2025-07-30 23:52:53 +00:00
Sebastian Ullrich
b8e801ecad fix: deriving BEq on public inductives with private ctors (#9630)
Make the instance public while the body becomes private
2025-07-30 14:57:17 +00:00
Wojciech Rozowski
7f17970551 feat: generate (co)induction proof principles for mutually (co)inductive predicates (#9358)
This PR adds support for generating lattice-theoretic (co)induction
proof principles for predicates defined via `mutual` blocks using
`inductive_fixpoint`/`coinductive_fixpoint` constructs.

### Key Changes
- The order on product lattices (used to define fixpoints of mutual
blocks) is unfolded.
- Hypotheses in generated principles are curried.
- Conclusions are projected to focus only on the predicate of interest
(rather than being a conjunction of conclusions for all functions
defined in the `mutual` block.

### Example
Given:
```lean4
mutual
    def f : Prop :=
      g
    coinductive_fixpoint

    def g : Prop :=
      f
    coinductive_fixpoint
  end
```
The system now generates these coinduction principles:
```lean4
f.coinduct (pred_1 pred_2 : Prop) (hyp_1 : pred_1 → pred_2) (hyp_2 : pred_2 → pred_1) : pred_1 → f
```
and 
```lean4
g.coinduct (pred_1 pred_2 : Prop) (hyp_1 : pred_1 → pred_2) (hyp_2 : pred_2 → pred_1) : pred_2 → g
```

---------

Co-authored-by: Joachim Breitner <mail@joachim-breitner.de>
2025-07-30 11:18:41 +00:00
Joachim Breitner
0f1fb8bafe chore: improve trace messages around wf_preprocess (#9625)
This PR improves trace messages around wf_preprocess.
2025-07-30 08:16:26 +00:00
Cameron Zwarich
7931e19572 perf: use xType field rather than conservatively recomputing it (#9345) 2025-07-30 04:34:13 +00:00
Kim Morrison
285f0e329f feat: add List/Array/Vector.sum_append_nat (#9622)
This PR adds a missing lemma about `List.sum`, and a grind annotation.

Noticed in @b-mehta's work.
2025-07-30 04:12:04 +00:00
Kim Morrison
9006af4a96 chore: rename Xor to XorOp (#9621)
This PR renames `Xor` to `XorOp`, to match `AndOp`, etc.
2025-07-30 00:51:10 +00:00
Kim Morrison
5f17e3bf15 feat: tweaks to List.Pairwise API (#9620)
This PR adds the separate directions of
`List.pairwise_iff_forall_sublist` as named lemmas.

I want to explore how they could/should be used by `grind` in Mathlib.
2025-07-29 23:47:33 +00:00
Kim Morrison
366b4b2810 feat: Nat.dfold (#7450)
This PR implements `Nat.dfold`, a dependent analogue of `Nat.fold`.
2025-07-29 23:36:47 +00:00
jcreedcmu
e3517f1c86 doc: freshen up Mac OSX build instructions (#9618)
This PR brings the Mac OSX build instructions up to date slightly. (They
currently refer to facts "...as of November 2014...")

- Remove specific OS version number from the title as it is out of date
with respect to filename.

- Nonetheless don't change filename for the sake of not breaking
incoming links.

- Update C++ language version to C++14, which I believe is what is
currently required, based on other platform documentation.

- Bump versions of C++ compilers that seem to be current. I expect the
exact values of these version numbers aren't crucial but maybe good for
the reader calibrating a vague sense of whether their compiler is in the
right ballpark.

- Add `lld` to the homebrew clang instructions, because homebrew changed
the way they package llvm tools, spinning the linker off into its own
package.
2025-07-29 21:42:24 +00:00
jrr6
fa1da03d50 feat: update structure/inductive error messages (#9592)
This PR updates the styling and wording of error messages produced in
inductive type declarations and anonymous constructor notation,
including hints for inferable constructor visibility updates.
2025-07-29 21:27:30 +00:00
Henrik Böving
5c2ae7b414 perf: optimize Name.toString (#9594)
This PR optimizes `Lean.Name.toString`, giving a 10% instruction
benefit.

Crucially this is a breaking change as the old `Lean.Name.toString`
method used to support a method for identifying tokens. This method is
now available as `Lean.Name.toStringWithToken` in order to allow for
specialization of the (highly common) `toString` code path which sets
this function to just return `false`.
2025-07-29 07:20:56 +00:00
David Thrane Christiansen
6ae31ea2d6 chore: simplify docstring for propext (#9593)
This PR simplifies the docstring for `propext` significantly.

The old docstring explained general concepts of axioms that are now
covered in the reference manual, and had a large example that was out of
date and has been subsumed by reference manual content.
2025-07-29 03:51:34 +00:00
Kim Morrison
edade0cea8 chore: add failing grind test about exponents (#9611) 2025-07-29 02:53:43 +00:00
Kim Morrison
969136b0d6 feat: add @[grind =] to Prod.lex_def (#9609)
This PR adds `@[grind =]` to `Prod.lex_def`. Note that `omega` has
special handling for `Prod.Lex`, and this is needed for `grind`'s cutsat
module to achieve parity.
2025-07-29 02:45:02 +00:00
Kim Morrison
1726a61e88 chore: add failing test for grind and BitVec (#9608)
This PR adds a failing test for `grind`, reported by @eric-wieser.

```
example {x : BitVec 2} : x - 2 • x + x = 0 := by
  grind -- fails
```

There are several independent problems here!

1. Cutsat doesn't evaluate `2 ^ 2`:
```
-- [cutsat] Assignment satisfying linear constraints
-- [assign] 「2 ^ 2」 := 0
```

2. We don't normalize `3 * 2 • x` to `6 * x` in the ring solver:
```
-- [ring] Rings ▼
--   [] Ring `BitVec 2` ▼
--     [diseqs] Disequalities ▼
--       [_] ¬2 * x + 3 * 2 • x = 0
```
This should then give a contradiction because the characteristic of
`BitVec 2` is 4.

3. In `Int`, we're not normalizing `*` and `•`:
```
-- [ring] Rings ▼
--   [] Ring `Int` ▼
--     [basis] Basis ▼
--       [_] 2 * ↑x + -1 * ↑(2 • x) + -4 * ((2 * ↑x + -1 * ↑(2 • x)) / 4) + -1 * ((2 * ↑x + -1 * ↑(2 • x)) % 4) = 0
```
2025-07-29 02:03:30 +00:00
Kim Morrison
9399b2ee36 chore: add failing grind test (#9607)
This PR adds a failing grind test.
2025-07-29 01:36:53 +00:00
jrr6
e53f944c83 fix: function field notation errors when head is an fvar (#9595)
This PR improves the error message displayed when writing an invalid
projection on a free variable of function type.
2025-07-28 23:07:02 +00:00
Mac Malone
dbfeb9e2da chore: upload build directory as artifact in cache CI (#9600)
This PR adds a step to the cached Linux Lake CI to upload the build
directory. This will help us debug any cache failures.
2025-07-28 20:29:51 +00:00
Lean stage0 autoupdater
ab87a6f797 chore: update stage0 2025-07-28 18:23:37 +00:00
Cameron Zwarich
475bd65c90 perf: during specialization, don't abstract all local fun decls under binders (#9596)
The `isUnderBinder` check is intended to avoid inlining repeated
computations into specializations, but this doesn’t apply to local
function decls whose bodies are already delayed.
2025-07-28 17:36:43 +00:00
Cameron Zwarich
0fe7cc8794 perf: increase usage of the tagged IR type for app return values (#9589) 2025-07-28 14:09:38 +00:00
Kim Morrison
2cdb547af0 chore: add 'public section' in Data/Vector/Algebra (#9588) 2025-07-28 13:07:21 +00:00
pandaman
fd25fd70f1 feat: add Iter.toArray lemmas (#9538)
This PR adds two lemmas related to `Iter.toArray`.

[Zulip
discussion](https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/Iterator.20Library.3A.20Request.20for.20Feedback/near/530567667)

---------

Co-authored-by: Paul Reichert <6992158+datokrat@users.noreply.github.com>
2025-07-28 11:46:38 +00:00
Sebastian Graf
8a58037df9 feat: Add List.zipWithM and Array.zipWithM (#9528)
This PR adds `List.zipWithM` and `Array.zipWithM`.
2025-07-28 08:39:52 +00:00
Kim Morrison
e38f0c6990 chore: remove bad grind annotation from pairwise_iff_forall_sublist (#9584) 2025-07-28 05:57:51 +00:00
Kim Morrison
557592aa97 feat: componentwise algebra operations on Vector (#9586)
This PR adds componentwise algebraic operations on `Vector α n`, and
relevant instances.
2025-07-28 05:56:10 +00:00
Eric Wieser
e5600afddc feat: missing pow lemmas for UInt (#9577)
This PR adds lemmas about `UIntX.toBitVec` and `UIntX.ofBitVec` and `^`.

These match the existing lemas for `*`.

After #7887 these can be made true by `rfl`.
2025-07-28 00:06:18 +00:00
Leonardo de Moura
87dae299b8 fix: ite and dite should not be used in E-matching patterns (#9579)
This PR ensures `ite` and `dite` are to selected as E-matching patterns.
They are bad patterns because the then/else branches are only
internalized after `grind` decided whether the condition is
`True`/`False`.

The issue reported by #9572 has been fixed, but the fix exposed another
issue. The patterns for `List.Pairwise` produce an unbounded number of
E-matching instances.
```lean
example (l : List α) : l.Pairwise R := by
  grind
```
2025-07-27 17:51:23 +00:00
Leonardo de Moura
7034310a3b fix: disequality proof construction in grind (#9578)
This PR fixes an issue in `grind`'s disequality proof construction. The
issue occurs when an equality is merged with the `False` equivalence
class, but it is not the root of its congruence class, and its
congruence root has not yet been merged into the `False` equivalence
class yet.

closes #9562
2025-07-27 14:49:10 +00:00
Cameron Zwarich
d7e7bd16a6 chore: increase code sharing in IR LiveVars computation (#9576) 2025-07-27 13:29:58 +00:00
Leonardo de Moura
bdd1918cd8 perf: optimizes grind ring proof terms (#9575)
This PR optimizes the proof terms generated by `grind ring`. For
example, before this PR, the kernel took 2.22 seconds (on a M4 Max) to
type-check the proof in the benchmark `grind_ring_5.lean`; it now takes
only 0.63 seconds.
2025-07-27 11:43:17 +00:00
Leonardo de Moura
30ba416fe3 feat: add grind option abstractProof (#9574)
This PR adds the option `abstractProof` to control whether `grind`
automatically creates an auxiliary theorem for the generated proof or
not.
2025-07-27 11:33:16 +00:00
Kim Morrison
95e753c6b4 feat: generalize Process.output/run to allow an input (#9532)
This PR generalizes `Process.output` and `Process.run` with an optional
`String` argument that can be piped to `stdin`.

To date we have been using shims `Process.runCmdWithInput` in Batteries.
2025-07-27 03:09:34 +00:00
Kyle Miller
5d54b0b13f fix: erroneous "no goals" in empty tactic list in induction/cases and other tactic info improvements (#9553)
This PR fixes a bug introduced in #7830 where if the cursor is at the
indicated position
```lean
example (as bs : List Nat) : (as.append bs).length = as.length + bs.length := by
  induction as with
  | nil => -- cursor
  | cons b bs ih =>
```
then the Infoview would show "no goals" rather than the `nil` goal. The
PR also fixes a separate bug where placing the cursor on the next line
after the `induction`/`cases` tactics like in
```lean
  induction as with
  | nil => sorry
  | cons b bs ih => sorry
  I -- < cursor
```
would report the original goal in the goal list. Furthermore, there are
numerous improvements to error recovery (including `allGoals`-type logic
for pre-tactics) and the visible tactic states when there are errors.
Adds `Tactic.throwOrLogErrorAt`/`Tactic.throwOrLogError` for throwing or
logging errors depending on the recovery state.
2025-07-26 23:15:31 +00:00
Wojciech Nawrocki
d2153064ec doc: clarify nondep behaviour (#9570)
This PR clarifies the behaviour of `mkLetFVars` and family on
nondependent `ldecl`s.

Zulip discussion
[here](https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/mkLetFVars.20with.20nondependent.20let).
2025-07-26 22:46:48 +00:00
Kyle Miller
4d295d85b6 fix: make zero/succ hoverable in induction/cases (#9571)
This PR restores the feature where in `induction`/`cases` for `Nat`, the
`zero` and `succ` labels are hoverable. This was added in #1660, but
broken in #3629 and #3655 when custom eliminators were added. In
general, if a custom eliminator `T.elim` for an inductive type `T` has
an alternative `foo`, and `T.foo` is a constant, then the `foo` label
will have `T.foo` hover information.
2025-07-26 22:31:53 +00:00
Cameron Zwarich
8ce0e045a8 chore: increase code sharing in IR FreeVars computation (#9569) 2025-07-26 20:37:10 +00:00
Cameron Zwarich
2be6c75c2b chore: replace partially redundant calls to FnBody.split with .body (#9568) 2025-07-26 20:18:37 +00:00
Lean stage0 autoupdater
af84f76f31 chore: update stage0 2025-07-26 15:58:53 +00:00
Cameron Zwarich
dd45a21257 chore: remove unused FnBody.mdata constructor (#9564) 2025-07-26 15:20:13 +00:00
Sebastian Ullrich
8177de88cd perf: remove grind blockers (#9328)
This PR removes all blocking waits in `grind_bitvec2.lean` that can be
avoided by more fine-grained requests.
2025-07-26 06:15:33 +00:00
Sebastian Ullrich
99dac6aec0 doc: building core with Lake (#9547) 2025-07-26 06:13:09 +00:00
jrr6
30afb0dbec feat: improve set_option error messages (#9496)
This PR improves the error messages produced by the `set_option`
command.
2025-07-26 02:04:45 +00:00
jrr6
309a3c364f fix: avoid RPC errors in nonexistent identifier hovers (#9494)
This PR fixes an issue that caused some error messages to attempt to
display hovers for nonexistent identifiers.
2025-07-26 02:04:43 +00:00
jrr6
fcbd1037fd refactor: update and consolidate attribute-related error messages (#9495)
This PR consolidates common attribute-related error messages into
reusable functions and updates the wording and formatting of relevant
error messages.
2025-07-26 02:03:18 +00:00
Cameron Zwarich
aa769e7677 chore: make inferVisibility LCNF pass style match others (#9558) 2025-07-26 00:49:52 +00:00
Cameron Zwarich
737105fd78 chore: remove syntax for extern arity specifications (#9556) 2025-07-26 00:44:36 +00:00
Cameron Zwarich
1285a3c9a7 chore: clean up IR.Arg usage (#9557) 2025-07-26 00:22:38 +00:00
jrr6
17a477393c feat: allow custom preview spans in hint suggestions (#9555)
This PR allows hints in message data to specify custom preview spans
that extend beyond the edit region specified by the code action.
2025-07-26 00:04:28 +00:00
Lean stage0 autoupdater
437b4a4f9b chore: update stage0 2025-07-25 22:06:31 +00:00
Cameron Zwarich
eddc3b421e chore: remove support for unused arity specification in ExternAttrData (#9552)
This just removes the data for this specification. Removing the parser
support for it seems to require a stage0 update in between.
2025-07-25 21:23:56 +00:00
Kyle Miller
98569c7cf0 fix: make sure "dependent elimination failed" error is on cases (#9551)
This PR fixes the error position for the "dependent elimination failed"
error for the `cases` tactic.
2025-07-25 19:02:42 +00:00
Cameron Zwarich
7f39e56a79 chore: adopt List.find? in getExternEntryForAux (#9550) 2025-07-25 17:38:22 +00:00
Sebastian Ullrich
bc5526cacb fix: widgets broken by overzealous modularization (#9548) 2025-07-25 16:51:18 +00:00
Cameron Zwarich
6300329057 perf: consider functions with ordinary implicit arguments of instance type to be template-like (#9536)
This extends the specialization behavior of functions taking instance
implicits to ordinary implicit arguments that are of instance type. The
choice between the two is often made for subtle inference-related
reasons. It also affects visibility of these functions, because the
module system makes template-like decls visible to the compiler in other
modules.
2025-07-25 16:03:30 +00:00
Cameron Zwarich
15f0cd9527 fix: run inferVisibility after saveMono (#9545)
This PR makes the second instance of the `inferVisibility` pass run
after the `saveMono` pass. As the comment above the first instance of
the pass indicates, this needs to be after `saveMono` in order to see
all decls with their updated bodies.
2025-07-25 15:36:52 +00:00
Sebastian Ullrich
b437232ab6 refactor: minimize Lean.DefEqAttrib imports (#9543) 2025-07-25 15:18:17 +00:00
Sebastian Ullrich
3ff069911e refactor: remove some unnecessary meta imports (#9542) 2025-07-25 15:14:02 +00:00
Sebastian Ullrich
e74a97d5a8 perf: do not even open .olean.server when not necessary (#9531) 2025-07-25 14:52:24 +00:00
Sebastian Ullrich
7f4d673d33 perf: make builtin_initialize backing def private (#9540) 2025-07-25 14:41:49 +00:00
Sebastian Ullrich
81fe5243d3 chore: add grind tests as benchmarks (#9537) 2025-07-25 14:21:38 +00:00
Lean stage0 autoupdater
3f19182afc chore: update stage0 2025-07-25 12:44:14 +00:00
Sebastian Ullrich
ff1d3138bf refactor: module-ize Lean (#9330) 2025-07-25 12:02:51 +00:00
Kim Morrison
0071bea64e feat: helper instances for NameSet (#9529)
This PR upstreams some helper instances for `NameSet` from Batteries.

(These could be generalized to an arbitrary TreeSet, but I'll leave that
for someone else.)
2025-07-25 09:33:19 +00:00
Sebastian Ullrich
5244ac3bb5 feat: note inaccessible private declarations in unknown constant error (#9516)
This PR ensures that private declarations made inaccessible by the
module system are noted in the relevant error messages
2025-07-25 09:23:52 +00:00
Sebastian Ullrich
26be599e65 fix: inaccessible private messages in the module system (#9518)
This PR ensures previous "is marked as private" messages are still
triggered under the module system
2025-07-25 09:09:17 +00:00
Sebastian Ullrich
671057eecf fix: unif_hint under the module system (#9530) 2025-07-25 09:05:31 +00:00
Kim Morrison
0ab69a32cb chore: parameterize NoNatZeroDivisors by NatModule instead of HMul (#9527)
This PR changes `Lean.Grind.NoNatZeroDivisors` so that it is
parametrised by a `NatModule` instance rather than just a `HMul`
instance. This is sufficiently general for our purposes, and is a
band-aid (~40% improvement) for the performance problems we've been
seeing coming from inference here. The problems observed in Mathlib may
not see much improvement, however.
2025-07-25 08:46:05 +00:00
Joachim Breitner
6995f280b4 fix: unfold abstracted proofs before processing recursion (#9191)
This PR lets the equation compiler unfold abstracted proofs again if
they would otherwise hide recursive calls.
    
This fixes #8939.

---------

Co-authored-by: Sebastian Ullrich <sebasti@nullri.ch>
2025-07-25 08:00:57 +00:00
Kim Morrison
73422d52fd chore: remove simp from unindexable Array.filterMap_some_fun (#9521) 2025-07-25 06:22:42 +00:00
3250 changed files with 39817 additions and 15555 deletions

View File

@@ -104,7 +104,7 @@ jobs:
# NOTE: must be in sync with `save` below and with `restore-cache` in `update-stage0.yml`
path: |
.ccache
${{ matrix.name == 'Linux Lake (cached)' && 'build/stage1/**/*.trace
${{ matrix.name == 'Linux Lake' && 'build/stage1/**/*.trace
build/stage1/**/*.olean*
build/stage1/**/*.ilean
build/stage1/**/*.ir
@@ -166,6 +166,22 @@ jobs:
# contortion to support empty OPTIONS with old macOS bash
cmake .. --preset ${{ matrix.CMAKE_PRESET || 'release' }} -B . ${{ matrix.CMAKE_OPTIONS }} ${OPTIONS[@]+"${OPTIONS[@]}"} -DLEAN_INSTALL_PREFIX=$PWD/..
time make $TARGET_STAGE -j$NPROC
# Should be done as early as possible and in particular *before* "Check rebootstrap" which
# changes the state of stage1/
- name: Save Cache
if: steps.restore-cache.outputs.cache-hit != 'true'
uses: actions/cache/save@v4
with:
# NOTE: must be in sync with `restore` above
path: |
.ccache
${{ matrix.name == 'Linux Lake' && 'build/stage1/**/*.trace
build/stage1/**/*.olean*
build/stage1/**/*.ilean
build/stage1/**/*.ir
build/stage1/**/*.c
build/stage1/**/*.c.o*' || '' }}
key: ${{ steps.restore-cache.outputs.cache-primary-key }}
- name: Install
run: |
make -C build/$TARGET_STAGE install
@@ -199,13 +215,13 @@ jobs:
path: pack/*
- name: Lean stats
run: |
build/stage1/bin/lean --stats src/Lean.lean
build/stage1/bin/lean --stats src/Lean.lean -Dexperimental.module=true
if: ${{ !matrix.cross }}
- name: Test
id: test
run: |
ulimit -c unlimited # coredumps
time ctest --preset ${{ matrix.CMAKE_PRESET || 'release' }} --test-dir build/$TARGET_STAGE -j$NPROC --output-junit test-results.xml ${{ matrix.CTARGET_OPTIONS }}
time ctest --preset ${{ matrix.CMAKE_PRESET || 'release' }} --test-dir build/$TARGET_STAGE -j$NPROC --output-junit test-results.xml
if: (matrix.wasm || !matrix.cross) && (inputs.check-level >= 1 || matrix.test)
- name: Test Summary
uses: test-summary/action@v2
@@ -235,9 +251,13 @@ jobs:
if: matrix.test-speedcenter
- name: Check rebootstrap
run: |
set -e
# clean rebuild in case of Makefile changes/Lake does not detect uncommited stage 0
# changes yet
make -C build update-stage0 && make -C build/stage1 clean-stdlib && make -C build -j$NPROC
make -C build update-stage0
make -C build/stage1 clean-stdlib
time make -C build -j$NPROC
time ctest --preset ${{ matrix.CMAKE_PRESET || 'release' }} --test-dir build/stage1 -j$NPROC
if: matrix.check-rebootstrap
- name: CCache stats
if: always()
@@ -249,17 +269,3 @@ jobs:
progbin="$(file $c | sed "s/.*execfn: '\([^']*\)'.*/\1/")"
echo bt | $GDB/bin/gdb -q $progbin $c || true
done
- name: Save Cache
if: always() && steps.restore-cache.outputs.cache-hit != 'true'
uses: actions/cache/save@v4
with:
# NOTE: must be in sync with `restore` above
path: |
.ccache
${{ matrix.name == 'Linux Lake (cached)' && 'build/stage1/**/*.trace
build/stage1/**/*.olean*
build/stage1/**/*.ilean
build/stage1/**/*.ir
build/stage1/**/*.c
build/stage1/**/*.c.o*' || '' }}
key: ${{ steps.restore-cache.outputs.cache-primary-key }}

View File

@@ -181,7 +181,9 @@ jobs:
"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'"
"CTEST_OPTIONS": "-E 'foreign'",
// not compatible with `prepare-llvm` currently
"CMAKE_OPTIONS": "-DUSE_LAKE=OFF",
},
{
"name": "Linux Lake",
@@ -192,13 +194,7 @@ jobs:
"check-stage3": level >= 2,
// NOTE: `test-speedcenter` currently seems to be broken on `ubuntu-latest`
"test-speedcenter": large && level >= 2,
"CMAKE_OPTIONS": "-DUSE_LAKE=ON",
},
{
"name": "Linux Lake (cached)",
"os": "ubuntu-latest",
"check-level": (isPr || isPushToMaster) ? 0 : 2,
"secondary": true,
// made explicit until it can be assumed to have propagated to PRs
"CMAKE_OPTIONS": "-DUSE_LAKE=ON",
},
{
@@ -228,7 +224,9 @@ jobs:
"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
"tar": "gtar", // https://github.com/actions/runner-images/issues/2619
// not compatible with `prepare-llvm` currently
"CMAKE_OPTIONS": "-DUSE_LAKE=OFF",
},
{
"name": "macOS aarch64",
@@ -244,6 +242,8 @@ jobs:
// See "Linux release" for release job levels; Grove is not a concern here
"check-level": isPr ? 0 : 2,
"secondary": isPr,
// not compatible with `prepare-llvm` currently
"CMAKE_OPTIONS": "-DUSE_LAKE=OFF",
},
{
"name": "Windows",
@@ -256,7 +256,9 @@ jobs:
"CTEST_OPTIONS": "--repeat until-pass:2",
"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"
"binary-check": "ldd",
// not compatible with `prepare-llvm` currently
"CMAKE_OPTIONS": "-DUSE_LAKE=OFF",
},
{
"name": "Linux aarch64",
@@ -266,7 +268,9 @@ jobs:
"check-level": 2,
"shell": "nix develop .#oldGlibcAArch -c bash -euxo pipefail {0}",
"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*"
"prepare-llvm": "../script/prepare-llvm-linux.sh lean-llvm*",
// not compatible with `prepare-llvm` currently
"CMAKE_OPTIONS": "-DUSE_LAKE=OFF",
},
// Started running out of memory building expensive modules, a 2GB heap is just not that much even before fragmentation
//{

View File

@@ -57,9 +57,14 @@ jobs:
uses: actions/cache/restore@v4
with:
# NOTE: must be in sync with `restore-cache` in `build-template.yml`
# TODO: actually switch to USE_LAKE once it caches more; for now it just caches more often than any other build type so let's use its cache
path: |
.ccache
build/stage1/**/*.trace
build/stage1/**/*.olean*
build/stage1/**/*.ilean
build/stage1/**/*.ir
build/stage1/**/*.c
build/stage1/**/*.c.o*
key: Linux Lake-build-v3-${{ github.sha }}
# fall back to (latest) previous cache
restore-keys: |

View File

@@ -147,6 +147,10 @@ add_custom_target(test
COMMAND $(MAKE) -C stage1 test
DEPENDS stage1)
add_custom_target(clean-stdlib
COMMAND $(MAKE) -C stage1 clean-stdlib
DEPENDS stage1)
install(CODE "execute_process(COMMAND make -C stage1 install)")
add_custom_target(check-stage3

View File

@@ -48,3 +48,7 @@
/src/Std/Do @sgraf812
/src/Std/Tactic/Do @sgraf812
/src/Lean/Elab/Tactic/Do @sgraf812
/src/Init/Data/Range/Polymorphic @datokrat
/src/Init/Data/Slice @datokrat
/src/Init/Data/Iterators @datokrat
/src/Std/Data/Iterators @datokrat

View File

@@ -1,6 +1,6 @@
# Lean Build Bootstrapping
Since version 4, Lean is a partially bootstrapped program: most parts of the
Lean is a bootstrapped program: the
frontend and compiler are written in Lean itself and thus need to be built before
building Lean itself - which is needed to again build those parts. This cycle is
broken by using pre-built C files checked into the repository (which ultimately
@@ -73,6 +73,11 @@ update the archived C source code of the stage 0 compiler in `stage0/src`.
The github repository will automatically update stage0 on `master` once
`src/stdlib_flags.h` and `stage0/src/stdlib_flags.h` are out of sync.
NOTE: A full rebuild of stage 1 will only be triggered when the *committed* contents of `stage0/` are changed.
Thus if you change files in it manually instead of through `update-stage0-commit` (see below) or fetching updates from git, you either need to commit those changes first or run `make -C build/release clean-stdlib`.
The same is true for further stages except that a rebuild of them is retriggered on any committed change, not just to a specific directory.
Thus when debugging e.g. stage 2 failures, you can resume the build from these failures on but may want to explicitly call `clean-stdlib` to either observe changes from `.olean` files of modules that built successfully or to check that you did not break modules that built successfully at some prior point.
If you have write access to the lean4 repository, you can also manually
trigger that process, for example to be able to use new features in the compiler itself.
You can do that on <https://github.com/leanprover/lean4/actions/workflows/update-stage0.yml>
@@ -82,13 +87,13 @@ gh workflow run update-stage0.yml
```
Leaving stage0 updates to the CI automation is preferable, but should you need
to do it locally, you can use `make update-stage0-commit` in `build/release` to
update `stage0` from `stage1` or `make -C stageN update-stage0-commit` to
to do it locally, you can use `make -C build/release update-stage0-commit` to
update `stage0` from `stage1` or `make -C build/release/stageN update-stage0-commit` to
update from another stage. This command will automatically stage the updated files
and introduce a commit,so make sure to commit your work before that.
and introduce a commit, so make sure to commit your work before that.
If you rebased the branch (either onto a newer version of `master`, or fixing
up some commits prior to the stage0 update, recreate the stage0 update commits.
up some commits prior to the stage0 update), recreate the stage0 update commits.
The script `script/rebase-stage0.sh` can be used for that.
The CI should prevent PRs with changes to stage0 (besides `stdlib_flags.h`)

View File

@@ -9,7 +9,7 @@ You should not edit the `stage0` directory except using the commands described i
## Development Setup
You can use any of the [supported editors](../setup.md) for editing the Lean source code.
If you set up `elan` as below, opening `src/` as a *workspace folder* should ensure that stage 0 (i.e. the stage that first compiles `src/`) will be used for files in that directory.
Please see below for specific instructions for VS Code.
### Dev setup using elan
@@ -68,6 +68,10 @@ code lean.code-workspace
```
on the command line.
You can use the `Refresh File Dependencies` command as in other projects to rebuild modules from inside VS Code but be aware that this does not trigger any non-Lake build targets.
In particular, after updating `stage0/` (or fetching an update to it), you will want to invoke `make` directly to rebuild `stage0/bin/lean` as described in [building Lean](../make/index.md).
You should then run the `Restart Server` command to update all open files and the server watchdog process as well.
### `ccache`
Lean's build process uses [`ccache`](https://ccache.dev/) if it is

View File

@@ -282,7 +282,7 @@ theorem BinTree.find_insert_of_ne (b : BinTree β) (ne : k ≠ k') (v : β)
let t, h := b; simp
induction t with simp
| leaf =>
intros le
intro le
exact Nat.lt_of_le_of_ne le ne
| node left key value right ihl ihr =>
let .node hl hr bl br := h

View File

@@ -44,12 +44,12 @@ Useful CMake Configuration Settings
Pass these along with the `cmake --preset release` command.
There are also two alternative presets that combine some of these options you can use instead of `release`: `debug` and `sandebug` (sanitize + debug).
* `-D CMAKE_BUILD_TYPE=`\
* `-DCMAKE_BUILD_TYPE=`\
Select the build type. Valid values are `RELEASE` (default), `DEBUG`,
`RELWITHDEBINFO`, and `MINSIZEREL`.
* `-D CMAKE_C_COMPILER=`\
`-D CMAKE_CXX_COMPILER=`\
* `-DCMAKE_C_COMPILER=`\
`-DCMAKE_CXX_COMPILER=`\
Select the C/C++ compilers to use. Official Lean releases currently use Clang;
see also `.github/workflows/ci.yml` for the CI config.

View File

@@ -1,4 +1,4 @@
# Install Packages on OS X 14.5
# Install Packages on OS X
We assume that you are using [homebrew][homebrew] as a package manager.
@@ -6,23 +6,23 @@ We assume that you are using [homebrew][homebrew] as a package manager.
## Compilers
You need a C++11-compatible compiler to build Lean. As of November
2014, you have three options:
You need a C++14-compatible compiler to build Lean. As of July
2025, you have three options:
- clang++-3.5 (shipped with OSX, Apple LLVM version 6.0)
- gcc-4.9.1 (homebrew)
- clang++-3.5 (homebrew)
- clang++ shipped with OSX (at time of writing v17.0.0)
- clang++ via homebrew (at time of writing, v20.1.8)
- gcc via homebrew (at time of writing, v15.1.0)
We recommend to use Apple's clang++ because it is pre-shipped with OS
X and requires no further installation.
To install gcc-4.9.1 via homebrew, please execute:
To install gcc via homebrew, please execute:
```bash
brew install gcc
```
To install clang++-3.5 via homebrew, please execute:
To install clang via homebrew, please execute:
```bash
brew install llvm
brew install llvm lld
```
To use compilers other than the default one (Apple's clang++), you
need to use `-DCMAKE_CXX_COMPILER` option to specify the compiler

View File

@@ -18,14 +18,14 @@
# An old nixpkgs for creating releases with an old glibc
pkgsDist-old-aarch = import inputs.nixpkgs-old { localSystem.config = "aarch64-unknown-linux-gnu"; };
lean-packages = pkgs.callPackage (./nix/packages.nix) { src = ./.; };
llvmPackages = pkgs.llvmPackages_15;
devShellWithDist = pkgsDist: pkgs.mkShell.override {
stdenv = pkgs.overrideCC pkgs.stdenv lean-packages.llvmPackages.clang;
stdenv = pkgs.overrideCC pkgs.stdenv llvmPackages.clang;
} ({
buildInputs = with pkgs; [
cmake gmp libuv ccache pkg-config
lean-packages.llvmPackages.llvm # llvm-symbolizer for asan/lsan
llvmPackages.llvm # llvm-symbolizer for asan/lsan
gdb
tree # for CI
];
@@ -60,12 +60,6 @@
GDB = pkgsDist.gdb;
});
in {
packages.${system} = {
# to be removed when Nix CI is not needed anymore
inherit (lean-packages) cacheRoots test update-stage0-commit ciShell;
deprecated = lean-packages;
};
devShells.${system} = {
# The default development shell for working on lean itself
default = devShellWithDist pkgs;

View File

@@ -1,7 +0,0 @@
set -eo pipefail
for pkg in $buildInputs; do
export PATH=$PATH:$pkg/bin
done
: ${outputs:=out}

View File

@@ -1,208 +0,0 @@
{ src, debug ? false, stage0debug ? false, extraCMakeFlags ? [],
stdenv, lib, cmake, pkg-config, gmp, libuv, cadical, git, gnumake, bash, buildLeanPackage, writeShellScriptBin, runCommand, symlinkJoin, lndir, perl, gnused, darwin, llvmPackages, linkFarmFromDrvs,
... } @ args:
with builtins;
lib.warn "The Nix-based build is deprecated" rec {
inherit stdenv;
sourceByRegex = p: rs: lib.sourceByRegex p (map (r: "(/src/)?${r}") rs);
buildCMake = args: stdenv.mkDerivation ({
nativeBuildInputs = [ cmake pkg-config ];
buildInputs = [ gmp libuv llvmPackages.llvm ];
# https://github.com/NixOS/nixpkgs/issues/60919
hardeningDisable = [ "all" ];
dontStrip = (args.debug or debug);
postConfigure = ''
patchShebangs .
'';
} // args // {
src = args.realSrc or (sourceByRegex args.src [ "[a-z].*" "CMakeLists\.txt" ]);
cmakeFlags = ["-DSMALL_ALLOCATOR=ON" "-DUSE_MIMALLOC=OFF"] ++ (args.cmakeFlags or [ "-DSTAGE=1" "-DPREV_STAGE=./faux-prev-stage" "-DUSE_GITHASH=OFF" "-DCADICAL=${cadical}/bin/cadical" ]) ++ (args.extraCMakeFlags or extraCMakeFlags) ++ lib.optional (args.debug or debug) [ "-DCMAKE_BUILD_TYPE=Debug" ];
preConfigure = args.preConfigure or "" + ''
# ignore absence of submodule
sed -i 's!lake/Lake.lean!!' CMakeLists.txt
'';
});
lean-bin-tools-unwrapped = buildCMake {
name = "lean-bin-tools";
outputs = [ "out" "leanc_src" ];
realSrc = sourceByRegex (src + "/src") [ "CMakeLists\.txt" "[a-z].*" ".*\.in" "Leanc\.lean" ];
dontBuild = true;
installPhase = ''
mkdir $out $leanc_src
mv bin/ include/ share/ $out/
mv leanc.sh $out/bin/leanc
mv leanc/Leanc.lean $leanc_src/
substituteInPlace $out/bin/leanc --replace '$root' "$out" --replace " sed " " ${gnused}/bin/sed "
substituteInPlace $out/bin/leanmake --replace "make" "${gnumake}/bin/make"
substituteInPlace $out/share/lean/lean.mk --replace "/usr/bin/env bash" "${bash}/bin/bash"
'';
};
leancpp = buildCMake {
name = "leancpp";
src = src + "/src";
buildFlags = [ "leancpp" "leanrt" "leanrt_initial-exec" "leanshell" "leanmain" ];
installPhase = ''
mkdir -p $out
mv lib/ $out/
mv runtime/libleanrt_initial-exec.a $out/lib
'';
};
stage0 = args.stage0 or (buildCMake {
name = "lean-stage0";
realSrc = src + "/stage0/src";
debug = stage0debug;
cmakeFlags = [ "-DSTAGE=0" ];
extraCMakeFlags = [];
preConfigure = ''
ln -s ${src + "/stage0/stdlib"} ../stdlib
'';
installPhase = ''
mkdir -p $out/bin $out/lib/lean
mv bin/lean $out/bin/
mv lib/lean/*.{so,dylib} $out/lib/lean
'';
meta.mainProgram = "lean";
});
stage = { stage, prevStage, self }:
let
desc = "stage${toString stage}";
build = args: buildLeanPackage.override {
lean = prevStage;
leanc = lean-bin-tools-unwrapped;
# use same stage for retrieving dependencies
lean-leanDeps = stage0;
lean-final = self;
} ({
src = src + "/src";
roots = [ { mod = args.name; glob = "andSubmodules"; } ];
fullSrc = src;
srcPath = "$PWD/src:$PWD/src/lake";
inherit debug;
leanFlags = [ "-DwarningAsError=true" ];
} // args);
Init' = build { name = "Init"; deps = []; };
Std' = build { name = "Std"; deps = [ Init' ]; };
Lean' = build { name = "Lean"; deps = [ Std' ]; };
attachSharedLib = sharedLib: pkg: pkg // {
inherit sharedLib;
mods = mapAttrs (_: m: m // { inherit sharedLib; propagatedLoadDynlibs = []; }) pkg.mods;
};
in (all: all // all.lean) rec {
inherit (Lean) emacs-dev emacs-package vscode-dev vscode-package;
Init = attachSharedLib leanshared Init';
Std = attachSharedLib leanshared Std' // { allExternalDeps = [ Init ]; };
Lean = attachSharedLib leanshared Lean' // { allExternalDeps = [ Std ]; };
Lake = build {
name = "Lake";
sharedLibName = "Lake_shared";
src = src + "/src/lake";
deps = [ Init Lean ];
};
Lake-Main = build {
name = "LakeMain";
roots = [{ glob = "one"; mod = "LakeMain"; }];
executableName = "lake";
deps = [ Lake ];
linkFlags = lib.optional stdenv.isLinux "-rdynamic";
src = src + "/src/lake";
};
stdlib = [ Init Std Lean Lake ];
modDepsFiles = symlinkJoin { name = "modDepsFiles"; paths = map (l: l.modDepsFile) (stdlib ++ [ Leanc ]); };
depRoots = symlinkJoin { name = "depRoots"; paths = map (l: l.depRoots) stdlib; };
iTree = symlinkJoin { name = "ileans"; paths = map (l: l.iTree) stdlib; };
Leanc = build { name = "Leanc"; src = lean-bin-tools-unwrapped.leanc_src; deps = stdlib; roots = [ "Leanc" ]; };
stdlibLinkFlags = "${lib.concatMapStringsSep " " (l: "-L${l.staticLib}") stdlib} -L${leancpp}/lib/lean";
libInit_shared = runCommand "libInit_shared" { buildInputs = [ stdenv.cc ]; libName = "libInit_shared${stdenv.hostPlatform.extensions.sharedLibrary}"; } ''
mkdir $out
touch empty.c
${stdenv.cc}/bin/cc -shared -o $out/$libName empty.c
'';
leanshared_1 = runCommand "leanshared_1" { buildInputs = [ stdenv.cc ]; libName = "leanshared_1${stdenv.hostPlatform.extensions.sharedLibrary}"; } ''
mkdir $out
touch empty.c
${stdenv.cc}/bin/cc -shared -o $out/$libName empty.c
'';
leanshared = runCommand "leanshared" { buildInputs = [ stdenv.cc ]; libName = "libleanshared${stdenv.hostPlatform.extensions.sharedLibrary}"; } ''
mkdir $out
LEAN_CC=${stdenv.cc}/bin/cc ${lean-bin-tools-unwrapped}/bin/leanc -shared ${lib.optionalString stdenv.isLinux "-Wl,-Bsymbolic"} \
-Wl,--whole-archive ${leancpp}/lib/temp/libleanshell.a -lInit -lStd -lLean -lleancpp ${leancpp}/lib/libleanrt_initial-exec.a -Wl,--no-whole-archive -lstdc++ \
-lm ${stdlibLinkFlags} \
$(${llvmPackages.libllvm.dev}/bin/llvm-config --ldflags --libs) \
-o $out/$libName
'';
mods = foldl' (mods: pkg: mods // pkg.mods) {} stdlib;
print-paths = Lean.makePrintPathsFor [] mods;
leanc = writeShellScriptBin "leanc" ''
LEAN_CC=${stdenv.cc}/bin/cc ${Leanc.executable}/bin/leanc -I${lean-bin-tools-unwrapped}/include ${stdlibLinkFlags} -L${libInit_shared} -L${leanshared_1} -L${leanshared} -L${Lake.sharedLib} "$@"
'';
lean = runCommand "lean" { buildInputs = lib.optional stdenv.isDarwin darwin.cctools; } ''
mkdir -p $out/bin
${leanc}/bin/leanc ${leancpp}/lib/temp/libleanmain.a ${libInit_shared}/* ${leanshared_1}/* ${leanshared}/* -o $out/bin/lean
'';
# derivation following the directory layout of the "basic" setup, mostly useful for running tests
lean-all = stdenv.mkDerivation {
name = "lean-${desc}";
buildCommand = ''
mkdir -p $out/bin $out/lib/lean
ln -sf ${leancpp}/lib/lean/* ${lib.concatMapStringsSep " " (l: "${l.modRoot}/* ${l.staticLib}/*") (lib.reverseList stdlib)} ${libInit_shared}/* ${leanshared_1}/* ${leanshared}/* ${Lake.sharedLib}/* $out/lib/lean/
# put everything in a single final derivation so `IO.appDir` references work
cp ${lean}/bin/lean ${leanc}/bin/leanc ${Lake-Main.executable}/bin/lake $out/bin
# NOTE: `lndir` will not override existing `bin/leanc`
${lndir}/bin/lndir -silent ${lean-bin-tools-unwrapped} $out
'';
meta.mainProgram = "lean";
};
cacheRoots = linkFarmFromDrvs "cacheRoots" ([
stage0 lean leanc lean-all iTree modDepsFiles depRoots Leanc.src
] ++ map (lib: lib.oTree) stdlib);
test = buildCMake {
name = "lean-test-${desc}";
realSrc = lib.sourceByRegex src [ "src.*" "tests.*" ];
buildInputs = [ gmp libuv perl git cadical ];
preConfigure = ''
cd src
'';
extraCMakeFlags = [ "-DLLVM=OFF" ];
postConfigure = ''
patchShebangs ../../tests ../lake
rm -r bin lib include share
ln -sf ${lean-all}/* .
'';
buildPhase = ''
ctest --output-junit test-results.xml --output-on-failure -E 'leancomptest_(doc_example|foreign)|leanlaketest_reverse-ffi|leanruntest_timeIO' -j$NIX_BUILD_CORES
'';
installPhase = ''
mkdir $out
mv test-results.xml $out
'';
};
update-stage0 =
let cTree = symlinkJoin { name = "cs"; paths = map (lib: lib.cTree) (stdlib ++ [Lake-Main]); }; in
writeShellScriptBin "update-stage0" ''
CSRCS=${cTree} CP_C_PARAMS="--dereference --no-preserve=all" ${src + "/script/lib/update-stage0"}
'';
update-stage0-commit = writeShellScriptBin "update-stage0-commit" ''
set -euo pipefail
${update-stage0}/bin/update-stage0
git commit -m "chore: update stage0"
'';
link-ilean = writeShellScriptBin "link-ilean" ''
dest=''${1:-src}
rm -rf $dest/build/lib || true
mkdir -p $dest/build/lib
ln -s ${iTree}/* $dest/build/lib
'';
benchmarks =
let
entries = attrNames (readDir (src + "/tests/bench"));
leanFiles = map (n: elemAt n 0) (filter (n: n != null) (map (match "(.*)\.lean") entries));
in lib.genAttrs leanFiles (n: (buildLeanPackage {
name = n;
src = filterSource (e: _: baseNameOf e == "${n}.lean") (src + "/tests/bench");
}).executable);
};
stage1 = stage { stage = 1; prevStage = stage0; self = stage1; };
stage2 = stage { stage = 2; prevStage = stage1; self = stage2; };
stage3 = stage { stage = 3; prevStage = stage2; self = stage3; };
}

View File

@@ -1,247 +0,0 @@
{ lean, lean-leanDeps ? lean, lean-final ? lean, leanc,
stdenv, lib, coreutils, gnused, writeShellScriptBin, bash, substituteAll, symlinkJoin, linkFarmFromDrvs,
runCommand, darwin, mkShell, ... }:
let lean-final' = lean-final; in
lib.makeOverridable (
{ name, src, fullSrc ? src, srcPrefix ? "", srcPath ? "$PWD/${srcPrefix}",
# Lean dependencies. Each entry should be an output of buildLeanPackage.
deps ? [ lean.Init lean.Std lean.Lean ],
# Static library dependencies. Each derivation `static` should contain a static library in the directory `${static}`.
staticLibDeps ? [],
# Whether to wrap static library inputs in a -Wl,--start-group [...] -Wl,--end-group to ensure dependencies are resolved.
groupStaticLibs ? false,
# Shared library dependencies included at interpretation with --load-dynlib and linked to. Each derivation `shared` should contain a
# shared library at the path `${shared}/${shared.libName or shared.name}` and a name to link to like `-l${shared.linkName or shared.name}`.
# These libs are also linked to in packages that depend on this one.
nativeSharedLibs ? [],
# Lean modules to include.
# A set of Lean modules names as strings (`"Foo.Bar"`) or attrsets (`{ name = "Foo.Bar"; glob = "one" | "submodules" | "andSubmodules"; }`);
# see Lake README for glob meanings. Dependencies of selected modules are always included.
roots ? [ name ],
# Output from `lean --deps-json` on package source files. Persist the corresponding output attribute to a file and pass it back in here to avoid IFD.
# Must be refreshed on any change in `import`s or set of source file names.
modDepsFile ? null,
# Whether to compile each module into a native shared library that is loaded whenever the module is imported in order to accelerate evaluation
precompileModules ? false,
# Whether to compile the package into a native shared library that is loaded whenever *any* of the package's modules is imported into another package.
# If `precompileModules` is also `true`, the latter only affects imports within the current package.
precompilePackage ? precompileModules,
# Lean plugin dependencies. Each derivation `plugin` should contain a plugin library at path `${plugin}/${plugin.name}`.
pluginDeps ? [],
# `overrideAttrs` for `buildMod`
overrideBuildModAttrs ? null,
debug ? false, leanFlags ? [], leancFlags ? [], linkFlags ? [], executableName ? lib.toLower name, libName ? name, sharedLibName ? libName,
srcTarget ? "..#stage0", srcArgs ? "(\${args[*]})", lean-final ? lean-final' }@args:
with builtins; let
# "Init.Core" ~> "Init/Core"
modToPath = mod: replaceStrings ["."] ["/"] mod;
modToAbsPath = mod: "${src}/${modToPath mod}";
# sanitize file name before copying to store, except when already in store
copyToStoreSafe = base: suffix: if lib.isDerivation base then base + suffix else
builtins.path { name = lib.strings.sanitizeDerivationName (baseNameOf suffix); path = base + suffix; };
modToLean = mod: copyToStoreSafe src "/${modToPath mod}.lean";
bareStdenv = ./bareStdenv;
mkBareDerivation = args: derivation (args // {
name = lib.strings.sanitizeDerivationName args.name;
stdenv = bareStdenv;
inherit (stdenv) system;
buildInputs = (args.buildInputs or []) ++ [ coreutils ];
builder = stdenv.shell;
args = [ "-c" ''
source $stdenv/setup
set -u
${args.buildCommand}
'' ];
}) // { overrideAttrs = f: mkBareDerivation (lib.fix (lib.extends f (_: args))); };
runBareCommand = name: args: buildCommand: mkBareDerivation (args // { inherit name buildCommand; });
runBareCommandLocal = name: args: buildCommand: runBareCommand name (args // {
preferLocalBuild = true;
allowSubstitutes = false;
}) buildCommand;
mkSharedLib = name: args: runBareCommand "${name}-dynlib" {
buildInputs = [ stdenv.cc ] ++ lib.optional stdenv.isDarwin darwin.cctools;
libName = "${name}${stdenv.hostPlatform.extensions.sharedLibrary}";
} ''
mkdir -p $out
${leanc}/bin/leanc -shared ${args} -o $out/$libName
'';
depRoot = name: deps: mkBareDerivation {
name = "${name}-depRoot";
inherit deps;
depRoots = map (drv: drv.LEAN_PATH) deps;
passAsFile = [ "deps" "depRoots" ];
buildCommand = ''
mkdir -p $out
for i in $(cat $depRootsPath); do
cp -dru --no-preserve=mode $i/. $out
done
for i in $(cat $depsPath); do
cp -drsu --no-preserve=mode $i/. $out
done
'';
};
srcRoot = src;
# A flattened list of Lean-module dependencies (`deps`)
allExternalDeps = lib.unique (lib.foldr (dep: allExternalDeps: allExternalDeps ++ [ dep ] ++ dep.allExternalDeps) [] deps);
allNativeSharedLibs =
lib.unique (lib.flatten (nativeSharedLibs ++ (map (dep: dep.allNativeSharedLibs or []) allExternalDeps)));
# A flattened list of all static library dependencies: this and every dep module's explicitly provided `staticLibDeps`,
# plus every dep module itself: `dep.staticLib`
allStaticLibDeps =
lib.unique (lib.flatten (staticLibDeps ++ (map (dep: [dep.staticLib] ++ dep.staticLibDeps or []) allExternalDeps)));
pathOfSharedLib = dep: dep.libPath or "${dep}/${dep.libName or dep.name}";
leanPluginFlags = lib.concatStringsSep " " (map (dep: "--plugin=${pathOfSharedLib dep}") pluginDeps);
loadDynlibsOfDeps = deps: lib.unique (concatMap (d: d.propagatedLoadDynlibs) deps);
# submodules "Init" = ["Init.List.Basic", "Init.Core", ...]
submodules = mod: let
dir = readDir (modToAbsPath mod);
f = p: t:
if t == "directory" then
submodules "${mod}.${p}"
else
let m = builtins.match "(.*)\.lean" p;
in lib.optional (m != null) "${mod}.${head m}";
in concatLists (lib.mapAttrsToList f dir);
# conservatively approximate list of source files matched by glob
expandGlobAllApprox = g:
if typeOf g == "string" then
# we can't know the required files without parsing dependencies (which is what we want this
# function for), so we approximate to the entire package.
let root = (head (split "\\." g));
in lib.optional (pathExists (src + "/${modToPath root}.lean")) root ++ lib.optionals (pathExists (modToAbsPath root)) (submodules root)
else if g.glob == "one" then expandGlobAllApprox g.mod
else if g.glob == "submodules" then submodules g.mod
else if g.glob == "andSubmodules" then [g.mod] ++ submodules g.mod
else throw "unknown glob kind '${g}'";
# list of modules that could potentially be involved in the build
candidateMods = lib.unique (concatMap expandGlobAllApprox roots);
candidateFiles = map modToLean candidateMods;
modDepsFile = args.modDepsFile or mkBareDerivation {
name = "${name}-deps.json";
candidateFiles = lib.concatStringsSep " " candidateFiles;
passAsFile = [ "candidateFiles" ];
buildCommand = ''
mkdir $out
${lean-leanDeps}/bin/lean --deps-json --stdin < $candidateFilesPath > $out/$name
'';
};
modDeps = fromJSON (
# the only possible references to store paths in the JSON should be inside errors, so no chance of missed dependencies from this
unsafeDiscardStringContext (readFile "${modDepsFile}/${modDepsFile.name}"));
# map from module name to list of imports
modDepsMap = listToAttrs (lib.zipListsWith lib.nameValuePair candidateMods modDeps.imports);
maybeOverrideAttrs = f: x: if f != null then x.overrideAttrs f else x;
# build module (.olean and .c) given derivations of all (immediate) dependencies
# TODO: make `rec` parts override-compatible?
buildMod = mod: deps: maybeOverrideAttrs overrideBuildModAttrs (mkBareDerivation rec {
name = "${mod}";
LEAN_PATH = depRoot mod deps;
LEAN_ABORT_ON_PANIC = "1";
relpath = modToPath mod;
buildInputs = [ lean ];
leanPath = relpath + ".lean";
# should be either single .lean file or directory directly containing .lean file plus dependencies
src = copyToStoreSafe srcRoot ("/" + leanPath);
outputs = [ "out" "ilean" "c" ];
oleanPath = relpath + ".olean";
ileanPath = relpath + ".ilean";
cPath = relpath + ".c";
inherit leanFlags leanPluginFlags;
leanLoadDynlibFlags = map (p: "--load-dynlib=${pathOfSharedLib p}") (loadDynlibsOfDeps deps);
buildCommand = ''
dir=$(dirname $relpath)
mkdir -p $dir $out/$dir $ilean/$dir $c/$dir
if [ -d $src ]; then cp -r $src/. .; else cp $src $leanPath; fi
lean -o $out/$oleanPath -i $out/$ileanPath -c $c/$cPath $leanPath $leanFlags $leanPluginFlags $leanLoadDynlibFlags
'';
}) // {
inherit deps;
propagatedLoadDynlibs = loadDynlibsOfDeps deps;
};
compileMod = mod: drv: mkBareDerivation {
name = "${mod}-cc";
buildInputs = [ leanc stdenv.cc ];
hardeningDisable = [ "all" ];
oPath = drv.relpath + ".o";
inherit leancFlags;
buildCommand = ''
mkdir -p $out/$(dirname ${drv.relpath})
# make local "copy" so `drv`'s Nix store path doesn't end up in ccache's hash
ln -s ${drv.c}/${drv.cPath} src.c
# on the other hand, a debug build is pretty fast anyway, so preserve the path for gdb
leanc -c -o $out/$oPath $leancFlags -fPIC ${if debug then "${drv.c}/${drv.cPath} -g" else "src.c -O3 -DNDEBUG -DLEAN_EXPORTING"}
'';
};
mkMod = mod: deps:
let drv = buildMod mod deps;
obj = compileMod mod drv;
# this attribute will only be used if any dependent module is precompiled
sharedLib = mkSharedLib mod "${obj}/${obj.oPath} ${lib.concatStringsSep " " (map (d: pathOfSharedLib d.sharedLib) deps)}";
in drv // {
inherit obj sharedLib;
} // lib.optionalAttrs precompileModules {
propagatedLoadDynlibs = [sharedLib];
};
externalModMap = lib.foldr (dep: depMap: depMap // dep.mods) {} allExternalDeps;
# map from module name to derivation
modCandidates = mapAttrs (mod: header:
let
deps = if header.errors == []
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:
if typeOf g == "string" then [g]
else if g.glob == "one" then [g.mod]
else if g.glob == "submodules" then submodules g.mod
else if g.glob == "andSubmodules" then [g.mod] ++ submodules g.mod
else throw "unknown glob kind '${g}'";
# 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}.result.imports) else [];
}));
allLinkFlags = lib.foldr (shared: acc: acc ++ [ "-L${shared}" "-l${shared.linkName or shared.name}" ]) linkFlags allNativeSharedLibs;
objects = mapAttrs (_: m: m.obj) mods';
bintools = if stdenv.isDarwin then darwin.cctools else stdenv.cc.bintools.bintools;
staticLib = runCommand "${name}-lib" { buildInputs = [ bintools ]; } ''
mkdir -p $out
ar Trcs $out/lib${libName}.a ${lib.concatStringsSep " " (map (drv: "${drv}/${drv.oPath}") (attrValues objects))};
'';
staticLibLinkWrapper = libs: if groupStaticLibs && !stdenv.isDarwin
then "-Wl,--start-group ${libs} -Wl,--end-group"
else "${libs}";
in rec {
inherit name lean deps staticLibDeps allNativeSharedLibs allLinkFlags allExternalDeps src objects staticLib modDepsFile;
mods = mapAttrs (_: m:
m //
# if neither precompilation option was set but a dependent module wants to be precompiled, default to precompiling this package whole
lib.optionalAttrs (precompilePackage || !precompileModules) { inherit sharedLib; } //
lib.optionalAttrs precompilePackage { propagatedLoadDynlibs = [sharedLib]; })
mods';
modRoot = depRoot name (attrValues mods);
depRoots = linkFarmFromDrvs "depRoots" (map (m: m.LEAN_PATH) (attrValues mods));
cTree = symlinkJoin { name = "${name}-cTree"; paths = map (mod: mod.c) (attrValues mods); };
oTree = symlinkJoin { name = "${name}-oTree"; paths = (attrValues objects); };
iTree = symlinkJoin { name = "${name}-iTree"; paths = map (mod: mod.ilean) (attrValues mods); };
sharedLib = mkSharedLib "lib${sharedLibName}" ''
${if stdenv.isDarwin then "-Wl,-force_load,${staticLib}/lib${libName}.a" else "-Wl,--whole-archive ${staticLib}/lib${libName}.a -Wl,--no-whole-archive"} \
${lib.concatStringsSep " " (map (d: "${d.sharedLib}/*") deps)}'';
executable = lib.makeOverridable ({ withSharedStdlib ? true }: let
objPaths = map (drv: "${drv}/${drv.oPath}") (attrValues objects) ++ lib.optional withSharedStdlib "${lean-final.leanshared}/*";
in runCommand executableName { buildInputs = [ stdenv.cc leanc ]; } ''
mkdir -p $out/bin
leanc ${staticLibLinkWrapper (lib.concatStringsSep " " (objPaths ++ map (d: "${d}/*.a") allStaticLibDeps))} \
-o $out/bin/${executableName} \
${lib.concatStringsSep " " allLinkFlags}
'') {};
})

View File

@@ -1,42 +0,0 @@
#!@bash@/bin/bash
set -euo pipefail
function pebkac() {
echo 'This is just a simple Nix adapter for `lake print-paths|serve`.'
exit 1
}
[[ $# -gt 0 ]] || pebkac
case $1 in
--version)
# minimum version for `lake serve` with fallback
echo 3.1.0
;;
print-paths)
shift
deps="$@"
root=.
# fall back to initial package if not in package
[[ ! -f "$root/flake.nix" ]] && root="@srcRoot@"
target="$root#print-paths"
args=()
# HACK: use stage 0 instead of 1 inside Lean's own `src/`
[[ -d Lean && -f ../flake.nix ]] && target="@srcTarget@print-paths" && args=@srcArgs@
for dep in $deps; do
target="$target.\"$dep\""
done
echo "Building dependencies..." >&2
# -v only has "built ...", but "-vv" is a bit too verbose
exec @nix@/bin/nix run "$target" ${args[*]} -v
;;
serve)
shift
[[ ${1:-} == "--" ]] && shift
# `link-ilean` puts them there
LEAN_PATH=${LEAN_PATH:+$LEAN_PATH:}$PWD/build/lib exec $(dirname $0)/lean --server "$@"
;;
*)
pebkac
;;
esac

View File

@@ -1,28 +0,0 @@
#!@bash@/bin/bash
set -euo pipefail
root="."
# find package root
while [[ "$root" != / ]]; do
[ -f "$root/flake.nix" ] && break
root="$(realpath "$root/..")"
done
# fall back to initial package if not in package
[[ ! -f "$root/flake.nix" ]] && root="@srcRoot@"
# use Lean w/ package unless in server mode (which has its own LEAN_PATH logic)
target="$root#lean-package"
for arg in "$@"; do
case $arg in
--server | --worker | -v | --version)
target="$root#lean"
;;
esac
done
args=(-- "$@")
# HACK: use stage 0 instead of 1 inside Lean's own `src/`
[[ -d Lean && -f ../flake.nix ]] && target="@srcTarget@" && args=@srcArgs@
LEAN_SYSROOT="$(dirname "$0")/.." exec @nix@/bin/nix ${LEAN_NIX_ARGS:-} run "$target" ${args[*]}

View File

@@ -1,52 +0,0 @@
{ src, pkgs, ... } @ args:
with pkgs;
let
# https://github.com/NixOS/nixpkgs/issues/130963
llvmPackages = if stdenv.isDarwin then llvmPackages_11 else llvmPackages_15;
cc = (ccacheWrapper.override rec {
cc = llvmPackages.clang;
extraConfig = ''
export CCACHE_DIR=/nix/var/cache/ccache
export CCACHE_UMASK=007
export CCACHE_BASE_DIR=$NIX_BUILD_TOP
# https://github.com/NixOS/nixpkgs/issues/109033
args=("$@")
for ((i=0; i<"''${#args[@]}"; i++)); do
case ''${args[i]} in
-frandom-seed=*) unset args[i]; break;;
esac
done
set -- "''${args[@]}"
[ -d $CCACHE_DIR ] || exec ${cc}/bin/$(basename "$0") "$@"
'';
}).overrideAttrs (old: {
# https://github.com/NixOS/nixpkgs/issues/119779
installPhase = builtins.replaceStrings ["use_response_file_by_default=1"] ["use_response_file_by_default=0"] old.installPhase;
});
stdenv' = if stdenv.isLinux then useGoldLinker stdenv else stdenv;
lean = callPackage (import ./bootstrap.nix) (args // {
stdenv = overrideCC stdenv' cc;
inherit src buildLeanPackage llvmPackages;
});
makeOverridableLeanPackage = f:
let newF = origArgs: f origArgs // {
overrideArgs = newArgs: makeOverridableLeanPackage f (origArgs // newArgs);
};
in lib.setFunctionArgs newF (lib.getFunctionArgs f) // {
override = args: makeOverridableLeanPackage (f.override args);
};
buildLeanPackage = makeOverridableLeanPackage (callPackage (import ./buildLeanPackage.nix) (args // {
inherit (lean) stdenv;
lean = lean.stage1;
inherit (lean.stage1) leanc;
}));
in {
inherit cc buildLeanPackage llvmPackages;
nixpkgs = pkgs;
ciShell = writeShellScriptBin "ciShell" ''
set -o pipefail
export PATH=${moreutils}/bin:$PATH
# prefix lines with cumulative and individual execution time
"$@" |& ts -i "(%.S)]" | ts -s "[%M:%S"
'';
} // lean.stage1

View File

@@ -1 +0,0 @@
#eval "Hello, world!"

View File

@@ -1,21 +0,0 @@
{
description = "My Lean package";
inputs.lean.url = "github:leanprover/lean4";
inputs.flake-utils.url = "github:numtide/flake-utils";
outputs = { self, lean, flake-utils }: flake-utils.lib.eachDefaultSystem (system:
let
leanPkgs = lean.packages.${system};
pkg = leanPkgs.buildLeanPackage {
name = "MyPackage"; # must match the name of the top-level .lean file
src = ./.;
};
in {
packages = pkg // {
inherit (leanPkgs) lean;
};
defaultPackage = pkg.modRoot;
});
}

View File

@@ -0,0 +1,101 @@
/-
Copyright (c) 2025 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
-/
import Lean
namespace Lean.Meta.Grind.Analyzer
/-!
A simple E-matching annotation analyzer.
For each theorem annotated as an E-matching candidate, it creates an artificial goal, executes `grind` and shows the
number of instances created.
For a theorem of the form `params -> type`, the artificial goal is of the form `params -> type -> False`.
-/
/--
`grind` configuration for the analyzer. We disable case-splits and lookahead,
increase the number of generations, and limit the number of instances generated.
-/
def config : Grind.Config := {
splits := 0
lookahead := false
mbtc := false
ematch := 20
instances := 100
gen := 10
}
structure Config where
/-- Minimum number of instantiations to trigger summary report -/
min : Nat := 10
/-- Minimum number of instantiations to trigger detailed report -/
detailed : Nat := 50
def mkParams : MetaM Params := do
let params Grind.mkParams config
let ematch getEMatchTheorems
let casesTypes Grind.getCasesTypes
return { params with ematch, casesTypes }
/-- Returns the total number of generated instances. -/
private def sum (cs : PHashMap Origin Nat) : Nat := Id.run do
let mut r := 0
for (_, c) in cs do
r := r + c
return r
private def thmsToMessageData (thms : PHashMap Origin Nat) : MetaM MessageData := do
let data := thms.toArray.filterMap fun (origin, c) =>
match origin with
| .decl declName => some (declName, c)
| _ => none
let data := data.qsort fun (d₁, c₁) (d₂, c₂) => if c₁ == c₂ then Name.lt d₁ d₂ else c₁ > c₂
let data data.mapM fun (declName, counter) =>
return .trace { cls := `thm } m!"{.ofConst (← mkConstWithLevelParams declName)} ↦ {counter}" #[]
return .trace { cls := `thm } "instances" data
/--
Analyzes theorem `declName`. That is, creates the artificial goal based on `declName` type,
and invokes `grind` on it.
-/
def analyzeEMatchTheorem (declName : Name) (c : Config) : MetaM Unit := do
let info getConstInfo declName
let mvarId forallTelescope info.type fun _ type => do
withLocalDeclD `h type fun _ => do
return ( mkFreshExprMVar (mkConst ``False)).mvarId!
let result Grind.main mvarId ( mkParams) (pure ())
let thms := result.counters.thm
let s := sum thms
if s > c.min then
IO.println s!"{declName} : {s}"
if s > c.detailed then
logInfo m!"{declName}\n{← thmsToMessageData thms}"
-- Not sure why this is failing: `down_pure` perhaps has an unnecessary universe parameter?
run_meta analyzeEMatchTheorem ``Std.Do.SPred.down_pure {}
/-- Analyzes all theorems in the standard library marked as E-matching theorems. -/
def analyzeEMatchTheorems (c : Config := {}) : MetaM Unit := do
let origins := ( getEMatchTheorems).getOrigins
let decls := origins.filterMap fun | .decl declName => some declName | _ => none
for declName in decls.mergeSort Name.lt do
try
analyzeEMatchTheorem declName c
catch e =>
logError m!"{declName} failed with {e.toMessageData}"
logInfo m!"Finished analyzing {decls.length} theorems"
/-- Macro for analyzing E-match theorems with unlimited heartbeats -/
macro "#analyzeEMatchTheorems" : command => `(
set_option maxHeartbeats 0 in
run_meta analyzeEMatchTheorems
)
#analyzeEMatchTheorems
-- -- We can analyze specific theorems using commands such as
set_option trace.grind.ematch.instance true in
run_meta analyzeEMatchTheorem ``List.filterMap_some {}

View File

@@ -1,16 +1,96 @@
#!/usr/bin/env bash
set -euo pipefail
set -euxo pipefail
cmake --preset release -DUSE_LAKE=ON 1>&2
cmake --preset release 1>&2
# We benchmark against stage 2 to test new optimizations.
timeout -s KILL 1h time make -C build/release -j$(nproc) stage2 1>&2
# We benchmark against stage2/bin to test new optimizations.
timeout -s KILL 1h time make -C build/release -j$(nproc) stage3 1>&2
export PATH=$PWD/build/release/stage2/bin:$PATH
# The extra opts used to be passed to the Makefile during benchmarking only but with Lake it is
# easier to configure them statically.
cmake -B build/release/stage2 -S src -DLEAN_EXTRA_LAKEFILE_TOML='weakLeanArgs=["-Dprofiler=true", "-Dprofiler.threshold=9999999", "--stats"]' 1>&2
cmake -B build/release/stage3 -S src -DLEAN_EXTRA_LAKEFILE_TOML='weakLeanArgs=["-Dprofiler=true", "-Dprofiler.threshold=9999999", "--stats"]' 1>&2
(
cd tests/bench
timeout -s KILL 1h time temci exec --config speedcenter.yaml --in speedcenter.exec.velcom.yaml 1>&2
temci report run_output.yaml --reporter codespeed2
)
if [ -d .git ]; then
DIR="$(git rev-parse @)"
BASE_URL="https://speed.lean-lang.org/lean4-out/$DIR"
{
cat <<'EOF'
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Lakeprof Report</title>
</head>
<h1>Lakeprof Report</h1>
<button type="button" id="btn_fetch">View build trace in Perfetto</button>
<script type="text/javascript">
const ORIGIN = 'https://ui.perfetto.dev';
const btnFetch = document.getElementById('btn_fetch');
async function fetchAndOpen(traceUrl) {
const resp = await fetch(traceUrl);
// Error checking is left as an exercise to the reader.
const blob = await resp.blob();
const arrayBuffer = await blob.arrayBuffer();
openTrace(arrayBuffer, traceUrl);
}
function openTrace(arrayBuffer, traceUrl) {
const win = window.open(ORIGIN);
if (!win) {
btnFetch.style.background = '#f3ca63';
btnFetch.onclick = () => openTrace(arrayBuffer);
btnFetch.innerText = 'Popups blocked, click here to open the trace file';
return;
}
const timer = setInterval(() => win.postMessage('PING', ORIGIN), 50);
const onMessageHandler = (evt) => {
if (evt.data !== 'PONG') return;
// We got a PONG, the UI is ready.
window.clearInterval(timer);
window.removeEventListener('message', onMessageHandler);
const reopenUrl = new URL(location.href);
reopenUrl.hash = `#reopen=${traceUrl}`;
win.postMessage({
perfetto: {
buffer: arrayBuffer,
title: 'Lake Build Trace',
url: reopenUrl.toString(),
}}, ORIGIN);
};
window.addEventListener('message', onMessageHandler);
}
// This is triggered when following the link from the Perfetto UI's sidebar.
if (location.hash.startsWith('#reopen=')) {
const traceUrl = location.hash.substr(8);
fetchAndOpen(traceUrl);
}
EOF
cat <<EOF
btnFetch.onclick = () => fetchAndOpen("$BASE_URL/lakeprof.trace_event");
</script>
EOF
echo "<pre><code>"
(cd src; lakeprof report -prc)
echo "</code></pre>"
echo "</body></html>"
} | tee index.html
curl -T index.html $BASE_URL/index.html
curl -T src/lakeprof.log $BASE_URL/lakeprof.log
curl -T src/lakeprof.trace_event $BASE_URL/lakeprof.trace_event
fi

View File

@@ -28,13 +28,6 @@ repositories:
branch: main
dependencies: []
- name: doc-gen4
url: https://github.com/leanprover/doc-gen4
toolchain-tag: true
stable-branch: false
branch: main
dependencies: [lean4-cli]
- name: verso
url: https://github.com/leanprover/verso
toolchain-tag: true
@@ -42,6 +35,28 @@ repositories:
branch: main
dependencies: []
- name: plausible
url: https://github.com/leanprover-community/plausible
toolchain-tag: true
stable-branch: false
branch: main
dependencies: []
- name: import-graph
url: https://github.com/leanprover-community/import-graph
toolchain-tag: true
stable-branch: false
branch: main
dependencies:
- lean4-cli
- name: doc-gen4
url: https://github.com/leanprover/doc-gen4
toolchain-tag: true
stable-branch: false
branch: main
dependencies: [lean4-cli]
- name: reference-manual
url: https://github.com/leanprover/reference-manual
toolchain-tag: true
@@ -65,22 +80,6 @@ repositories:
dependencies:
- batteries
- name: import-graph
url: https://github.com/leanprover-community/import-graph
toolchain-tag: true
stable-branch: false
branch: main
dependencies:
- lean4-cli
- batteries
- name: plausible
url: https://github.com/leanprover-community/plausible
toolchain-tag: true
stable-branch: false
branch: main
dependencies: []
- name: mathlib4
url: https://github.com/leanprover-community/mathlib4
toolchain-tag: true

View File

@@ -10,7 +10,7 @@ endif()
include(ExternalProject)
project(LEAN CXX C)
set(LEAN_VERSION_MAJOR 4)
set(LEAN_VERSION_MINOR 23)
set(LEAN_VERSION_MINOR 24)
set(LEAN_VERSION_PATCH 0)
set(LEAN_VERSION_IS_RELEASE 0) # This number is 1 in the release revision, and 0 otherwise.
set(LEAN_SPECIAL_VERSION_DESC "" CACHE STRING "Additional version description like 'nightly-2018-03-11'")
@@ -81,7 +81,7 @@ option(USE_MIMALLOC "use mimalloc" ON)
# development-specific options
option(CHECK_OLEAN_VERSION "Only load .olean files compiled with the current version of Lean" OFF)
option(USE_LAKE "Use Lake instead of lean.mk for building core libs from language server" OFF)
option(USE_LAKE "Use Lake instead of lean.mk for building core libs from language server" ON)
set(LEAN_EXTRA_MAKE_OPTS "" CACHE STRING "extra options to lean --make")
set(LEANC_CC ${CMAKE_C_COMPILER} CACHE STRING "C compiler to use in `leanc`")
@@ -684,12 +684,17 @@ if (LLVM AND ${STAGE} GREATER 0)
set(EXTRA_LEANMAKE_OPTS "LLVM=1")
endif()
set(STDLIBS Init Std Lean Leanc)
if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
list(APPEND STDLIBS Lake)
endif()
add_custom_target(make_stdlib ALL
WORKING_DIRECTORY ${LEAN_SOURCE_DIR}
# The actual rule is in a separate makefile because we want to prefix it with '+' to use the Make job server
# for a parallelized nested build, but CMake doesn't let us do that.
# We use `lean` from the previous stage, but `leanc`, headers, etc. from the current stage
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make Init Std Lean Leanc
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make ${STDLIBS}
VERBATIM)
# if we have LLVM enabled, then build `lean.h.bc` which has the LLVM bitcode
@@ -733,14 +738,9 @@ else()
endif()
if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
add_custom_target(lake_lib
WORKING_DIRECTORY ${LEAN_SOURCE_DIR}
DEPENDS leanshared
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make Lake
VERBATIM)
add_custom_target(lake_shared
WORKING_DIRECTORY ${LEAN_SOURCE_DIR}
DEPENDS lake_lib
DEPENDS leanshared
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make libLake_shared
VERBATIM)
add_custom_target(lake ALL

View File

@@ -9,7 +9,7 @@ prelude
public import Init.Core
public import Init.BinderNameHint
public section
@[expose] public section
universe u v w

View File

@@ -187,6 +187,9 @@ match [a, b] with
simplifies to `a`. -/
syntax (name := simpMatch) "simp_match" : conv
/-- Removes one or more hypotheses from the local context. -/
syntax (name := clear) "clear" (ppSpace colGt term:max)+ : conv
/-- Executes the given tactic block without converting `conv` goal into a regular goal. -/
syntax (name := nestedTacticCore) "tactic'" " => " tacticSeq : conv

View File

@@ -29,6 +29,29 @@ theorem id_def {α : Sort u} (a : α) : id a = a := rfl
attribute [grind] id
/--
A helper gadget for instructing the kernel to eagerly reduce terms.
When the gadget wraps the argument of an application, then when checking that
the expected and inferred type of the argument match, the kernel will evaluate terms more eagerly.
It is often used to wrap `Eq.refl true` proof terms as `eagerReduce (Eq.refl true)`
when using proof by reflection.
As an example, consider the theorem:
```
theorem eq_norm (ctx : Context) (p₁ p₂ : Poly) (h : (p₁.norm == p₂) = true) :
p₁.denote ctx = 0 → p₂.denote ctx = 0
```
The argument `h : (p₁.norm == p₂) = true` is a candidate for `eagerReduce`.
When applying this theorem, we would write:
```
eq_norm ctx p q (eagerReduce (Eq.refl true)) h
```
to instruct the kernel to use eager reduction when establishing that `(p.norm == q) = true` is
definitionally equal to `true = true`.
-/
@[expose] def eagerReduce {α : Sort u} (a : α) : α := a
/--
`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.
@@ -1536,38 +1559,13 @@ end Setoid
/-! # Propositional extensionality -/
/--
The axiom of **propositional extensionality**. It asserts that if propositions
`a` and `b` are logically equivalent (i.e. we can prove `a` from `b` and vice versa),
then `a` and `b` are *equal*, meaning that we can replace `a` with `b` in all
contexts.
The [axiom](lean-manual://section/axioms) of **propositional extensionality**. It asserts that if
propositions `a` and `b` are logically equivalent (that is, if `a` can be proved from `b` and vice
versa), then `a` and `b` are *equal*, meaning `a` can be replaced with `b` in all contexts.
For simple expressions like `a ∧ c d → e` we can prove that because all the logical
connectives respect logical equivalence, we can replace `a` with `b` in this expression
without using `propext`. However, for higher order expressions like `P a` where
`P : Prop → Prop` is unknown, or indeed for `a = b` itself, we cannot replace `a` with `b`
without an axiom which says exactly this.
This is a relatively uncontroversial axiom, which is intuitionistically valid.
It does however block computation when using `#reduce` to reduce proofs directly
(which is not recommended), meaning that canonicity,
the property that all closed terms of type `Nat` normalize to numerals,
fails to hold when this (or any) axiom is used:
```
set_option pp.proofs true
def foo : Nat := by
have : (True → True) ↔ True := ⟨λ _ => trivial, λ _ _ => trivial⟩
have := propext this ▸ (2 : Nat)
exact this
#reduce foo
-- propext { mp := fun x x => True.intro, mpr := fun x => True.intro } ▸ 2
#eval foo -- 2
```
`#eval` can evaluate it to a numeral because the compiler erases casts and
does not evaluate proofs, so `propext`, whose return type is a proposition,
can never block it.
The standard logical connectives provably respect propositional extensionality. However, an axiom is
needed for higher order expressions like `P a` where `P : Prop → Prop` is unknown, as well as for
equality. Propositional extensionality is intuitionistically valid.
-/
axiom propext {a b : Prop} : (a b) a = b
@@ -1598,6 +1596,7 @@ gen_injective_theorems% MProd
gen_injective_theorems% NonScalar
gen_injective_theorems% Option
gen_injective_theorems% PLift
gen_injective_theorems% PULift
gen_injective_theorems% PNonScalar
gen_injective_theorems% PProd
gen_injective_theorems% Prod
@@ -2523,12 +2522,17 @@ class Antisymm (r : αα → Prop) : Prop where
/-- An antisymmetric relation `r` satisfies `r a b → r b a → a = b`. -/
antisymm (a b : α) : r a b r b a a = b
/-- `Asymm X r` means that the binary relation `r` on `X` is asymmetric, that is,
/-- `Asymm r` means that the binary relation `r` is asymmetric, that is,
`r a b → ¬ r b a`. -/
class Asymm (r : α α Prop) : Prop where
/-- An asymmetric relation satisfies `r a b → ¬ r b a`. -/
asymm : a b, r a b ¬r b a
/-- `Symm r` means that the binary relation `r` is symmetric, that is, `r a b → r b a`. -/
class Symm (r : α α Prop) : Prop where
/-- A symmetric relation satisfies `r a b → r b a`. -/
symm : a b, r a b r b a
/-- `Total X r` means that the binary relation `r` on `X` is total, that is, that for any
`x y : X` we have `r x y` or `r y x`. -/
class Total (r : α α Prop) : Prop where
@@ -2542,3 +2546,7 @@ class Irrefl (r : αα → Prop) : Prop where
irrefl : a, ¬r a a
end Std
/-- Deprecated alias for `XorOp`. -/
@[deprecated XorOp (since := "2025-07-30")]
abbrev Xor := XorOp

View File

@@ -49,5 +49,7 @@ public import Init.Data.Vector
public import Init.Data.Iterators
public import Init.Data.Range.Polymorphic
public import Init.Data.Slice
public import Init.Data.Order
public import Init.Data.Rat
public section

View File

@@ -163,7 +163,7 @@ representation of arrays. While this is not provable, `Array.usize` always retur
the array since the implementation only supports arrays of size less than `USize.size`.
-/
@[extern "lean_array_size", simp]
def usize (a : @& Array α) : USize := a.size.toUSize
def usize (xs : @& Array α) : USize := xs.size.toUSize
/--
Low-level indexing operator which is as fast as a C array read.
@@ -171,8 +171,8 @@ Low-level indexing operator which is as fast as a C array read.
This avoids overhead due to unboxing a `Nat` used as an index.
-/
@[extern "lean_array_uget", simp, expose]
def uget (a : @& Array α) (i : USize) (h : i.toNat < a.size) : α :=
a[i.toNat]
def uget (xs : @& Array α) (i : USize) (h : i.toNat < xs.size) : α :=
xs[i.toNat]
/--
Low-level modification operator which is as fast as a C array write. The modification is performed
@@ -1165,7 +1165,7 @@ Examples:
def zipIdx (xs : Array α) (start := 0) : Array (α × Nat) :=
xs.mapIdx fun i a => (a, start + i)
@[deprecated zipIdx (since := "2025-01-21")] abbrev zipWithIndex := @zipIdx
/--
Returns the first element of the array for which the predicate `p` returns `true`, or `none` if no
@@ -1285,7 +1285,7 @@ def findFinIdx? {α : Type u} (p : α → Bool) (as : Array α) : Option (Fin as
decreasing_by simp_wf; decreasing_trivial_pre_omega
loop 0
theorem findIdx?_loop_eq_map_findFinIdx?_loop_val {xs : Array α} {p : α Bool} {j} :
private theorem findIdx?_loop_eq_map_findFinIdx?_loop_val {xs : Array α} {p : α Bool} {j} :
findIdx?.loop p xs j = (findFinIdx?.loop p xs j).map (·.val) := by
unfold findIdx?.loop
unfold findFinIdx?.loop
@@ -1322,8 +1322,7 @@ def idxOfAux [BEq α] (xs : Array α) (v : α) (i : Nat) : Option (Fin xs.size)
else none
decreasing_by simp_wf; decreasing_trivial_pre_omega
@[deprecated idxOfAux (since := "2025-01-29")]
abbrev indexOfAux := @idxOfAux
/--
Returns the index of the first element equal to `a`, or the size of the array if no element is equal
@@ -1338,8 +1337,7 @@ Examples:
def finIdxOf? [BEq α] (xs : Array α) (v : α) : Option (Fin xs.size) :=
idxOfAux xs v 0
@[deprecated "`Array.indexOf?` has been deprecated, use `idxOf?` or `finIdxOf?` instead." (since := "2025-01-29")]
abbrev indexOf? := @finIdxOf?
/--
Returns the index of the first element equal to `a`, or the size of the array if no element is equal
@@ -1956,16 +1954,16 @@ def isPrefixOf [BEq α] (as bs : Array α) : Bool :=
false
@[semireducible, specialize] -- This is otherwise irreducible because it uses well-founded recursion.
def zipWithAux (as : Array α) (bs : Array β) (f : α β γ) (i : Nat) (cs : Array γ) : Array γ :=
def zipWithMAux {m : Type v Type w} [Monad m] (as : Array α) (bs : Array β) (f : α β m γ) (i : Nat) (cs : Array γ) : m (Array γ) := do
if h : i < as.size then
let a := as[i]
if h : i < bs.size then
let b := bs[i]
zipWithAux as bs f (i+1) <| cs.push <| f a b
zipWithMAux as bs f (i+1) <| cs.push ( f a b)
else
cs
return cs
else
cs
return cs
decreasing_by simp_wf; decreasing_trivial_pre_omega
/--
@@ -1979,7 +1977,7 @@ Examples:
* `#[x₁, x₂, x₃].zipWith f #[y₁, y₂, y₃, y₄] = #[f x₁ y₁, f x₂ y₂, f x₃ y₃]`
-/
@[inline] def zipWith (f : α β γ) (as : Array α) (bs : Array β) : Array γ :=
zipWithAux as bs f 0 #[]
Id.run (zipWithMAux as bs (pure <| f · ·) 0 #[])
/--
Combines two arrays into an array of pairs in which the first and second components are the
@@ -2016,6 +2014,13 @@ where go (as : Array α) (bs : Array β) (i : Nat) (cs : Array γ) :=
termination_by max as.size bs.size - i
decreasing_by simp_wf; decreasing_trivial_pre_omega
/--
Applies a monadic function to the corresponding elements of two arrays, left-to-right, stopping at
the end of the shorter array. `zipWithM f as bs` is equivalent to `mapM id (zipWith f as bs)`.
-/
@[inline] def zipWithM {m : Type v Type w} [Monad m] (f : α β m γ) (as : Array α) (bs : Array β) : m (Array γ) :=
zipWithMAux as bs f 0 #[]
/--
Separates an array of pairs into two arrays that contain the respective first and second components.

View File

@@ -34,8 +34,8 @@ the index is in bounds. This is because the tactic itself needs to look up value
arrays.
-/
@[deprecated "Use indexing notation `as[i]` instead" (since := "2025-02-17")]
def get {α : Type u} (a : @& Array α) (i : @& Nat) (h : LT.lt i a.size) : α :=
a.toList.get i, h
def get {α : Type u} (xs : @& Array α) (i : @& Nat) (h : LT.lt i xs.size) : α :=
xs.toList.get i, h
/--
Use the indexing notation `a[i]!` instead.
@@ -43,8 +43,8 @@ Use the indexing notation `a[i]!` instead.
Access an element from an array, or panic if the index is out of bounds.
-/
@[deprecated "Use indexing notation `as[i]!` instead" (since := "2025-02-17"), expose]
def get! {α : Type u} [Inhabited α] (a : @& Array α) (i : @& Nat) : α :=
Array.getD a i default
def get! {α : Type u} [Inhabited α] (xs : @& Array α) (i : @& Nat) : α :=
Array.getD xs i default
theorem foldlM_toList.aux [Monad m]
{f : β α m β} {xs : Array α} {i j} (H : xs.size i + j) {b} :
@@ -123,15 +123,9 @@ abbrev pop_toList := @Array.toList_pop
@[simp, grind =] theorem append_empty {xs : Array α} : xs ++ #[] = xs := by
apply ext'; simp only [toList_append, List.append_nil]
@[deprecated append_empty (since := "2025-01-13")]
abbrev append_nil := @append_empty
@[simp, grind =] theorem empty_append {xs : Array α} : #[] ++ xs = xs := by
apply ext'; simp only [toList_append, List.nil_append]
@[deprecated empty_append (since := "2025-01-13")]
abbrev nil_append := @empty_append
@[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]
@@ -142,7 +136,6 @@ abbrev nil_append := @empty_append
rw [ appendList_eq_append]; unfold Array.appendList
induction l generalizing xs <;> simp [*]
@[deprecated toList_appendList (since := "2024-12-11")]
abbrev appendList_toList := @toList_appendList
end Array

View File

@@ -8,19 +8,20 @@ module
prelude
public import Init.Data.Nat.Lemmas
public import Init.Data.List.Range
public import all Init.Data.List.Control
public import Init.Data.List.Nat.TakeDrop
public import Init.Data.List.Nat.Modify
public import Init.Data.List.Nat.Basic
public import Init.Data.List.Monadic
public import Init.Data.List.OfFn
public import all Init.Data.Array.Bootstrap
public import Init.Data.Array.Mem
public import Init.Data.Array.DecidableEq
public import Init.Data.Array.Lex.Basic
public import Init.Data.Range.Lemmas
public import Init.TacticsExtra
public import Init.Data.List.ToArray
import all Init.Data.List.Control
import all Init.Data.Array.Basic
import all Init.Data.Array.Bootstrap
public section
@@ -311,7 +312,7 @@ theorem eq_push_pop_back!_of_size_ne_zero [Inhabited α] {xs : Array α} (h : xs
xs = xs.pop.push xs.back! := by
apply ext
· simp [Nat.sub_add_cancel (Nat.zero_lt_of_ne_zero h)]
· intros i h h'
· intro i h h'
if hlt : i < xs.pop.size then
rw [getElem_push_lt (h:=hlt), getElem_pop]
else
@@ -837,15 +838,10 @@ theorem mem_of_contains_eq_true [BEq α] [LawfulBEq α] {a : α} {as : Array α}
cases as
simp
@[deprecated mem_of_contains_eq_true (since := "2024-12-12")]
abbrev mem_of_elem_eq_true := @mem_of_contains_eq_true
theorem contains_eq_true_of_mem [BEq α] [LawfulBEq α] {a : α} {as : Array α} (h : a as) : as.contains a = true := by
theorem contains_eq_true_of_mem [BEq α] [ReflBEq α] {a : α} {as : Array α} (h : a as) :
as.contains a = true := by
cases as
simpa using h
@[deprecated contains_eq_true_of_mem (since := "2024-12-12")]
abbrev elem_eq_true_of_mem := @contains_eq_true_of_mem
simpa using List.elem_eq_true_of_mem (Array.mem_toList_iff.mpr h)
@[simp] theorem elem_eq_contains [BEq α] {a : α} {xs : Array α} :
elem a xs = xs.contains a := by
@@ -904,15 +900,9 @@ theorem all_push {xs : Array α} {a : α} {p : α → Bool} :
cases xs
simp
@[deprecated getElem_set_self (since := "2024-12-11")]
abbrev getElem_set_eq := @getElem_set_self
@[simp] theorem getElem?_set_self {xs : Array α} {i : Nat} (h : i < xs.size) {v : α} :
(xs.set i v)[i]? = some v := by simp [h]
@[deprecated getElem?_set_self (since := "2024-12-11")]
abbrev getElem?_set_eq := @getElem?_set_self
@[simp] theorem getElem_set_ne {xs : Array α} {i : Nat} (h' : i < xs.size) {v : α} {j : Nat}
(pj : j < xs.size) (h : i j) :
(xs.set i v)[j]'(by simp [*]) = xs[j] := by
@@ -1003,9 +993,6 @@ grind_pattern mem_or_eq_of_mem_set => a ∈ xs.set i b
theorem setIfInBounds_def (xs : Array α) (i : Nat) (a : α) :
xs.setIfInBounds i a = if h : i < xs.size then xs.set i a else xs := rfl
@[deprecated set!_eq_setIfInBounds (since := "2024-12-12")]
abbrev set!_is_setIfInBounds := @set!_eq_setIfInBounds
@[simp, grind] theorem size_setIfInBounds {xs : Array α} {i : Nat} {a : α} :
(xs.setIfInBounds i a).size = xs.size := by
if h : i < xs.size then
@@ -1027,9 +1014,6 @@ abbrev set!_is_setIfInBounds := @set!_eq_setIfInBounds
simp at h
simp only [setIfInBounds, h, reduceDIte, getElem_set_self]
@[deprecated getElem_setIfInBounds_self (since := "2024-12-11")]
abbrev getElem_setIfInBounds_eq := @getElem_setIfInBounds_self
@[simp] theorem getElem_setIfInBounds_ne {xs : Array α} {i : Nat} {a : α} {j : Nat}
(hj : j < xs.size) (h : i j) :
(xs.setIfInBounds i a)[j]'(by simpa using hj) = xs[j] := by
@@ -1049,9 +1033,6 @@ theorem getElem?_setIfInBounds_self_of_lt {xs : Array α} {i : Nat} {a : α} (h
(xs.setIfInBounds i a)[i]? = some a := by
simp [h]
@[deprecated getElem?_setIfInBounds_self (since := "2024-12-11")]
abbrev getElem?_setIfInBounds_eq := @getElem?_setIfInBounds_self
@[simp] theorem getElem?_setIfInBounds_ne {xs : Array α} {i j : Nat} (h : i j) {a : α} :
(xs.setIfInBounds i a)[j]? = xs[j]? := by
simp [getElem?_setIfInBounds, h]
@@ -1377,23 +1358,6 @@ theorem mapM_eq_mapM_toList [Monad m] [LawfulMonad m] {f : α → m β} {xs : Ar
toList <$> xs.mapM f = xs.toList.mapM f := by
simp [mapM_eq_mapM_toList]
@[deprecated "Use `mapM_eq_foldlM` instead" (since := "2025-01-08")]
theorem mapM_map_eq_foldl {as : Array α} {f : α β} {i : Nat} :
mapM.map (m := Id) (pure <| f ·) as i b = pure (as.foldl (start := i) (fun acc a => acc.push (f a)) b) := by
unfold mapM.map
split <;> rename_i h
· ext : 1
dsimp [foldl, foldlM]
rw [mapM_map_eq_foldl, dif_pos (by omega), foldlM.loop, dif_pos h]
-- Calling `split` here gives a bad goal.
have : size as - i = Nat.succ (size as - i - 1) := by omega
rw [this]
simp [foldl, foldlM, Nat.sub_add_eq]
· dsimp [foldl, foldlM]
rw [dif_pos (by omega), foldlM.loop, dif_neg h]
rfl
termination_by as.size - i
/--
Use this as `induction ass using array₂_induction` on a hypothesis of the form `ass : Array (Array α)`.
The hypothesis `ass` will be replaced with a hypothesis `ass : List (List α)`,
@@ -1676,12 +1640,12 @@ theorem filterMap_eq_map' {f : α → β} (w : stop = as.size) :
filterMap (fun x => some (f x)) as 0 stop = map f as :=
filterMap_eq_map w
@[simp] theorem filterMap_some_fun : filterMap (some : α Option α) = id := by
theorem filterMap_some_fun : filterMap (some : α Option α) = id := by
funext xs
cases xs
simp
@[grind] theorem filterMap_some {xs : Array α} : filterMap some xs = xs := by
@[simp, grind] theorem filterMap_some {xs : Array α} : filterMap some xs = xs := by
cases xs
simp
@@ -2930,14 +2894,14 @@ theorem getElem_extract_loop_ge_aux {xs ys : Array α} {size start : Nat} (hge :
exact Nat.sub_lt_left_of_lt_add hge h
theorem getElem_extract_loop_ge {xs ys : Array α} {size start : Nat} (hge : i ys.size)
(h : i < (extract.loop xs size start ys).size)
(h' := getElem_extract_loop_ge_aux hge h) :
(extract.loop xs size start ys)[i] = xs[start + i - ys.size] := by
(h : i < (extract.loop xs size start ys).size) :
(extract.loop xs size start ys)[i] = xs[start + i - ys.size]'(getElem_extract_loop_ge_aux hge h) := by
induction size using Nat.recAux generalizing start ys with
| zero =>
rw [size_extract_loop, Nat.zero_min, Nat.add_zero] at h
omega
| succ size ih =>
have h' : start + i - ys.size < xs.size := getElem_extract_loop_ge_aux hge h
have : start < xs.size := by
apply Nat.lt_of_le_of_lt (Nat.le_add_right start (i - ys.size))
rwa [ Nat.add_sub_assoc hge]
@@ -2991,7 +2955,7 @@ theorem getElem?_extract {xs : Array α} {start stop : Nat} :
apply List.ext_getElem
· simp only [length_toList, size_extract, List.length_take, List.length_drop]
omega
· intros n h₁ h₂
· intro n h₁ h₂
simp
@[simp] theorem extract_size {xs : Array α} : xs.extract 0 xs.size = xs := by
@@ -2999,9 +2963,6 @@ theorem getElem?_extract {xs : Array α} {start stop : Nat} :
· rw [size_extract, Nat.min_self, Nat.sub_zero]
· intros; rw [getElem_extract]; congr; rw [Nat.zero_add]
@[deprecated extract_size (since := "2025-01-19")]
abbrev extract_all := @extract_size
theorem extract_empty_of_stop_le_start {xs : Array α} {start stop : Nat} (h : stop start) :
xs.extract start stop = #[] := by
simp only [extract, Nat.sub_eq, emptyWithCapacity_eq]
@@ -3036,14 +2997,14 @@ theorem take_size {xs : Array α} : xs.take xs.size = xs := by
/-! ### shrink -/
@[simp] theorem size_shrink_loop {xs : Array α} {n : Nat} : (shrink.loop n xs).size = xs.size - n := by
@[simp] private theorem size_shrink_loop {xs : Array α} {n : Nat} : (shrink.loop n xs).size = xs.size - n := by
induction n generalizing xs with
| zero => simp [shrink.loop]
| succ n ih =>
simp [shrink.loop, ih]
omega
@[simp] theorem getElem_shrink_loop {xs : Array α} {n i : Nat} (h : i < (shrink.loop n xs).size) :
@[simp] private theorem getElem_shrink_loop {xs : Array α} {n i : Nat} (h : i < (shrink.loop n xs).size) :
(shrink.loop n xs)[i] = xs[i]'(by simp at h; omega) := by
induction n generalizing xs i with
| zero => simp [shrink.loop]
@@ -3312,11 +3273,11 @@ theorem foldl_induction
let rec go {i j b} (h₁ : j as.size) (h₂ : as.size i + j) (H : motive j b) :
(motive as.size) (foldlM.loop (m := Id) f as as.size (Nat.le_refl _) i j b) := by
unfold foldlM.loop; split
· next hj =>
next hj =>
split
· cases Nat.not_le_of_gt (by simp [hj]) h₂
· exact go hj (by rwa [Nat.succ_add] at h₂) (hf j, hj b H)
· next hj => exact Nat.le_antisymm h₁ (Nat.ge_of_not_lt hj) H
next hj => exact Nat.le_antisymm h₁ (Nat.ge_of_not_lt hj) H
simpa [foldl, foldlM] using go (Nat.zero_le _) (Nat.le_refl _) h0
theorem foldr_induction
@@ -3326,13 +3287,13 @@ theorem foldr_induction
let rec go {i b} (hi : i as.size) (H : motive i b) :
(motive 0) (foldrM.fold (m := Id) f as 0 i hi b) := by
unfold foldrM.fold; simp; split
· next hi => exact (hi H)
· next hi =>
next hi => exact (hi H)
next hi =>
split; {simp at hi}
· next i hi' =>
next i hi' =>
exact go _ (hf i, hi' b H)
simp [foldr, foldrM]; split; {exact go _ h0}
· next h => exact (Nat.eq_zero_of_not_pos h h0)
next h => exact (Nat.eq_zero_of_not_pos h h0)
@[congr]
theorem foldl_congr {as bs : Array α} (h₀ : as = bs) {f g : β α β} (h₁ : f = g)
@@ -4319,7 +4280,7 @@ Examples:
/-! ### Preliminaries about `ofFn` -/
@[simp] theorem size_ofFn_go {n} {f : Fin n α} {i acc h} :
@[simp] private theorem size_ofFn_go {n} {f : Fin n α} {i acc h} :
(ofFn.go f acc i h).size = acc.size + i := by
induction i generalizing acc with
| zero => simp [ofFn.go]
@@ -4329,7 +4290,7 @@ Examples:
@[simp] theorem size_ofFn {n : Nat} {f : Fin n α} : (ofFn f).size = n := by simp [ofFn]
-- Recall `ofFn.go f acc i h = acc ++ #[f (n - i), ..., f(n - 1)]`
theorem getElem_ofFn_go {f : Fin n α} {acc i k} (h : i n) (w₁ : k < acc.size + i) :
private theorem getElem_ofFn_go {f : Fin n α} {acc i k} (h : i n) (w₁ : k < acc.size + i) :
(ofFn.go f acc i h)[k]'(by simpa using w₁) =
if w₂ : k < acc.size then acc[k] else f n - i + k - acc.size, by omega := by
induction i generalizing acc k with
@@ -4413,10 +4374,17 @@ theorem getElem?_range {n : Nat} {i : Nat} : (Array.range n)[i]? = if i < n then
-- Without further algebraic hypotheses, there's no useful `sum_push` lemma.
@[simp, grind =]
theorem sum_eq_sum_toList [Add α] [Zero α] {as : Array α} : as.toList.sum = as.sum := by
cases as
simp [Array.sum, List.sum]
@[simp, grind =]
theorem sum_append_nat {as₁ as₂ : Array Nat} : (as₁ ++ as₂).sum = as₁.sum + as₂.sum := by
cases as₁
cases as₂
simp [List.sum_append_nat]
theorem foldl_toList_eq_flatMap {l : List α} {acc : Array β}
{F : Array β α Array β} {G : α List β}
(H : acc a, (F acc a).toList = acc.toList ++ G a) :
@@ -4727,27 +4695,10 @@ end List
/-! ### Deprecations -/
namespace Array
@[deprecated size_toArray (since := "2024-12-11")]
theorem size_mk (as : List α) : (Array.mk as).size = as.length := by simp
@[deprecated getElem?_eq_getElem (since := "2024-12-11")]
theorem getElem?_lt
(xs : Array α) {i : Nat} (h : i < xs.size) : xs[i]? = some xs[i] := dif_pos h
@[deprecated getElem?_eq_none (since := "2024-12-11")]
theorem getElem?_ge
(xs : Array α) {i : Nat} (h : i xs.size) : xs[i]? = none := dif_neg (Nat.not_lt_of_le h)
set_option linter.deprecated false in
@[deprecated "`get?` is deprecated" (since := "2025-02-12"), simp]
theorem get?_eq_getElem? (xs : Array α) (i : Nat) : xs.get? i = xs[i]? := rfl
@[deprecated getElem?_eq_none (since := "2024-12-11")]
theorem getElem?_len_le (xs : Array α) {i : Nat} (h : xs.size i) : xs[i]? = none := by
simp [h]
@[deprecated getD_getElem? (since := "2024-12-11")] abbrev getD_get? := @getD_getElem?
@[deprecated getD_eq_getD_getElem? (since := "2025-02-12")] abbrev getD_eq_get? := @getD_eq_getD_getElem?
set_option linter.deprecated false in
@@ -4772,64 +4723,9 @@ theorem get?_eq_get?_toList (xs : Array α) (i : Nat) : xs.get? i = xs.toList.ge
set_option linter.deprecated false in
@[deprecated get!_eq_getD_getElem? (since := "2025-02-12")] abbrev get!_eq_get? := @get!_eq_getD_getElem?
@[deprecated getElem_set_self (since := "2025-01-17")]
theorem get_set_eq (xs : Array α) (i : Nat) (v : α) (h : i < xs.size) :
(xs.set i v h)[i]'(by simp [h]) = v := by
simp only [set, getElem_toList, List.getElem_set_self]
@[deprecated Array.getElem_toList (since := "2024-12-08")]
theorem getElem_eq_getElem_toList {xs : Array α} (h : i < xs.size) : xs[i] = xs.toList[i] := rfl
@[deprecated Array.getElem?_toList (since := "2024-12-08")]
theorem getElem?_eq_getElem?_toList (xs : Array α) (i : Nat) : xs[i]? = xs.toList[i]? := by
rw [getElem?_def]
split <;> simp_all
@[deprecated LawfulGetElem.getElem?_def (since := "2024-12-08")]
theorem getElem?_eq {xs : Array α} {i : Nat} :
xs[i]? = if h : i < xs.size then some xs[i] else none := by
rw [getElem?_def]
/-! ### map -/
@[deprecated "Use `toList_map` or `List.map_toArray` to characterize `Array.map`." (since := "2025-01-06")]
theorem map_induction (xs : Array α) (f : α β) (motive : Nat Prop) (h0 : motive 0)
(p : Fin xs.size β Prop) (hs : i, motive i.1 p i (f xs[i]) motive (i+1)) :
motive xs.size
eq : (xs.map f).size = xs.size, i h, p i, h ((xs.map f)[i]) := by
have t := foldl_induction (as := xs) (β := Array β)
(motive := fun i xs => motive i xs.size = i i h2, p i xs[i.1])
(init := #[]) (f := fun acc a => acc.push (f a)) ?_ ?_
obtain m, eq, w := t
· refine m, by simp, ?_
intro i h
simp only [eq] at w
specialize w i, h h
simpa using w
· exact h0, rfl, nofun
· intro i bs m, eq, w
refine ?_, ?_, ?_
· exact (hs _ m).2
· simp_all
· intro j h
simp at h
by_cases h' : j < size bs
· rw [getElem_push]
simp_all
· rw [getElem_push, dif_neg h']
simp only [show j = i by omega]
exact (hs _ m).1
set_option linter.deprecated false in
@[deprecated "Use `toList_map` or `List.map_toArray` to characterize `Array.map`." (since := "2025-01-06")]
theorem map_spec (xs : Array α) (f : α β) (p : Fin xs.size β Prop)
(hs : i, p i (f xs[i])) :
eq : (xs.map f).size = xs.size, i h, p i, h ((xs.map f)[i]) := by
simpa using map_induction xs f (fun _ => True) trivial p (by simp_all)
/-! ### set -/
@[deprecated getElem?_set_eq (since := "2025-02-27")] abbrev get?_set_eq := @getElem?_set_self
@[deprecated getElem?_set_self (since := "2025-02-27")] abbrev get?_set_eq := @getElem?_set_self
@[deprecated getElem?_set_ne (since := "2025-02-27")] abbrev get?_set_ne := @getElem?_set_ne
@[deprecated getElem?_set (since := "2025-02-27")] abbrev get?_set := @getElem?_set
@[deprecated get_set (since := "2025-02-27")] abbrev get_set := @getElem_set

View File

@@ -12,9 +12,12 @@ public import Init.Data.Array.Lemmas
public import Init.Data.List.Lex
import Init.Data.Range.Polymorphic.Lemmas
import Init.Data.Range.Polymorphic.NatLemmas
import Init.Data.Order.Lemmas
public section
open Std
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
@@ -28,8 +31,8 @@ namespace Array
@[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
grind_pattern _root_.List.lt_toArray => l₁.toArray < l₂.toArray
grind_pattern _root_.List.le_toArray => l₁.toArray l₂.toArray
grind_pattern _root_.List.lt_toArray => l₁.toArray < l₂.toArray
grind_pattern _root_.List.le_toArray => l₁.toArray l₂.toArray
grind_pattern lt_toList => xs.toList < ys.toList
grind_pattern le_toList => xs.toList ys.toList
@@ -100,6 +103,14 @@ theorem singleton_lex_singleton [BEq α] {lt : αα → Bool} : #[a].lex #[
xs.toList.lex ys.toList lt = xs.lex ys lt := by
cases xs <;> cases ys <;> simp
instance [LT α] [LE α] [LawfulOrderLT α] [IsLinearOrder α] : IsLinearOrder (Array α) := by
apply IsLinearOrder.of_le
· constructor
intro _ _ hab hba
simpa using Std.le_antisymm (α := List α) hab hba
· constructor; exact Std.le_trans (α := List α)
· constructor; exact fun _ _ => Std.le_total (α := List α)
protected theorem lt_irrefl [LT α] [Std.Irrefl (· < · : α α Prop)] (xs : Array α) : ¬ xs < xs :=
List.lt_irrefl xs.toList
@@ -131,27 +142,35 @@ instance [LT α] [Trans (· < · : αα → Prop) (· < ·) (· < ·)] :
Trans (· < · : Array α Array α Prop) (· < ·) (· < ·) where
trans h₁ h₂ := Array.lt_trans h₁ h₂
protected theorem lt_of_le_of_lt [LT α]
[i₀ : Std.Irrefl (· < · : α α Prop)]
protected theorem lt_of_le_of_lt [LE α] [LT α] [LawfulOrderLT α] [IsLinearOrder α]
{xs ys zs : Array α} (h₁ : xs ys) (h₂ : ys < zs) : xs < zs :=
Std.lt_of_le_of_lt (α := List α) h₁ h₂
@[deprecated Array.lt_of_le_of_lt (since := "2025-08-01")]
protected theorem lt_of_le_of_lt' [LT α]
[i₁ : Std.Asymm (· < · : α α Prop)]
[i₂ : Std.Antisymm (¬ · < · : α α Prop)]
[i₃ : Trans (¬ · < · : α α Prop) (¬ · < ·) (¬ · < ·)]
{xs ys zs : Array α} (h₁ : xs ys) (h₂ : ys < zs) : xs < zs :=
List.lt_of_le_of_lt h₁ h₂
letI := LE.ofLT α
haveI : IsLinearOrder α := IsLinearOrder.of_lt
Array.lt_of_le_of_lt h₁ h₂
protected theorem le_trans [LT α]
[Std.Irrefl (· < · : α α Prop)]
[Std.Asymm (· < · : α α Prop)]
[Std.Antisymm (¬ · < · : α α Prop)]
[Trans (¬ · < · : α α Prop) (¬ · < ·) (¬ · < ·)]
protected theorem le_trans [LE α] [LT α] [LawfulOrderLT α] [IsLinearOrder α]
{xs ys zs : Array α} (h₁ : xs ys) (h₂ : ys zs) : xs zs :=
fun h₃ => h₁ (Array.lt_of_le_of_lt h₂ h₃)
instance [LT α]
[Std.Irrefl (· < · : α α Prop)]
[Std.Asymm (· < · : α α Prop)]
[Std.Antisymm (¬ · < · : α α Prop)]
[Trans (¬ · < · : α α Prop) (¬ · < ·) (¬ · < ·)] :
@[deprecated Array.le_trans (since := "2025-08-01")]
protected theorem le_trans' [LT α]
[i₁ : Std.Asymm (· < · : α α Prop)]
[i₂ : Std.Antisymm (¬ · < · : α α Prop)]
[i₃ : Trans (¬ · < · : α α Prop) (¬ · < ·) (¬ · < ·)]
{xs ys zs : Array α} (h₁ : xs ys) (h₂ : ys zs) : xs zs :=
letI := LE.ofLT α
haveI : IsLinearOrder α := IsLinearOrder.of_lt
Array.le_trans h₁ h₂
instance [LE α] [LT α] [LawfulOrderLT α] [IsLinearOrder α] :
Trans (· · : Array α Array α Prop) (· ·) (· ·) where
trans h₁ h₂ := Array.le_trans h₁ h₂
@@ -165,7 +184,7 @@ instance [LT α]
asymm _ _ := Array.lt_asymm
protected theorem le_total [LT α]
[i : Std.Total (¬ · < · : α α Prop)] (xs ys : Array α) : xs ys ys xs :=
[i : Std.Asymm (· < · : α α Prop)] (xs ys : Array α) : xs ys ys xs :=
List.le_total xs.toList ys.toList
@[simp] protected theorem not_lt [LT α]
@@ -175,19 +194,22 @@ protected theorem le_total [LT α]
{xs ys : Array α} : ¬ ys xs xs < ys := Classical.not_not
protected theorem le_of_lt [LT α]
[i : Std.Total (¬ · < · : α α Prop)]
[i : Std.Asymm (· < · : α α Prop)]
{xs ys : Array α} (h : xs < ys) : xs ys :=
List.le_of_lt h
protected theorem le_iff_lt_or_eq [LT α]
[Std.Irrefl (· < · : α α Prop)]
[Std.Antisymm (¬ · < · : α α Prop)]
[Std.Total (¬ · < · : α α Prop)]
[Std.Asymm (· < · : α α Prop)]
{xs ys : Array α} : xs ys xs < ys xs = ys := by
simpa using List.le_iff_lt_or_eq (l₁ := xs.toList) (l₂ := ys.toList)
instance [LT α]
[Std.Total (¬ · < · : α α Prop)] :
protected theorem le_antisymm [LT α] [LE α] [IsLinearOrder α] [LawfulOrderLT α]
{xs ys : Array α} : xs ys ys xs xs = ys := by
simpa using List.le_antisymm (as := xs.toList) (bs := ys.toList)
instance [LT α] [Std.Asymm (· < · : α α Prop)] :
Std.Total (· · : Array α Array α Prop) where
total := Array.le_total
@@ -266,7 +288,6 @@ protected theorem lt_iff_exists [LT α] {xs ys : Array α} :
simp [List.lt_iff_exists]
protected theorem le_iff_exists [LT α]
[Std.Irrefl (· < · : α α Prop)]
[Std.Asymm (· < · : α α Prop)]
[Std.Antisymm (¬ · < · : α α Prop)] {xs ys : Array α} :
xs ys
@@ -286,7 +307,6 @@ theorem append_left_lt [LT α] {xs ys zs : Array α} (h : ys < zs) :
simpa using List.append_left_lt h
theorem append_left_le [LT α]
[Std.Irrefl (· < · : α α Prop)]
[Std.Asymm (· < · : α α Prop)]
[Std.Antisymm (¬ · < · : α α Prop)]
{xs ys zs : Array α} (h : ys zs) :
@@ -310,10 +330,8 @@ protected theorem map_lt [LT α] [LT β]
simpa using List.map_lt w h
protected theorem map_le [LT α] [LT β]
[Std.Irrefl (· < · : α α Prop)]
[Std.Asymm (· < · : α α Prop)]
[Std.Antisymm (¬ · < · : α α Prop)]
[Std.Irrefl (· < · : β β Prop)]
[Std.Asymm (· < · : β β Prop)]
[Std.Antisymm (¬ · < · : β β Prop)]
{xs ys : Array α} {f : α β} (w : x y, x < y f x < f y) (h : xs ys) :

View File

@@ -60,7 +60,7 @@ theorem mapFinIdx_spec {xs : Array α} {f : (i : Nat) → α → (h : i < xs.siz
@[simp, grind =] theorem size_zipIdx {xs : Array α} {k : Nat} : (xs.zipIdx k).size = xs.size :=
Array.size_mapFinIdx
@[deprecated size_zipIdx (since := "2025-01-21")] abbrev size_zipWithIndex := @size_zipIdx
@[simp, grind =] theorem getElem_mapFinIdx {xs : Array α} {f : (i : Nat) α (h : i < xs.size) β} {i : Nat}
(h : i < (xs.mapFinIdx f).size) :
@@ -132,23 +132,20 @@ namespace Array
(xs.zipIdx k)[i] = (xs[i]'(by simp_all), k + i) := by
simp [zipIdx]
@[deprecated getElem_zipIdx (since := "2025-01-21")]
abbrev getElem_zipWithIndex := @getElem_zipIdx
@[simp, grind =] theorem zipIdx_toArray {l : List α} {k : Nat} :
l.toArray.zipIdx k = (l.zipIdx k).toArray := by
ext i hi₁ hi₂ <;> simp
@[deprecated zipIdx_toArray (since := "2025-01-21")]
abbrev zipWithIndex_toArray := @zipIdx_toArray
@[simp, grind =] theorem toList_zipIdx {xs : Array α} {k : Nat} :
(xs.zipIdx k).toList = xs.toList.zipIdx k := by
rcases xs with xs
simp
@[deprecated toList_zipIdx (since := "2025-01-21")]
abbrev toList_zipWithIndex := @toList_zipIdx
theorem mk_mem_zipIdx_iff_le_and_getElem?_sub {k i : Nat} {x : α} {xs : Array α} :
(x, i) xs.zipIdx k k i xs[i - k]? = some x := by
@@ -173,11 +170,7 @@ theorem mem_zipIdx_iff_getElem? {x : α × Nat} {xs : Array α} :
x xs.zipIdx xs[x.2]? = some x.1 := by
rw [mk_mem_zipIdx_iff_getElem?]
@[deprecated mk_mem_zipIdx_iff_getElem? (since := "2025-01-21")]
abbrev mk_mem_zipWithIndex_iff_getElem? := @mk_mem_zipIdx_iff_getElem?
@[deprecated mem_zipIdx_iff_getElem? (since := "2025-01-21")]
abbrev mem_zipWithIndex_iff_getElem? := @mem_zipIdx_iff_getElem?
/-! ### mapFinIdx -/
@@ -222,8 +215,7 @@ theorem mapFinIdx_eq_zipIdx_map {xs : Array α} {f : (i : Nat) → α → (h : i
f i x (by simp [mk_mem_zipIdx_iff_getElem?, getElem?_eq_some_iff] at m; exact m.1) := by
ext <;> simp
@[deprecated mapFinIdx_eq_zipIdx_map (since := "2025-01-21")]
abbrev mapFinIdx_eq_zipWithIndex_map := @mapFinIdx_eq_zipIdx_map
@[simp]
theorem mapFinIdx_eq_empty_iff {xs : Array α} {f : (i : Nat) α (h : i < xs.size) β} :
@@ -332,8 +324,7 @@ theorem mapIdx_eq_zipIdx_map {xs : Array α} {f : Nat → α → β} :
xs.mapIdx f = xs.zipIdx.map fun a, i => f i a := by
ext <;> simp
@[deprecated mapIdx_eq_zipIdx_map (since := "2025-01-21")]
abbrev mapIdx_eq_zipWithIndex_map := @mapIdx_eq_zipIdx_map
@[grind =]
theorem mapIdx_append {xs ys : Array α} :

View File

@@ -18,11 +18,11 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
namespace Array
theorem sizeOf_lt_of_mem [SizeOf α] {as : Array α} (h : a as) : sizeOf a < sizeOf as := by
cases as with | _ as =>
cases as with | _ as
exact Nat.lt_trans (List.sizeOf_lt_of_mem h.val) (by simp +arith)
theorem sizeOf_get [SizeOf α] (as : Array α) (i : Nat) (h : i < as.size) : sizeOf as[i] < sizeOf as := by
cases as with | _ as =>
cases as with | _ as
simpa using Nat.lt_trans (List.sizeOf_get _ i, h) (by simp +arith)
@[simp] theorem sizeOf_getElem [SizeOf α] (as : Array α) (i : Nat) (h : i < as.size) :

View File

@@ -7,7 +7,7 @@ module
prelude
public import Init.Data.Vector.Basic
public import Init.Data.Ord
public import Init.Data.Ord.Basic
public section

View File

@@ -118,7 +118,7 @@ theorem zipWith_foldl_eq_zip_foldl {f : α → β → γ} {i : δ} :
theorem zipWith_eq_empty_iff {f : α β γ} {as : Array α} {bs : Array β} : zipWith f as bs = #[] as = #[] bs = #[] := by
cases as <;> cases bs <;> simp
@[grind =]
@[simp, grind =]
theorem map_zipWith {δ : Type _} {f : α β} {g : γ δ α} {cs : Array γ} {ds : Array δ} :
map f (zipWith g cs ds) = zipWith (fun x y => f (g x y)) cs ds := by
cases cs
@@ -354,6 +354,15 @@ theorem map_zipWithAll {δ : Type _} {f : α → β} {g : Option γ → Option
@[deprecated zipWithAll_replicate (since := "2025-03-18")]
abbrev zipWithAll_mkArray := @zipWithAll_replicate
/-! ### zipWithM -/
@[simp, grind =]
theorem zipWithM_eq_mapM_id_zipWith {m : Type v Type w} [Monad m] [LawfulMonad m] {f : α β m γ} {as : Array α} {bs : Array β} :
zipWithM f as bs = mapM id (zipWith f as bs) := by
cases as
cases bs
simp [List.zipWithM_toArray, List.zipWithM'_eq_zipWithM]
/-! ### unzip -/
@[deprecated fst_unzip (since := "2025-05-26")]

View File

@@ -23,11 +23,14 @@ class PartialEquivBEq (α) [BEq α] : Prop where
/-- Transitivity for `BEq`. If `a == b` and `b == c` then `a == c`. -/
trans : (a : α) == b b == c a == c
instance [BEq α] [PartialEquivBEq α] : Std.Symm (α := α) (· == ·) where
symm _ _ h := PartialEquivBEq.symm h
/-- `EquivBEq` says that the `BEq` implementation is an equivalence relation. -/
class EquivBEq (α) [BEq α] : Prop extends PartialEquivBEq α, ReflBEq α
theorem BEq.symm [BEq α] [PartialEquivBEq α] {a b : α} : a == b b == a :=
PartialEquivBEq.symm
theorem BEq.symm [BEq α] [Std.Symm (α := α) (· == ·)] {a b : α} : a == b b == a :=
Std.Symm.symm a b (r := (· == ·))
theorem BEq.comm [BEq α] [PartialEquivBEq α] {a b : α} : (a == b) = (b == a) :=
Bool.eq_iff_iff.2 BEq.symm, BEq.symm

View File

@@ -552,7 +552,7 @@ Example:
@[expose]
protected def xor (x y : BitVec n) : BitVec n :=
(x.toNat ^^^ y.toNat)#'(Nat.xor_lt_two_pow x.isLt y.isLt)
instance : Xor (BitVec w) := .xor
instance : XorOp (BitVec w) := .xor
/--
Bitwise complement for bitvectors. Usually accessed via the `~~~` prefix operator.

View File

@@ -338,11 +338,11 @@ theorem add_eq_or_of_and_eq_zero {w : Nat} (x y : BitVec w)
· rfl
· simp only [adcb, atLeastTwo, Bool.and_false, Bool.or_false, bne_false,
Prod.mk.injEq, and_eq_false_imp]
intros i
intro i
replace h : (x &&& y).getLsbD i = (0#w).getLsbD i := by rw [h]
simp only [getLsbD_and, getLsbD_zero, and_eq_false_imp] at h
constructor
· intros hx
· intro hx
simp_all
· by_cases hx : x.getLsbD i <;> simp_all
@@ -1666,7 +1666,7 @@ private theorem neg_udiv_eq_intMin_iff_eq_intMin_eq_one_of_msb_eq_true
{x y : BitVec w} (hx : x.msb = true) (hy : y.msb = false) :
-x / y = intMin w (x = intMin w y = 1#w) := by
constructor
· intros h
· intro h
rcases w with _ | w; decide +revert
have : (-x / y).msb = true := by simp [h, msb_intMin]
rw [msb_udiv] at this
@@ -1742,7 +1742,7 @@ theorem msb_sdiv_eq_decide {x y : BitVec w} :
Bool.and_self, ne_zero_of_msb_true, decide_false, Bool.and_true, Bool.true_and, Bool.not_true,
Bool.false_and, Bool.or_false, bool_to_prop]
have : x / -y intMin (w + 1) := by
intros h
intro h
have : (x / -y).msb = (intMin (w + 1)).msb := by simp only [h]
simp only [msb_udiv, msb_intMin, show 0 < w + 1 by omega, decide_true, and_eq_true, beq_iff_eq] at this
obtain hcontra, _ := this
@@ -1871,7 +1871,7 @@ theorem toInt_dvd_toInt_iff {x y : BitVec w} :
y.toInt x.toInt (if x.msb then -x else x) % (if y.msb then -y else y) = 0#w := by
constructor
<;> by_cases hxmsb : x.msb <;> by_cases hymsb: y.msb
<;> intros h
<;> intro h
<;> simp only [hxmsb, hymsb, reduceIte, false_eq_true, toNat_eq, toNat_umod, toNat_ofNat,
zero_mod, toInt_eq_neg_toNat_neg_of_msb_true, Int.dvd_neg, Int.neg_dvd,
toInt_eq_toNat_of_msb] at h
@@ -2141,7 +2141,7 @@ theorem add_shiftLeft_eq_or_shiftLeft {x y : BitVec w} :
ext i hi
simp only [shiftLeft_eq', getElem_and, getElem_shiftLeft, getElem_zero, and_eq_false_imp,
not_eq_eq_eq_not, Bool.not_true, decide_eq_false_iff_not, Nat.not_lt]
intros hxi hxval
intro hxi hxval
have : 2^i x.toNat := two_pow_le_toNat_of_getElem_eq_true hi hxi
have : i < 2^i := by exact Nat.lt_two_pow_self
omega

View File

@@ -19,9 +19,12 @@ public import Init.Data.Int.LemmasAux
public import Init.Data.Int.Pow
public import Init.Data.Int.LemmasAux
public import Init.Data.BitVec.Bootstrap
public import Init.Data.Order.Factories
public section
open Std
set_option linter.missingDocs true
namespace BitVec
@@ -238,11 +241,11 @@ theorem eq_of_getLsbD_eq_iff {w : Nat} {x y : BitVec w} :
x = y (i : Nat), i < w x.getLsbD i = y.getLsbD i := by
have iff := @BitVec.eq_of_getElem_eq_iff w x y
constructor
· intros heq i lt
· intro heq i lt
have hext := iff.mp heq i lt
simp only [ getLsbD_eq_getElem] at hext
exact hext
· intros heq
· intro heq
exact iff.mpr heq
theorem eq_of_getMsbD_eq {x y : BitVec w}
@@ -702,7 +705,7 @@ theorem eq_zero_or_eq_one (a : BitVec 1) : a = 0#1 a = 1#1 := by
have acases : a = 0 a = 1 := by omega
rcases acases with rfl | rfl
· simp
· case inr h =>
case inr h =>
subst h
simp
@@ -818,14 +821,14 @@ its most significant bit is true.
theorem slt_zero_iff_msb_cond {x : BitVec w} : x.slt 0#w x.msb = true := by
have := toInt_eq_msb_cond x
constructor
· intros h
· intro h
apply Classical.byContradiction
intros hmsb
intro hmsb
simp only [Bool.not_eq_true] at hmsb
simp only [hmsb, Bool.false_eq_true, reduceIte] at this
simp only [BitVec.slt, toInt_zero, decide_eq_true_eq] at h
omega /- Can't have `x.toInt` which is equal to `x.toNat` be strictly less than zero -/
· intros h
· intro h
simp only [h, reduceIte] at this
simp only [BitVec.slt, this, toInt_zero, decide_eq_true_eq]
omega
@@ -1393,7 +1396,7 @@ theorem or_eq_zero_iff {x y : BitVec w} : (x ||| y) = 0#w ↔ x = 0#w ∧ y = 0#
· intro h
constructor
all_goals
· ext i ih
ext i ih
have := BitVec.eq_of_getElem_eq_iff.mp h i ih
simp only [getElem_or, getElem_zero, Bool.or_eq_false_iff] at this
simp [this]
@@ -1493,7 +1496,7 @@ theorem and_eq_allOnes_iff {x y : BitVec w} :
· intro h
constructor
all_goals
· ext i ih
ext i ih
have := BitVec.eq_of_getElem_eq_iff.mp h i ih
simp only [getElem_and, getElem_allOnes, Bool.and_eq_true] at this
simp [this]
@@ -2094,7 +2097,7 @@ theorem toInt_ushiftRight_of_lt {x : BitVec w} {n : Nat} (hn : 0 < n) :
(x >>> n).toInt = x.toNat >>> n := by
rw [toInt_eq_toNat_cond]
simp only [toNat_ushiftRight, ite_eq_left_iff, Nat.not_lt]
intros h
intro h
by_cases hn : n w
· have h1 := Nat.mul_lt_mul_of_pos_left (toNat_ushiftRight_lt x n hn) Nat.two_pos
simp only [toNat_ushiftRight, Nat.zero_lt_succ, Nat.mul_lt_mul_left] at h1
@@ -2232,7 +2235,7 @@ theorem getLsbD_sshiftRight (x : BitVec w) (s i : Nat) :
omega
· simp only [hi, decide_false, Bool.not_false, Bool.true_and, Bool.eq_and_self,
decide_eq_true_eq]
intros hlsb
intro hlsb
apply BitVec.lt_of_getLsbD hlsb
· by_cases hi : i w
· simp [hi]
@@ -2286,7 +2289,7 @@ theorem msb_sshiftRight {n : Nat} {x : BitVec w} :
· simp [hw₀]
· simp only [show ¬(w w - 1) by omega, decide_false, Bool.not_false, Bool.true_and,
ite_eq_right_iff]
intros h
intro h
simp [show n = 0 by omega]
@[simp] theorem sshiftRight_zero {x : BitVec w} : x.sshiftRight 0 = x := by
@@ -2774,7 +2777,7 @@ theorem toInt_append {x : BitVec n} {y : BitVec m} :
(x ++ 0#m).toInt = (2 ^ m) * x.toInt := by
simp only [toInt_append, beq_iff_eq, toInt_zero, toNat_ofNat, Nat.zero_mod, Int.cast_ofNat_Int, Int.add_zero,
ite_eq_right_iff]
intros h
intro h
subst h
simp [BitVec.eq_nil x]
@@ -2958,7 +2961,7 @@ theorem extractLsb'_append_extractLsb'_eq_extractLsb' {x : BitVec w} (h : start
ext i h
simp only [getElem_append, getElem_extractLsb', dite_eq_ite, getElem_cast, ite_eq_left_iff,
Nat.not_lt]
intros hi
intro hi
congr 1
omega
@@ -2985,7 +2988,7 @@ theorem signExtend_eq_append_extractLsb' {w v : Nat} {x : BitVec w} :
· simp only [hx, signExtend_eq_setWidth_of_msb_false, getElem_setWidth, Bool.false_eq_true,
reduceIte, getElem_append, getElem_extractLsb', Nat.zero_add, getElem_zero, dite_eq_ite,
Bool.if_false_right, Bool.eq_and_self, decide_eq_true_eq]
intros hi
intro hi
have hw : i < w := lt_of_getLsbD hi
omega
· simp [signExtend_eq_not_setWidth_not_of_msb_true hx, getElem_append, Nat.lt_min, hi]
@@ -3033,7 +3036,7 @@ theorem extractLsb'_append_eq_ite {v w} {xhi : BitVec v} {xlo : BitVec w} {start
· simp only [hlen, reduceDIte]
ext i hi
simp only [getElem_extractLsb', getLsbD_append, ite_eq_left_iff, Nat.not_lt]
intros hcontra
intro hcontra
omega
· simp only [hlen, reduceDIte]
ext i hi
@@ -3480,7 +3483,7 @@ theorem toInt_sub_toInt_lt_twoPow_iff {x y : BitVec w} :
have := two_mul_toInt_lt (x := y)
simp only [Nat.add_one_sub_one]
constructor
· intros h
· intro h
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))
@@ -3496,7 +3499,7 @@ theorem twoPow_le_toInt_sub_toInt_iff {x y : BitVec w} :
have := le_two_mul_toInt (x := y); have := two_mul_toInt_lt (x := y)
simp only [Nat.add_one_sub_one]
constructor
· intros h
· intro h
simp only [show 0 x.toInt by omega, show y.toInt < 0 by omega, _root_.true_and]
rw_mod_cast [ Int.sub_bmod_right, Int.bmod_eq_of_le (by omega) (by omega)]
omega
@@ -4015,6 +4018,16 @@ protected theorem ne_of_lt {x y : BitVec n} : x < y → x ≠ y := by
simp only [lt_def, ne_eq, toNat_eq]
apply Nat.ne_of_lt
instance instIsLinearOrder : IsLinearOrder (BitVec n) := by
apply IsLinearOrder.of_le
case le_antisymm => constructor; apply BitVec.le_antisymm
case le_trans => constructor; apply BitVec.le_trans
case le_total => constructor; apply BitVec.le_total
instance instLawfulOrderLT : LawfulOrderLT (BitVec n) := by
apply LawfulOrderLT.of_le
simpa using fun _ _ => BitVec.lt_asymm
protected theorem umod_lt (x : BitVec n) {y : BitVec n} : 0 < y x % y < y := by
simp only [ofNat_eq_ofNat, lt_def, toNat_ofNat, Nat.zero_mod]
apply Nat.mod_lt
@@ -4419,8 +4432,8 @@ theorem neg_one_ediv_toInt_eq {w : Nat} {y : BitVec w} :
rcases w with _|_|w
· simp [of_length_zero]
· cases eq_zero_or_eq_one y
· case _ h => simp [h]
· case _ h => simp [h]
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
@@ -5766,16 +5779,16 @@ theorem clzAuxRec_eq_clzAuxRec_of_le (x : BitVec w) (h : w - 1 ≤ n) :
let k := n - (w - 1)
rw [show n = (w - 1) + k by omega]
induction k
· case zero => simp
· case succ k ihk =>
case zero => simp
case succ k ihk =>
simp [show w - 1 + (k + 1) = (w - 1 + k) + 1 by omega, clzAuxRec_succ, ihk,
show x.getLsbD (w - 1 + k + 1) = false by simp only [show w w - 1 + k + 1 by omega, getLsbD_of_ge]]
theorem clzAuxRec_eq_clzAuxRec_of_getLsbD_false {x : BitVec w} (h : i, n < i x.getLsbD i = false) :
x.clzAuxRec n = x.clzAuxRec (n + k) := by
induction k
· case zero => simp
· case succ k ihk =>
case zero => simp
case succ k ihk =>
simp only [show n + (k + 1) = (n + k) + 1 by omega, clzAuxRec_succ]
by_cases hxn : x.getLsbD (n + k + 1)
· have : ¬ (i : Nat), n < i x.getLsbD i = false := by
@@ -5842,13 +5855,8 @@ set_option linter.missingDocs false
@[deprecated toFin_uShiftRight (since := "2025-02-18")]
abbrev toFin_uShiftRight := @toFin_ushiftRight
@[deprecated signExtend_eq_setWidth_of_msb_false (since := "2024-12-08")]
abbrev signExtend_eq_not_setWidth_not_of_msb_false := @signExtend_eq_setWidth_of_msb_false
@[deprecated replicate_zero (since := "2025-01-08")]
abbrev replicate_zero_eq := @replicate_zero
@[deprecated replicate_succ (since := "2025-01-08")]
abbrev replicate_succ_eq := @replicate_succ
end BitVec

View File

@@ -696,3 +696,23 @@ but may be used locally.
@[simp] theorem Subtype.beq_iff {α : Type u} [BEq α] {p : α Prop} {x y : {a : α // p a}} :
(x == y) = (x.1 == y.1) := rfl
/-! ### Proof by reflection support -/
@[expose] protected noncomputable def Bool.and' (a b : Bool) : Bool :=
Bool.rec false b a
@[expose] protected noncomputable def Bool.or' (a b : Bool) : Bool :=
Bool.rec b true a
@[expose] protected noncomputable def Bool.not' (a : Bool) : Bool :=
Bool.rec true false a
@[simp] theorem Bool.and'_eq_and (a b : Bool) : a.and' b = a.and b := by
cases a <;> simp [Bool.and']
@[simp] theorem Bool.or'_eq_or (a b : Bool) : a.or' b = a.or b := by
cases a <;> simp [Bool.or']
@[simp] theorem Bool.not'_eq_not (a : Bool) : a.not' = a.not := by
cases a <;> simp [Bool.not']

View File

@@ -8,5 +8,6 @@ module
prelude
public import Init.Data.Char.Basic
public import Init.Data.Char.Lemmas
public import Init.Data.Char.Order
public section

View File

@@ -61,6 +61,7 @@ instance leTotal : Std.Total (· ≤ · : Char → Char → Prop) where
total := Char.le_total
-- This instance is useful while setting up instances for `String`.
@[deprecated ltAsymm (since := "2025-08-01")]
def notLTTotal : Std.Total (¬ · < · : Char Char Prop) where
total := fun x y => by simpa using Char.le_total y x

View File

@@ -0,0 +1,27 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Paul Reichert
-/
module
prelude
public import Init.Data.Char.Basic
import Init.Data.Char.Lemmas
public import Init.Data.Order.Factories
open Std
namespace Char
public instance instIsLinearOrder : IsLinearOrder Char := by
apply IsLinearOrder.of_le
case le_antisymm => constructor; apply Char.le_antisymm
case le_trans => constructor; apply Char.le_trans
case le_total => constructor; apply Char.le_total
public instance : LawfulOrderLT Char where
lt_iff a b := by
simp [ Char.not_le, Decidable.imp_iff_not_or, Std.Total.total]
end Char

View File

@@ -221,7 +221,7 @@ instance : AndOp (Fin n) where
and := Fin.land
instance : OrOp (Fin n) where
or := Fin.lor
instance : Xor (Fin n) where
instance : XorOp (Fin n) where
xor := Fin.xor
instance : ShiftLeft (Fin n) where
shiftLeft := Fin.shiftLeft

View File

@@ -107,14 +107,14 @@ Fin.foldrM n f xₙ = do
subst w
rfl
theorem foldlM_loop_lt [Monad m] (f : α Fin n m α) (x) (h : i < n) :
private theorem foldlM_loop_lt [Monad m] (f : α Fin n m α) (x) (h : i < n) :
foldlM.loop n f x i = f x i, h >>= (foldlM.loop n f . (i+1)) := by
rw [foldlM.loop, dif_pos h]
theorem foldlM_loop_eq [Monad m] (f : α Fin n m α) (x) : foldlM.loop n f x n = pure x := by
private theorem foldlM_loop_eq [Monad m] (f : α Fin n m α) (x) : foldlM.loop n f x n = pure x := by
rw [foldlM.loop, dif_neg (Nat.lt_irrefl _)]
theorem foldlM_loop [Monad m] (f : α Fin (n+1) m α) (x) (h : i < n+1) :
private theorem foldlM_loop [Monad m] (f : α Fin (n+1) m α) (x) (h : i < n+1) :
foldlM.loop (n+1) f x i = f x i, h >>= (foldlM.loop n (fun x j => f x j.succ) . i) := by
if h' : i < n then
rw [foldlM_loop_lt _ _ h]
@@ -170,15 +170,15 @@ theorem foldlM_add [Monad m] [LawfulMonad m] (f : α → Fin (n + k) → m α) :
subst w
rfl
theorem foldrM_loop_zero [Monad m] (f : Fin n α m α) (x) :
private theorem foldrM_loop_zero [Monad m] (f : Fin n α m α) (x) :
foldrM.loop n f 0, Nat.zero_le _ x = pure x := by
rw [foldrM.loop]
theorem foldrM_loop_succ [Monad m] (f : Fin n α m α) (x) (h : i < n) :
private theorem foldrM_loop_succ [Monad m] (f : Fin n α m α) (x) (h : i < n) :
foldrM.loop n f i+1, h x = f i, h x >>= foldrM.loop n f i, Nat.le_of_lt h := by
rw [foldrM.loop]
theorem foldrM_loop [Monad m] [LawfulMonad m] (f : Fin (n+1) α m α) (x) (h : i+1 n+1) :
private theorem foldrM_loop [Monad m] [LawfulMonad m] (f : Fin (n+1) α m α) (x) (h : i+1 n+1) :
foldrM.loop (n+1) f i+1, h x =
foldrM.loop n (fun j => f j.succ) i, Nat.le_of_succ_le_succ h x >>= f 0 := by
induction i generalizing x with
@@ -228,14 +228,14 @@ theorem foldrM_add [Monad m] [LawfulMonad m] (f : Fin (n + k) → α → m α) :
subst w
rfl
theorem foldl_loop_lt (f : α Fin n α) (x) (h : i < n) :
private theorem foldl_loop_lt (f : α Fin n α) (x) (h : i < n) :
foldl.loop n f x i = foldl.loop n f (f x i, h) (i+1) := by
rw [foldl.loop, dif_pos h]
theorem foldl_loop_eq (f : α Fin n α) (x) : foldl.loop n f x n = x := by
private theorem foldl_loop_eq (f : α Fin n α) (x) : foldl.loop n f x n = x := by
rw [foldl.loop, dif_neg (Nat.lt_irrefl _)]
theorem foldl_loop (f : α Fin (n+1) α) (x) (h : i < n+1) :
private theorem foldl_loop (f : α Fin (n+1) α) (x) (h : i < n+1) :
foldl.loop (n+1) f x i = foldl.loop n (fun x j => f x j.succ) (f x i, h) i := by
if h' : i < n then
rw [foldl_loop_lt _ _ h]
@@ -285,15 +285,15 @@ theorem foldlM_pure [Monad m] [LawfulMonad m] {n} {f : α → Fin n → α} :
subst w
rfl
theorem foldr_loop_zero (f : Fin n α α) (x) :
private theorem foldr_loop_zero (f : Fin n α α) (x) :
foldr.loop n f 0 (Nat.zero_le _) x = x := by
rw [foldr.loop]
theorem foldr_loop_succ (f : Fin n α α) (x) (h : i < n) :
private theorem foldr_loop_succ (f : Fin n α α) (x) (h : i < n) :
foldr.loop n f (i+1) h x = foldr.loop n f i (Nat.le_of_lt h) (f i, h x) := by
rw [foldr.loop]
theorem foldr_loop (f : Fin (n+1) α α) (x) (h : i+1 n+1) :
private theorem foldr_loop (f : Fin (n+1) α α) (x) (h : i+1 n+1) :
foldr.loop (n+1) f (i+1) h x =
f 0 (foldr.loop n (fun j => f j.succ) i (Nat.le_of_succ_le_succ h) x) := by
induction i generalizing x with

View File

@@ -12,9 +12,13 @@ public import Init.Ext
public import Init.ByCases
public import Init.Conv
public import Init.Omega
public import Init.Data.Order.Factories
import Init.Data.Order.Lemmas
public section
open Std
namespace Fin
@[simp] theorem ofNat_zero (n : Nat) [NeZero n] : Fin.ofNat n 0 = 0 := rfl
@@ -251,6 +255,16 @@ protected theorem le_antisymm_iff {x y : Fin n} : x = y ↔ x ≤ y ∧ y ≤ x
protected theorem le_antisymm {x y : Fin n} (h1 : x y) (h2 : y x) : x = y :=
Fin.le_antisymm_iff.2 h1, h2
instance instIsLinearOrder : IsLinearOrder (Fin n) := by
apply IsLinearOrder.of_le
case le_antisymm => constructor; apply Fin.le_antisymm
case le_total => constructor; apply Fin.le_total
case le_trans => constructor; apply Fin.le_trans
instance : LawfulOrderLT (Fin n) where
lt_iff := by
simp [ Fin.not_le, Decidable.imp_iff_not_or, Std.Total.total]
@[simp, grind =] theorem val_rev (i : Fin n) : rev i = n - (i + 1) := rfl
@[simp] theorem rev_rev (i : Fin n) : rev (rev i) = i := Fin.ext <| by
@@ -938,7 +952,7 @@ For the induction:
(reverseInduction zero succ (Fin.last n) : motive (Fin.last n)) = zero := by
rw [reverseInduction, reverseInduction.go]; simp
@[simp] theorem reverseInduction_castSucc_aux {n : Nat} {motive : Fin (n + 1) Sort _} {succ}
private theorem reverseInduction_castSucc_aux {n : Nat} {motive : Fin (n + 1) Sort _} {succ}
(i : Fin n) (j : Nat) (h) (h2 : i.1 < j) (zero : motive j, h) :
reverseInduction.go (motive := motive) succ i.castSucc j h (Nat.le_of_lt h2) zero =
succ i (reverseInduction.go succ i.succ j h h2 zero) := by

View File

@@ -314,7 +314,7 @@ Examples:
* `(0 : Int).natAbs = 0`
* `((-11 : Int).natAbs = 11`
-/
@[extern "lean_nat_abs"]
@[extern "lean_nat_abs", expose]
def natAbs (m : @& Int) : Nat :=
match m with
| ofNat m => m
@@ -404,6 +404,26 @@ instance : Min Int := minOfLe
instance : Max Int := maxOfLe
/-- Equality predicate for kernel reduction. -/
@[expose] protected noncomputable def beq' (a b : Int) : Bool :=
Int.rec
(fun a => Int.rec (fun b => Nat.beq a b) (fun _ => false) b)
(fun a => Int.rec (fun _ => false) (fun b => Nat.beq a b) b) a
/-- `x ≤ y` for kernel reduction. -/
@[expose] protected noncomputable def ble' (a b : Int) : Bool :=
Int.rec
(fun a => Int.rec (fun b => Nat.ble a b) (fun _ => false) b)
(fun a => Int.rec (fun _ => true) (fun b => Nat.ble b a) b)
a
/-- `x < y` for kernel reduction. -/
@[expose] protected noncomputable def blt' (a b : Int) : Bool :=
Int.rec
(fun a => Int.rec (fun b => Nat.blt a b) (fun _ => false) b)
(fun a => Int.rec (fun _ => true) (fun b => Nat.blt b a) b)
a
end Int
/--

View File

@@ -50,4 +50,21 @@ protected def shiftRight : Int → Nat → Int
instance : HShiftRight Int Nat Int := .shiftRight
/--
Bitwise left shift, usually accessed via the `<<<` operator.
Examples:
* `1 <<< 2 = 4`
* `1 <<< 3 = 8`
* `0 <<< 3 = 0`
* `0xf1 <<< 4 = 0xf10`
* `(-1) <<< 3 = -8`
-/
@[expose]
protected def shiftLeft : Int Nat Int
| Int.ofNat n, s => Int.ofNat (n <<< s)
| Int.negSucc n, s => Int.negSucc (((n + 1) <<< s) - 1)
instance : HShiftLeft Int Nat Int := .shiftLeft
end Int

View File

@@ -37,11 +37,11 @@ theorem shiftRight_eq_div_pow (m : Int) (n : Nat) :
· rw [negSucc_ediv _ (by norm_cast; exact Nat.pow_pos (Nat.zero_lt_two))]
rfl
@[simp]
@[simp, grind =]
theorem zero_shiftRight (n : Nat) : (0 : Int) >>> n = 0 := by
simp [Int.shiftRight_eq_div_pow]
@[simp]
@[simp, grind =]
theorem shiftRight_zero (n : Int) : n >>> 0 = n := by
simp [Int.shiftRight_eq_div_pow]
@@ -88,4 +88,32 @@ theorem shiftRight_le_of_nonpos {n : Int} {s : Nat} (h : n ≤ 0) : (n >>> s)
have rl : n / 2 ^ s 0 := Int.ediv_nonpos_of_nonpos_of_neg (by omega) (by norm_cast at *; omega)
norm_cast at *
@[simp, grind =]
theorem shiftLeft_zero (n : Int) : n <<< 0 = n := by
change Int.shiftLeft _ _ = _
match n with
| Int.ofNat n
| Int.negSucc n => simp [Int.shiftLeft]
theorem shiftLeft_succ (m : Int) (n : Nat) : m <<< (n + 1) = (m <<< n) * 2 := by
change Int.shiftLeft _ _ = Int.shiftLeft _ _ * 2
match m with
| (m : Nat) =>
dsimp [Int.shiftLeft]
rw [Nat.shiftLeft_succ, Nat.mul_comm, natCast_mul, ofNat_two]
| Int.negSucc m =>
dsimp [Int.shiftLeft]
rw [Nat.shiftLeft_succ, Nat.mul_comm, Int.negSucc_eq]
have := Nat.le_shiftLeft (a := m + 1) (b := n)
omega
theorem shiftLeft_eq (a : Int) (b : Nat) : a <<< b = a * 2 ^ b := by
induction b with
| zero => simp
| succ b ih =>
rw [shiftLeft_succ, ih, Int.pow_succ, Int.mul_assoc]
theorem shiftLeft_eq' (a : Int) (b : Nat) : a <<< b = a * (2 ^ b : Nat) := by
simp [shiftLeft_eq]
end Int

View File

@@ -6,7 +6,7 @@ Authors: Leonardo de Moura, Jeremy Avigad, Mario Carneiro, Paul Reichert
module
prelude
public import all Init.Data.Ord
public import all Init.Data.Ord.Basic
public import Init.Data.Int.Order
public section
@@ -27,7 +27,7 @@ theorem compare_eq_ite_lt (a b : Int) :
simp only [compare, compareOfLessAndEq]
split
· rfl
· next h =>
next h =>
match Int.lt_or_eq_of_le (Int.not_lt.1 h) with
| .inl h => simp [h, Int.ne_of_gt h]
| .inr rfl => simp
@@ -36,11 +36,11 @@ theorem compare_eq_ite_le (a b : Int) :
compare a b = if a b then if b a then .eq else .lt else .gt := by
rw [compare_eq_ite_lt]
split
· next hlt => simp [Int.le_of_lt hlt, Int.not_le.2 hlt]
· next hge =>
next hlt => simp [Int.le_of_lt hlt, Int.not_le.2 hlt]
next hge =>
split
· next hgt => simp [Int.not_le.2 hgt]
· next hle => simp [Int.not_lt.1 hge, Int.not_lt.1 hle]
next hgt => simp [Int.not_le.2 hgt]
next hle => simp [Int.not_lt.1 hge, Int.not_lt.1 hle]
protected theorem compare_swap (a b : Int) : (compare a b).swap = compare b a := by
simp only [compare_eq_ite_le]; (repeat' split) <;> try rfl

View File

@@ -956,6 +956,12 @@ theorem neg_mul_ediv_cancel_left (a b : Int) (h : a ≠ 0) : -(a * b) / a = -b :
@[simp] theorem emod_one (a : Int) : a % 1 = 0 := by
simp [emod_def, Int.one_mul, Int.sub_self]
theorem ediv_minus_one (a : Int) : a / (-1) = -a := by
simp
theorem emod_minus_one (a : Int) : a % (-1) = 0 := by
simp
@[deprecated sub_emod_right (since := "2025-04-11")]
theorem emod_sub_cancel (x y : Int) : (x - y) % y = x % y :=
sub_emod_right ..

View File

@@ -86,6 +86,17 @@ theorem negSucc_coe (n : Nat) : -[n+1] = -↑(n + 1) := rfl
@[simp, norm_cast] theorem cast_ofNat_Int :
(Nat.cast (no_index (OfNat.ofNat n)) : Int) = OfNat.ofNat n := rfl
@[simp] theorem beq'_eq (a b : Int) : Int.beq' a b = (a = b) := by
cases a <;> cases b <;> simp [Int.beq', ofNat_inj]
@[simp] theorem beq'_ne (a b : Int) : (Int.beq' a b = false) = (a b) := by
rw [Ne, beq'_eq, Bool.not_eq_true]
theorem beq'_eq_beq (a b : Int) : (Int.beq' a b) = (a == b) := by
have h : (Int.beq' a b = true) = (a == b) := by simp
have : {a b : Bool}, (a = true) = (b = true) a = b := by intro a b; cases a <;> cases b <;> simp
exact this h
/- ## neg -/
@[simp] protected theorem neg_neg : a : Int, -(-a) = a
@@ -350,10 +361,10 @@ theorem negSucc_coe' (n : Nat) : -[n+1] = -↑n - 1 := by
protected theorem subNatNat_eq_coe {m n : Nat} : subNatNat m n = m - n := by
apply subNatNat_elim m n fun m n i => i = m - n
· intros i n
· intro i n
rw [Int.natCast_add, Int.sub_eq_add_neg, Int.add_assoc, Int.add_left_comm,
Int.add_right_neg, Int.add_zero]
· intros i n
· intro i n
simp only [negSucc_eq, natCast_add, ofNat_one, Int.sub_eq_add_neg, Int.neg_add, Int.add_assoc]
rw [Int.add_neg_eq_sub (a := n), ofNat_sub, Nat.sub_self, ofNat_zero, Int.zero_add]
apply Nat.le_refl

View File

@@ -69,6 +69,18 @@ theorem natCast_succ_pos (n : Nat) : 0 < (n.succ : Int) := natCast_pos.2 n.succ_
@[simp, norm_cast] theorem cast_id {n : Int} : Int.cast n = n := rfl
@[simp] theorem ble'_eq_true (a b : Int) : (Int.ble' a b = true) = (a b) := by
cases a <;> cases b <;> simp [Int.ble'] <;> omega
@[simp] theorem blt'_eq_true (a b : Int) : (Int.blt' a b = true) = (a < b) := by
cases a <;> cases b <;> simp [Int.blt'] <;> omega
@[simp] theorem ble'_eq_false (a b : Int) : (Int.ble' a b = false) = ¬(a b) := by
simp [ Bool.not_eq_true]
@[simp] theorem blt'_eq_false (a b : Int) : (Int.blt' a b = false) = ¬ (a < b) := by
simp [ Bool.not_eq_true]
/-! ### toNat -/
@[simp] theorem toNat_sub' (a : Int) (b : Nat) : (a - b).toNat = a.toNat - b := by

File diff suppressed because it is too large Load Diff

View File

@@ -9,6 +9,7 @@ prelude
public import Init.Data.Int.Lemmas
public import Init.Data.Int.DivMod
public import Init.Data.Int.Linear
public import Init.GrindInstances.ToInt
public import Init.Data.RArray
public section
@@ -83,6 +84,10 @@ theorem mod_congr {a b : Nat} {a' b' : Int}
(h₁ : NatCast.natCast a = a') (h₂ : NatCast.natCast b = b') : NatCast.natCast (a % b) = a' % b' := by
simp_all [Int.natCast_emod]
theorem finVal {n : Nat} {a : Fin n} {a' : Int}
(h₁ : Lean.Grind.ToInt.toInt a = a') : NatCast.natCast (a.val) = a' := by
rw [ h₁, Lean.Grind.ToInt.toInt, Lean.Grind.instToIntFinCoOfNatIntCast]
end Nat.ToInt
namespace Int.Nonneg

View File

@@ -8,9 +8,13 @@ module
prelude
public import Init.Data.Int.Lemmas
public import Init.ByCases
public import Init.Data.Order.Factories
import Init.Data.Order.Lemmas
public section
open Std
/-!
# Results about the order properties of the integers, and the integers as an ordered ring.
-/
@@ -1415,4 +1419,14 @@ theorem natAbs_eq_iff_mul_eq_zero : natAbs a = n ↔ (a - n) * (a + n) = 0 := by
@[deprecated natAbs_eq_iff_mul_eq_zero (since := "2025-03-11")]
abbrev eq_natAbs_iff_mul_eq_zero := @natAbs_eq_iff_mul_eq_zero
instance instIsLinearOrder : IsLinearOrder Int := by
apply IsLinearOrder.of_le
case le_antisymm => constructor; apply Int.le_antisymm
case le_total => constructor; apply Int.le_total
case le_trans => constructor; apply Int.le_trans
instance : LawfulOrderLT Int where
lt_iff := by
simp [ Int.not_le, Decidable.imp_iff_not_or, Std.Total.total]
end Int

View File

@@ -21,6 +21,11 @@ protected theorem pow_succ (b : Int) (e : Nat) : b ^ (e+1) = (b ^ e) * b := rfl
protected theorem pow_succ' (b : Int) (e : Nat) : b ^ (e+1) = b * (b ^ e) := by
rw [Int.mul_comm, Int.pow_succ]
protected theorem pow_add (a : Int) (m n : Nat) : a ^ (m + n) = a ^ m * a ^ n := by
induction n with
| zero => rw [Nat.add_zero, Int.pow_zero, Int.mul_one]
| succ _ ih => rw [Nat.add_succ, Int.pow_succ, Int.pow_succ, ih, Int.mul_assoc]
protected theorem zero_pow {n : Nat} (h : n 0) : (0 : Int) ^ n = 0 := by
match n, h with
| n + 1, _ => simp [Int.pow_succ]

View File

@@ -10,6 +10,7 @@ public import all Init.Data.Iterators.Combinators.Attach
public import all Init.Data.Iterators.Combinators.Monadic.Attach
public import Init.Data.Iterators.Lemmas.Combinators.Monadic.Attach
public import Init.Data.Iterators.Lemmas.Consumers.Collect
public import Init.Data.Array.Attach
public section
@@ -68,4 +69,16 @@ theorem Iter.unattach_toArray_attachWith [Iterator α Id β]
(it.attachWith P hP).toListRev.unattach = it.toListRev := by
simp [toListRev_eq]
@[simp]
theorem Iter.toArray_attachWith [Iterator α Id β]
{it : Iter (α := α) β} {hP}
[Finite α Id] [IteratorCollect α Id Id]
[LawfulIteratorCollect α Id Id] :
(it.attachWith P hP).toArray = it.toArray.attachWith P
(fun out h => hP out (isPlausibleIndirectOutput_of_mem_toArray h)) := by
suffices (it.attachWith P hP).toArray.toList = (it.toArray.attachWith P
(fun out h => hP out (isPlausibleIndirectOutput_of_mem_toArray h))).toList by
simpa only [Array.toList_inj]
simp [Iter.toList_toArray]
end Std.Iterators

View File

@@ -251,7 +251,7 @@ instance {α β γ : Type w} {m : Type w → Type w'} {n : Type w → Type w''}
simp only [LawfulIteratorCollect.toArrayMapped_eq]
simp only [IteratorCollect.toArrayMapped]
rw [LawfulIteratorCollect.toArrayMapped_eq]
induction it using IterM.inductSteps with | step it ih_yield ih_skip =>
induction it using IterM.inductSteps with | step it ih_yield ih_skip
rw [IterM.DefaultConsumers.toArrayMapped_eq_match_step]
rw [IterM.DefaultConsumers.toArrayMapped_eq_match_step]
simp only [bind_assoc]

View File

@@ -97,7 +97,7 @@ theorem Iter.getElem?_toList_eq_atIdxSlow? {α β}
[Iterator α Id β] [Finite α Id] [IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
{it : Iter (α := α) β} {k : Nat} :
it.toList[k]? = it.atIdxSlow? k := by
induction it using Iter.inductSteps generalizing k with | step it ihy ihs =>
induction it using Iter.inductSteps generalizing k with | step it ihy ihs
rw [toList_eq_match_step, atIdxSlow?]
obtain step, h := it.step
cases step
@@ -117,7 +117,7 @@ theorem Iter.isPlausibleIndirectOutput_of_mem_toList
[Iterator α Id β] [Finite α Id] [IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
{it : Iter (α := α) β} {b : β} :
b it.toList it.IsPlausibleIndirectOutput b := by
induction it using Iter.inductSteps with | step it ihy ihs =>
induction it using Iter.inductSteps with | step it ihy ihs
rw [toList_eq_match_step]
cases it.step using PlausibleIterStep.casesOn
case yield it' out h =>
@@ -144,4 +144,13 @@ theorem Iter.isPlausibleIndirectOutput_of_mem_toListRev
apply isPlausibleIndirectOutput_of_mem_toList
simpa [toListRev_eq] using h
theorem Iter.isPlausibleIndirectOutput_of_mem_toArray
[Iterator α Id β] [Finite α Id] [IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
{it : Iter (α := α) β} {b : β} :
b it.toArray it.IsPlausibleIndirectOutput b := by
intro h
apply isPlausibleIndirectOutput_of_mem_toList
rw [ Array.mem_toList_iff] at h
simpa [toList_toArray] using h
end Std.Iterators

View File

@@ -143,7 +143,7 @@ theorem Iter.mem_toList_iff_isPlausibleIndirectOutput {α β} [Iterator α Id β
[LawfulIteratorCollect α Id Id] [LawfulDeterministicIterator α Id]
{it : Iter (α := α) β} {out : β} :
out it.toList it.IsPlausibleIndirectOutput out := by
induction it using Iter.inductSteps with | step it ihy ihs =>
induction it using Iter.inductSteps with | step it ihy ihs
rw [toList_eq_match_step]
constructor
· intro h
@@ -194,7 +194,7 @@ theorem Iter.forIn'_toList {α β : Type w} [Iterator α Id β]
{f : (out : β) _ γ m (ForInStep γ)} :
letI : ForIn' m (Iter (α := α) β) β _ := Iter.instForIn'
ForIn'.forIn' it.toList init f = ForIn'.forIn' it init (fun out h acc => f out (Iter.mem_toList_iff_isPlausibleIndirectOutput.mpr h) acc) := by
induction it using Iter.inductSteps generalizing init with case step it ihy ihs =>
induction it using Iter.inductSteps generalizing init with | step it ihy ihs
have := it.toList_eq_match_step
generalize hs : it.step = step at this
rw [forIn'_toList.aux this]
@@ -239,7 +239,7 @@ theorem Iter.forIn_toList {α β : Type w} [Iterator α Id β]
{f : β γ m (ForInStep γ)} :
ForIn.forIn it.toList init f = ForIn.forIn it init f := by
rw [List.forIn_eq_foldlM]
induction it using Iter.inductSteps generalizing init with case step it ihy ihs =>
induction it using Iter.inductSteps generalizing init with | step it ihy ihs
rw [forIn_eq_match_step, Iter.toList_eq_match_step]
simp only [map_eq_pure_bind]
generalize it.step = step

View File

@@ -21,8 +21,7 @@ theorem IterM.DefaultConsumers.toArrayMapped.go.aux₁ [Monad n] [LawfulMonad n]
[Finite α m] {b : γ} {bs : Array γ} :
IterM.DefaultConsumers.toArrayMapped.go lift f it (#[b] ++ bs) (m := m) =
(#[b] ++ ·) <$> IterM.DefaultConsumers.toArrayMapped.go lift f it bs (m := m) := by
induction it, bs using IterM.DefaultConsumers.toArrayMapped.go.induct
next it bs ih₁ ih₂ =>
induction it, bs using IterM.DefaultConsumers.toArrayMapped.go.induct with | _ it bs ih₁ ih₂
rw [go, map_eq_pure_bind, go, bind_assoc]
apply bind_congr
intro step
@@ -93,8 +92,7 @@ theorem IterM.toList_eq_match_step [Monad m] [LawfulMonad m] [Iterator α m β]
theorem IterM.toListRev.go.aux₁ [Monad m] [LawfulMonad m] [Iterator α m β] [Finite α m]
{it : IterM (α := α) m β} {b : β} {bs : List β} :
IterM.toListRev.go it (bs ++ [b]) = (· ++ [b]) <$> IterM.toListRev.go it bs:= by
induction it, bs using IterM.toListRev.go.induct
next it bs ih₁ ih₂ =>
induction it, bs using IterM.toListRev.go.induct with | _ it bs ih₁ ih₂
rw [go, go, map_eq_pure_bind, bind_assoc]
apply bind_congr
intro step

View File

@@ -208,7 +208,7 @@ theorem IterM.toList_eq_fold {α β : Type w} {m : Type w → Type w'} [Iterator
it.fold (init := l') (fun l out => l ++ [out]) by
specialize h []
simpa using h
induction it using IterM.inductSteps with | step it ihy ihs =>
induction it using IterM.inductSteps with | step it ihy ihs
intro l'
rw [IterM.toList_eq_match_step, IterM.fold_eq_match_step]
simp only [map_eq_pure_bind, bind_assoc]
@@ -253,7 +253,7 @@ theorem IterM.drain_eq_map_toList {α β : Type w} {m : Type w → Type w'} [Ite
[IteratorCollect α m m] [LawfulIteratorCollect α m m]
{it : IterM (α := α) m β} :
it.drain = (fun _ => .unit) <$> it.toList := by
induction it using IterM.inductSteps with | step it ihy ihs =>
induction it using IterM.inductSteps with | step it ihy ihs
rw [IterM.drain_eq_match_step, IterM.toList_eq_match_step]
simp only [map_eq_pure_bind, bind_assoc]
apply bind_congr

View File

@@ -7,7 +7,7 @@ module
prelude
public import Init.Control.Lawful.Basic
public import Init.Data.Subtype
public import Init.Data.Subtype.Basic
public import Init.PropLemmas
public section

View File

@@ -8,7 +8,7 @@ module
prelude
public import all Init.Data.List.Lemmas -- for dsimping with `getElem?_cons_succ`
public import Init.Data.List.Count
public import Init.Data.Subtype
public import Init.Data.Subtype.Basic
public import Init.BinderNameHint
public section
@@ -123,7 +123,7 @@ theorem attachWith_congr {l₁ l₂ : List α} (w : l₁ = l₂) {P : α → Pro
x, mem_cons_self :: xs.attach.map fun y, h => y, mem_cons_of_mem x h := by
simp only [attach, attachWith, pmap, map_pmap, cons.injEq, true_and]
apply pmap_congr_left
intros a _ m' _
intro a _ m' _
rfl
@[simp, grind =]

View File

@@ -242,8 +242,7 @@ instance instLT [LT α] : LT (List α) := ⟨List.lt⟩
instance decidableLT [DecidableEq α] [LT α] [DecidableLT α] (l₁ l₂ : List α) :
Decidable (l₁ < l₂) := decidableLex (· < ·) l₁ l₂
@[deprecated decidableLT (since := "2024-12-13"), inherit_doc decidableLT]
abbrev hasDecidableLt := @decidableLT
/--
Non-strict ordering of lists with respect to a strict ordering of their elements.
@@ -1722,14 +1721,8 @@ Examples:
-/
def idxOf [BEq α] (a : α) : List α Nat := findIdx (· == a)
/-- Returns the index of the first element equal to `a`, or the length of the list otherwise. -/
@[deprecated idxOf (since := "2025-01-29")] abbrev indexOf := @idxOf
@[simp] theorem idxOf_nil [BEq α] : ([] : List α).idxOf x = 0 := rfl
@[deprecated idxOf_nil (since := "2025-01-29")]
theorem indexOf_nil [BEq α] : ([] : List α).idxOf x = 0 := rfl
/-! ### findIdx? -/
/--
@@ -1760,10 +1753,6 @@ Examples:
-/
@[inline] def idxOf? [BEq α] (a : α) : List α Option Nat := findIdx? (· == a)
/-- Return the index of the first occurrence of `a` in the list. -/
@[deprecated idxOf? (since := "2025-01-29")]
abbrev indexOf? := @idxOf?
/-! ### findFinIdx? -/
/--
@@ -2119,21 +2108,10 @@ def range' : (start len : Nat) → (step : Nat := 1) → List Nat
| _, 0, _ => []
| s, n+1, step => s :: range' (s+step) n step
/-! ### iota -/
/--
`O(n)`. `iota n` is the numbers from `1` to `n` inclusive, in decreasing order.
* `iota 5 = [5, 4, 3, 2, 1]`
-/
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20")]
def iota : Nat List Nat
| 0 => []
| m@(n+1) => m :: iota n
set_option linter.deprecated false in
@[simp] theorem iota_zero : iota 0 = [] := rfl
set_option linter.deprecated false in
@[simp] theorem iota_succ : iota (i+1) = (i+1) :: iota i := rfl
@[simp, grind =] theorem range'_zero : range' s 0 step = [] := rfl
@[simp, grind =] theorem range'_one {s step : Nat} : range' s 1 step = [s] := rfl
-- The following theorem is intentionally not a simp lemma.
theorem range'_succ : range' s (n + 1) step = s :: range' (s + step) n step := rfl
/-! ### zipIdx -/
@@ -2153,38 +2131,6 @@ def zipIdx : (l : List α) → (n : Nat := 0) → List (α × Nat)
@[simp] theorem zipIdx_nil : ([] : List α).zipIdx i = [] := rfl
@[simp] theorem zipIdx_cons : (a::as).zipIdx i = (a, i) :: as.zipIdx (i+1) := rfl
/-! ### enumFrom -/
/--
`O(|l|)`. `enumFrom n l` is like `enum` but it allows you to specify the initial index.
* `enumFrom 5 [a, b, c] = [(5, a), (6, b), (7, c)]`
-/
@[deprecated "Use `zipIdx` instead; note the signature change." (since := "2025-01-21")]
def enumFrom : Nat List α List (Nat × α)
| _, [] => nil
| n, x :: xs => (n, x) :: enumFrom (n + 1) xs
set_option linter.deprecated false in
@[deprecated zipIdx_nil (since := "2025-01-21"), simp]
theorem enumFrom_nil : ([] : List α).enumFrom i = [] := rfl
set_option linter.deprecated false in
@[deprecated zipIdx_cons (since := "2025-01-21"), simp]
theorem enumFrom_cons : (a::as).enumFrom i = (i, a) :: as.enumFrom (i+1) := rfl
/-! ### enum -/
set_option linter.deprecated false in
/--
`O(|l|)`. `enum l` pairs up each element with its index in the list.
* `enum [a, b, c] = [(0, a), (1, b), (2, c)]`
-/
@[deprecated "Use `zipIdx` instead; note the signature change." (since := "2025-01-21")]
def enum : List α List (Nat × α) := enumFrom 0
set_option linter.deprecated false in
@[deprecated zipIdx_nil (since := "2025-01-21"), simp]
theorem enum_nil : ([] : List α).enum = [] := rfl
/-! ## Minima and maxima -/
/-! ### min? -/
@@ -2604,25 +2550,6 @@ Examples:
exact go s n (m + 1)
exact (go s n 0).symm
/-! ### iota -/
/-- Tail-recursive version of `List.iota`. -/
@[deprecated "Use `List.range' 1 n` instead of `iota n`." (since := "2025-01-20")]
def iotaTR (n : Nat) : List Nat :=
let rec go : Nat List Nat List Nat
| 0, r => r.reverse
| m@(n+1), r => go n (m::r)
go n []
set_option linter.deprecated false in
@[csimp]
theorem iota_eq_iotaTR : @iota = @iotaTR :=
have aux (n : Nat) (r : List Nat) : iotaTR.go n r = r.reverse ++ iota n := by
induction n generalizing r with
| zero => simp [iota, iotaTR.go]
| succ n ih => simp [iota, iotaTR.go, ih, append_assoc]
funext fun n => by simp [iotaTR, aux]
/-! ## Other list operations -/
/-! ### intersperse -/

View File

@@ -104,6 +104,20 @@ def forA {m : Type u → Type v} [Applicative m] {α : Type w} (as : List α) (f
| [] => pure
| a :: as => f a *> forA as f
/--
Applies the monadic action `f` to the corresponding elements of two lists, left-to-right, stopping
at the end of the shorter list. `zipWithM f as bs` is equivalent to `mapM id (zipWith f as bs)`
for lawful `Monad` instances.
This implementation is tail recursive. `List.zipWithM'` is a a non-tail-recursive variant that may
be more convenient to reason about.
-/
@[inline, expose]
def zipWithM {m : Type u Type v} [Monad m] {α : Type w} {β : Type x} {γ : Type u} (f : α β m γ) (as : List α) (bs : List β) : m (List γ) :=
let rec @[specialize] loop
| a::as, b::bs, acc => do loop as bs (acc.push ( f a b))
| _, _, acc => pure acc.toList
loop as bs #[]
@[specialize]
def filterAuxM {m : Type Type v} [Monad m] {α : Type} (f : α m Bool) : List α List α m (List α)

View File

@@ -57,15 +57,9 @@ theorem eraseP_of_forall_not {l : List α} (h : ∀ a, a ∈ l → ¬p a) : l.er
rintro x h' rfl
simp_all
@[deprecated eraseP_eq_nil_iff (since := "2025-01-30")]
abbrev eraseP_eq_nil := @eraseP_eq_nil_iff
theorem eraseP_ne_nil_iff {xs : List α} {p : α Bool} : xs.eraseP p [] xs [] x, p x xs [x] := by
simp
@[deprecated eraseP_ne_nil_iff (since := "2025-01-30")]
abbrev eraseP_ne_nil := @eraseP_ne_nil_iff
theorem exists_of_eraseP : {l : List α} {a} (_ : a l) (_ : p a),
a l₁ l₂, ( b l₁, ¬p b) p a l = l₁ ++ a :: l₂ l.eraseP p = l₁ ++ l₂
| b :: l, _, al, pa =>
@@ -337,7 +331,7 @@ theorem erase_of_not_mem [LawfulBEq α] {a : α} : ∀ {l : List α}, a ∉ l
theorem erase_eq_eraseP' (a : α) (l : List α) : l.erase a = l.eraseP (· == a) := by
induction l
· simp
· next b t ih =>
next b t ih =>
rw [erase_cons, eraseP_cons, ih]
if h : b == a then simp [h] else simp [h]
@@ -352,17 +346,11 @@ theorem erase_eq_eraseP [LawfulBEq α] (a : α) : ∀ (l : List α), l.erase a =
rw [erase_eq_eraseP]
simp
@[deprecated erase_eq_nil_iff (since := "2025-01-30")]
abbrev erase_eq_nil := @erase_eq_nil_iff
theorem erase_ne_nil_iff [LawfulBEq α] {xs : List α} {a : α} :
xs.erase a [] xs [] xs [a] := by
rw [erase_eq_eraseP]
simp
@[deprecated erase_ne_nil_iff (since := "2025-01-30")]
abbrev erase_ne_nil := @erase_ne_nil_iff
theorem exists_erase_eq [LawfulBEq α] {a : α} {l : List α} (h : a l) :
l₁ l₂, a l₁ l = l₁ ++ a :: l₂ l.erase a = l₁ ++ l₂ := by
let _, l₁, l₂, h₁, e, h₂, h₃ := exists_of_eraseP h (beq_self_eq_true _)
@@ -439,7 +427,7 @@ theorem erase_append_left [LawfulBEq α] {l₁ : List α} (l₂) (h : a ∈ l₁
theorem erase_append_right [LawfulBEq α] {a : α} {l₁ : List α} (l₂ : List α) (h : a l₁) :
(l₁ ++ l₂).erase a = (l₁ ++ l₂.erase a) := by
rw [erase_eq_eraseP, erase_eq_eraseP, eraseP_append_right]
intros b h' h''; rw [eq_of_beq h''] at h; exact h h'
intro b h' h''; rw [eq_of_beq h''] at h; exact h h'
@[grind =]
theorem erase_append [LawfulBEq α] {a : α} {l₁ l₂ : List α} :
@@ -582,8 +570,7 @@ theorem eraseIdx_eq_take_drop_succ :
| a::l, 0
| a::l, i + 1 => simp
@[deprecated eraseIdx_eq_nil_iff (since := "2025-01-30")]
abbrev eraseIdx_eq_nil := @eraseIdx_eq_nil_iff
theorem eraseIdx_ne_nil_iff {l : List α} {i : Nat} : eraseIdx l i [] 2 l.length (l.length = 1 i 0) := by
match l with
@@ -591,8 +578,7 @@ theorem eraseIdx_ne_nil_iff {l : List α} {i : Nat} : eraseIdx l i ≠ [] ↔ 2
| [a]
| a::b::l => simp
@[deprecated eraseIdx_ne_nil_iff (since := "2025-01-30")]
abbrev eraseIdx_ne_nil := @eraseIdx_ne_nil_iff
@[grind]
theorem eraseIdx_sublist : (l : List α) (k : Nat), eraseIdx l k <+ l
@@ -700,7 +686,6 @@ theorem erase_eq_eraseIdx_of_idxOf [BEq α] [LawfulBEq α]
rw [eq_comm, eraseIdx_eq_self]
exact Nat.le_of_eq (idxOf_eq_length h).symm
@[deprecated erase_eq_eraseIdx_of_idxOf (since := "2025-01-29")]
abbrev erase_eq_eraseIdx_of_indexOf := @erase_eq_eraseIdx_of_idxOf
end List

View File

@@ -46,7 +46,7 @@ theorem finRange_succ_last {n} :
getElem_map, Fin.castSucc_mk, getElem_singleton]
split
· rfl
· next h => exact Fin.eq_last_of_not_lt h
next h => exact Fin.eq_last_of_not_lt h
@[grind _=_]
theorem finRange_reverse {n} : (finRange n).reverse = (finRange n).map Fin.rev := by

View File

@@ -1098,15 +1098,9 @@ theorem idxOf_cons [BEq α] :
dsimp [idxOf]
simp [findIdx_cons]
@[deprecated idxOf_cons (since := "2025-01-29")]
abbrev indexOf_cons := @idxOf_cons
@[simp] theorem idxOf_cons_self [BEq α] [ReflBEq α] {l : List α} : (a :: l).idxOf a = 0 := by
simp [idxOf_cons]
@[deprecated idxOf_cons_self (since := "2025-01-29")]
abbrev indexOf_cons_self := @idxOf_cons_self
@[grind =]
theorem idxOf_append [BEq α] [LawfulBEq α] {l₁ l₂ : List α} {a : α} :
(l₁ ++ l₂).idxOf a = if a l₁ then l₁.idxOf a else l₂.idxOf a + l₁.length := by
@@ -1117,8 +1111,7 @@ theorem idxOf_append [BEq α] [LawfulBEq α] {l₁ l₂ : List α} {a : α} :
· rw [if_neg]
simpa using h
@[deprecated idxOf_append (since := "2025-01-29")]
abbrev indexOf_append := @idxOf_append
theorem idxOf_eq_length [BEq α] [LawfulBEq α] {l : List α} (h : a l) : l.idxOf a = l.length := by
induction l with
@@ -1128,8 +1121,7 @@ theorem idxOf_eq_length [BEq α] [LawfulBEq α] {l : List α} (h : a ∉ l) : l.
simp only [idxOf_cons, cond_eq_if, beq_iff_eq]
split <;> simp_all
@[deprecated idxOf_eq_length (since := "2025-01-29")]
abbrev indexOf_eq_length := @idxOf_eq_length
theorem idxOf_lt_length_of_mem [BEq α] [EquivBEq α] {l : List α} (h : a l) : l.idxOf a < l.length := by
induction l with
@@ -1159,8 +1151,7 @@ theorem idxOf_lt_length_iff [BEq α] [LawfulBEq α] {l : List α} {a : α} :
grind_pattern idxOf_lt_length_iff => l.idxOf a, l.length
@[deprecated idxOf_lt_length_of_mem (since := "2025-01-29")]
abbrev indexOf_lt_length := @idxOf_lt_length_of_mem
/-! ### finIdxOf?
@@ -1231,8 +1222,7 @@ The lemmas below should be made consistent with those for `findIdx?` (and proved
· rintro w x h rfl
contradiction
@[deprecated idxOf?_eq_none_iff (since := "2025-01-29")]
abbrev indexOf?_eq_none_iff := @idxOf?_eq_none_iff
@[simp, grind =]
theorem isSome_idxOf? [BEq α] [LawfulBEq α] {l : List α} {a : α} :
@@ -1251,9 +1241,9 @@ theorem isNone_idxOf? [BEq α] [LawfulBEq α] {l : List α} {a : α} :
/-! ### lookup -/
section lookup
variable [BEq α] [LawfulBEq α]
variable [BEq α]
@[simp] theorem lookup_cons_self {k : α} : ((k,b) :: es).lookup k = some b := by
@[simp] theorem lookup_cons_self [ReflBEq α] {k : α} : ((k,b) :: es).lookup k = some b := by
simp [lookup_cons]
@[simp] theorem lookup_singleton {a b : α} : [(a,b)].lookup c = if c == a then some b else none := by
@@ -1272,13 +1262,13 @@ theorem lookup_eq_findSome? {l : List (α × β)} {k : α} :
@[simp, grind =] theorem lookup_eq_none_iff {l : List (α × β)} {k : α} :
l.lookup k = none p l, k != p.1 := by
simp [lookup_eq_findSome?]
simp [lookup_eq_findSome?, bne]
@[simp] theorem lookup_isSome_iff {l : List (α × β)} {k : α} :
(l.lookup k).isSome p l, k == p.1 := by
simp [lookup_eq_findSome?]
theorem lookup_eq_some_iff {l : List (α × β)} {k : α} {b : β} :
theorem lookup_eq_some_iff [LawfulBEq α] {l : List (α × β)} {k : α} {b : β} :
l.lookup k = some b l₁ l₂, l = l₁ ++ (k, b) :: l₂ p l₁, k != p.1 := by
simp only [lookup_eq_findSome?, findSome?_eq_some_iff]
constructor
@@ -1308,11 +1298,11 @@ theorem lookup_replicate_of_pos {k : α} (h : 0 < n) :
(replicate n (a, b)).lookup k = if k == a then some b else none := by
simp [lookup_replicate, Nat.ne_of_gt h]
theorem lookup_replicate_self {a : α} :
theorem lookup_replicate_self [ReflBEq α] {a : α} :
(replicate n (a, b)).lookup a = if n = 0 then none else some b := by
simp [lookup_replicate]
@[simp] theorem lookup_replicate_self_of_pos {a : α} (h : 0 < n) :
@[simp] theorem lookup_replicate_self_of_pos [ReflBEq α] {a : α} (h : 0 < n) :
(replicate n (a, b)).lookup a = some b := by
simp [lookup_replicate_self, Nat.ne_of_gt h]

View File

@@ -98,7 +98,7 @@ Example:
[10, 14, 14]
```
-/
@[inline] def filterMapTR (f : α Option β) (l : List α) : List β := go l #[] where
@[inline, expose] def filterMapTR (f : α Option β) (l : List α) : List β := go l #[] where
/-- Auxiliary for `filterMap`: `filterMap.go f l = acc.toList ++ filterMap f l` -/
@[specialize] go : List α Array β List β
| [], acc => acc.toList
@@ -367,7 +367,7 @@ def modifyTR (l : List α) (i : Nat) (f : αα) : List α := go l i #[] whe
| a :: l, 0, acc => acc.toListAppend (f a :: l)
| a :: l, i+1, acc => go l i (acc.push a)
theorem modifyTR_go_eq : l i, modifyTR.go f l i acc = acc.toList ++ modify l i f
private theorem modifyTR_go_eq : l i, modifyTR.go f l i acc = acc.toList ++ modify l i f
| [], i => by cases i <;> simp [modifyTR.go, modify]
| a :: l, 0 => by simp [modifyTR.go, modify]
| a :: l, i+1 => by simp [modifyTR.go, modify, modifyTR_go_eq l]
@@ -399,7 +399,7 @@ Examples:
| _, [], acc => acc.toList
| n+1, a :: l, acc => go n l (acc.push a)
theorem insertIdxTR_go_eq : i l, insertIdxTR.go a i l acc = acc.toList ++ insertIdx l i a
private theorem insertIdxTR_go_eq : i l, insertIdxTR.go a i l acc = acc.toList ++ insertIdx l i a
| 0, l | _+1, [] => by simp [insertIdxTR.go, insertIdx]
| n+1, a :: l => by simp [insertIdxTR.go, insertIdx, insertIdxTR_go_eq n l]
@@ -564,24 +564,7 @@ def zipIdxTR (l : List α) (n : Nat := 0) : List (α × Nat) :=
/-! ### enumFrom -/
/-- Tail recursive version of `List.enumFrom`. -/
@[deprecated zipIdxTR (since := "2025-01-21")]
def enumFromTR (n : Nat) (l : List α) : List (Nat × α) :=
let as := l.toArray
(as.foldr (fun a (n, acc) => (n-1, (n-1, a) :: acc)) (n + as.size, [])).2
set_option linter.deprecated false in
@[deprecated zipIdx_eq_zipIdxTR (since := "2025-01-21"), csimp]
theorem enumFrom_eq_enumFromTR : @enumFrom = @enumFromTR := by
funext α n l; simp only [enumFromTR]
let f := fun (a : α) (n, acc) => (n-1, (n-1, a) :: acc)
let rec go : l n, l.foldr f (n + l.length, []) = (n, enumFrom n l)
| [], n => rfl
| a::as, n => by
rw [ show _ + as.length = n + (a::as).length from Nat.succ_add .., foldr, go as]
simp [enumFrom, f]
rw [ Array.foldr_toList]
simp +zetaDelta [go]
/-! ## Other list operations -/

View File

@@ -70,7 +70,7 @@ See also
Further results, which first require developing further automation around `Nat`, appear in
* `Init.Data.List.Nat.Basic`: miscellaneous lemmas
* `Init.Data.List.Nat.Range`: `List.range` and `List.enum`
* `Init.Data.List.Nat.Range`: `List.range`, `List.range'` and `List.enum`
* `Init.Data.List.Nat.TakeDrop`: `List.take` and `List.drop`
Also
@@ -1084,6 +1084,12 @@ theorem getLast?_tail {l : List α} : (tail l).getLast? = if l.length = 1 then n
rw [if_neg]
rintro
@[simp, grind =]
theorem cons_head_tail (h : l []) : l.head h :: l.tail = l := by
induction l with
| nil => contradiction
| cons ih => simp_all
/-! ## Basic operations -/
/-! ### map -/
@@ -1429,12 +1435,12 @@ theorem filterMap_eq_map {f : α → β} : filterMap (some ∘ f) = map f := by
theorem filterMap_eq_map' {f : α β} : filterMap (fun x => some (f x)) = map f :=
filterMap_eq_map
@[simp] theorem filterMap_some_fun : filterMap (some : α Option α) = id := by
theorem filterMap_some_fun : filterMap (some : α Option α) = id := by
funext l
erw [filterMap_eq_map]
simp
@[grind] theorem filterMap_some {l : List α} : filterMap some l = l := by
@[simp, grind] theorem filterMap_some {l : List α} : filterMap some l = l := by
rw [filterMap_some_fun, id]
theorem map_filterMap_some_eq_filter_map_isSome {f : α Option β} {l : List α} :
@@ -1586,9 +1592,7 @@ theorem filterMap_eq_cons_iff {l} {b} {bs} :
theorem not_mem_append {a : α} {s t : List α} (h₁ : a s) (h₂ : a t) : a s ++ t :=
mt mem_append.1 $ not_or.mpr h₁, h₂
@[deprecated mem_append (since := "2025-01-13")]
theorem mem_append_eq {a : α} {s t : List α} : (a s ++ t) = (a s a t) :=
propext mem_append
/--
See also `eq_append_cons_of_mem`, which proves a stronger version
@@ -1698,7 +1702,7 @@ theorem getLast_concat {a : α} : ∀ {l : List α}, getLast (l ++ [a]) (by simp
@[simp] theorem append_eq_nil_iff : p ++ q = [] p = [] q = [] := by
cases p <;> simp
@[deprecated append_eq_nil_iff (since := "2025-01-13")] abbrev append_eq_nil := @append_eq_nil_iff
theorem nil_eq_append_iff : [] = a ++ b a = [] b = [] := by
simp
@@ -1849,6 +1853,14 @@ theorem append_eq_map_iff {f : α → β} :
L₁ ++ L₂ = map f l l₁ l₂, l = l₁ ++ l₂ map f l₁ = L₁ map f l₂ = L₂ := by
rw [eq_comm, map_eq_append_iff]
@[simp, grind =]
theorem sum_append_nat {l₁ l₂ : List Nat} : (l₁ ++ l₂).sum = l₁.sum + l₂.sum := by
induction l₁ generalizing l₂ <;> simp_all [Nat.add_assoc]
@[simp, grind =]
theorem sum_reverse_nat (xs : List Nat) : xs.reverse.sum = xs.sum := by
induction xs <;> simp_all [Nat.add_comm]
/-! ### concat
Note that `concat_eq_append` is a `@[simp]` lemma, so `concat` should usually not appear in goals.
@@ -2148,7 +2160,7 @@ theorem flatMap_eq_foldl {f : α → List β} {l : List α} :
intro l'
induction l generalizing l'
· simp
· next ih => rw [flatMap_cons, append_assoc, ih, foldl_cons]
next ih => rw [flatMap_cons, append_assoc, ih, foldl_cons]
/-! ### replicate -/
@@ -2264,8 +2276,7 @@ theorem map_const' {l : List α} {b : β} : map (fun _ => b) l = replicate l.len
simp only [mem_append, mem_replicate, ne_eq]
rintro (-, rfl | _, rfl) <;> rfl
@[deprecated replicate_append_replicate (since := "2025-01-16")]
abbrev append_replicate_replicate := @replicate_append_replicate
theorem append_eq_replicate_iff {l₁ l₂ : List α} {a : α} :
l₁ ++ l₂ = replicate n a
@@ -2653,8 +2664,6 @@ theorem foldl_map_hom {g : α → β} {f : ααα} {f' : β → β →
· simp
· simp [*]
@[deprecated foldl_map_hom (since := "2025-01-20")] abbrev foldl_map' := @foldl_map_hom
theorem foldr_map_hom {g : α β} {f : α α α} {f' : β β β} {a : α} {l : List α}
(h : x y, f' (g x) (g y) = g (f x y)) :
(l.map g).foldr f' (g a) = g (l.foldr f a) := by
@@ -2662,8 +2671,6 @@ theorem foldr_map_hom {g : α → β} {f : ααα} {f' : β → β →
· simp
· simp [*]
@[deprecated foldr_map_hom (since := "2025-01-20")] abbrev foldr_map' := @foldr_map_hom
@[simp] theorem foldrM_append [Monad m] [LawfulMonad m] {f : α β m β} {b : β} {l l' : List α} :
(l ++ l').foldrM f b = l'.foldrM f b >>= l.foldrM f := by
induction l <;> simp [*]
@@ -3731,12 +3738,6 @@ theorem mem_iff_get? {a} {l : List α} : a ∈ l ↔ ∃ n, l.get? n = some a :=
/-! ### Deprecations -/
@[deprecated _root_.isSome_getElem? (since := "2024-12-09")]
theorem isSome_getElem? {l : List α} {i : Nat} : l[i]?.isSome i < l.length := by
simp
@[deprecated _root_.isNone_getElem? (since := "2024-12-09")]
theorem isNone_getElem? {l : List α} {i : Nat} : l[i]?.isNone l.length i := by
simp
end List

View File

@@ -8,9 +8,13 @@ module
prelude
public import Init.Data.List.Lemmas
public import Init.Data.List.Nat.TakeDrop
public import Init.Data.Order.Factories
import Init.Data.Order.Lemmas
public section
open Std
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
@@ -18,6 +22,11 @@ namespace List
/-! ### Lexicographic ordering -/
instance [LT α] [Std.Asymm (α := List α) (· < ·)] : LawfulOrderLT (List α) where
lt_iff := by
simp only [LE.le, List.le, Classical.not_not, iff_and_self]
apply Std.Asymm.asymm
@[simp] theorem lex_lt [LT α] {l₁ l₂ : List α} : Lex (· < ·) l₁ l₂ l₁ < l₂ := Iff.rfl
@[simp] theorem not_lex_lt [LT α] {l₁ l₂ : List α} : ¬ Lex (· < ·) l₁ l₂ l₂ l₁ := Iff.rfl
@@ -79,7 +88,6 @@ theorem not_cons_lex_cons_iff [DecidableEq α] [DecidableRel r] {a b} {l₁ l₂
rw [cons_lex_cons_iff, not_or, Decidable.not_and_iff_or_not, and_or_left]
theorem cons_le_cons_iff [LT α]
[i₀ : Std.Irrefl (· < · : α α Prop)]
[i₁ : Std.Asymm (· < · : α α Prop)]
[i₂ : Std.Antisymm (¬ · < · : α α Prop)]
{a b} {l₁ l₂ : List α} :
@@ -101,19 +109,22 @@ theorem cons_le_cons_iff [LT α]
exact i₂.antisymm _ _ h₃ h₁, h₂
· rintro (h | h₁, h₂)
· left
exact i₁.asymm _ _ h, fun w => i₀.irrefl _ (w h)
exact i₁.asymm _ _ h, fun w => Irrefl.irrefl _ (w h)
· right
exact fun w => i₀.irrefl _ (h₁ w), h₂
exact fun w => Irrefl.irrefl _ (h₁ w), h₂
theorem not_lt_of_cons_le_cons [LT α]
[i₀ : Std.Irrefl (· < · : α α Prop)]
[i₁ : Std.Asymm (· < · : α α Prop)]
[i₂ : Std.Antisymm (¬ · < · : α α Prop)]
{a b : α} {l₁ l₂ : List α} (h : a :: l₁ b :: l₂) : ¬ b < a := by
rw [cons_le_cons_iff] at h
rcases h with h | rfl, h
· exact i₁.asymm _ _ h
· exact i₀.irrefl _
· exact Irrefl.irrefl _
theorem left_le_left_of_cons_le_cons [LT α] [LE α] [IsLinearOrder α]
[LawfulOrderLT α] {a b : α} {l₁ l₂ : List α} (h : a :: l₁ b :: l₂) : a b := by
simpa [not_lt] using not_lt_of_cons_le_cons h
theorem le_of_cons_le_cons [LT α]
[i₀ : Std.Irrefl (· < · : α α Prop)]
@@ -163,14 +174,9 @@ instance [LT α] [Trans (· < · : αα → Prop) (· < ·) (· < ·)] :
Trans (· < · : List α List α Prop) (· < ·) (· < ·) where
trans h₁ h₂ := List.lt_trans h₁ h₂
@[deprecated List.le_antisymm (since := "2024-12-13")]
protected abbrev lt_antisymm := @List.le_antisymm
protected theorem lt_of_le_of_lt [LT α]
[i₀ : Std.Irrefl (· < · : α α Prop)]
[i₁ : Std.Asymm (· < · : α α Prop)]
[i₂ : Std.Antisymm (¬ · < · : α α Prop)]
[i₃ : Trans (¬ · < · : α α Prop) (¬ · < ·) (¬ · < ·)]
protected theorem lt_of_le_of_lt [LT α] [LE α] [IsLinearOrder α] [LawfulOrderLT α]
{l₁ l₂ l₃ : List α} (h₁ : l₁ l₂) (h₂ : l₂ < l₃) : l₁ < l₃ := by
induction h₂ generalizing l₁ with
| nil => simp_all
@@ -180,11 +186,8 @@ protected theorem lt_of_le_of_lt [LT α]
| nil => simp_all
| cons c l₁ =>
apply Lex.rel
replace h₁ := not_lt_of_cons_le_cons h₁
apply Classical.byContradiction
intro h₂
have := i₃.trans h₁ h₂
contradiction
replace h₁ := left_le_left_of_cons_le_cons h₁
exact lt_of_le_of_lt h₁ hab
| cons w₃ ih =>
rename_i a as bs
cases l₁ with
@@ -194,21 +197,34 @@ protected theorem lt_of_le_of_lt [LT α]
by_cases w₅ : a = c
· subst w₅
exact Lex.cons (ih (le_of_cons_le_cons h₁))
· exact Lex.rel (Classical.byContradiction fun w₆ => w₅ (i₂.antisymm _ _ w₄ w₆))
· simp only [not_lt] at w₄
exact Lex.rel (lt_of_le_of_ne w₄ (w₅.imp Eq.symm))
protected theorem le_trans [LT α]
[Std.Irrefl (· < · : α α Prop)]
@[deprecated List.lt_of_le_of_lt (since := "2025-08-01")]
protected theorem lt_of_le_of_lt' [LT α]
[Std.Asymm (· < · : α α Prop)]
[Std.Antisymm (¬ · < · : α α Prop)]
[Trans (¬ · < · : α α Prop) (¬ · < ·) (¬ · < ·)]
{l₁ l₂ l₃ : List α} (h₁ : l₁ l₂) (h₂ : l₂ < l₃) : l₁ < l₃ :=
letI : LE α := .ofLT α
haveI : IsLinearOrder α := IsLinearOrder.of_lt
List.lt_of_le_of_lt h₁ h₂
protected theorem le_trans [LT α] [LE α] [IsLinearOrder α] [LawfulOrderLT α]
{l₁ l₂ l₃ : List α} (h₁ : l₁ l₂) (h₂ : l₂ l₃) : l₁ l₃ :=
fun h₃ => h₁ (List.lt_of_le_of_lt h₂ h₃)
@[deprecated List.le_trans (since := "2025-08-01")]
protected theorem le_trans' [LT α]
[Std.Asymm (· < · : α α Prop)]
[Std.Antisymm (¬ · < · : α α Prop)]
[Trans (¬ · < · : α α Prop) (¬ · < ·) (¬ · < ·)]
{l₁ l₂ l₃ : List α} (h₁ : l₁ l₂) (h₂ : l₂ l₃) : l₁ l₃ :=
fun h₃ => h₁ (List.lt_of_le_of_lt h₂ h₃)
letI := LE.ofLT α
haveI : IsLinearOrder α := IsLinearOrder.of_lt
List.le_trans h₁ h₂
instance [LT α]
[Std.Irrefl (· < · : α α Prop)]
[Std.Asymm (· < · : α α Prop)]
[Std.Antisymm (¬ · < · : α α Prop)]
[Trans (¬ · < · : α α Prop) (¬ · < ·) (¬ · < ·)] :
instance [LT α] [LE α] [IsLinearOrder α] [LawfulOrderLT α] :
Trans (· · : List α List α Prop) (· ·) (· ·) where
trans h₁ h₂ := List.le_trans h₁ h₂
@@ -248,14 +264,21 @@ theorem not_lex_total {r : αα → Prop}
obtain (_ | _) := not_lex_total h l₁ l₂ <;> contradiction
protected theorem le_total [LT α]
[i : Std.Total (¬ · < · : α α Prop)] (l₁ l₂ : List α) : l₁ l₂ l₂ l₁ :=
not_lex_total i.total l₂ l₁
[i : Std.Asymm (· < · : α α Prop)] (l₁ l₂ : List α) : l₁ l₂ l₂ l₁ :=
not_lex_total i.total_not.total l₂ l₁
instance [LT α]
[Std.Total (¬ · < · : α α Prop)] :
protected theorem le_total_of_asymm [LT α]
[i : Std.Asymm (· < · : α α Prop)] (l₁ l₂ : List α) : l₁ l₂ l₂ l₁ :=
List.le_total l₁ l₂
instance [LT α] [Std.Asymm (· < · : α α Prop)] :
Std.Total (· · : List α List α Prop) where
total := List.le_total
@[no_expose]
instance instIsLinearOrder [LT α] [LE α] [IsLinearOrder α] [LawfulOrderLT α] :
IsLinearOrder (List α) := IsLinearOrder.of_le
@[simp] protected theorem not_lt [LT α]
{l₁ l₂ : List α} : ¬ l₁ < l₂ l₂ l₁ := Iff.rfl
@@ -263,7 +286,7 @@ instance [LT α]
{l₁ l₂ : List α} : ¬ l₂ l₁ l₁ < l₂ := Classical.not_not
protected theorem le_of_lt [LT α]
[i : Std.Total (¬ · < · : α α Prop)]
[i : Std.Asymm (· < · : α α Prop)]
{l₁ l₂ : List α} (h : l₁ < l₂) : l₁ l₂ := by
obtain (h' | h') := List.le_total l₁ l₂
· exact h'
@@ -273,7 +296,7 @@ protected theorem le_of_lt [LT α]
protected theorem le_iff_lt_or_eq [LT α]
[Std.Irrefl (· < · : α α Prop)]
[Std.Antisymm (¬ · < · : α α Prop)]
[Std.Total (¬ · < · : α α Prop)]
[Std.Asymm (· < · : α α Prop)]
{l₁ l₂ : List α} : l₁ l₂ l₁ < l₂ l₁ = l₂ := by
constructor
· intro h
@@ -457,7 +480,6 @@ protected theorem lt_iff_exists [LT α] {l₁ l₂ : List α} :
simp
protected theorem le_iff_exists [LT α]
[Std.Irrefl (· < · : α α Prop)]
[Std.Asymm (· < · : α α Prop)]
[Std.Antisymm (¬ · < · : α α Prop)] {l₁ l₂ : List α} :
l₁ l₂
@@ -481,7 +503,6 @@ theorem append_left_lt [LT α] {l₁ l₂ l₃ : List α} (h : l₂ < l₃) :
| cons a l₁ ih => simp [cons_lt_cons_iff, ih]
theorem append_left_le [LT α]
[Std.Irrefl (· < · : α α Prop)]
[Std.Asymm (· < · : α α Prop)]
[Std.Antisymm (¬ · < · : α α Prop)]
{l₁ l₂ l₃ : List α} (h : l₂ l₃) :
@@ -515,10 +536,8 @@ protected theorem map_lt [LT α] [LT β]
simp [cons_lt_cons_iff, w, h]
protected theorem map_le [LT α] [LT β]
[Std.Irrefl (· < · : α α Prop)]
[Std.Asymm (· < · : α α Prop)]
[Std.Antisymm (¬ · < · : α α Prop)]
[Std.Irrefl (· < · : β β Prop)]
[Std.Asymm (· < · : β β Prop)]
[Std.Antisymm (¬ · < · : β β Prop)]
{l₁ l₂ : List α} {f : α β} (w : x y, x < y f x < f y) (h : l₁ l₂) :

View File

@@ -61,7 +61,7 @@ proof that the index is valid.
`List.mapIdxM` is a variant that does not provide the function with evidence that the index is
valid.
-/
@[inline] def mapFinIdxM [Monad m] (as : List α) (f : (i : Nat) α (h : i < as.length) m β) : m (List β) :=
@[inline, expose] def mapFinIdxM [Monad m] (as : List α) (f : (i : Nat) α (h : i < as.length) m β) : m (List β) :=
go as #[] (by simp)
where
/-- Auxiliary for `mapFinIdxM`:
@@ -78,7 +78,7 @@ found, returning the list of results.
`List.mapFinIdxM` is a variant that additionally provides the function with a proof that the index
is valid.
-/
@[inline] def mapIdxM [Monad m] (f : Nat α m β) (as : List α) : m (List β) := go as #[] where
@[inline, expose] def mapIdxM [Monad m] (f : Nat α m β) (as : List α) : m (List β) := go as #[] where
/-- Auxiliary for `mapIdxM`:
`mapIdxM.go [a₀, a₁, ...] acc = acc.toList ++ [f acc.size a₀, f (acc.size + 1) a₁, ...]` -/
@[specialize] go : List α Array β m (List β)
@@ -182,8 +182,7 @@ theorem mapFinIdx_eq_zipIdx_map {l : List α} {f : (i : Nat) → α → (h : i <
f i x (by rw [mk_mem_zipIdx_iff_getElem?, getElem?_eq_some_iff] at m; exact m.1) := by
apply ext_getElem <;> simp
@[deprecated mapFinIdx_eq_zipIdx_map (since := "2025-01-21")]
abbrev mapFinIdx_eq_zipWithIndex_map := @mapFinIdx_eq_zipIdx_map
@[simp]
theorem mapFinIdx_eq_nil_iff {l : List α} {f : (i : Nat) α (h : i < l.length) β} :
@@ -220,7 +219,7 @@ theorem mapFinIdx_eq_cons_iff {l : List α} {b : β} {f : (i : Nat) → α → (
cases l with
| nil => simp
| cons x l' =>
simp only [mapFinIdx_cons, cons.injEq,
simp only [mapFinIdx_cons, cons.injEq,
]
constructor
· rintro rfl, rfl
@@ -384,8 +383,7 @@ theorem mapIdx_eq_zipIdx_map {l : List α} {f : Nat → α → β} :
simp only [getElem?_mapIdx, Option.map, getElem?_map, getElem?_zipIdx]
split <;> simp
@[deprecated mapIdx_eq_zipIdx_map (since := "2025-01-21")]
abbrev mapIdx_eq_enum_map := @mapIdx_eq_zipIdx_map
@[simp, grind =]
theorem mapIdx_cons {l : List α} {a : α} :

View File

@@ -8,9 +8,14 @@ module
prelude
public import Init.Data.List.Lemmas
public import Init.Data.List.Pairwise
public import Init.Data.Order.Factories
public import Init.Data.Subtype.Order
import Init.Data.Order.Lemmas
public section
open Std
/-!
# Lemmas about `List.min?` and `List.max?.
-/
@@ -55,7 +60,7 @@ theorem min?_eq_head? {α : Type u} [Min α] {l : List α}
have hx : min x y = x := rel_of_pairwise_cons h mem_cons_self
rw [foldl_cons, ih _ (hx.symm h.sublist (by simp)), hx]
theorem min?_mem [Min α] (min_eq_or : a b : α, min a b = a min a b = b) :
theorem min?_mem [Min α] [MinEqOr α] :
{xs : List α} xs.min? = some a a xs := by
intro xs
match xs with
@@ -72,13 +77,10 @@ theorem min?_mem [Min α] (min_eq_or : ∀ a b : α, min a b = a min a b = b
have p := ind _ eq
cases p with
| inl p =>
cases min_eq_or x y with | _ q => simp [p, q]
cases MinEqOr.min_eq_or x y with | _ q => simp [p, q]
| inr p => simp [p, mem_cons]
-- See also `Init.Data.List.Nat.Basic` for specialisations of the next two results to `Nat`.
theorem le_min?_iff [Min α] [LE α]
(le_min_iff : a b c : α, a min b c a b a c) :
theorem le_min?_iff [Min α] [LE α] [LawfulOrderInf α] :
{xs : List α} xs.min? = some a {x}, x a b, b xs x b
| nil => by simp
| cons x xs => by
@@ -93,34 +95,64 @@ theorem le_min?_iff [Min α] [LE α]
simp at eq
simp [ih _ eq, le_min_iff, and_assoc]
-- This could be refactored by designing appropriate typeclasses to replace `le_refl`, `min_eq_or`,
-- and `le_min_iff`.
theorem min?_eq_some_iff [Min α] [LE α]
(le_refl : a : α, a a)
(min_eq_or : a b : α, min a b = a min a b = b)
(le_min_iff : a b c : α, a min b c a b a c) {xs : List α}
(anti : a b, a xs b xs a b b a a = b := by
exact fun a b _ _ => Std.Antisymm.antisymm a b) :
theorem min?_eq_some_iff [Min α] [LE α] {xs : List α} [IsLinearOrder α] [LawfulOrderMin α] :
xs.min? = some a a xs b, b xs a b := by
refine fun h => min?_mem min_eq_or h, (le_min?_iff le_min_iff h).1 (le_refl _), ?_
refine fun h => min?_mem h, (le_min?_iff h).1 (le_refl _), ?_
intro h₁, h₂
cases xs with
| nil => simp at h₁
| cons x xs =>
exact congrArg some <| anti _ _ (min?_mem min_eq_or rfl) h₁
((le_min?_iff le_min_iff (xs := x::xs) rfl).1 (le_refl _) _ h₁)
(h₂ _ (min?_mem min_eq_or (xs := x::xs) rfl))
rw [List.min?]
exact congrArg some <| le_antisymm
((le_min?_iff (xs := x :: xs) rfl).1 (le_refl _) _ h₁)
(h₂ _ (min?_mem (xs := x :: xs) rfl))
theorem min?_replicate [Min α] {n : Nat} {a : α} (w : min a a = a) :
private theorem min?_attach [Min α] [MinEqOr α] {xs : List α} :
xs.attach.min? = (xs.min?.pmap (fun m hm => m, min?_mem hm) (fun _ => id)) := by
cases xs with
| nil => simp
| cons x xs =>
simp only [min?, attach_cons, Option.some.injEq, Option.pmap_some]
rw [foldl_map]
simp only [Subtype.ext_iff]
rw [ foldl_attach (l := xs)]
apply Eq.trans (foldl_hom (f := Subtype.val) ?_).symm
· rfl
· intros; rfl
theorem min?_eq_min?_attach [Min α] [MinEqOr α] {xs : List α} :
xs.min? = (xs.attach.min?.map Subtype.val) := by
simp [min?_attach, Option.map_pmap]
theorem min?_eq_some_iff_subtype [Min α] [LE α] {xs : List α}
[MinEqOr α] [IsLinearOrder (Subtype (· xs))] [LawfulOrderMin (Subtype (· xs))] :
xs.min? = some a a xs b, b xs a b := by
have := fun a => min?_eq_some_iff (xs := xs.attach) (a := a)
rw [min?_eq_min?_attach]
simp [min?_eq_some_iff]
constructor
· rintro ha, h
exact ha, h
· rintro ha, h
exact ha, h
theorem min?_replicate [Min α] [Std.IdempotentOp (min : α α α)] {n : Nat} {a : α} :
(replicate n a).min? = if n = 0 then none else some a := by
induction n with
| zero => rfl
| succ n ih => cases n <;> simp_all [replicate_succ, min?_cons']
| succ n ih => cases n <;> simp_all [replicate_succ, min?_cons', Std.IdempotentOp.idempotent]
@[simp] theorem min?_replicate_of_pos [Min α] {n : Nat} {a : α} (w : min a a = a) (h : 0 < n) :
@[simp] theorem min?_replicate_of_pos [Min α] [MinEqOr α] {n : Nat} {a : α} (h : 0 < n) :
(replicate n a).min? = some a := by
simp [min?_replicate, Nat.ne_of_gt h, w]
simp [min?_replicate, Nat.ne_of_gt h]
/--
This lemma is also applicable given the following instances:
```
[LE α] [Min α] [IsLinearOrder α] [LawfulOrderMin α]
```
-/
theorem foldl_min [Min α] [Std.IdempotentOp (min : α α α)] [Std.Associative (min : α α α)]
{l : List α} {a : α} : l.foldl (init := a) min = min a (l.min?.getD a) := by
cases l <;> simp [min?, foldl_assoc, Std.IdempotentOp.idempotent]
@@ -144,54 +176,124 @@ theorem isSome_max?_of_mem {l : List α} [Max α] {a : α} (h : a ∈ l) :
l.max?.isSome := by
cases l <;> simp_all [max?_cons']
theorem max?_mem [Max α] (min_eq_or : a b : α, max a b = a max a b = b) :
{xs : List α} xs.max? = some a a xs
| nil => by simp
| cons x xs => by
rw [max?]; rintro
induction xs generalizing x with simp at *
| cons y xs ih =>
rcases ih (max x y) with h | h <;> simp [h]
simp [ or_assoc, min_eq_or x y]
-- See also `Init.Data.List.Nat.Basic` for specialisations of the next two results to `Nat`.
theorem max?_le_iff [Max α] [LE α]
(max_le_iff : a b c : α, max b c a b a c a) :
{xs : List α} xs.max? = some a {x}, a x b xs, b x
| nil => by simp
| cons x xs => by
rw [max?]; rintro y
induction xs generalizing x with
theorem max?_eq_head? {α : Type u} [Max α] {l : List α}
(h : l.Pairwise (fun a b => max a b = a)) : l.max? = l.head? := by
cases l with
| nil => rfl
| cons x l =>
rw [head?_cons, max?_cons', Option.some.injEq]
induction l generalizing x with
| nil => simp
| cons y xs ih => simp [ih, max_le_iff, and_assoc]
| cons y l ih =>
have hx : max x y = x := rel_of_pairwise_cons h mem_cons_self
rw [foldl_cons, ih _ (hx.symm h.sublist (by simp)), hx]
-- This could be refactored by designing appropriate typeclasses to replace `le_refl`, `max_eq_or`,
-- and `le_min_iff`.
theorem max?_eq_some_iff [Max α] [LE α] [anti : Std.Antisymm (· · : α α Prop)]
(le_refl : a : α, a a)
(max_eq_or : a b : α, max a b = a max a b = b)
(max_le_iff : a b c : α, max b c a b a c a) {xs : List α} :
xs.max? = some a a xs b xs, b a := by
refine fun h => max?_mem max_eq_or h, (max?_le_iff max_le_iff h).1 (le_refl _), ?_
theorem max?_mem [Max α] [MaxEqOr α] :
{xs : List α} xs.max? = some a a xs := by
intro xs
match xs with
| nil => simp
| x :: xs =>
simp only [max?_cons', Option.some.injEq, mem_cons]
intro eq
induction xs generalizing x with
| nil =>
simp at eq
simp [eq]
| cons y xs ind =>
simp at eq
have p := ind _ eq
cases p with
| inl p =>
cases MaxEqOr.max_eq_or x y with | _ q => simp [p, q]
| inr p => simp [p, mem_cons]
theorem max?_le_iff [Max α] [LE α] [LawfulOrderSup α] :
{xs : List α} xs.max? = some a {x}, a x b, b xs b x
| nil => by simp
| cons x xs => by
rw [max?]
intro eq y
simp only [Option.some.injEq] at eq
induction xs generalizing x with
| nil =>
simp at eq
simp [eq]
| cons z xs ih =>
simp at eq
simp [ih _ eq, max_le_iff, and_assoc]
theorem max?_eq_some_iff [Max α] [LE α] {xs : List α} [IsLinearOrder (α)]
[LawfulOrderMax α] : xs.max? = some a a xs b, b xs b a := by
refine fun h => max?_mem h, (max?_le_iff h).1 (le_refl _), ?_
intro h₁, h₂
cases xs with
| nil => simp at h₁
| cons x xs =>
exact congrArg some <| anti.1 _ _
(h₂ _ (max?_mem max_eq_or (xs := x::xs) rfl))
((max?_le_iff max_le_iff (xs := x::xs) rfl).1 (le_refl _) _ h₁)
rw [List.max?]
exact congrArg some <| le_antisymm
(h₂ _ (max?_mem (xs := x :: xs) rfl))
((max?_le_iff (xs := x :: xs) rfl).1 (le_refl _) _ h₁)
theorem max?_replicate [Max α] {n : Nat} {a : α} (w : max a a = a) :
private theorem max?_attach [Max α] [MaxEqOr α] {xs : List α} :
xs.attach.max? = (xs.max?.pmap (fun m hm => m, max?_mem hm) (fun _ => id)) := by
cases xs with
| nil => simp
| cons x xs =>
simp only [max?, attach_cons, Option.some.injEq, Option.pmap_some]
rw [foldl_map]
simp only [Subtype.ext_iff]
rw [ foldl_attach (l := xs)]
apply Eq.trans (foldl_hom (f := Subtype.val) ?_).symm
· rfl
· intros; rfl
theorem max?_eq_max?_attach [Max α] [MaxEqOr α] {xs : List α} :
xs.max? = (xs.attach.max?.map Subtype.val) := by
simp [max?_attach, Option.map_pmap]
theorem max?_eq_some_iff_subtype [Max α] [LE α] {xs : List α}
[MaxEqOr α] [IsLinearOrder (Subtype (· xs))]
[LawfulOrderMax (Subtype (· xs))] :
xs.max? = some a a xs b, b xs b a := by
have := fun a => max?_eq_some_iff (xs := xs.attach) (a := a)
rw [max?_eq_max?_attach]
simp [max?_eq_some_iff]
constructor
· rintro ha, h
exact ha, h
· rintro ha, h
exact ha, h
@[deprecated max?_eq_some_iff (since := "2025-08-01")]
theorem max?_eq_some_iff_legacy [Max α] [LE α] [anti : Std.Antisymm (· · : α α Prop)]
(le_refl : a : α, a a)
(max_eq_or : a b : α, max a b = a max a b = b)
(max_le_iff : a b c : α, max b c a b a c a) {xs : List α} :
xs.max? = some a a xs b xs, b a := by
haveI : MaxEqOr α := max_eq_or
haveI : LawfulOrderMax α := .of_max_le_iff (fun _ _ _ => max_le_iff _ _ _) max_eq_or
haveI : Refl (α := α) (· ·) := le_refl
haveI : IsLinearOrder α := .of_refl_of_antisymm_of_lawfulOrderMax
apply max?_eq_some_iff
theorem max?_replicate [Max α] [Std.IdempotentOp (max : α α α)] {n : Nat} {a : α} :
(replicate n a).max? = if n = 0 then none else some a := by
induction n with
| zero => rfl
| succ n ih => cases n <;> simp_all [replicate_succ, max?_cons']
| succ n ih => cases n <;> simp_all [replicate_succ, max?_cons', Std.IdempotentOp.idempotent]
@[simp] theorem max?_replicate_of_pos [Max α] {n : Nat} {a : α} (w : max a a = a) (h : 0 < n) :
@[simp] theorem max?_replicate_of_pos [Max α] [MaxEqOr α] {n : Nat} {a : α} (h : 0 < n) :
(replicate n a).max? = some a := by
simp [max?_replicate, Nat.ne_of_gt h, w]
simp [max?_replicate, Nat.ne_of_gt h]
/--
This lemma is also applicable given the following instances:
```
[LE α] [Min α] [IsLinearOrder α] [LawfulOrderMax α]
```
-/
theorem foldl_max [Max α] [Std.IdempotentOp (max : α α α)] [Std.Associative (max : α α α)]
{l : List α} {a : α} : l.foldl (init := a) max = max a (l.max?.getD a) := by
cases l <;> simp [max?, foldl_assoc, Std.IdempotentOp.idempotent]

View File

@@ -101,7 +101,7 @@ theorem mapM_eq_reverse_foldlM_cons [Monad m] [LawfulMonad m] {f : α → m β}
induction l with
| nil => simp
| cons a as ih =>
simp only [mapM'_cons, ih, bind_map_left, foldlM_cons,
simp only [mapM'_cons, ih, bind_map_left, foldlM_cons,
foldlM_cons_eq_append, _root_.map_bind, Functor.map_map, reverse_append,
reverse_cons, reverse_nil, nil_append, singleton_append]
simp [bind_pure_comp]
@@ -137,6 +137,43 @@ theorem filterMapM_loop_eq [Monad m] [LawfulMonad m] {f : α → m (Option β)}
rw [filterMapM_loop_eq, filterMapM]
simp
/-! ### zipWithM -/
/--
Applies the monadic action `f` to the corresponding elements of two lists, left-to-right, stopping
at the end of the shorter list. `zipWithM' f as bs` is equivalent to `mapM id (zipWith f as bs)`
for lawful `Monad` instances.
-/
@[expose]
def zipWithM' {m : Type u Type v} [Monad m] {α : Type w} {β : Type x} {γ : Type u} (f : α β m γ) : (xs : List α) (ys : List β) m (List γ)
| x::xs, y::ys => do
let z f x y
let zs zipWithM' f xs ys
pure (z :: zs)
| _, _ => pure []
@[simp, grind =] theorem zipWithM'_nil_left [Monad m] {f : α β m γ} : zipWithM' f [] l = pure (f := m) [] := by simp only [zipWithM']
@[simp, grind =] theorem zipWithM'_nil_right [Monad m] {f : α β m γ} : zipWithM' f l [] = pure (f := m) [] := by simp only [zipWithM']
@[simp, grind =] theorem zipWithM'_cons_cons [Monad m] {f : α β m γ} :
zipWithM' f (a :: as) (b :: bs) = do return ( f a b) :: ( zipWithM' f as bs) := by simp only [zipWithM']
@[grind =]
theorem zipWithM'_eq_zipWithM [Monad m] [LawfulMonad m] {f : α β m γ} {l : List α} {l' : List β} :
zipWithM' f l l' = zipWithM f l l' := by simp [zipWithM, go l l' #[]] where
go l l' acc : zipWithM.loop f l l' acc = return acc.toList ++ ( zipWithM' f l l') := by
fun_induction zipWithM.loop <;> simp [zipWithM', *]
@[simp, grind =] theorem zipWithM_nil_left [Monad m] {f : α β m γ} : zipWithM f [] l = pure (f := m) [] := rfl
@[simp, grind =] theorem zipWithM_nil_right [Monad m] {f : α β m γ} : zipWithM f l [] = pure (f := m) [] := by simp only [zipWithM, zipWithM.loop]
@[simp, grind =] theorem zipWithM_cons_cons [Monad m] [LawfulMonad m] {f : α β m γ} :
zipWithM f (a :: as) (b :: bs) = do return ( f a b) :: ( zipWithM f as bs) := by
simp [ zipWithM'_eq_zipWithM]
@[simp, grind =]
theorem zipWithM'_eq_mapM_id_zipWith {m : Type v Type w} [Monad m] [LawfulMonad m] {f : α β m γ} {as : List α} {bs : List β} :
zipWithM' f as bs = mapM id (zipWith f as bs) := by
fun_induction zipWithM' <;> simp [zipWith, *]
/-! ### flatMapM -/
@[simp, grind =] theorem flatMapM_nil [Monad m] {f : α m (List β)} : [].flatMapM f = pure [] := rfl
@@ -224,13 +261,7 @@ theorem foldrM_filter [Monad m] [LawfulMonad m] {p : α → Bool} {g : α → β
/-! ### forM -/
@[deprecated forM_nil (since := "2025-01-31")]
theorem forM_nil' [Monad m] : ([] : List α).forM f = (pure .unit : m PUnit) := rfl
@[deprecated forM_cons (since := "2025-01-31")]
theorem forM_cons' [Monad m] :
(a::as).forM f = (f a >>= fun _ => as.forM f : m PUnit) :=
List.forM_cons
@[simp, grind =] theorem forM_append [Monad m] [LawfulMonad m] {l₁ l₂ : List α} {f : α m PUnit} :
forM (l₁ ++ l₂) f = (do forM l₁ f; forM l₂ f) := by

View File

@@ -10,6 +10,7 @@ public import Init.Data.List.Count
public import Init.Data.List.Find
public import Init.Data.List.MinMax
public import Init.Data.Nat.Lemmas
import Init.Data.Nat.Order
public section
@@ -210,12 +211,10 @@ theorem mem_eraseIdx_iff_getElem? {x : α} {l} {k} : x ∈ eraseIdx l k ↔ ∃
/-! ### min? -/
-- A specialization of `min?_eq_some_iff` to Nat.
@[deprecated min?_eq_some_iff (since := "2025-08-08")]
theorem min?_eq_some_iff' {xs : List Nat} :
xs.min? = some a (a xs b xs, a b) :=
min?_eq_some_iff
(le_refl := Nat.le_refl)
(min_eq_or := fun _ _ => Nat.min_def .. by split <;> simp)
(le_min_iff := fun _ _ _ => Nat.le_min)
xs.min? = some a (a xs b xs, a b) := by
exact min?_eq_some_iff
theorem min?_get_le_of_mem {l : List Nat} {a : Nat} (h : a l) :
l.min?.get (isSome_min?_of_mem h) a := by
@@ -237,12 +236,10 @@ theorem min?_getD_le_of_mem {l : List Nat} {a k : Nat} (h : a ∈ l) : l.min?.ge
/-! ### max? -/
-- A specialization of `max?_eq_some_iff` to Nat.
@[deprecated max?_eq_some_iff (since := "2025-08-08")]
theorem max?_eq_some_iff' {xs : List Nat} :
xs.max? = some a (a xs b xs, b a) :=
max?_eq_some_iff
(le_refl := Nat.le_refl)
(max_eq_or := fun _ _ => Nat.max_def .. by split <;> simp)
(max_le_iff := fun _ _ _ => Nat.max_le)
theorem le_max?_get_of_mem {l : List Nat} {a : Nat} (h : a l) :
a l.max?.get (isSome_max?_of_mem h) := by

View File

@@ -61,10 +61,10 @@ theorem pairwise_iff_getElem {l : List α} : Pairwise R l ↔
(i j : Nat) (_hi : i < l.length) (_hj : j < l.length) (_hij : i < j), R l[i] l[j] := by
rw [pairwise_iff_forall_sublist]
constructor <;> intro h
· intros i j hi hj h'
· intro i j hi hj h'
apply h
simpa [h'] using map_getElem_sublist (is := [i, hi, j, hj])
· intros a b h'
· intro a b h'
have is, h', hij := sublist_eq_map_getElem h'
rcases is with | a', | b', <;> simp at h'
rcases h' with rfl, rfl

View File

@@ -58,7 +58,7 @@ theorem pairwise_lt_range' {s n} (step := 1) (pos : 0 < step := by simp) :
| s, n + 1, step, pos => by
simp only [range'_succ, pairwise_cons]
constructor
· intros n m
· intro n m
rw [mem_range'] at m
omega
· exact pairwise_lt_range' (s := s + step) step pos
@@ -70,7 +70,7 @@ theorem pairwise_le_range' {s n} (step := 1) :
| s, n + 1, step => by
simp only [range'_succ, pairwise_cons]
constructor
· intros n m
· intro n m
rw [mem_range'] at m
omega
· exact pairwise_le_range' (s := s + step) step
@@ -90,31 +90,27 @@ theorem map_sub_range' {a s : Nat} (h : a ≤ s) (n : Nat) :
rintro rfl
omega
@[deprecated range'_eq_singleton_iff (since := "2025-01-29")]
abbrev range'_eq_singleton := @range'_eq_singleton_iff
theorem range'_eq_append_iff : range' s n = xs ++ ys k, k n xs = range' s k ys = range' (s + k) (n - k) := by
theorem range'_eq_append_iff : range' s n step = xs ++ ys k, k n xs = range' s k step ys = range' (s + k * step) (n - k) step := by
induction n generalizing s xs ys with
| zero => simp
| succ n ih =>
simp only [range'_succ]
rw [cons_eq_append_iff]
have add_mul' (k n m : Nat) : (n + m) * k = m * k + n * k := by rw [Nat.add_mul]; omega
constructor
· rintro (rfl, rfl | _, rfl, h)
· exact 0, by simp [range'_succ]
· simp only [ih] at h
obtain k, h, rfl, rfl := h
refine k + 1, ?_
simp_all [range'_succ]
omega
simp_all [range'_succ, Nat.add_assoc]
· rintro k, h, rfl, rfl
cases k with
| zero => simp [range'_succ]
| succ k =>
simp only [range'_succ, reduceCtorEq, false_and, cons.injEq, true_and, ih, range'_inj, exists_eq_left', or_true, and_true, false_or]
simp only [range'_succ, reduceCtorEq, false_and, cons.injEq, true_and, ih, exists_eq_left', false_or]
refine k, ?_
simp_all
omega
simp_all [Nat.add_assoc]
@[simp] theorem find?_range'_eq_some {s n : Nat} {i : Nat} {p : Nat Bool} :
(range' s n).find? p = some i p i i range' s n j, s j j < i !p j := by
@@ -181,6 +177,46 @@ theorem count_range_1' {a s n} :
specialize h (a - s)
omega
@[simp, grind =]
theorem sum_range' : (range' start n step).sum = n * start + n * (n - 1) * step / 2 := by
induction n generalizing start with
| zero => simp
| succ n ih =>
simp_all only [List.range'_succ, List.sum_cons, Nat.mul_add, Nat.add_assoc,
Nat.add_mul, Nat.one_mul, Nat.add_one_sub_one]
have : n * step + n * (n - 1) * step / 2 = (n * n * step + n * step) / 2 := by
apply Nat.eq_div_of_mul_eq_left (by omega)
rw [Nat.add_mul, Nat.div_mul_cancel]
· calc n * step * 2 + n * (n - 1) * step
_ = n * step * 2 + n * step * (n - 1) := by simp [Nat.mul_comm, Nat.mul_assoc]
_ = n * step + n * step * n := by cases n <;> simp [Nat.mul_succ, Nat.add_assoc, Nat.add_comm]
_ = n * n * step + n * step := by simp [Nat.mul_comm, Nat.add_comm, Nat.mul_left_comm]
· have : 2 n 2 (n - 1) := by omega
apply Nat.dvd_mul_right_of_dvd
apply Nat.dvd_mul.mpr
cases this with
| inl h => exists 2, 1; omega
| inr h => exists 1, 2; omega
omega
@[simp, grind =]
theorem drop_range' : (List.range' start n step).drop k = List.range' (start + k * step) (n - k) step := by
induction k generalizing start n with
| zero => simp
| succ => cases n <;> simp [*, List.range'_succ, Nat.add_mul, Nat.add_assoc, Nat.add_right_comm]
@[simp, grind =]
theorem take_range'_of_length_le (h : n k) : (List.range' start n step).take k = List.range' start n step := by
induction n generalizing start k with
| zero => simp
| succ n ih => cases k <;> simp_all [List.range'_succ]
@[simp, grind =]
theorem take_range'_of_length_ge (h : n k) : (List.range' start n step).take k = List.range' start k step := by
induction k generalizing start n with
| zero => simp
| succ k ih => cases n <;> simp_all [List.range'_succ]
/-! ### range -/
theorem reverse_range' : {s n : Nat}, reverse (range' s n) = map (s + n - 1 - ·) (range n)
@@ -230,152 +266,6 @@ theorem count_range {a n} :
rw [range_eq_range', count_range_1']
simp
/-! ### iota -/
section
set_option linter.deprecated false
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20")]
theorem iota_eq_reverse_range' : n : Nat, iota n = reverse (range' 1 n)
| 0 => rfl
| n + 1 => by simp [iota, range'_concat, iota_eq_reverse_range' n, reverse_append, Nat.add_comm]
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20"), simp]
theorem length_iota (n : Nat) : length (iota n) = n := by simp [iota_eq_reverse_range']
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20"), simp]
theorem iota_eq_nil {n : Nat} : iota n = [] n = 0 := by
cases n <;> simp
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20")]
theorem iota_ne_nil {n : Nat} : iota n [] n 0 := by
cases n <;> simp
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20"), simp]
theorem mem_iota {m n : Nat} : m iota n 0 < m m n := by
simp [iota_eq_reverse_range', Nat.add_comm, Nat.lt_succ]
omega
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20"), simp]
theorem iota_inj : iota n = iota n' n = n' := by
constructor
· intro h
have h' := congrArg List.length h
simp at h'
exact h'
· rintro rfl
simp
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20")]
theorem iota_eq_cons_iff : iota n = a :: xs n = a 0 < n xs = iota (n - 1) := by
simp [iota_eq_reverse_range']
simp [range'_eq_append_iff, reverse_eq_iff]
constructor
· rintro k, h, rfl, h'
rw [eq_comm, range'_eq_singleton] at h'
simp only [reverse_inj, range'_inj, or_true, and_true]
omega
· rintro rfl, h, rfl
refine n - 1, by simp, rfl, ?_
rw [eq_comm, range'_eq_singleton]
omega
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20")]
theorem iota_eq_append_iff : iota n = xs ++ ys k, k n xs = (range' (k + 1) (n - k)).reverse ys = iota k := by
simp only [iota_eq_reverse_range']
rw [reverse_eq_append_iff]
rw [range'_eq_append_iff]
simp only [reverse_eq_iff]
constructor
· rintro k, h, rfl, rfl
simp; omega
· rintro k, h, rfl, rfl
exact k, by simp; omega
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20")]
theorem pairwise_gt_iota (n : Nat) : Pairwise (· > ·) (iota n) := by
simpa only [iota_eq_reverse_range', pairwise_reverse] using pairwise_lt_range'
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20")]
theorem nodup_iota (n : Nat) : Nodup (iota n) :=
(pairwise_gt_iota n).imp Nat.ne_of_gt
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20"), simp]
theorem head?_iota (n : Nat) : (iota n).head? = if n = 0 then none else some n := by
cases n <;> simp
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20"), simp]
theorem head_iota (n : Nat) (h) : (iota n).head h = n := by
cases n with
| zero => simp at h
| succ n => simp
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20"), simp]
theorem tail_iota (n : Nat) : (iota n).tail = iota (n - 1) := by
cases n <;> simp
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20"), simp]
theorem reverse_iota : reverse (iota n) = range' 1 n := by
induction n with
| zero => simp
| succ n ih =>
rw [iota_succ, reverse_cons, ih, range'_1_concat, Nat.add_comm]
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20"), simp]
theorem getLast?_iota (n : Nat) : (iota n).getLast? = if n = 0 then none else some 1 := by
rw [getLast?_eq_head?_reverse]
simp [head?_range']
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20"), simp]
theorem getLast_iota (n : Nat) (h) : (iota n).getLast h = 1 := by
rw [getLast_eq_head_reverse]
simp
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20")]
theorem find?_iota_eq_none {n : Nat} {p : Nat Bool} :
(iota n).find? p = none i, 0 < i i n !p i := by
simp
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20"), simp]
theorem find?_iota_eq_some {n : Nat} {i : Nat} {p : Nat Bool} :
(iota n).find? p = some i p i i iota n j, i < j j n !p j := by
rw [find?_eq_some_iff_append]
simp only [iota_eq_reverse_range', reverse_eq_append_iff, reverse_cons, append_assoc, cons_append,
nil_append, Bool.not_eq_eq_eq_not, Bool.not_true, exists_and_right, mem_reverse, mem_range'_1,
and_congr_right_iff]
intro h
constructor
· rintro as, xs, h, h'
constructor
· replace h : i range' 1 n := by
rw [h]
exact mem_append_cons_self
simpa using h
· rw [range'_eq_append_iff] at h
simp [reverse_eq_iff] at h
obtain k, h₁, rfl, h₂ := h
rw [eq_comm, range'_eq_cons_iff, reverse_eq_iff] at h₂
obtain rfl, -, rfl := h₂
intro j j₁ j₂
apply h'
simp; omega
· rintro i₁, i₂, h
refine (range' (i+1) (n-i)).reverse, (range' 1 (i-1)).reverse, ?_, ?_
· simp [ range'_succ]
rw [range'_eq_append_iff]
refine i-1, ?_
constructor
· omega
· simp
omega
· simp
intros a a₁ a₂
apply h
· omega
· omega
end
/-! ### zipIdx -/
@[simp, grind =]
@@ -504,245 +394,10 @@ theorem zipIdx_eq_append_iff {l : List α} {k : Nat} :
simp only [length_range'] at h
obtain rfl := h
refine ws, xs, rfl, ?_
simp only [zipIdx_eq_zip_range', length_append, true_and]
congr
omega
simp [zipIdx_eq_zip_range', length_append]
· rintro l₁', l₂', rfl, rfl, rfl
simp only [zipIdx_eq_zip_range']
refine l₁', l₂', range' k l₁'.length, range' (k + l₁'.length) l₂'.length, ?_
simp
/-! ### enumFrom -/
section
set_option linter.deprecated false
@[deprecated zipIdx_singleton (since := "2025-01-21"), simp]
theorem enumFrom_singleton (x : α) (n : Nat) : enumFrom n [x] = [(n, x)] :=
rfl
@[deprecated head?_zipIdx (since := "2025-01-21"), simp]
theorem head?_enumFrom (n : Nat) (l : List α) :
(enumFrom n l).head? = l.head?.map fun a => (n, a) := by
simp [head?_eq_getElem?]
@[deprecated getLast?_zipIdx (since := "2025-01-21"), simp]
theorem getLast?_enumFrom (n : Nat) (l : List α) :
(enumFrom n l).getLast? = l.getLast?.map fun a => (n + l.length - 1, a) := by
simp [getLast?_eq_getElem?]
cases l <;> simp
@[deprecated mk_add_mem_zipIdx_iff_getElem? (since := "2025-01-21")]
theorem mk_add_mem_enumFrom_iff_getElem? {n i : Nat} {x : α} {l : List α} :
(n + i, x) enumFrom n l l[i]? = some x := by
simp [mem_iff_get?]
@[deprecated mk_mem_zipIdx_iff_le_and_getElem?_sub (since := "2025-01-21")]
theorem mk_mem_enumFrom_iff_le_and_getElem?_sub {n i : Nat} {x : α} {l : List α} :
(i, x) enumFrom n l n i l[i - n]? = some x := by
if h : n i then
rcases Nat.exists_eq_add_of_le h with i, rfl
simp [mk_add_mem_enumFrom_iff_getElem?, Nat.add_sub_cancel_left]
else
have : k, n + k i := by rintro k rfl; simp at h
simp [h, mem_iff_get?, this]
@[deprecated le_snd_of_mem_zipIdx (since := "2025-01-21")]
theorem le_fst_of_mem_enumFrom {x : Nat × α} {n : Nat} {l : List α} (h : x enumFrom n l) :
n x.1 :=
(mk_mem_enumFrom_iff_le_and_getElem?_sub.1 h).1
@[deprecated snd_lt_add_of_mem_zipIdx (since := "2025-01-21")]
theorem fst_lt_add_of_mem_enumFrom {x : Nat × α} {n : Nat} {l : List α} (h : x enumFrom n l) :
x.1 < n + length l := by
rcases mem_iff_get.1 h with i, rfl
simpa using i.isLt
@[deprecated map_zipIdx (since := "2025-01-21")]
theorem map_enumFrom (f : α β) (n : Nat) (l : List α) :
map (Prod.map id f) (enumFrom n l) = enumFrom n (map f l) := by
induction l generalizing n <;> simp_all
@[deprecated fst_mem_of_mem_zipIdx (since := "2025-01-21")]
theorem snd_mem_of_mem_enumFrom {x : Nat × α} {n : Nat} {l : List α} (h : x enumFrom n l) : x.2 l :=
enumFrom_map_snd n l mem_map_of_mem h
@[deprecated fst_eq_of_mem_zipIdx (since := "2025-01-21")]
theorem snd_eq_of_mem_enumFrom {x : Nat × α} {n : Nat} {l : List α} (h : x enumFrom n l) :
x.2 = l[x.1 - n]'(by have := le_fst_of_mem_enumFrom h; have := fst_lt_add_of_mem_enumFrom h; omega) := by
induction l generalizing n with
| nil => cases h
| cons hd tl ih =>
cases h with
| head _ => simp
| tail h m =>
specialize ih m
have : x.1 - n = x.1 - (n + 1) + 1 := by
have := le_fst_of_mem_enumFrom m
omega
simp [this, ih]
@[deprecated mem_zipIdx (since := "2025-01-21")]
theorem mem_enumFrom {x : α} {i j : Nat} {xs : List α} (h : (i, x) xs.enumFrom j) :
j i i < j + xs.length
x = xs[i - j]'(by have := le_fst_of_mem_enumFrom h; have := fst_lt_add_of_mem_enumFrom h; omega) :=
le_fst_of_mem_enumFrom h, fst_lt_add_of_mem_enumFrom h, snd_eq_of_mem_enumFrom h
@[deprecated zipIdx_map (since := "2025-01-21")]
theorem enumFrom_map (n : Nat) (l : List α) (f : α β) :
enumFrom n (l.map f) = (enumFrom n l).map (Prod.map id f) := by
induction l with
| nil => rfl
| cons hd tl IH =>
rw [map_cons, enumFrom_cons', enumFrom_cons', map_cons, map_map, IH, map_map]
rfl
@[deprecated zipIdx_append (since := "2025-01-21")]
theorem enumFrom_append (xs ys : List α) (n : Nat) :
enumFrom n (xs ++ ys) = enumFrom n xs ++ enumFrom (n + xs.length) ys := by
induction xs generalizing ys n with
| nil => simp
| cons x xs IH =>
rw [cons_append, enumFrom_cons, IH, cons_append, enumFrom_cons, length, Nat.add_right_comm,
Nat.add_assoc]
@[deprecated zipIdx_eq_cons_iff (since := "2025-01-21")]
theorem enumFrom_eq_cons_iff {l : List α} {n : Nat} :
l.enumFrom n = x :: l' a as, l = a :: as x = (n, a) l' = enumFrom (n + 1) as := by
rw [enumFrom_eq_zip_range', zip_eq_cons_iff]
constructor
· rintro l₁, l₂, h, rfl, rfl
rw [range'_eq_cons_iff] at h
obtain rfl, -, rfl := h
exact x.2, l₂, by simp [enumFrom_eq_zip_range']
· rintro a, as, rfl, rfl, rfl
refine range' (n+1) as.length, as, ?_
simp [enumFrom_eq_zip_range', range'_succ]
@[deprecated zipIdx_eq_append_iff (since := "2025-01-21")]
theorem enumFrom_eq_append_iff {l : List α} {n : Nat} :
l.enumFrom n = l₁ ++ l₂
l₁' l₂', l = l₁' ++ l₂' l₁ = l₁'.enumFrom n l₂ = l₂'.enumFrom (n + l₁'.length) := by
rw [enumFrom_eq_zip_range', zip_eq_append_iff]
constructor
· rintro ws, xs, ys, zs, h, h', rfl, rfl, rfl
rw [range'_eq_append_iff] at h'
obtain k, -, rfl, rfl := h'
simp only [length_range'] at h
obtain rfl := h
refine ys, zs, rfl, ?_
simp only [enumFrom_eq_zip_range', length_append, true_and]
congr
omega
· rintro l₁', l₂', rfl, rfl, rfl
simp only [enumFrom_eq_zip_range']
refine range' n l₁'.length, range' (n + l₁'.length) l₂'.length, l₁', l₂', ?_
simp
end
/-! ### enum -/
section
set_option linter.deprecated false
@[deprecated zipIdx_eq_nil_iff (since := "2025-01-21"), simp]
theorem enum_eq_nil_iff {l : List α} : List.enum l = [] l = [] := enumFrom_eq_nil
@[deprecated zipIdx_singleton (since := "2025-01-21"), simp]
theorem enum_singleton (x : α) : enum [x] = [(0, x)] := rfl
@[deprecated length_zipIdx (since := "2025-01-21"), simp]
theorem enum_length : (enum l).length = l.length :=
enumFrom_length
@[deprecated getElem?_zipIdx (since := "2025-01-21"), simp]
theorem getElem?_enum (l : List α) (i : Nat) : (enum l)[i]? = l[i]?.map fun a => (i, a) := by
rw [enum, getElem?_enumFrom, Nat.zero_add]
@[deprecated getElem_zipIdx (since := "2025-01-21"), simp]
theorem getElem_enum (l : List α) (i : Nat) (h : i < l.enum.length) :
l.enum[i] = (i, l[i]'(by simpa [enum_length] using h)) := by
simp [enum]
@[deprecated head?_zipIdx (since := "2025-01-21"), simp] theorem head?_enum (l : List α) :
l.enum.head? = l.head?.map fun a => (0, a) := by
simp [head?_eq_getElem?]
@[deprecated getLast?_zipIdx (since := "2025-01-21"), simp]
theorem getLast?_enum (l : List α) :
l.enum.getLast? = l.getLast?.map fun a => (l.length - 1, a) := by
simp [getLast?_eq_getElem?]
@[deprecated tail_zipIdx (since := "2025-01-21"), simp]
theorem tail_enum (l : List α) : (enum l).tail = enumFrom 1 l.tail := by
simp [enum]
@[deprecated mk_mem_zipIdx_iff_getElem? (since := "2025-01-21")]
theorem mk_mem_enum_iff_getElem? {i : Nat} {x : α} {l : List α} : (i, x) enum l l[i]? = some x := by
simp [enum, mk_mem_enumFrom_iff_le_and_getElem?_sub]
@[deprecated mem_zipIdx_iff_getElem? (since := "2025-01-21")]
theorem mem_enum_iff_getElem? {x : Nat × α} {l : List α} : x enum l l[x.1]? = some x.2 :=
mk_mem_enum_iff_getElem?
@[deprecated snd_lt_of_mem_zipIdx (since := "2025-01-21")]
theorem fst_lt_of_mem_enum {x : Nat × α} {l : List α} (h : x enum l) : x.1 < length l := by
simpa using fst_lt_add_of_mem_enumFrom h
@[deprecated fst_mem_of_mem_zipIdx (since := "2025-01-21")]
theorem snd_mem_of_mem_enum {x : Nat × α} {l : List α} (h : x enum l) : x.2 l :=
snd_mem_of_mem_enumFrom h
@[deprecated fst_eq_of_mem_zipIdx (since := "2025-01-21")]
theorem snd_eq_of_mem_enum {x : Nat × α} {l : List α} (h : x enum l) :
x.2 = l[x.1]'(fst_lt_of_mem_enum h) :=
snd_eq_of_mem_enumFrom h
@[deprecated mem_zipIdx (since := "2025-01-21")]
theorem mem_enum {x : α} {i : Nat} {xs : List α} (h : (i, x) xs.enum) :
i < xs.length x = xs[i]'(fst_lt_of_mem_enum h) :=
by simpa using mem_enumFrom h
@[deprecated map_zipIdx (since := "2025-01-21")]
theorem map_enum (f : α β) (l : List α) : map (Prod.map id f) (enum l) = enum (map f l) :=
map_enumFrom f 0 l
@[deprecated zipIdx_map_snd (since := "2025-01-21"), simp]
theorem enum_map_fst (l : List α) : map Prod.fst (enum l) = range l.length := by
simp only [enum, enumFrom_map_fst, range_eq_range']
@[deprecated zipIdx_map_fst (since := "2025-01-21"), simp]
theorem enum_map_snd (l : List α) : map Prod.snd (enum l) = l :=
enumFrom_map_snd _ _
@[deprecated zipIdx_map (since := "2025-01-21")]
theorem enum_map (l : List α) (f : α β) : (l.map f).enum = l.enum.map (Prod.map id f) :=
enumFrom_map _ _ _
@[deprecated zipIdx_append (since := "2025-01-21")]
theorem enum_append (xs ys : List α) : enum (xs ++ ys) = enum xs ++ enumFrom xs.length ys := by
simp [enum, enumFrom_append]
@[deprecated zipIdx_eq_zip_range' (since := "2025-01-21")]
theorem enum_eq_zip_range (l : List α) : l.enum = (range l.length).zip l :=
zip_of_prod (enum_map_fst _) (enum_map_snd _)
@[deprecated unzip_zipIdx_eq_prod (since := "2025-01-21"), simp]
theorem unzip_enum_eq_prod (l : List α) : l.enum.unzip = (range l.length, l) := by
simp only [enum_eq_zip_range, unzip_zip, length_range]
@[deprecated zipIdx_eq_cons_iff (since := "2025-01-21")]
theorem enum_eq_cons_iff {l : List α} :
l.enum = x :: l' a as, l = a :: as x = (0, a) l' = enumFrom 1 as := by
rw [enum, enumFrom_eq_cons_iff]
@[deprecated zipIdx_eq_append_iff (since := "2025-01-21")]
theorem enum_eq_append_iff {l : List α} :
l.enum = l₁ ++ l₂
l₁' l₂', l = l₁' ++ l₂' l₁ = l₁'.enum l₂ = l₂'.enumFrom l₁'.length := by
simp [enum, enumFrom_eq_append_iff]
end
end List

View File

@@ -61,8 +61,8 @@ theorem getElem?_take_eq_none {l : List α} {i j : Nat} (h : i ≤ j) :
@[grind =] theorem getElem?_take {l : List α} {i j : Nat} :
(l.take i)[j]? = if j < i then l[j]? else none := by
split
· next h => exact getElem?_take_of_lt h
· next h => exact getElem?_take_eq_none (Nat.le_of_not_lt h)
next h => exact getElem?_take_of_lt h
next h => exact getElem?_take_eq_none (Nat.le_of_not_lt h)
theorem head?_take {l : List α} {i : Nat} :
(l.take i).head? = if i = 0 then none else l.head? := by
@@ -114,7 +114,7 @@ theorem take_set_of_le {a : α} {i j : Nat} {l : List α} (h : j ≤ i) :
List.ext_getElem? fun i => by
rw [getElem?_take, getElem?_take]
split
· next h' => rw [getElem?_set_ne (by omega)]
next h' => rw [getElem?_set_ne (by omega)]
· rfl
@[deprecated take_set_of_le (since := "2025-02-04")]
@@ -384,7 +384,7 @@ theorem take_reverse {α} {xs : List α} {i : Nat} :
by_cases h : i xs.length
· induction xs generalizing i <;>
simp only [reverse_cons, drop, reverse_nil, Nat.zero_sub, length, take_nil]
next hd tl xs_ih =>
rename_i hd tl xs_ih
cases Nat.lt_or_eq_of_le h with
| inl h' =>
have h' := Nat.le_of_succ_le_succ h'

View File

@@ -213,7 +213,7 @@ theorem pairwise_append_comm {R : αα → Prop} (s : ∀ {x y}, R x y →
@[grind] theorem Pairwise.take {l : List α} {i : Nat} (h : List.Pairwise R l) : List.Pairwise R (l.take i) :=
h.sublist (take_sublist _ _)
@[grind =]
-- This theorem is not annotated with `grind` because it leads to a loop of instantiations with `Pairwise.sublist`.
theorem pairwise_iff_forall_sublist : l.Pairwise R ( {a b}, [a,b] <+ l R a b) := by
induction l with
| nil => simp
@@ -232,6 +232,10 @@ theorem pairwise_iff_forall_sublist : l.Pairwise R ↔ (∀ {a b}, [a,b] <+ l
intro a b hab
apply h; exact hab.cons _
theorem pairwise_of_forall_sublist (g : {a b}, [a,b] <+ l R a b) : l.Pairwise R := pairwise_iff_forall_sublist.mpr g
theorem Pairwise.forall_sublist (h : l.Pairwise R) : {a b}, [a,b] <+ l R a b := pairwise_iff_forall_sublist.mp h
theorem Pairwise.rel_of_mem_take_of_mem_drop
{l : List α} (h : l.Pairwise R) (hx : x l.take i) (hy : y l.drop i) : R x y := by
apply pairwise_iff_forall_sublist.mp h

View File

@@ -29,34 +29,31 @@ open Nat
/-! ### range' -/
theorem range'_succ {s n step} : range' s (n + 1) step = s :: range' (s + step) n step := by
simp [range']
@[simp] theorem length_range' {s step} : {n : Nat}, length (range' s n step) = n
@[simp, grind =] theorem length_range' {s step} : {n : Nat}, length (range' s n step) = n
| 0 => rfl
| _ + 1 => congrArg succ length_range'
@[simp] theorem range'_eq_nil_iff : range' s n step = [] n = 0 := by
@[simp, grind =] theorem range'_eq_nil_iff : range' s n step = [] n = 0 := by
rw [ length_eq_zero_iff, length_range']
@[deprecated range'_eq_nil_iff (since := "2025-01-29")] abbrev range'_eq_nil := @range'_eq_nil_iff
theorem range'_ne_nil_iff (s : Nat) {n step : Nat} : range' s n step [] n 0 := by
cases n <;> simp
@[deprecated range'_ne_nil_iff (since := "2025-01-29")] abbrev range'_ne_nil := @range'_ne_nil_iff
theorem range'_eq_cons_iff : range' s n step = a :: xs s = a 0 < n xs = range' (a + step) (n - 1) step := by
induction n generalizing s with
| zero => simp
| succ n ih =>
simp only [range'_succ]
simp only [cons.injEq, and_congr_right_iff]
rintro rfl
simp [eq_comm]
@[simp] theorem range'_zero : range' s 0 step = [] := by
simp
@[simp] theorem range'_one {s step : Nat} : range' s 1 step = [s] := rfl
@[simp] theorem tail_range' : (range' s n step).tail = range' (s + step) (n - 1) step := by
@[simp, grind =] theorem tail_range' : (range' s n step).tail = range' (s + step) (n - 1) step := by
cases n with
| zero => simp
| succ n => simp [range'_succ]
@[simp] theorem range'_inj : range' s n = range' s' n' n = n' (n = 0 s = s') := by
@[simp, grind =] theorem range'_inj : range' s n = range' s' n' n = n' (n = 0 s = s') := by
constructor
· intro h
have h' := congrArg List.length h
@@ -85,14 +82,14 @@ theorem getElem?_range' {s step} :
exact (getElem?_range' (s := s + step) (by exact succ_lt_succ_iff.mp h)).trans <| by
simp [Nat.mul_succ, Nat.add_assoc, Nat.add_comm]
@[simp] theorem getElem_range' {n m step} {i} (H : i < (range' n m step).length) :
@[simp, grind =] theorem getElem_range' {n m step} {i} (H : i < (range' n m step).length) :
(range' n m step)[i] = n + step * i :=
(getElem?_eq_some_iff.1 <| getElem?_range' (by simpa using H)).2
theorem head?_range' : (range' s n).head? = if n = 0 then none else some s := by
induction n <;> simp_all [range'_succ]
@[simp] theorem head_range' (h) : (range' s n).head h = s := by
@[simp, grind =] theorem head_range' (h) : (range' s n).head h = s := by
repeat simp_all [head?_range', head_eq_iff_head?_eq_some]
theorem map_add_range' {a} : s n step, map (a + ·) (range' s n step) = range' (a + s) n step
@@ -111,7 +108,7 @@ theorem range'_append : ∀ {s m n step : Nat},
simpa [range', Nat.mul_succ, Nat.add_assoc, Nat.add_comm]
using range'_append (s := s + step)
@[simp] theorem range'_append_1 {s m n : Nat} :
@[simp, grind =] theorem range'_append_1 {s m n : Nat} :
range' s m ++ range' (s + m) n = range' s (m + n) := by simpa using range'_append (step := 1)
theorem range'_sublist_right {s m n : Nat} : range' s m step <+ range' s n step m n :=
@@ -133,15 +130,6 @@ theorem range'_concat {s n : Nat} : range' s (n + 1) step = range' s n step ++ [
theorem range'_1_concat {s n : Nat} : range' s (n + 1) = range' s n ++ [s + n] := by
simp [range'_concat]
theorem range'_eq_cons_iff : range' s n = a :: xs s = a 0 < n xs = range' (a + 1) (n - 1) := by
induction n generalizing s with
| zero => simp
| succ n ih =>
simp only [range'_succ]
simp only [cons.injEq, and_congr_right_iff]
rintro rfl
simp [eq_comm]
/-! ### range -/
@[simp, grind =] theorem range_one : range 1 = [0] := rfl
@@ -156,7 +144,7 @@ theorem range_eq_range' {n : Nat} : range n = range' 0 n :=
theorem getElem?_range {i n : Nat} (h : i < n) : (range n)[i]? = some i := by
simp [range_eq_range', getElem?_range' h]
@[simp] theorem getElem_range (h : j < (range n).length) : (range n)[j] = j := by
@[simp, grind =] theorem getElem_range (h : j < (range n).length) : (range n)[j] = j := by
simp [range_eq_range']
theorem range_succ_eq_map {n : Nat} : range (n + 1) = 0 :: map succ (range n) := by
@@ -166,23 +154,23 @@ theorem range_succ_eq_map {n : Nat} : range (n + 1) = 0 :: map succ (range n) :=
theorem range'_eq_map_range {s n : Nat} : range' s n = map (s + ·) (range n) := by
rw [range_eq_range', map_add_range']; rfl
@[simp] theorem length_range {n : Nat} : (range n).length = n := by
@[simp, grind =] theorem length_range {n : Nat} : (range n).length = n := by
simp only [range_eq_range', length_range']
@[simp] theorem range_eq_nil {n : Nat} : range n = [] n = 0 := by
@[simp, grind =] theorem range_eq_nil {n : Nat} : range n = [] n = 0 := by
rw [ length_eq_zero_iff, length_range]
theorem range_ne_nil {n : Nat} : range n [] n 0 := by
cases n <;> simp
@[simp] theorem tail_range : (range n).tail = range' 1 (n - 1) := by
@[simp, grind =] theorem tail_range : (range n).tail = range' 1 (n - 1) := by
rw [range_eq_range', tail_range']
@[simp]
@[simp, grind =]
theorem range_sublist {m n : Nat} : range m <+ range n m n := by
simp only [range_eq_range', range'_sublist_right]
@[simp]
@[simp, grind =]
theorem range_subset {m n : Nat} : range m range n m n := by
simp only [range_eq_range', range'_subset_right, lt_succ_self]
@@ -200,7 +188,7 @@ theorem head?_range {n : Nat} : (range n).head? = if n = 0 then none else some 0
simp only [range_succ, head?_append, ih]
split <;> simp_all
@[simp] theorem head_range {n : Nat} (h) : (range n).head h = 0 := by
@[simp, grind =] theorem head_range {n : Nat} (h) : (range n).head h = 0 := by
cases n with
| zero => simp at h
| succ n => simp [head?_range, head_eq_iff_head?_eq_some]
@@ -212,7 +200,7 @@ theorem getLast?_range {n : Nat} : (range n).getLast? = if n = 0 then none else
simp only [range_succ, getLast?_append, ih]
split <;> simp_all
@[simp] theorem getLast_range {n : Nat} (h) : (range n).getLast h = n - 1 := by
@[simp, grind =] theorem getLast_range {n : Nat} (h) : (range n).getLast h = n - 1 := by
cases n with
| zero => simp at h
| succ n => simp [getLast?_range, getLast_eq_iff_getLast?_eq_some]
@@ -295,107 +283,4 @@ theorem zipIdx_eq_map_add {l : List α} {i : Nat} :
| nil => rfl
| cons _ _ ih => simp [ih (i := i + 1), zipIdx_succ, Nat.add_assoc, Nat.add_comm 1]
/-! ### enumFrom -/
section
set_option linter.deprecated false
@[deprecated zipIdx_eq_nil_iff (since := "2025-01-21"), simp]
theorem enumFrom_eq_nil {n : Nat} {l : List α} : List.enumFrom n l = [] l = [] := by
cases l <;> simp
@[deprecated length_zipIdx (since := "2025-01-21"), simp]
theorem enumFrom_length : {n} {l : List α}, (enumFrom n l).length = l.length
| _, [] => rfl
| _, _ :: _ => congrArg Nat.succ enumFrom_length
@[deprecated getElem?_zipIdx (since := "2025-01-21"), simp]
theorem getElem?_enumFrom :
i (l : List α) j, (enumFrom i l)[j]? = l[j]?.map fun a => (i + j, a)
| _, [], _ => rfl
| _, _ :: _, 0 => by simp
| n, _ :: l, m + 1 => by
simp only [enumFrom_cons, getElem?_cons_succ]
exact (getElem?_enumFrom (n + 1) l m).trans <| by rw [Nat.add_right_comm]; rfl
@[deprecated getElem_zipIdx (since := "2025-01-21"), simp]
theorem getElem_enumFrom (l : List α) (n) (i : Nat) (h : i < (l.enumFrom n).length) :
(l.enumFrom n)[i] = (n + i, l[i]'(by simpa [enumFrom_length] using h)) := by
simp only [enumFrom_length] at h
rw [getElem_eq_getElem?_get]
simp only [getElem?_enumFrom, getElem?_eq_getElem h]
simp
@[deprecated tail_zipIdx (since := "2025-01-21"), simp]
theorem tail_enumFrom (l : List α) (n : Nat) : (enumFrom n l).tail = enumFrom (n + 1) l.tail := by
induction l generalizing n with
| nil => simp
| cons _ l ih => simp [enumFrom_cons]
@[deprecated map_snd_add_zipIdx_eq_zipIdx (since := "2025-01-21"), simp]
theorem map_fst_add_enumFrom_eq_enumFrom (l : List α) (n k : Nat) :
map (Prod.map (· + n) id) (enumFrom k l) = enumFrom (n + k) l :=
ext_getElem? fun i by simp [Nat.add_comm, Nat.add_left_comm]; rfl
@[deprecated map_snd_add_zipIdx_eq_zipIdx (since := "2025-01-21"), simp]
theorem map_fst_add_enum_eq_enumFrom (l : List α) (n : Nat) :
map (Prod.map (· + n) id) (enum l) = enumFrom n l :=
map_fst_add_enumFrom_eq_enumFrom l _ _
@[deprecated zipIdx_cons' (since := "2025-01-21"), simp]
theorem enumFrom_cons' (n : Nat) (x : α) (xs : List α) :
enumFrom n (x :: xs) = (n, x) :: (enumFrom n xs).map (Prod.map (· + 1) id) := by
rw [enumFrom_cons, Nat.add_comm, map_fst_add_enumFrom_eq_enumFrom]
@[deprecated zipIdx_map_snd (since := "2025-01-21"), simp]
theorem enumFrom_map_fst (n) :
(l : List α), map Prod.fst (enumFrom n l) = range' n l.length
| [] => rfl
| _ :: _ => congrArg (cons _) (enumFrom_map_fst _ _)
@[deprecated zipIdx_map_fst (since := "2025-01-21"), simp]
theorem enumFrom_map_snd : (n) (l : List α), map Prod.snd (enumFrom n l) = l
| _, [] => rfl
| _, _ :: _ => congrArg (cons _) (enumFrom_map_snd _ _)
@[deprecated zipIdx_eq_zip_range' (since := "2025-01-21")]
theorem enumFrom_eq_zip_range' (l : List α) {n : Nat} : l.enumFrom n = (range' n l.length).zip l :=
zip_of_prod (enumFrom_map_fst _ _) (enumFrom_map_snd _ _)
@[deprecated unzip_zipIdx_eq_prod (since := "2025-01-21"), simp]
theorem unzip_enumFrom_eq_prod (l : List α) {n : Nat} :
(l.enumFrom n).unzip = (range' n l.length, l) := by
simp only [enumFrom_eq_zip_range', unzip_zip, length_range']
end
/-! ### enum -/
section
set_option linter.deprecated false
@[deprecated zipIdx_cons (since := "2025-01-21")]
theorem enum_cons : (a::as).enum = (0, a) :: as.enumFrom 1 := rfl
@[deprecated zipIdx_cons (since := "2025-01-21")]
theorem enum_cons' (x : α) (xs : List α) :
enum (x :: xs) = (0, x) :: (enum xs).map (Prod.map (· + 1) id) :=
enumFrom_cons' _ _ _
@[deprecated "These are now both `l.zipIdx 0`" (since := "2025-01-21")]
theorem enum_eq_enumFrom {l : List α} : l.enum = l.enumFrom 0 := rfl
@[deprecated "Use the reverse direction of `map_snd_add_zipIdx_eq_zipIdx` instead" (since := "2025-01-21")]
theorem enumFrom_eq_map_enum (l : List α) (n : Nat) :
enumFrom n l = (enum l).map (Prod.map (· + n) id) := by
induction l generalizing n with
| nil => simp
| cons x xs ih =>
simp only [enumFrom_cons, ih, enum_cons, map_cons, Prod.map_apply, Nat.zero_add, id_eq, map_map,
cons.injEq, map_inj_left, Function.comp_apply, Prod.forall, Prod.mk.injEq, and_true, true_and]
intro a b _
exact (succ_add a n).symm
end
end List

View File

@@ -57,7 +57,7 @@ where go : List α → List α → List α → List α
else
go (x :: xs) ys (y :: acc)
theorem mergeTR_go_eq : mergeTR.go le l₁ l₂ acc = acc.reverse ++ merge l₁ l₂ le := by
private theorem mergeTR_go_eq : mergeTR.go le l₁ l₂ acc = acc.reverse ++ merge l₁ l₂ le := by
induction l₁ generalizing l₂ acc with
| nil => simp [mergeTR.go, reverseAux_eq]
| cons x l₁ ih₁ =>
@@ -84,7 +84,7 @@ def splitRevAt (n : Nat) (l : List α) : List α × List α := go l n [] where
| x :: xs, n+1, acc => go xs n (x :: acc)
| xs, _, acc => (acc, xs)
theorem splitRevAt_go (xs : List α) (i : Nat) (acc : List α) :
private theorem splitRevAt_go (xs : List α) (i : Nat) (acc : List α) :
splitRevAt.go xs i acc = ((take i xs).reverse ++ acc, drop i xs) := by
induction xs generalizing i acc with
| nil => simp [splitRevAt.go]
@@ -172,7 +172,7 @@ theorem splitRevInTwo_snd (l : { l : List α // l.length = n }) :
(splitRevInTwo l).2 = (splitInTwo l).2.1, by simp; omega := by
simp only [splitRevInTwo, splitRevAt_eq, reverse_take, splitInTwo_snd]
theorem mergeSortTR_run_eq_mergeSort : {n : Nat} (l : { l : List α // l.length = n }) mergeSortTR.run le l = mergeSort l.1 le
private theorem mergeSortTR_run_eq_mergeSort : {n : Nat} (l : { l : List α // l.length = n }) mergeSortTR.run le l = mergeSort l.1 le
| 0, [], _
| 1, [a], _ => by simp [mergeSortTR.run]
| n+2, a :: b :: l, h => by
@@ -189,7 +189,7 @@ theorem mergeSort_eq_mergeSortTR : @mergeSort = @mergeSortTR := by
-- This mutual block is unfortunately quite slow to elaborate.
set_option maxHeartbeats 400000 in
mutual
theorem mergeSortTR₂_run_eq_mergeSort : {n : Nat} (l : { l : List α // l.length = n }) mergeSortTR₂.run le l = mergeSort l.1 le
private theorem mergeSortTR₂_run_eq_mergeSort : {n : Nat} (l : { l : List α // l.length = n }) mergeSortTR₂.run le l = mergeSort l.1 le
| 0, [], _
| 1, [a], _ => by simp [mergeSortTR₂.run]
| n+2, a :: b :: l, h => by
@@ -201,7 +201,7 @@ theorem mergeSortTR₂_run_eq_mergeSort : {n : Nat} → (l : { l : List α // l.
rw [reverse_reverse]
termination_by n => n
theorem mergeSortTR₂_run'_eq_mergeSort : {n : Nat} (l : { l : List α // l.length = n }) (w : l' = l.1.reverse) mergeSortTR₂.run' le l = mergeSort l' le
private theorem mergeSortTR₂_run'_eq_mergeSort : {n : Nat} (l : { l : List α // l.length = n }) (w : l' = l.1.reverse) mergeSortTR₂.run' le l = mergeSort l' le
| 0, [], _, w
| 1, [a], _, w => by simp_all [mergeSortTR₂.run']
| n+2, a :: b :: l, h, w => by

View File

@@ -352,14 +352,14 @@ where go : ∀ (i : Nat) (l : List α),
rw [merge_stable]
· rw [go, go]
· simp only [mem_mergeSort, Prod.forall]
intros j x k y mx my
intro j x k y mx my
have := mem_zipIdx mx
have := mem_zipIdx my
simp_all
omega
termination_by _ l => l.length
@[deprecated mergeSort_zipIdx (since := "2025-01-21")] abbrev mergeSort_enum := @mergeSort_zipIdx
theorem mergeSort_cons {le : α α Bool}
(trans : (a b c : α), le a b le b c le a c)

View File

@@ -68,9 +68,9 @@ theorem take_of_length_le {l : List α} (h : l.length ≤ i) : take i l = l := b
theorem lt_length_of_take_ne_self {l : List α} {i} (h : l.take i l) : i < l.length :=
gt_of_not_le (mt take_of_length_le h)
@[simp] theorem drop_length {l : List α} : l.drop l.length = [] := drop_of_length_le (Nat.le_refl _)
@[simp, grind =] theorem drop_length {l : List α} : l.drop l.length = [] := drop_of_length_le (Nat.le_refl _)
@[simp] theorem take_length {l : List α} : l.take l.length = l := take_of_length_le (Nat.le_refl _)
@[simp, grind =] theorem take_length {l : List α} : l.take l.length = l := take_of_length_le (Nat.le_refl _)
@[simp]
theorem getElem_cons_drop : {l : List α} {i : Nat} (h : i < l.length),

View File

@@ -222,11 +222,11 @@ theorem forM_toArray [Monad m] (l : List α) (f : α → m PUnit) :
congr
ext1 (_|_) <;> simp [ih]
theorem findSomeRevM?_find_toArray [Monad m] [LawfulMonad m] (f : α m (Option β)) (l : List α)
private theorem findSomeRevM?_find_toArray [Monad m] [LawfulMonad m] (f : α m (Option β)) (l : List α)
(i : Nat) (h) :
findSomeRevM?.find f l.toArray i h = (l.take i).reverse.findSomeM? f := by
induction i generalizing l with
| zero => simp [Array.findSomeRevM?.find.eq_def]
| zero => simp [Array.findSomeRevM?.find]
| succ i ih =>
rw [size_toArray] at h
rw [Array.findSomeRevM?.find, take_succ, getElem?_eq_getElem (by omega)]
@@ -398,45 +398,46 @@ theorem isPrefixOfAux_toArray_zero [BEq α] (l₁ l₂ : List α) (hle : l₁.le
rw [ih]
simp_all
theorem zipWithAux_toArray_succ (as : List α) (bs : List β) (f : α β γ) (i : Nat) (xs : Array γ) :
zipWithAux as.toArray bs.toArray f (i + 1) xs = zipWithAux as.tail.toArray bs.tail.toArray f i xs := by
rw [zipWithAux]
conv => rhs; rw [zipWithAux]
theorem zipWithMAux_toArray_succ {m : Type u Type v} [Monad m] (as : List α) (bs : List β) (f : α β m γ) (i : Nat) (xs : Array γ) :
zipWithMAux as.toArray bs.toArray f (i + 1) xs = zipWithMAux as.tail.toArray bs.tail.toArray f i xs := by
rw [zipWithMAux]
conv => rhs; rw [zipWithMAux]
simp only [size_toArray, getElem_toArray, length_tail, getElem_tail]
split <;> rename_i h₁
· split <;> rename_i h₂
· rw [dif_pos (by omega), dif_pos (by omega), zipWithAux_toArray_succ]
· rw [dif_pos (by omega), dif_pos (by omega)]
simp only [zipWithMAux_toArray_succ as bs f (i+1)]
· rw [dif_pos (by omega)]
rw [dif_neg (by omega)]
· rw [dif_neg (by omega)]
theorem zipWithAux_toArray_succ' (as : List α) (bs : List β) (f : α β γ) (i : Nat) (xs : Array γ) :
zipWithAux as.toArray bs.toArray f (i + 1) xs = zipWithAux (as.drop (i+1)).toArray (bs.drop (i+1)).toArray f 0 xs := by
theorem zipWithMAux_toArray_succ' {m : Type u Type v} [Monad m] (as : List α) (bs : List β) (f : α β m γ) (i : Nat) (xs : Array γ) :
zipWithMAux as.toArray bs.toArray f (i + 1) xs = zipWithMAux (as.drop (i+1)).toArray (bs.drop (i+1)).toArray f 0 xs := by
induction i generalizing as bs xs with
| zero => simp [zipWithAux_toArray_succ]
| zero => simp [zipWithMAux_toArray_succ]
| succ i ih =>
rw [zipWithAux_toArray_succ, ih]
rw [zipWithMAux_toArray_succ, ih]
simp
theorem zipWithAux_toArray_zero (f : α β γ) (as : List α) (bs : List β) (xs : Array γ) :
zipWithAux as.toArray bs.toArray f 0 xs = xs ++ (List.zipWith f as bs).toArray := by
rw [Array.zipWithAux]
theorem zipWithMAux_toArray_zero {m : Type u Type v} [Monad m] [LawfulMonad m] (f : α β m γ) (as : List α) (bs : List β) (xs : Array γ) :
zipWithMAux as.toArray bs.toArray f 0 xs = do return xs ++ ( List.zipWithM f as bs).toArray := by
rw [Array.zipWithMAux]
match as, bs with
| [], _ => simp
| _, [] => simp
| a :: as, b :: bs =>
simp [zipWith_cons_cons, zipWithAux_toArray_succ', zipWithAux_toArray_zero, push_append_toArray]
simp [zipWithMAux_toArray_succ', zipWithMAux_toArray_zero, push_append_toArray]
@[simp, grind =] theorem zipWith_toArray (as : List α) (bs : List β) (f : α β γ) :
Array.zipWith f as.toArray bs.toArray = (List.zipWith f as bs).toArray := by
rw [Array.zipWith]
simp [zipWithAux_toArray_zero]
simp [zipWithMAux_toArray_zero, zipWithM'_eq_zipWithM]
@[simp, grind =] theorem zip_toArray (as : List α) (bs : List β) :
Array.zip as.toArray bs.toArray = (List.zip as bs).toArray := by
simp [Array.zip, zipWith_toArray, zip]
theorem zipWithAll_go_toArray (as : List α) (bs : List β) (f : Option α Option β γ) (i : Nat) (xs : Array γ) :
private theorem zipWithAll_go_toArray (as : List α) (bs : List β) (f : Option α Option β γ) (i : Nat) (xs : Array γ) :
zipWithAll.go f as.toArray bs.toArray i xs = xs ++ (List.zipWithAll f (as.drop i) (bs.drop i)).toArray := by
unfold zipWithAll.go
split <;> rename_i h
@@ -474,6 +475,11 @@ theorem zipWithAll_go_toArray (as : List α) (bs : List β) (f : Option α → O
Array.zipWithAll f as.toArray bs.toArray = (List.zipWithAll f as bs).toArray := by
simp [Array.zipWithAll, zipWithAll_go_toArray]
@[simp, grind =] theorem zipWithM_toArray {m : Type u Type v} [Monad m] [LawfulMonad m] (f : α β m γ) (as : List α) (bs : List β) :
Array.zipWithM f as.toArray bs.toArray = do return ( List.zipWithM f as bs).toArray := by
rw [Array.zipWithM]
simp [zipWithMAux_toArray_zero]
@[simp, grind =] theorem toArray_appendList (l₁ l₂ : List α) :
l₁.toArray ++ l₂ = (l₁ ++ l₂).toArray := by
apply ext'
@@ -483,7 +489,7 @@ theorem zipWithAll_go_toArray (as : List α) (bs : List β) (f : Option α → O
apply ext'
simp
theorem takeWhile_go_succ (p : α Bool) (a : α) (l : List α) (i : Nat) :
private theorem takeWhile_go_succ (p : α Bool) (a : α) (l : List α) (i : Nat) :
takeWhile.go p (a :: l).toArray (i+1) r = takeWhile.go p l.toArray i r := by
rw [takeWhile.go, takeWhile.go]
simp only [size_toArray, length_cons, Nat.add_lt_add_iff_right,
@@ -492,7 +498,7 @@ theorem takeWhile_go_succ (p : α → Bool) (a : α) (l : List α) (i : Nat) :
rw [takeWhile_go_succ]
rfl
theorem takeWhile_go_toArray (p : α Bool) (l : List α) (i : Nat) :
private theorem takeWhile_go_toArray (p : α Bool) (l : List α) (i : Nat) :
Array.takeWhile.go p l.toArray i r = r ++ (takeWhile p (l.drop i)).toArray := by
induction l generalizing i r with
| nil => simp [takeWhile.go]

View File

@@ -31,6 +31,6 @@ both `List.toArray` and `Array.mk`.
-/
-- This function is exported to C, where it is called by `Array.mk`
-- (the constructor) to implement this functionality.
@[inline, match_pattern, pp_nodot, export lean_list_to_array]
@[inline, expose, match_pattern, pp_nodot, export lean_list_to_array]
def List.toArrayImpl (xs : List α) : Array α :=
xs.toArrayAux (Array.mkEmpty xs.length)

View File

@@ -42,7 +42,7 @@ theorem zipWith_self {f : αα → δ} : ∀ {l : List α}, zipWith f l l =
| [] => rfl
| _ :: _ => congrArg _ zipWith_self
@[deprecated zipWith_self (since := "2025-01-29")] abbrev zipWith_same := @zipWith_self
/--
See also `getElem?_zipWith'` for a variant
@@ -127,7 +127,7 @@ theorem zipWith_foldl_eq_zip_foldl {f : α → β → γ} {i : δ} {g : δ →
theorem zipWith_eq_nil_iff {f : α β γ} {l l'} : zipWith f l l' = [] l = [] l' = [] := by
cases l <;> cases l' <;> simp
@[grind =]
@[simp, grind =]
theorem map_zipWith {δ : Type _} {f : α β} {g : γ δ α} {l : List γ} {l' : List δ} :
map f (zipWith g l l') = zipWith (fun x y => f (g x y)) l l' := by
induction l generalizing l' with
@@ -306,7 +306,7 @@ theorem of_mem_zip {a b} : ∀ {l₁ : List α} {l₂ : List β}, (a, b) ∈ zip
cases h
case head => simp
case tail h =>
· have := of_mem_zip h
have := of_mem_zip h
exact Mem.tail _ this.1, Mem.tail _ this.2
theorem map_fst_zip :

View File

@@ -10,7 +10,9 @@ public import Init.Data.Nat.Basic
public import Init.Data.Nat.Div
public import Init.Data.Nat.Dvd
public import Init.Data.Nat.Gcd
public import Init.Data.Nat.Coprime
public import Init.Data.Nat.MinMax
public import Init.Data.Nat.Order
public import Init.Data.Nat.Bitwise
public import Init.Data.Nat.Control
public import Init.Data.Nat.Log2
@@ -23,5 +25,6 @@ public import Init.Data.Nat.Lcm
public import Init.Data.Nat.Compare
public import Init.Data.Nat.Simproc
public import Init.Data.Nat.Fold
public import Init.Data.Nat.Order
public section

View File

@@ -97,7 +97,7 @@ def shiftRight : @& Nat → @& Nat → Nat
instance : AndOp Nat := Nat.land
instance : OrOp Nat := Nat.lor
instance : Xor Nat := Nat.xor
instance : XorOp Nat := Nat.xor
instance : ShiftLeft Nat := Nat.shiftLeft
instance : ShiftRight Nat := Nat.shiftRight

View File

@@ -78,8 +78,7 @@ noncomputable def div2Induction {motive : Nat → Sort u}
simp only [HAnd.hAnd, AndOp.and, land] at andz
simp only [HAnd.hAnd, AndOp.and, land]
unfold bitwise
cases mod_two_eq_zero_or_one x with | _ p =>
simp [xz, p, andz]
cases mod_two_eq_zero_or_one x with | _ p => simp [xz, p, andz]
/-! ### testBit -/
@@ -182,9 +181,8 @@ theorem exists_testBit_ne_of_ne {x y : Nat} (p : x ≠ y) : ∃ i, testBit x i
apply Exists.intro 0
simp only [testBit_zero]
revert lsb_diff
cases mod_two_eq_zero_or_one x with | _ p =>
cases mod_two_eq_zero_or_one y with | _ q =>
simp [p,q]
cases mod_two_eq_zero_or_one x with
| _ p => cases mod_two_eq_zero_or_one y with | _ q => simp [p,q]
@[deprecated exists_testBit_ne_of_ne (since := "2025-04-04")]
abbrev ne_implies_bit_diff := @exists_testBit_ne_of_ne
@@ -260,8 +258,7 @@ private theorem succ_mod_two : succ x % 2 = 1 - x % 2 := by
private theorem testBit_succ_zero : testBit (x + 1) 0 = !(testBit x 0) := by
simp only [testBit_eq_decide_div_mod_eq, Nat.pow_zero, Nat.div_one, succ_mod_two]
cases Nat.mod_two_eq_zero_or_one x with | _ p =>
simp [p]
cases Nat.mod_two_eq_zero_or_one x with | _ p => simp [p]
theorem testBit_two_pow_add_eq (x i : Nat) : testBit (2^i + x) i = !(testBit x i) := by
simp only [testBit_eq_decide_div_mod_eq, add_div_left, Nat.two_pow_pos, succ_mod_two]
@@ -277,8 +274,7 @@ theorem testBit_mul_two_pow_add_eq (a b i : Nat) :
testBit_mul_two_pow_add_eq a,
testBit_two_pow_add_eq,
Nat.succ_mod_two]
cases mod_two_eq_zero_or_one a with
| _ p => simp [p]
cases mod_two_eq_zero_or_one a with | _ p => simp [p]
theorem testBit_two_pow_add_gt {i j : Nat} (j_lt_i : j < i) (x : Nat) :
testBit (2^i + x) j = testBit x j := by
@@ -587,22 +583,20 @@ protected theorem or_assoc (x y z : Nat) : (x ||| y) ||| z = x ||| (y ||| z) :=
apply Nat.eq_of_testBit_eq
simp [Bool.or_assoc]
@[grind _=_]
@[grind =]
theorem and_or_distrib_left (x y z : Nat) : x &&& (y ||| z) = (x &&& y) ||| (x &&& z) := by
apply Nat.eq_of_testBit_eq
simp [Bool.and_or_distrib_left]
@[grind _=_]
@[grind =]
theorem and_distrib_right (x y z : Nat) : (x ||| y) &&& z = (x &&& z) ||| (y &&& z) := by
apply Nat.eq_of_testBit_eq
simp [Bool.and_or_distrib_right]
@[grind _=_]
theorem or_and_distrib_left (x y z : Nat) : x ||| (y &&& z) = (x ||| y) &&& (x ||| z) := by
apply Nat.eq_of_testBit_eq
simp [Bool.or_and_distrib_left]
@[grind _=_]
theorem or_and_distrib_right (x y z : Nat) : (x &&& y) ||| z = (x ||| z) &&& (y ||| z) := by
apply Nat.eq_of_testBit_eq
simp [Bool.or_and_distrib_right]
@@ -641,7 +635,7 @@ theorem or_mod_two_pow : (a ||| b) % 2 ^ n = a % 2 ^ n ||| b % 2 ^ n :=
@[simp, grind =] theorem testBit_xor (x y i : Nat) :
(x ^^^ y).testBit i = ((x.testBit i) ^^ (y.testBit i)) := by
simp [HXor.hXor, Xor.xor, xor, testBit_bitwise ]
simp [HXor.hXor, XorOp.xor, xor, testBit_bitwise ]
@[simp, grind =] theorem zero_xor (x : Nat) : 0 ^^^ x = x := by
apply Nat.eq_of_testBit_eq
@@ -678,12 +672,12 @@ instance : Std.LawfulCommIdentity (α := Nat) (· ^^^ ·) 0 where
theorem xor_lt_two_pow {x y n : Nat} (left : x < 2^n) (right : y < 2^n) : x ^^^ y < 2^n :=
bitwise_lt_two_pow left right
@[grind _=_]
@[grind =]
theorem and_xor_distrib_right {a b c : Nat} : (a ^^^ b) &&& c = (a &&& c) ^^^ (b &&& c) := by
apply Nat.eq_of_testBit_eq
simp [Bool.and_xor_distrib_right]
@[grind _=_]
@[grind =]
theorem and_xor_distrib_left {a b c : Nat} : a &&& (b ^^^ c) = (a &&& b) ^^^ (a &&& c) := by
apply Nat.eq_of_testBit_eq
simp [Bool.and_xor_distrib_left]
@@ -853,8 +847,7 @@ theorem shiftLeft_add_eq_or_of_lt {b : Nat} (b_lt : b < 2^i) (a : Nat) :
/-! ### le -/
theorem le_of_testBit {n m : Nat} (h : i, n.testBit i = true m.testBit i = true) : n m := by
induction n using div2Induction generalizing m
next n ih =>
induction n using div2Induction generalizing m with | _ n ih
have : n / 2 m / 2 := by
rcases n with (_|n)
· simp

View File

@@ -6,7 +6,7 @@ Authors: Leonardo de Moura, Jeremy Avigad, Mario Carneiro
module
prelude
public import all Init.Data.Ord
public import all Init.Data.Ord.Basic
public section
@@ -24,7 +24,7 @@ theorem compare_eq_ite_lt (a b : Nat) :
simp only [compare, compareOfLessAndEq]
split
· rfl
· next h =>
next h =>
match Nat.lt_or_eq_of_le (Nat.not_lt.1 h) with
| .inl h => simp [h, Nat.ne_of_gt h]
| .inr rfl => simp
@@ -36,11 +36,11 @@ theorem compare_eq_ite_le (a b : Nat) :
compare a b = if a b then if b a then .eq else .lt else .gt := by
rw [compare_eq_ite_lt]
split
· next hlt => simp [Nat.le_of_lt hlt, Nat.not_le.2 hlt]
· next hge =>
next hlt => simp [Nat.le_of_lt hlt, Nat.not_le.2 hlt]
next hge =>
split
· next hgt => simp [Nat.not_le.2 hgt]
· next hle => simp [Nat.not_lt.1 hge, Nat.not_lt.1 hle]
next hgt => simp [Nat.not_le.2 hgt]
next hle => simp [Nat.not_lt.1 hge, Nat.not_lt.1 hle]
@[deprecated compare_eq_ite_le (since := "2025-03_28")]
def compare_def_le := compare_eq_ite_le

View File

@@ -0,0 +1,194 @@
/-
Copyright (c) 2014 Jeremy Avigad. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jeremy Avigad, Leonardo de Moura, Mario Carneiro
-/
module
prelude
public import Init.Data.Nat.Gcd
public section
/-!
# Definitions and properties of `coprime`
-/
namespace Nat
/-!
### `coprime`
-/
/-- `m` and `n` are coprime, or relatively prime, if their `gcd` is 1. -/
@[reducible, expose] def Coprime (m n : Nat) : Prop := gcd m n = 1
-- if we don't inline this, then the compiler computes the GCD even if it already has it
@[inline] instance (m n : Nat) : Decidable (Coprime m n) := inferInstanceAs (Decidable (_ = 1))
theorem coprime_iff_gcd_eq_one : Coprime m n gcd m n = 1 := .rfl
theorem Coprime.gcd_eq_one : Coprime m n gcd m n = 1 := id
theorem Coprime.symm : Coprime n m Coprime m n := (gcd_comm m n).trans
theorem coprime_comm : Coprime n m Coprime m n := Coprime.symm, Coprime.symm
theorem Coprime.dvd_of_dvd_mul_right (H1 : Coprime k n) (H2 : k m * n) : k m := by
have t := dvd_gcd (Nat.dvd_mul_left k m) H2
rwa [gcd_mul_left, H1.gcd_eq_one, Nat.mul_one] at t
theorem Coprime.dvd_of_dvd_mul_left (H1 : Coprime k m) (H2 : k m * n) : k n :=
H1.dvd_of_dvd_mul_right (by rwa [Nat.mul_comm])
theorem Coprime.gcd_mul_left_cancel (m : Nat) (H : Coprime k n) : gcd (k * m) n = gcd m n :=
have H1 : Coprime (gcd (k * m) n) k := by
rw [Coprime, Nat.gcd_assoc, H.symm.gcd_eq_one, gcd_one_right]
Nat.dvd_antisymm
(dvd_gcd (H1.dvd_of_dvd_mul_left (gcd_dvd_left _ _)) (gcd_dvd_right _ _))
(gcd_dvd_gcd_mul_left_left _ _ _)
theorem Coprime.gcd_mul_right_cancel (m : Nat) (H : Coprime k n) : gcd (m * k) n = gcd m n := by
rw [Nat.mul_comm m k, H.gcd_mul_left_cancel m]
theorem Coprime.gcd_mul_left_cancel_right (n : Nat)
(H : Coprime k m) : gcd m (k * n) = gcd m n := by
rw [gcd_comm m n, gcd_comm m (k * n), H.gcd_mul_left_cancel n]
theorem Coprime.gcd_mul_right_cancel_right (n : Nat)
(H : Coprime k m) : gcd m (n * k) = gcd m n := by
rw [Nat.mul_comm n k, H.gcd_mul_left_cancel_right n]
theorem coprime_div_gcd_div_gcd
(H : 0 < gcd m n) : Coprime (m / gcd m n) (n / gcd m n) := by
rw [coprime_iff_gcd_eq_one, gcd_div (gcd_dvd_left m n) (gcd_dvd_right m n), Nat.div_self H]
theorem not_coprime_of_dvd_of_dvd (dgt1 : 1 < d) (Hm : d m) (Hn : d n) : ¬ Coprime m n :=
fun co => Nat.not_le_of_gt dgt1 <| Nat.le_of_dvd Nat.zero_lt_one <| by
rw [ co.gcd_eq_one]; exact dvd_gcd Hm Hn
theorem exists_coprime (m n : Nat) :
m' n', Coprime m' n' m = m' * gcd m n n = n' * gcd m n := by
cases eq_zero_or_pos (gcd m n) with
| inl h0 =>
rw [gcd_eq_zero_iff] at h0
refine 1, 1, gcd_one_left 1, ?_
simp [h0]
| inr hpos =>
exact _, _, coprime_div_gcd_div_gcd hpos,
(Nat.div_mul_cancel (gcd_dvd_left m n)).symm,
(Nat.div_mul_cancel (gcd_dvd_right m n)).symm
theorem exists_coprime' (H : 0 < gcd m n) :
g m' n', 0 < g Coprime m' n' m = m' * g n = n' * g :=
let m', n', h := exists_coprime m n; _, m', n', H, h
theorem Coprime.mul_left (H1 : Coprime m k) (H2 : Coprime n k) : Coprime (m * n) k :=
(H1.gcd_mul_left_cancel n).trans H2
theorem Coprime.mul_right (H1 : Coprime k m) (H2 : Coprime k n) : Coprime k (m * n) :=
(H1.symm.mul_left H2.symm).symm
theorem Coprime.coprime_dvd_left (H1 : m k) (H2 : Coprime k n) : Coprime m n := by
apply eq_one_of_dvd_one
rw [Coprime] at H2
have := Nat.gcd_dvd_gcd_of_dvd_left n H1
rwa [ H2]
theorem Coprime.coprime_dvd_right (H1 : n m) (H2 : Coprime k m) : Coprime k n :=
(H2.symm.coprime_dvd_left H1).symm
theorem Coprime.coprime_mul_left (H : Coprime (k * m) n) : Coprime m n :=
H.coprime_dvd_left (Nat.dvd_mul_left _ _)
theorem Coprime.coprime_mul_right (H : Coprime (m * k) n) : Coprime m n :=
H.coprime_dvd_left (Nat.dvd_mul_right _ _)
theorem Coprime.coprime_mul_left_right (H : Coprime m (k * n)) : Coprime m n :=
H.coprime_dvd_right (Nat.dvd_mul_left _ _)
theorem Coprime.coprime_mul_right_right (H : Coprime m (n * k)) : Coprime m n :=
H.coprime_dvd_right (Nat.dvd_mul_right _ _)
theorem Coprime.coprime_div_left (cmn : Coprime m n) (dvd : a m) : Coprime (m / a) n := by
match eq_zero_or_pos a with
| .inl h0 =>
rw [h0] at dvd
rw [Nat.eq_zero_of_zero_dvd dvd] at cmn
simp; assumption
| .inr hpos =>
let k, hk := dvd
rw [hk, Nat.mul_div_cancel_left _ hpos]
rw [hk] at cmn
exact cmn.coprime_mul_left
theorem Coprime.coprime_div_right (cmn : Coprime m n) (dvd : a n) : Coprime m (n / a) :=
(cmn.symm.coprime_div_left dvd).symm
theorem coprime_mul_iff_left : Coprime (m * n) k Coprime m k Coprime n k :=
fun h => h.coprime_mul_right, h.coprime_mul_left,
fun h, _ => by rwa [coprime_iff_gcd_eq_one, h.gcd_mul_left_cancel n]
theorem coprime_mul_iff_right : Coprime k (m * n) Coprime k m Coprime k n := by
rw [@coprime_comm k, @coprime_comm k, @coprime_comm k, coprime_mul_iff_left]
theorem Coprime.gcd_left (k : Nat) (hmn : Coprime m n) : Coprime (gcd k m) n :=
hmn.coprime_dvd_left <| gcd_dvd_right k m
theorem Coprime.gcd_right (k : Nat) (hmn : Coprime m n) : Coprime m (gcd k n) :=
hmn.coprime_dvd_right <| gcd_dvd_right k n
theorem Coprime.gcd_both (k l : Nat) (hmn : Coprime m n) : Coprime (gcd k m) (gcd l n) :=
(hmn.gcd_left k).gcd_right l
theorem Coprime.mul_dvd_of_dvd_of_dvd (hmn : Coprime m n) (hm : m a) (hn : n a) : m * n a :=
let _, hk := hm
hk.symm Nat.mul_dvd_mul_left _ (hmn.symm.dvd_of_dvd_mul_left (hk hn))
@[simp] theorem coprime_zero_left (n : Nat) : Coprime 0 n n = 1 := by simp [Coprime]
@[simp] theorem coprime_zero_right (n : Nat) : Coprime n 0 n = 1 := by simp [Coprime]
theorem coprime_one_left : n, Coprime 1 n := gcd_one_left
theorem coprime_one_right : n, Coprime n 1 := gcd_one_right
@[simp] theorem coprime_one_left_eq_true (n) : Coprime 1 n = True := eq_true (coprime_one_left _)
@[simp] theorem coprime_one_right_eq_true (n) : Coprime n 1 = True := eq_true (coprime_one_right _)
@[simp] theorem coprime_self (n : Nat) : Coprime n n n = 1 := by simp [Coprime]
theorem Coprime.pow_left (n : Nat) (H1 : Coprime m k) : Coprime (m ^ n) k := by
induction n with
| zero => exact coprime_one_left _
| succ n ih => have hm := H1.mul_left ih; rwa [Nat.pow_succ, Nat.mul_comm]
theorem Coprime.pow_right (n : Nat) (H1 : Coprime k m) : Coprime k (m ^ n) :=
(H1.symm.pow_left n).symm
theorem Coprime.pow {k l : Nat} (m n : Nat) (H1 : Coprime k l) : Coprime (k ^ m) (l ^ n) :=
(H1.pow_left _).pow_right _
theorem Coprime.eq_one_of_dvd {k m : Nat} (H : Coprime k m) (d : k m) : k = 1 := by
rw [ H.gcd_eq_one, gcd_eq_left d]
theorem Coprime.gcd_mul (k : Nat) (h : Coprime m n) : gcd k (m * n) = gcd k m * gcd k n :=
Nat.dvd_antisymm
(gcd_mul_right_dvd_mul_gcd k m n)
((h.gcd_both k k).mul_dvd_of_dvd_of_dvd
(gcd_dvd_gcd_mul_right_right ..)
(gcd_dvd_gcd_mul_left_right ..))
theorem Coprime.mul_gcd (h : Coprime m n) (k : Nat) : gcd (m * n) k = gcd m k * gcd n k := by
rw [gcd_comm, h.gcd_mul, gcd_comm k, gcd_comm k]
theorem gcd_mul_gcd_of_coprime_of_mul_eq_mul
(cop : Coprime c d) (h : a * b = c * d) : a.gcd c * b.gcd c = c := by
apply Nat.dvd_antisymm
· apply ((cop.gcd_left _).mul_left (cop.gcd_left _)).dvd_of_dvd_mul_right
rw [ h]
apply Nat.mul_dvd_mul (gcd_dvd ..).1 (gcd_dvd ..).1
· rw [gcd_comm a, gcd_comm b]
refine Nat.dvd_trans ?_ (gcd_mul_right_dvd_mul_gcd ..)
rw [h, gcd_mul_right_right d c]; apply Nat.dvd_refl

View File

@@ -128,7 +128,7 @@ theorem fold_congr {α : Type u} {n m : Nat} (w : n = m)
subst m
rfl
theorem foldTR_loop_congr {α : Type u} {n m : Nat} (w : n = m)
private theorem foldTR_loop_congr {α : Type u} {n m : Nat} (w : n = m)
(f : (i : Nat) i < n α α) (j : Nat) (h : j n) (init : α) :
foldTR.loop n f j h init = foldTR.loop m (fun i h => f i (by omega)) j (by omega) init := by
subst m
@@ -154,7 +154,7 @@ theorem any_congr {n m : Nat} (w : n = m) (f : (i : Nat) → i < n → Bool) : a
subst m
rfl
theorem anyTR_loop_congr {n m : Nat} (w : n = m) (f : (i : Nat) i < n Bool) (j : Nat) (h : j n) :
private theorem anyTR_loop_congr {n m : Nat} (w : n = m) (f : (i : Nat) i < n Bool) (j : Nat) (h : j n) :
anyTR.loop n f j h = anyTR.loop m (fun i h => f i (by omega)) j (by omega) := by
subst m
rfl
@@ -179,7 +179,7 @@ theorem all_congr {n m : Nat} (w : n = m) (f : (i : Nat) → i < n → Bool) : a
subst m
rfl
theorem allTR_loop_congr {n m : Nat} (w : n = m) (f : (i : Nat) i < n Bool) (j : Nat) (h : j n) : allTR.loop n f j h = allTR.loop m (fun i h => f i (by omega)) j (by omega) := by
private theorem allTR_loop_congr {n m : Nat} (w : n = m) (f : (i : Nat) i < n Bool) (j : Nat) (h : j n) : allTR.loop n f j h = allTR.loop m (fun i h => f i (by omega)) j (by omega) := by
subst m
rfl
@@ -199,6 +199,62 @@ theorem allTR_loop_congr {n m : Nat} (w : n = m) (f : (i : Nat) → i < n → Bo
omega
go n 0 f
/-! # `dfold` -/
private def dfoldCast {n : Nat} (α : (i : Nat) (h : i n := by omega) Type u)
{i j : Nat} {hi : i n} (w : i = j) (x : α i hi) : α j (by omega) := by
subst w
exact x
@[local simp] private theorem dfoldCast_rfl (h : i n) (w : i = i) (x : α i h) : dfoldCast α w x = x := by
simp [dfoldCast]
@[local simp] private theorem dfoldCast_trans (h : i n) (w₁ : i = j) (w₂ : j = k) (x : α i h) :
dfoldCast @α w₂ (dfoldCast @α w₁ x) = dfoldCast @α (w₁.trans w₂) x := by
cases w₁
cases w₂
simp [dfoldCast]
@[local simp] private theorem dfoldCast_eq_dfoldCast_iff {α : (i : Nat) (h : i n := by omega) Type u} {w₁ w₂ : i = j} {h : i n} {x : α i h} :
dfoldCast @α w₁ x = dfoldCast (n := n) (fun i h => α i) (hi := hi) w₂ x w₁ = w₂ := by
cases w₁
cases w₂
simp [dfoldCast]
private theorem apply_dfoldCast {α : (i : Nat) (h : i n := by omega) Type u}
(f : (i : Nat) (h : i < n) α i α (i + 1)) {i j : Nat} (h : i < n) {w : i = j} {x : α i (by omega)} :
f j (by omega) (dfoldCast @α w x) = dfoldCast (n := n) (fun i h => α i) (hi := by omega) (by omega) (f i h x) := by
subst w
simp
/--
`Nat.dfold` evaluates `f` on the numbers up to `n` exclusive, in increasing order:
* `Nat.dfold f 3 init = init |> f 0 |> f 1 |> f 2`
The input and output types of `f` are indexed by the current index, i.e. `f : (i : Nat) → (h : i < n) → α i → α (i + 1)`.
-/
@[specialize]
def dfold (n : Nat) {α : (i : Nat) (h : i n := by omega) Type u}
(f : (i : Nat) (h : i < n) α i α (i + 1)) (init : α 0) : α n :=
let rec @[specialize] loop : j, j n α (n - j) α n
| 0, h, a => a
| succ m, h, a =>
loop m (by omega) (dfoldCast @α (by omega) (f (n - succ m) (by omega) a))
loop n (by omega) (dfoldCast @α (by omega) init)
/--
`Nat.dfoldRev` evaluates `f` on the numbers up to `n` exclusive, in decreasing order:
* `Nat.dfoldRev f 3 init = f 0 <| f 1 <| f 2 <| init`
The input and output types of `f` are indexed by the current index, i.e. `f : (i : Nat) → (h : i < n) → α (i + 1) → α i`.
-/
@[specialize]
def dfoldRev (n : Nat) {α : (i : Nat) (h : i n := by omega) Type u}
(f : (i : Nat) (h : i < n) α (i + 1) α i) (init : α n) : α 0 :=
match n with
| 0 => init
| (n + 1) => dfoldRev n (α := fun i h => α i) (fun i h => f i (by omega)) (f n (by omega) init)
/-! # Theorems -/
/-! ### `fold` -/
@[simp] theorem fold_zero {α : Type u} (f : (i : Nat) i < 0 α α) (init : α) :
@@ -255,6 +311,102 @@ theorem allTR_loop_congr {n m : Nat} (w : n = m) (f : (i : Nat) → i < n → Bo
| zero => simp
| succ n ih => simp [ih, List.finRange_succ_last, List.all_map, Function.comp_def]
/-! ### `dfold` -/
@[simp]
theorem dfold_zero
{α : (i : Nat) (h : i 0 := by omega) Type u}
(f : (i : Nat) (h : i < 0) α i α (i + 1)) (init : α 0) :
dfold 0 f init = init := by
simp [dfold, dfold.loop]
private theorem dfold_loop_succ
{α : (i : Nat) (h : i n + 1 := by omega) Type u}
(f : (i : Nat) (h : i < n + 1) α i α (i + 1))
(a : α (n + 1 - (j + 1))) (w : j n):
dfold.loop (n + 1) f (j + 1) (by omega) a =
f n (by omega)
(dfold.loop n (α := fun i h => α i) (fun i h => f i (by omega)) j w (dfoldCast @α (by omega) a)) := by
induction j with
| zero => simp [dfold.loop]
| succ j ih =>
rw [dfold.loop, ih _ (by omega)]
congr 2
simp only [succ_eq_add_one, dfoldCast_trans]
rw [apply_dfoldCast (h := by omega) f]
· erw [dfoldCast_trans (h := by omega)]
erw [dfoldCast_eq_dfoldCast_iff]
omega
@[simp]
theorem dfold_succ
{α : (i : Nat) (h : i n + 1 := by omega) Type u}
(f : (i : Nat) (h : i < n + 1) α i α (i + 1)) (init : α 0) :
dfold (n + 1) f init =
f n (by omega) (dfold n (α := fun i h => α i) (fun i h => f i (by omega)) init) := by
simp [dfold]
rw [dfold_loop_succ (w := Nat.le_refl _)]
congr 2
simp only [dfoldCast_trans]
erw [dfoldCast_eq_dfoldCast_iff]
exact le_add_left 0 (n + 1)
-- This isn't a proper `@[congr]` lemma, but it doesn't seem possible to state one.
theorem dfold_congr
{n m : Nat} (w : n = m)
{α : (i : Nat) (h : i n := by omega) Type u}
(f : (i : Nat) (h : i < n) α i α (i + 1)) (init : α 0) :
dfold n f init =
cast (by subst w; rfl)
(dfold m (α := fun i h => α i) (fun i h => f i (by omega)) init) := by
subst w
rfl
theorem dfold_add
{α : (i : Nat) (h : i n + m := by omega) Type u}
(f : (i : Nat) (h : i < n + m) α i α (i + 1)) (init : α 0) :
dfold (n + m) f init =
dfold m (α := fun i h => α (n + i)) (fun i h => f (n + i) (by omega))
(dfold n (α := fun i h => α i) (fun i h => f i (by omega)) init) := by
induction m with
| zero => simp; rfl
| succ m ih =>
simp [dfold_congr (Nat.add_assoc n m 1).symm, ih]
@[simp] theorem dfoldRev_zero
{α : (i : Nat) (h : i 0 := by omega) Type u}
(f : (i : Nat) (_ : i < 0) α (i + 1) α i) (init : α 0) :
dfoldRev 0 f init = init := by
simp [dfoldRev]
@[simp] theorem dfoldRev_succ
{α : (i : Nat) (h : i n + 1 := by omega) Type u}
(f : (i : Nat) (h : i < n + 1) α (i + 1) α i) (init : α (n + 1)) :
dfoldRev (n + 1) f init =
dfoldRev n (α := fun i h => α i) (fun i h => f i (by omega)) (f n (by omega) init) := by
simp [dfoldRev]
@[congr]
theorem dfoldRev_congr
{n m : Nat} (w : n = m)
{α : (i : Nat) (h : i n := by omega) Type u}
(f : (i : Nat) (h : i < n) α (i + 1) α i) (init : α n) :
dfoldRev n f init =
dfoldRev m (α := fun i h => α i) (fun i h => f i (by omega))
(cast (by subst w; rfl) init) := by
subst w
rfl
theorem dfoldRev_add
{α : (i : Nat) (h : i n + m := by omega) Type u}
(f : (i : Nat) (h : i < n + m) α (i + 1) α i) (init : α (n + m)) :
dfoldRev (n + m) f init =
dfoldRev n (α := fun i h => α i) (fun i h => f i (by omega))
(dfoldRev m (α := fun i h => α (n + i)) (fun i h => f (n + i) (by omega)) init) := by
induction m with
| zero => simp; rfl
| succ m ih => simp [ Nat.add_assoc, ih]
end Nat
namespace Prod

View File

@@ -1145,8 +1145,7 @@ protected theorem pow_lt_pow_succ (h : 1 < a) : a ^ n < a ^ (n + 1) := by
protected theorem pow_lt_pow_of_lt {a n m : Nat} (h : 1 < a) (w : n < m) : a ^ n < a ^ m := by
have := Nat.exists_eq_add_of_lt w
cases this
case intro k p =>
cases this with | intro k p
rw [Nat.add_right_comm] at p
subst p
rw [Nat.pow_add, Nat.mul_one (a^n)]
@@ -1165,7 +1164,7 @@ protected theorem pow_le_pow_iff_right {a n m : Nat} (h : 1 < a) :
a ^ n a ^ m n m := by
constructor
· apply Decidable.by_contra
intros w
intro w
simp at w
apply Nat.lt_irrefl (a ^ n)
exact Nat.lt_of_le_of_lt w.1 (Nat.pow_lt_pow_of_lt h w.2)
@@ -1178,7 +1177,7 @@ protected theorem pow_lt_pow_iff_right {a n m : Nat} (h : 1 < a) :
a ^ n < a ^ m n < m := by
constructor
· apply Decidable.by_contra
intros w
intro w
simp at w
apply Nat.lt_irrefl (a ^ n)
exact Nat.lt_of_lt_of_le w.1 (Nat.pow_le_pow_of_le h w.2)
@@ -1332,6 +1331,25 @@ theorem le_log2 (h : n ≠ 0) : k ≤ n.log2 ↔ 2 ^ k ≤ n := by
theorem log2_lt (h : n 0) : n.log2 < k n < 2 ^ k := by
rw [ Nat.not_le, Nat.not_le, le_log2 h]
theorem log2_eq_iff (h : n 0) : n.log2 = k 2 ^ k n n < 2 ^ (k + 1) := by
constructor
· intro w
exact (le_log2 h).mp (Nat.le_of_eq w.symm), (log2_lt h).mp (by subst w; apply lt_succ_self)
· intro w
apply Nat.le_antisymm
· apply Nat.le_of_lt_add_one
exact (log2_lt h).mpr w.2
· exact (le_log2 h).mpr w.1
theorem log2_two_mul (h : n 0) : (2 * n).log2 = n.log2 + 1 := by
obtain h₁, h₂ := (log2_eq_iff h).mp rfl
rw [log2_eq_iff (Nat.mul_ne_zero (by decide) h)]
constructor
· rw [Nat.pow_succ, Nat.mul_comm]
exact mul_le_mul_left 2 h₁
· rw [Nat.pow_succ, Nat.mul_comm]
rwa [Nat.mul_lt_mul_right (by decide)]
@[simp]
theorem log2_two_pow : (2 ^ n).log2 = n := by
apply Nat.eq_of_le_of_lt_succ <;> simp [le_log2, log2_lt, NeZero.ne, Nat.pow_lt_pow_iff_right]
@@ -1561,7 +1579,7 @@ theorem mul_add_mod_of_lt {a b c : Nat} (h : c < b) : (a * b + c) % b = c := by
@[simp] theorem mod_div_self (m n : Nat) : m % n / n = 0 := by
cases n
· exact (m % 0).div_zero
· case succ n => exact Nat.div_eq_of_lt (m.mod_lt n.succ_pos)
case succ n => exact Nat.div_eq_of_lt (m.mod_lt n.succ_pos)
theorem mod_eq_iff {a b c : Nat} :
a % b = c (b = 0 a = c) (c < b Exists fun k => a = b * k + c) :=

View File

@@ -42,7 +42,7 @@ decreasing_by exact log2_terminates _ _
theorem log2_le_self (n : Nat) : Nat.log2 n n := by
unfold Nat.log2; split
· next h =>
next h =>
have := log2_le_self (n / 2)
exact Nat.lt_of_le_of_lt this (Nat.div_lt_self (Nat.le_of_lt h) (by decide))
· apply Nat.zero_le

View File

@@ -0,0 +1,41 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Paul Reichert
-/
module
prelude
public import Init.Data.Nat.Basic
import Init.Data.Nat.MinMax
public import Init.Data.Order.Factories
open Std
namespace Nat
public instance instIsLinearOrder : IsLinearOrder Nat := by
apply IsLinearOrder.of_le
· constructor; apply Nat.le_antisymm
· constructor; apply Nat.le_trans
· constructor; apply Nat.le_total
public instance : LawfulOrderLT Nat := by
apply LawfulOrderLT.of_le
simp [Nat.lt_iff_le_and_ne]
public instance : LawfulOrderMin Nat := by
apply LawfulOrderMin.of_le_min_iff
· apply Nat.le_min
· intro a b
simp only [Nat.min_def]
split <;> simp
public instance : LawfulOrderMax Nat := by
apply LawfulOrderMax.of_max_le_iff
· apply Nat.max_le
· intro a b
simp only [Nat.max_def]
split <;> simp
end Nat

View File

@@ -58,9 +58,9 @@ theorem getD_of_ne_none {x : Option α} (hx : x ≠ none) (y : α) : some (x.get
theorem getD_eq_iff {o : Option α} {a b} : o.getD a = b (o = some b o = none a = b) := by
cases o <;> simp
@[simp, grind] theorem get!_none [Inhabited α] : (none : Option α).get! = default := rfl
@[simp, grind =] theorem get!_none [Inhabited α] : (none : Option α).get! = default := rfl
@[simp, grind] theorem get!_some [Inhabited α] {a : α} : (some a).get! = a := rfl
@[simp, grind =] theorem get!_some [Inhabited α] {a : α} : (some a).get! = a := rfl
theorem get_eq_get! [Inhabited α] : (o : Option α) {h : o.isSome} o.get h = o.get!
| some _, _ => rfl
@@ -120,7 +120,7 @@ theorem isSome_of_eq_some {x : Option α} {y : α} (h : x = some y) : x.isSome :
@[simp] theorem isNone_eq_false_iff : isNone a = false a.isSome = true := by
cases a <;> simp
@[simp, grind]
@[simp, grind =]
theorem not_isSome (a : Option α) : (!a.isSome) = a.isNone := by
cases a <;> simp
@@ -129,7 +129,7 @@ theorem not_comp_isSome : (! ·) ∘ @Option.isSome α = Option.isNone := by
funext
simp
@[simp, grind]
@[simp, grind =]
theorem not_isNone (a : Option α) : (!a.isNone) = a.isSome := by
cases a <;> simp
@@ -191,11 +191,15 @@ theorem forall_ne_none {p : Option α → Prop} : (∀ x (_ : x ≠ none), p x)
@[deprecated forall_ne_none (since := "2025-04-04")]
abbrev ball_ne_none := @forall_ne_none
@[simp, grind] theorem pure_def : pure = @some α := rfl
@[simp] theorem pure_def : pure = @some α := rfl
@[simp, grind] theorem bind_eq_bind : bind = @Option.bind α β := rfl
@[grind =] theorem pure_apply : pure x = some x := rfl
@[simp, grind] theorem bind_fun_some (x : Option α) : x.bind some = x := by cases x <;> rfl
@[simp] theorem bind_eq_bind : bind = @Option.bind α β := rfl
@[grind =] theorem bind_apply : bind x f = Option.bind x f := rfl
@[simp, grind =] theorem bind_fun_some (x : Option α) : x.bind some = x := by cases x <;> rfl
@[simp] theorem bind_fun_none (x : Option α) : x.bind (fun _ => none (α := β)) = none := by
cases x <;> rfl
@@ -216,7 +220,7 @@ theorem bind_eq_none' {o : Option α} {f : α → Option β} :
o.bind f = none b a, o = some a f a some b := by
cases o <;> simp [eq_none_iff_forall_ne_some]
@[grind] theorem mem_bind_iff {o : Option α} {f : α Option β} :
@[grind =] theorem mem_bind_iff {o : Option α} {f : α Option β} :
b o.bind f a, a o b f a := by
cases o <;> simp
@@ -224,7 +228,7 @@ theorem bind_comm {f : α → β → Option γ} (a : Option α) (b : Option β)
(a.bind fun x => b.bind (f x)) = b.bind fun y => a.bind fun x => f x y := by
cases a <;> cases b <;> rfl
@[grind]
@[grind =]
theorem bind_assoc (x : Option α) (f : α Option β) (g : β Option γ) :
(x.bind f).bind g = x.bind fun y => (f y).bind g := by cases x <;> rfl
@@ -232,12 +236,12 @@ theorem bind_congr {α β} {o : Option α} {f g : α → Option β} :
(h : a, o = some a f a = g a) o.bind f = o.bind g := by
cases o <;> simp
@[grind]
@[grind =]
theorem isSome_bind {α β : Type _} (x : Option α) (f : α Option β) :
(x.bind f).isSome = x.any (fun x => (f x).isSome) := by
cases x <;> rfl
@[grind]
@[grind =]
theorem isNone_bind {α β : Type _} (x : Option α) (f : α Option β) :
(x.bind f).isNone = x.all (fun x => (f x).isNone) := by
cases x <;> rfl
@@ -250,7 +254,7 @@ theorem isSome_apply_of_isSome_bind {α β : Type _} {x : Option α} {f : α
(h : (x.bind f).isSome) : (f (x.get (isSome_of_isSome_bind h))).isSome := by
cases x <;> trivial
@[simp, grind] theorem get_bind {α β : Type _} {x : Option α} {f : α Option β} (h : (x.bind f).isSome) :
@[simp, grind =] theorem get_bind {α β : Type _} {x : Option α} {f : α Option β} (h : (x.bind f).isSome) :
(x.bind f).get h = (f (x.get (isSome_of_isSome_bind h))).get
(isSome_apply_of_isSome_bind h) := by
cases x <;> trivial
@@ -263,7 +267,7 @@ theorem isSome_apply_of_isSome_bind {α β : Type _} {x : Option α} {f : α
(o.bind f).all p = o.all (Option.all p f) := by
cases o <;> simp
@[grind] theorem bind_id_eq_join {x : Option (Option α)} : x.bind id = x.join := rfl
@[grind =] theorem bind_id_eq_join {x : Option (Option α)} : x.bind id = x.join := rfl
theorem join_eq_some_iff : x.join = some a x = some (some a) := by
simp [ bind_id_eq_join, bind_eq_some_iff]
@@ -287,7 +291,9 @@ theorem bind_join {f : α → Option β} {o : Option (Option α)} :
o.join.bind f = o.bind (·.bind f) := by
cases o <;> simp
@[simp, grind] theorem map_eq_map : Functor.map f = Option.map f := rfl
@[simp] theorem map_eq_map : Functor.map f = Option.map f := rfl
@[grind =] theorem map_apply : Functor.map f x = Option.map f x := rfl
@[deprecated map_none (since := "2025-04-10")]
abbrev map_none' := @map_none
@@ -313,13 +319,13 @@ abbrev map_eq_none := @map_eq_none_iff
@[deprecated map_eq_none_iff (since := "2025-04-10")]
abbrev map_eq_none' := @map_eq_none_iff
@[simp, grind] theorem isSome_map {x : Option α} : (x.map f).isSome = x.isSome := by
@[simp, grind =] theorem isSome_map {x : Option α} : (x.map f).isSome = x.isSome := by
cases x <;> simp
@[deprecated isSome_map (since := "2025-04-10")]
abbrev isSome_map' := @isSome_map
@[simp, grind] theorem isNone_map {x : Option α} : (x.map f).isNone = x.isNone := by
@[simp, grind =] theorem isNone_map {x : Option α} : (x.map f).isNone = x.isNone := by
cases x <;> simp
theorem map_eq_bind {x : Option α} : x.map f = x.bind (some f) := by
@@ -329,28 +335,32 @@ theorem map_congr {x : Option α} (h : ∀ a, x = some a → f a = g a) :
x.map f = x.map g := by
cases x <;> simp only [map_none, map_some, h]
@[simp, grind] theorem map_id_fun {α : Type u} : Option.map (id : α α) = id := by
@[simp] theorem map_id_fun {α : Type u} : Option.map (id : α α) = id := by
funext; simp [map_id]
@[grind =] theorem map_id_apply {α : Type u} {x : Option α} : Option.map (id : α α) x = x := by simp
theorem map_id' {x : Option α} : (x.map fun a => a) = x := congrFun map_id x
@[simp, grind] theorem map_id_fun' {α : Type u} : Option.map (fun (a : α) => a) = id := by
@[simp] theorem map_id_fun' {α : Type u} : Option.map (fun (a : α) => a) = id := by
funext; simp [map_id']
@[simp, grind] theorem get_map {f : α β} {o : Option α} {h : (o.map f).isSome} :
theorem map_id_apply' {α : Type u} {x : Option α} : Option.map (fun (a : α) => a) x = x := by simp
@[simp, grind =] theorem get_map {f : α β} {o : Option α} {h : (o.map f).isSome} :
(o.map f).get h = f (o.get (by simpa using h)) := by
cases o with
| none => simp at h
| some a => simp
@[simp, grind _=_] theorem map_map (h : β γ) (g : α β) (x : Option α) :
@[simp] theorem map_map (h : β γ) (g : α β) (x : Option α) :
(x.map g).map h = x.map (h g) := by
cases x <;> simp only [map_none, map_some, ··]
theorem comp_map (h : β γ) (g : α β) (x : Option α) : x.map (h g) = (x.map g).map h :=
(map_map ..).symm
@[simp, grind _=_] theorem map_comp_map (f : α β) (g : β γ) :
@[simp] theorem map_comp_map (f : α β) (g : β γ) :
Option.map g Option.map f = Option.map (g f) := by funext x; simp
theorem mem_map_of_mem (g : α β) (h : a x) : g a Option.map g x := h.symm map_some ..
@@ -372,9 +382,9 @@ theorem map_inj_right {f : α → β} {o o' : Option α} (w : ∀ x y, f x = f y
(if h : c then some (a h) else none).map f = if h : c then some (f (a h)) else none := by
split <;> rfl
@[simp, grind] theorem filter_none (p : α Bool) : none.filter p = none := rfl
@[simp, grind =] theorem filter_none (p : α Bool) : none.filter p = none := rfl
@[grind] theorem filter_some : Option.filter p (some a) = if p a then some a else none := rfl
@[grind =] theorem filter_some : Option.filter p (some a) = if p a then some a else none := rfl
theorem filter_some_pos (h : p a) : Option.filter p (some a) = some a := by
rw [filter_some, if_pos h]
@@ -417,12 +427,12 @@ theorem filter_some_eq_some : Option.filter p (some a) = some a ↔ p a := by si
theorem filter_some_eq_none : Option.filter p (some a) = none ¬p a := by simp
@[grind]
@[grind =]
theorem mem_filter_iff {p : α Bool} {a : α} {o : Option α} :
a o.filter p a o p a := by
simp
@[grind]
@[grind =]
theorem bind_guard (x : Option α) (p : α Bool) :
x.bind (Option.guard p) = x.filter p := by
cases x <;> rfl
@@ -457,7 +467,7 @@ theorem filter_eq_bind (x : Option α) (p : α → Bool) :
| false => by simp [filter_some_neg h, h]
| true => by simp [filter_some_pos h, h]
@[simp, grind] theorem isSome_filter : Option.isSome (Option.filter p o) = Option.any p o :=
@[simp, grind =] theorem isSome_filter : Option.isSome (Option.filter p o) = Option.any p o :=
match o with
| none => rfl
| some a =>
@@ -536,12 +546,12 @@ theorem get_of_any_eq_true (p : α → Bool) (x : Option α) (h : x.any p = true
p (x.get (isSome_of_any h)) :=
any_eq_true_iff_get p x |>.1 h |>.2
@[grind]
@[grind =]
theorem any_map {α β : Type _} {x : Option α} {f : α β} {p : β Bool} :
(x.map f).any p = x.any (fun a => p (f a)) := by
cases x <;> rfl
@[grind]
@[grind =]
theorem all_map {α β : Type _} {x : Option α} {f : α β} {p : β Bool} :
(x.map f).all p = x.all (fun a => p (f a)) := by
cases x <;> rfl
@@ -549,13 +559,13 @@ theorem all_map {α β : Type _} {x : Option α} {f : α → β} {p : β → Boo
theorem bind_map_comm {α β} {x : Option (Option α)} {f : α β} :
x.bind (Option.map f) = (x.map (Option.map f)).bind id := by cases x <;> simp
@[grind] theorem bind_map {f : α β} {g : β Option γ} {x : Option α} :
@[grind =] theorem bind_map {f : α β} {g : β Option γ} {x : Option α} :
(x.map f).bind g = x.bind (g f) := by cases x <;> simp
@[simp, grind] theorem map_bind {f : α Option β} {g : β γ} {x : Option α} :
@[simp, grind =] theorem map_bind {f : α Option β} {g : β γ} {x : Option α} :
(x.bind f).map g = x.bind (Option.map g f) := by cases x <;> simp
@[grind] theorem join_map_eq_map_join {f : α β} {x : Option (Option α)} :
@[grind =] theorem join_map_eq_map_join {f : α β} {x : Option (Option α)} :
(x.map (Option.map f)).join = x.join.map f := by cases x <;> simp
@[grind _=_] theorem join_join {x : Option (Option (Option α))} : x.join.join = (x.map join).join := by
@@ -652,10 +662,11 @@ theorem get_none_eq_iff_true {h} : (none : Option α).get h = a ↔ True := by
simp only [guard]
split <;> simp
@[grind]
theorem guard_def (p : α Bool) :
Option.guard p = fun x => if p x then some x else none := rfl
@[grind =] theorem guard_apply : Option.guard p x = if p x then some x else none := rfl
@[deprecated guard_def (since := "2025-05-15")]
theorem guard_eq_map (p : α Bool) :
Option.guard p = fun x => Option.map (fun _ => x) (if p x then some x else none) := by
@@ -704,13 +715,13 @@ theorem merge_eq_or_eq {f : ααα} (h : ∀ a b, f a b = a f a b
| none, some _ => .inr rfl
| some a, some b => by have := h a b; simp [merge] at this ; exact this
@[simp, grind] theorem merge_none_left {f} {b : Option α} : merge f none b = b := by
@[simp, grind =] theorem merge_none_left {f} {b : Option α} : merge f none b = b := by
cases b <;> rfl
@[simp, grind] theorem merge_none_right {f} {a : Option α} : merge f a none = a := by
@[simp, grind =] theorem merge_none_right {f} {a : Option α} : merge f a none = a := by
cases a <;> rfl
@[simp, grind] theorem merge_some_some {f} {a b : α} :
@[simp, grind =] theorem merge_some_some {f} {a b : α} :
merge f (some a) (some b) = some (f a b) := rfl
@[deprecated merge_eq_or_eq (since := "2025-04-04")]
@@ -784,9 +795,9 @@ theorem get_merge {o o' : Option α} {f : ααα} {i : α} [Std.Lawful
(o.merge f o').get h = f (o.getD i) (o'.getD i) := by
cases o <;> cases o' <;> simp [Std.LawfulLeftIdentity.left_id, Std.LawfulRightIdentity.right_id]
@[simp, grind] theorem elim_none (x : β) (f : α β) : none.elim x f = x := rfl
@[simp, grind =] theorem elim_none (x : β) (f : α β) : none.elim x f = x := rfl
@[simp, grind] theorem elim_some (x : β) (f : α β) (a : α) : (some a).elim x f = f a := rfl
@[simp, grind =] theorem elim_some (x : β) (f : α β) (a : α) : (some a).elim x f = f a := rfl
@[grind =] theorem elim_filter {o : Option α} {b : β} :
Option.elim (Option.filter p o) b f = Option.elim o b (fun a => if p a then f a else b) :=
@@ -804,7 +815,8 @@ theorem get_merge {o o' : Option α} {f : ααα} {i : α} [Std.Lawful
theorem elim_guard : (guard p a).elim b f = if p a then f a else b := by
cases h : p a <;> simp [*, guard]
@[simp, grind] theorem getD_map (f : α β) (x : α) (o : Option α) :
-- I don't see how to construct a good grind pattern to instantiate this.
@[simp] theorem getD_map (f : α β) (x : α) (o : Option α) :
(o.map f).getD (f x) = f (getD o x) := by cases o <;> rfl
section choice
@@ -867,37 +879,37 @@ theorem get!_choice [Inhabited α] : (choice α).get! = (choice α).get isSome_c
end choice
@[simp, grind] theorem toList_some (a : α) : (some a).toList = [a] := rfl
@[simp, grind] theorem toList_none (α : Type _) : (none : Option α).toList = [] := rfl
@[simp, grind =] theorem toList_some (a : α) : (some a).toList = [a] := rfl
@[simp, grind =] theorem toList_none (α : Type _) : (none : Option α).toList = [] := rfl
@[simp, grind] theorem toArray_some (a : α) : (some a).toArray = #[a] := rfl
@[simp, grind] theorem toArray_none (α : Type _) : (none : Option α).toArray = #[] := rfl
@[simp, grind =] theorem toArray_some (a : α) : (some a).toArray = #[a] := rfl
@[simp, grind =] theorem toArray_none (α : Type _) : (none : Option α).toArray = #[] := rfl
-- See `Init.Data.Option.List` for lemmas about `toList`.
@[simp, grind] theorem some_or : (some a).or o = some a := rfl
@[simp, grind] theorem none_or : none.or o = o := rfl
@[simp, grind =] theorem some_or : (some a).or o = some a := rfl
@[simp, grind =] theorem none_or : none.or o = o := rfl
theorem or_eq_right_of_none {o o' : Option α} (h : o = none) : o.or o' = o' := by
cases h; simp
@[simp, grind] theorem or_some {o : Option α} : o.or (some a) = some (o.getD a) := by
@[simp, grind =] theorem or_some {o : Option α} : o.or (some a) = some (o.getD a) := by
cases o <;> rfl
@[deprecated or_some (since := "2025-05-03")]
abbrev or_some' := @or_some
@[simp, grind]
@[simp, grind =]
theorem or_none : or o none = o := by
cases o <;> rfl
theorem or_eq_bif : or o o' = bif o.isSome then o else o' := by
cases o <;> rfl
@[simp, grind] theorem isSome_or : (or o o').isSome = (o.isSome || o'.isSome) := by
@[simp, grind =] theorem isSome_or : (or o o').isSome = (o.isSome || o'.isSome) := by
cases o <;> rfl
@[simp, grind] theorem isNone_or : (or o o').isNone = (o.isNone && o'.isNone) := by
@[simp, grind =] theorem isNone_or : (or o o').isNone = (o.isNone && o'.isNone) := by
cases o <;> rfl
@[simp] theorem or_eq_none_iff : or o o' = none o = none o' = none := by
@@ -912,7 +924,7 @@ abbrev or_eq_none := @or_eq_none_iff
@[deprecated or_eq_some_iff (since := "2025-04-10")]
abbrev or_eq_some := @or_eq_some_iff
@[grind] theorem or_assoc : or (or o₁ o₂) o₃ = or o₁ (or o₂ o₃) := by
@[grind _=_] theorem or_assoc : or (or o₁ o₂) o₃ = or o₁ (or o₂ o₃) := by
cases o₁ <;> cases o₂ <;> rfl
instance : Std.Associative (or (α := α)) := @or_assoc _
@@ -923,7 +935,7 @@ instance : Std.LawfulIdentity (or (α := α)) none where
left_id := @none_or _
right_id := @or_none _
@[simp, grind]
@[simp, grind =]
theorem or_self : or o o = o := by
cases o <;> rfl
instance : Std.IdempotentOp (or (α := α)) := @or_self _
@@ -962,13 +974,15 @@ theorem guard_or_guard : (guard p a).or (guard q a) = guard (fun x => p x || q x
/-! ### `orElse` -/
/-- The `simp` normal form of `o <|> o'` is `o.or o'` via `orElse_eq_orElse` and `orElse_eq_or`. -/
@[simp, grind] theorem orElse_eq_orElse : HOrElse.hOrElse = @Option.orElse α := rfl
@[simp] theorem orElse_eq_orElse : HOrElse.hOrElse = @Option.orElse α := rfl
@[grind =] theorem orElse_apply : HOrElse.hOrElse o o' = Option.orElse o o' := rfl
theorem or_eq_orElse : or o o' = o.orElse (fun _ => o') := by
cases o <;> rfl
/-- The `simp` normal form of `o.orElse f` is o.or (f ())`. -/
@[simp, grind] theorem orElse_eq_or {o : Option α} {f} : o.orElse f = o.or (f ()) := by
@[simp, grind =] theorem orElse_eq_or {o : Option α} {f} : o.orElse f = o.or (f ()) := by
simp [or_eq_orElse]
@[deprecated or_some (since := "2025-05-03")]
@@ -1001,13 +1015,13 @@ section beq
variable [BEq α]
@[simp, grind] theorem none_beq_none : ((none : Option α) == none) = true := rfl
@[simp, grind] theorem none_beq_some (a : α) : ((none : Option α) == some a) = false := rfl
@[simp, grind] theorem some_beq_none (a : α) : ((some a : Option α) == none) = false := rfl
@[simp, grind] theorem some_beq_some {a b : α} : (some a == some b) = (a == b) := rfl
@[simp, grind =] theorem none_beq_none : ((none : Option α) == none) = true := rfl
@[simp, grind =] theorem none_beq_some (a : α) : ((none : Option α) == some a) = false := rfl
@[simp, grind =] theorem some_beq_none (a : α) : ((some a : Option α) == none) = false := rfl
@[simp, grind =] theorem some_beq_some {a b : α} : (some a == some b) = (a == b) := rfl
/-- We simplify away `isEqSome` in terms of `==`. -/
@[simp, grind] theorem isEqSome_eq_beq_some {o : Option α} : isEqSome o y = (o == some y) := by
@[simp, grind =] theorem isEqSome_eq_beq_some {o : Option α} : isEqSome o y = (o == some y) := by
cases o <;> simp [isEqSome]
@[simp] theorem reflBEq_iff : ReflBEq (Option α) ReflBEq α := by
@@ -1128,12 +1142,15 @@ theorem mem_ite_none_right {x : α} {_ : Decidable p} {l : Option α} :
@[simp] theorem isSome_dite {p : Prop} {_ : Decidable p} {b : p β} :
(if h : p then some (b h) else none).isSome = true p := by
split <;> simpa
@[simp] theorem isSome_ite {p : Prop} {_ : Decidable p} :
(if p then some b else none).isSome = true p := by
split <;> simpa
@[simp] theorem isSome_dite' {p : Prop} {_ : Decidable p} {b : ¬ p β} :
(if h : p then none else some (b h)).isSome = true ¬ p := by
split <;> simpa
@[simp] theorem isSome_ite' {p : Prop} {_ : Decidable p} :
(if p then none else some b).isSome = true ¬ p := by
split <;> simpa
@@ -1145,9 +1162,11 @@ theorem mem_ite_none_right {x : α} {_ : Decidable p} {l : Option α} :
· exfalso
simp at w
contradiction
@[simp] theorem get_ite {p : Prop} {_ : Decidable p} (h) :
(if p then some b else none).get h = b := by
simpa using get_dite (p := p) (fun _ => b) (by simpa using h)
@[simp] theorem get_dite' {p : Prop} {_ : Decidable p} (b : ¬ p β) (w) :
(if h : p then none else some (b h)).get w = b (by simpa using w) := by
split
@@ -1155,13 +1174,14 @@ theorem mem_ite_none_right {x : α} {_ : Decidable p} {l : Option α} :
simp at w
contradiction
· simp
@[simp] theorem get_ite' {p : Prop} {_ : Decidable p} (h) :
(if p then none else some b).get h = b := by
simpa using get_dite' (p := p) (fun _ => b) (by simpa using h)
end ite
@[simp, grind] theorem get_filter {α : Type _} {x : Option α} {f : α Bool} (h : (x.filter f).isSome) :
@[simp, grind =] theorem get_filter {α : Type _} {x : Option α} {f : α Bool} (h : (x.filter f).isSome) :
(x.filter f).get h = x.get (isSome_of_isSome_filter f x h) := by
cases x
· contradiction
@@ -1176,16 +1196,16 @@ end ite
@[grind = gen] theorem pbind_none' (h : x = none) : pbind x f = none := by subst h; rfl
@[grind = gen] theorem pbind_some' (h : x = some a) : pbind x f = f a h := by subst h; rfl
@[simp, grind] theorem map_pbind {o : Option α} {f : (a : α) o = some a Option β}
@[simp, grind =] theorem map_pbind {o : Option α} {f : (a : α) o = some a Option β}
{g : β γ} : (o.pbind f).map g = o.pbind (fun a h => (f a h).map g) := by
cases o <;> rfl
@[simp, grind] theorem pbind_map {α β γ : Type _} (o : Option α)
@[simp, grind =] theorem pbind_map {α β γ : Type _} (o : Option α)
(f : α β) (g : (x : β) o.map f = some x Option γ) :
(o.map f).pbind g = o.pbind (fun x h => g (f x) (h rfl)) := by
cases o <;> rfl
@[simp, grind] theorem pbind_eq_bind {α β : Type _} (o : Option α)
@[simp] theorem pbind_eq_bind {α β : Type _} (o : Option α)
(f : α Option β) : o.pbind (fun x _ => f x) = o.bind f := by
cases o <;> rfl
@@ -1253,11 +1273,11 @@ theorem get_pbind {o : Option α} {f : (a : α) → o = some a → Option β} {h
pmap f o h = none o = none := by
cases o <;> simp
@[simp, grind] theorem isSome_pmap {p : α Prop} {f : (a : α), p a β} {o : Option α} {h} :
@[simp, grind =] theorem isSome_pmap {p : α Prop} {f : (a : α), p a β} {o : Option α} {h} :
(pmap f o h).isSome = o.isSome := by
cases o <;> simp
@[simp, grind] theorem isNone_pmap {p : α Prop} {f : (a : α), p a β} {o : Option α} {h} :
@[simp, grind =] theorem isNone_pmap {p : α Prop} {f : (a : α), p a β} {o : Option α} {h} :
(pmap f o h).isNone = o.isNone := by
cases o <;> simp
@@ -1279,11 +1299,11 @@ theorem pmap_eq_map (p : α → Prop) (f : α → β) (o : Option α) (H) :
@pmap _ _ p (fun a _ => f a) o H = Option.map f o := by
cases o <;> simp
@[grind] theorem map_pmap {p : α Prop} (g : β γ) (f : a, p a β) (o H) :
@[grind =] theorem map_pmap {p : α Prop} (g : β γ) (f : a, p a β) (o H) :
Option.map g (pmap f o H) = pmap (fun a h => g (f a h)) o H := by
cases o <;> simp
@[grind] theorem pmap_map (o : Option α) (f : α β) {p : β Prop} (g : b, p b γ) (H) :
@[grind =] theorem pmap_map (o : Option α) (f : α β) {p : β Prop} (g : b, p b γ) (H) :
pmap g (o.map f) H =
pmap (fun a h => g (f a) h) o (fun a m => H (f a) (map_eq_some_iff.2 _, m, rfl)) := by
cases o <;> simp
@@ -1340,7 +1360,7 @@ theorem get_pmap {p : α → Bool} {f : (x : α) → p x → β} {o : Option α}
@[simp] theorem pelim_eq_elim : pelim o b (fun a _ => f a) = o.elim b f := by
cases o <;> simp
@[simp, grind] theorem elim_pmap {p : α Prop} (f : (a : α) p a β) (o : Option α)
@[simp, grind =] theorem elim_pmap {p : α Prop} (f : (a : α) p a β) (o : Option α)
(H : (a : α), o = some a p a) (g : γ) (g' : β γ) :
(o.pmap f H).elim g g' =
o.pelim g (fun a h => g' (f a (H a h))) := by
@@ -1387,11 +1407,11 @@ theorem pfilter_congr {α : Type u} {o o' : Option α} (ho : o = o')
congr; funext a ha
exact hf a ha
@[simp, grind] theorem pfilter_none {α : Type _} {p : (a : α) none = some a Bool} :
@[simp, grind =] theorem pfilter_none {α : Type _} {p : (a : α) none = some a Bool} :
none.pfilter p = none := by
rfl
@[simp, grind] theorem pfilter_some {α : Type _} {x : α} {p : (a : α) some x = some a Bool} :
@[simp, grind =] theorem pfilter_some {α : Type _} {x : α} {p : (a : α) some x = some a Bool} :
(some x).pfilter p = if p x rfl then some x else none := by
simp only [pfilter, cond_eq_if]
@@ -1416,7 +1436,7 @@ theorem isNone_pfilter_iff {o : Option α} {p : (a : α) → o = some a → Bool
Bool.not_eq_true, some.injEq]
exact fun h _ h' => h' h, fun h => h _ rfl
@[simp, grind] theorem get_pfilter {α : Type _} {o : Option α} {p : (a : α) o = some a Bool}
@[simp, grind =] theorem get_pfilter {α : Type _} {o : Option α} {p : (a : α) o = some a Bool}
(h : (o.pfilter p).isSome) :
(o.pfilter p).get h = o.get (isSome_of_isSome_pfilter h) := by
cases o <;> simp

View File

@@ -1,817 +1,14 @@
/-
Copyright (c) 2021 Microsoft Corporation. All rights reserved.
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Dany Fabian, Sebastian Ullrich
Authors: Kim Morrison
-/
module
prelude
public import Init.Data.String.Basic
public import Init.Data.Array.Basic
public import Init.Data.SInt.Basic
public import all Init.Data.Vector.Basic
public section
/--
The result of a comparison according to a total order.
The relationship between the compared items may be:
* `Ordering.lt`: less than
* `Ordering.eq`: equal
* `Ordering.gt`: greater than
-/
inductive Ordering where
/-- Less than. -/
| lt
/-- Equal. -/
| eq
/-- Greater than. -/
| gt
deriving Inhabited, DecidableEq, Repr
namespace Ordering
/--
Swaps less-than and greater-than ordering results.
Examples:
* `Ordering.lt.swap = Ordering.gt`
* `Ordering.eq.swap = Ordering.eq`
* `Ordering.gt.swap = Ordering.lt`
-/
@[inline, expose]
def swap : Ordering Ordering
| .lt => .gt
| .eq => .eq
| .gt => .lt
/--
If `a` and `b` are `Ordering`, then `a.then b` returns `a` unless it is `.eq`, in which case it
returns `b`. Additionally, it has “short-circuiting” behavior similar to boolean `&&`: if `a` is not
`.eq` then the expression for `b` is not evaluated.
This is a useful primitive for constructing lexicographic comparator functions. The `deriving Ord`
syntax on a structure uses the `Ord` instance to compare each field in order, combining the results
equivalently to `Ordering.then`.
Use `compareLex` to lexicographically combine two comparison functions.
Examples:
```lean example
structure Person where
name : String
age : Nat
-- Sort people first by name (in ascending order), and people with the same name by age (in
-- descending order)
instance : Ord Person where
compare a b := (compare a.name b.name).then (compare b.age a.age)
```
```lean example
#eval Ord.compare (⟨"Gert", 33⟩ : Person) ⟨"Dana", 50⟩
```
```output
Ordering.gt
```
```lean example
#eval Ord.compare (⟨"Gert", 33⟩ : Person) ⟨"Gert", 50⟩
```
```output
Ordering.gt
```
```lean example
#eval Ord.compare (⟨"Gert", 33⟩ : Person) ⟨"Gert", 20⟩
```
```output
Ordering.lt
```
-/
@[macro_inline, expose] def «then» (a b : Ordering) : Ordering :=
match a with
| .eq => b
| a => a
/--
Checks whether the ordering is `eq`.
-/
@[inline, expose]
def isEq : Ordering Bool
| eq => true
| _ => false
/--
Checks whether the ordering is not `eq`.
-/
@[inline, expose]
def isNe : Ordering Bool
| eq => false
| _ => true
/--
Checks whether the ordering is `lt` or `eq`.
-/
@[inline, expose]
def isLE : Ordering Bool
| gt => false
| _ => true
/--
Checks whether the ordering is `lt`.
-/
@[inline, expose]
def isLT : Ordering Bool
| lt => true
| _ => false
/--
Checks whether the ordering is `gt`.
-/
@[inline, expose]
def isGT : Ordering Bool
| gt => true
| _ => false
/--
Checks whether the ordering is `gt` or `eq`.
-/
@[inline, expose]
def isGE : Ordering Bool
| lt => false
| _ => true
section Lemmas
protected theorem «forall» {p : Ordering Prop} : ( o, p o) p .lt p .eq p .gt := by
constructor
· intro h
exact h _, h _, h _
· rintro h₁, h₂, h₃ (_ | _ | _) <;> assumption
protected theorem «exists» {p : Ordering Prop} : ( o, p o) p .lt p .eq p .gt := by
constructor
· rintro (_ | _ | _), h
· exact .inl h
· exact .inr (.inl h)
· exact .inr (.inr h)
· rintro (h | h | h) <;> exact _, h
instance [DecidablePred p] : Decidable ( o : Ordering, p o) :=
decidable_of_decidable_of_iff Ordering.«forall».symm
instance [DecidablePred p] : Decidable ( o : Ordering, p o) :=
decidable_of_decidable_of_iff Ordering.«exists».symm
@[simp] theorem isLT_lt : lt.isLT := rfl
@[simp] theorem isLE_lt : lt.isLE := rfl
@[simp] theorem isEq_lt : lt.isEq = false := rfl
@[simp] theorem isNe_lt : lt.isNe = true := rfl
@[simp] theorem isGE_lt : lt.isGE = false := rfl
@[simp] theorem isGT_lt : lt.isGT = false := rfl
@[simp] theorem isLT_eq : eq.isLT = false := rfl
@[simp] theorem isLE_eq : eq.isLE := rfl
@[simp] theorem isEq_eq : eq.isEq := rfl
@[simp] theorem isNe_eq : eq.isNe = false := rfl
@[simp] theorem isGE_eq : eq.isGE := rfl
@[simp] theorem isGT_eq : eq.isGT = false := rfl
@[simp] theorem isLT_gt : gt.isLT = false := rfl
@[simp] theorem isLE_gt : gt.isLE = false := rfl
@[simp] theorem isEq_gt : gt.isEq = false := rfl
@[simp] theorem isNe_gt : gt.isNe = true := rfl
@[simp] theorem isGE_gt : gt.isGE := rfl
@[simp] theorem isGT_gt : gt.isGT := rfl
@[simp] theorem lt_beq_eq : (lt == eq) = false := rfl
@[simp] theorem lt_beq_gt : (lt == gt) = false := rfl
@[simp] theorem eq_beq_lt : (eq == lt) = false := rfl
@[simp] theorem eq_beq_gt : (eq == gt) = false := rfl
@[simp] theorem gt_beq_lt : (gt == lt) = false := rfl
@[simp] theorem gt_beq_eq : (gt == eq) = false := rfl
@[simp] theorem swap_lt : lt.swap = .gt := rfl
@[simp] theorem swap_eq : eq.swap = .eq := rfl
@[simp] theorem swap_gt : gt.swap = .lt := rfl
theorem eq_eq_of_isLE_of_isLE_swap : {o : Ordering}, o.isLE o.swap.isLE o = .eq := by decide
theorem eq_eq_of_isGE_of_isGE_swap : {o : Ordering}, o.isGE o.swap.isGE o = .eq := by decide
theorem eq_eq_of_isLE_of_isGE : {o : Ordering}, o.isLE o.isGE o = .eq := by decide
theorem eq_swap_iff_eq_eq : {o : Ordering}, o = o.swap o = .eq := by decide
theorem eq_eq_of_eq_swap : {o : Ordering}, o = o.swap o = .eq := eq_swap_iff_eq_eq.mp
@[simp] theorem isLE_eq_false : {o : Ordering}, o.isLE = false o = .gt := by decide
@[simp] theorem isGE_eq_false : {o : Ordering}, o.isGE = false o = .lt := by decide
@[simp] theorem isNe_eq_false : {o : Ordering}, o.isNe = false o = .eq := by decide
@[simp] theorem isEq_eq_false : {o : Ordering}, o.isEq = false ¬o = .eq := by decide
@[simp] theorem swap_eq_gt : {o : Ordering}, o.swap = .gt o = .lt := by decide
@[simp] theorem swap_eq_lt : {o : Ordering}, o.swap = .lt o = .gt := by decide
@[simp] theorem swap_eq_eq : {o : Ordering}, o.swap = .eq o = .eq := by decide
@[simp] theorem isLT_swap : {o : Ordering}, o.swap.isLT = o.isGT := by decide
@[simp] theorem isLE_swap : {o : Ordering}, o.swap.isLE = o.isGE := by decide
@[simp] theorem isEq_swap : {o : Ordering}, o.swap.isEq = o.isEq := by decide
@[simp] theorem isNe_swap : {o : Ordering}, o.swap.isNe = o.isNe := by decide
@[simp] theorem isGE_swap : {o : Ordering}, o.swap.isGE = o.isLE := by decide
@[simp] theorem isGT_swap : {o : Ordering}, o.swap.isGT = o.isLT := by decide
theorem isLE_of_eq_lt : {o : Ordering}, o = .lt o.isLE := by decide
theorem isLE_of_eq_eq : {o : Ordering}, o = .eq o.isLE := by decide
theorem isGE_of_eq_gt : {o : Ordering}, o = .gt o.isGE := by decide
theorem isGE_of_eq_eq : {o : Ordering}, o = .eq o.isGE := by decide
theorem ne_eq_of_eq_lt : {o : Ordering}, o = .lt o .eq := by decide
theorem ne_eq_of_eq_gt : {o : Ordering}, o = .gt o .eq := by decide
@[simp] theorem isLT_iff_eq_lt : {o : Ordering}, o.isLT o = .lt := by decide
@[simp] theorem isGT_iff_eq_gt : {o : Ordering}, o.isGT o = .gt := by decide
@[simp] theorem isEq_iff_eq_eq : {o : Ordering}, o.isEq o = .eq := by decide
@[simp] theorem isNe_iff_ne_eq : {o : Ordering}, o.isNe ¬o = .eq := by decide
theorem isLE_iff_ne_gt : {o : Ordering}, o.isLE ¬o = .gt := by decide
theorem isGE_iff_ne_lt : {o : Ordering}, o.isGE ¬o = .lt := by decide
theorem isLE_iff_eq_lt_or_eq_eq : {o : Ordering}, o.isLE o = .lt o = .eq := by decide
theorem isGE_iff_eq_gt_or_eq_eq : {o : Ordering}, o.isGE o = .gt o = .eq := by decide
theorem isLT_eq_beq_lt : {o : Ordering}, o.isLT = (o == .lt) := by decide
theorem isLE_eq_not_beq_gt : {o : Ordering}, o.isLE = (!o == .gt) := by decide
theorem isLE_eq_isLT_or_isEq : {o : Ordering}, o.isLE = (o.isLT || o.isEq) := by decide
theorem isGT_eq_beq_gt : {o : Ordering}, o.isGT = (o == .gt) := by decide
theorem isGE_eq_not_beq_lt : {o : Ordering}, o.isGE = (!o == .lt) := by decide
theorem isGE_eq_isGT_or_isEq : {o : Ordering}, o.isGE = (o.isGT || o.isEq) := by decide
theorem isEq_eq_beq_eq : {o : Ordering}, o.isEq = (o == .eq) := by decide
theorem isNe_eq_not_beq_eq : {o : Ordering}, o.isNe = (!o == .eq) := by decide
theorem isNe_eq_isLT_or_isGT : {o : Ordering}, o.isNe = (o.isLT || o.isGT) := by decide
@[simp] theorem not_isLT_eq_isGE : {o : Ordering}, (!o.isLT) = o.isGE := by decide
@[simp] theorem not_isLE_eq_isGT : {o : Ordering}, (!o.isLE) = o.isGT := by decide
@[simp] theorem not_isGT_eq_isLE : {o : Ordering}, (!o.isGT) = o.isLE := by decide
@[simp] theorem not_isGE_eq_isLT : {o : Ordering}, (!o.isGE) = o.isLT := by decide
@[simp] theorem not_isNe_eq_isEq : {o : Ordering}, (!o.isNe) = o.isEq := by decide
theorem not_isEq_eq_isNe : {o : Ordering}, (!o.isEq) = o.isNe := by decide
theorem ne_lt_iff_isGE : {o : Ordering}, ¬o = .lt o.isGE := by decide
theorem ne_gt_iff_isLE : {o : Ordering}, ¬o = .gt o.isLE := by decide
@[simp] theorem swap_swap : {o : Ordering}, o.swap.swap = o := by decide
@[simp] theorem swap_inj : {o₁ o₂ : Ordering}, o₁.swap = o₂.swap o₁ = o₂ := by decide
theorem swap_then : (o₁ o₂ : Ordering), (o₁.then o₂).swap = o₁.swap.then o₂.swap := by decide
theorem then_eq_lt : {o₁ o₂ : Ordering}, o₁.then o₂ = lt o₁ = lt o₁ = eq o₂ = lt := by decide
theorem then_eq_gt : {o₁ o₂ : Ordering}, o₁.then o₂ = gt o₁ = gt o₁ = eq o₂ = gt := by decide
@[simp] theorem then_eq_eq : {o₁ o₂ : Ordering}, o₁.then o₂ = eq o₁ = eq o₂ = eq := by decide
theorem isLT_then : {o₁ o₂ : Ordering}, (o₁.then o₂).isLT = (o₁.isLT || o₁.isEq && o₂.isLT) := by decide
theorem isEq_then : {o₁ o₂ : Ordering}, (o₁.then o₂).isEq = (o₁.isEq && o₂.isEq) := by decide
theorem isNe_then : {o₁ o₂ : Ordering}, (o₁.then o₂).isNe = (o₁.isNe || o₂.isNe) := by decide
theorem isGT_then : {o₁ o₂ : Ordering}, (o₁.then o₂).isGT = (o₁.isGT || o₁.isEq && o₂.isGT) := by decide
@[simp] theorem lt_then {o : Ordering} : lt.then o = lt := rfl
@[simp] theorem gt_then {o : Ordering} : gt.then o = gt := rfl
@[simp] theorem eq_then {o : Ordering} : eq.then o = o := rfl
@[simp] theorem then_eq : {o : Ordering}, o.then eq = o := by decide
@[simp] theorem then_self : {o : Ordering}, o.then o = o := by decide
theorem then_assoc : (o₁ o₂ o₃ : Ordering), (o₁.then o₂).then o₃ = o₁.then (o₂.then o₃) := by decide
theorem isLE_then_iff_or : {o₁ o₂ : Ordering}, (o₁.then o₂).isLE o₁ = lt (o₁ = eq o₂.isLE) := by decide
theorem isLE_then_iff_and : {o₁ o₂ : Ordering}, (o₁.then o₂).isLE o₁.isLE (o₁ = lt o₂.isLE) := by decide
theorem isLE_left_of_isLE_then : {o₁ o₂ : Ordering}, (o₁.then o₂).isLE o₁.isLE := by decide
theorem isGE_left_of_isGE_then : {o₁ o₂ : Ordering}, (o₁.then o₂).isGE o₁.isGE := by decide
instance : Std.Associative Ordering.then := then_assoc
instance : Std.IdempotentOp Ordering.then := fun _ => then_self
instance : Std.LawfulIdentity Ordering.then eq where
left_id _ := eq_then
right_id _ := then_eq
end Lemmas
end Ordering
/--
Uses decidable less-than and equality relations to find an `Ordering`.
In particular, if `x < y` then the result is `Ordering.lt`. If `x = y` then the result is
`Ordering.eq`. Otherwise, it is `Ordering.gt`.
`compareOfLessAndBEq` uses `BEq` instead of `DecidableEq`.
-/
@[inline, expose] def compareOfLessAndEq {α} (x y : α) [LT α] [Decidable (x < y)] [DecidableEq α] : Ordering :=
if x < y then Ordering.lt
else if x = y then Ordering.eq
else Ordering.gt
/--
Uses a decidable less-than relation and Boolean equality to find an `Ordering`.
In particular, if `x < y` then the result is `Ordering.lt`. If `x == y` then the result is
`Ordering.eq`. Otherwise, it is `Ordering.gt`.
`compareOfLessAndEq` uses `DecidableEq` instead of `BEq`.
-/
@[inline] def compareOfLessAndBEq {α} (x y : α) [LT α] [Decidable (x < y)] [BEq α] : Ordering :=
if x < y then .lt
else if x == y then .eq
else .gt
/--
Compares `a` and `b` lexicographically by `cmp₁` and `cmp₂`.
`a` and `b` are first compared by `cmp₁`. If this returns `Ordering.eq`, `a` and `b` are compared
by `cmp₂` to break the tie.
To lexicographically combine two `Ordering`s, use `Ordering.then`.
-/
@[inline, expose] def compareLex (cmp₁ cmp₂ : α β Ordering) (a : α) (b : β) : Ordering :=
(cmp₁ a b).then (cmp₂ a b)
section Lemmas
@[simp]
theorem compareLex_eq_eq {α} {cmp₁ cmp₂} {a b : α} :
compareLex cmp₁ cmp₂ a b = .eq cmp₁ a b = .eq cmp₂ a b = .eq := by
simp [compareLex]
theorem compareOfLessAndEq_eq_swap_of_lt_iff_not_gt_and_ne {α : Type u} [LT α] [DecidableLT α] [DecidableEq α]
(h : x y : α, x < y ¬ y < x x y) {x y : α} :
compareOfLessAndEq x y = (compareOfLessAndEq y x).swap := by
simp only [compareOfLessAndEq]
split
· rename_i h'
rw [h] at h'
simp only [h'.1, h'.2.symm, reduceIte, Ordering.swap_gt]
· split
· rename_i h'
have : ¬ y < y := Not.imp (·.2 rfl) <| (h y y).mp
simp only [h', this, reduceIte, Ordering.swap_eq]
· rename_i h' h''
replace h' := (h y x).mpr h', Ne.symm h''
simp only [h', reduceIte, Ordering.swap_lt]
theorem lt_iff_not_gt_and_ne_of_antisymm_of_total_of_not_le
{α : Type u} [LT α] [LE α] [DecidableLT α] [DecidableEq α]
(antisymm : {x y : α}, x y y x x = y)
(total : (x y : α), x y y x) (not_le : {x y : α}, ¬ x y y < x) (x y : α) :
x < y ¬ y < x x y := by
simp only [ not_le, Classical.not_not]
constructor
· intro h
have refl := by cases total y y <;> assumption
exact (total _ _).resolve_left h, fun h' => (h' h) refl
· intro h₁, h₂ h₃
exact h₂ (antisymm h₁ h₃)
theorem compareOfLessAndEq_eq_swap
{α : Type u} [LT α] [LE α] [DecidableLT α] [DecidableEq α]
(antisymm : {x y : α}, x y y x x = y)
(total : (x y : α), x y y x) (not_le : {x y : α}, ¬ x y y < x) {x y : α} :
compareOfLessAndEq x y = (compareOfLessAndEq y x).swap := by
apply compareOfLessAndEq_eq_swap_of_lt_iff_not_gt_and_ne
exact lt_iff_not_gt_and_ne_of_antisymm_of_total_of_not_le antisymm total not_le
@[simp]
theorem compareOfLessAndEq_eq_lt
{α : Type u} [LT α] [LE α] [DecidableLT α] [DecidableEq α] {x y : α} :
compareOfLessAndEq x y = .lt x < y := by
rw [compareOfLessAndEq]
repeat' split <;> simp_all
theorem compareOfLessAndEq_eq_eq
{α : Type u} [LT α] [LE α] [DecidableLT α] [DecidableLE α] [DecidableEq α]
(refl : (x : α), x x) (not_le : {x y : α}, ¬ x y y < x) {x y : α} :
compareOfLessAndEq x y = .eq x = y := by
rw [compareOfLessAndEq]
repeat' split <;> try (simp_all; done)
simp only [reduceCtorEq, false_iff]
rintro rfl
rename_i hlt
simp [ not_le] at hlt
exact hlt (refl x)
theorem compareOfLessAndEq_eq_gt_of_lt_iff_not_gt_and_ne
{α : Type u} [LT α] [LE α] [DecidableLT α] [DecidableEq α] {x y : α}
(h : x y : α, x < y ¬ y < x x y) :
compareOfLessAndEq x y = .gt y < x := by
rw [compareOfLessAndEq_eq_swap_of_lt_iff_not_gt_and_ne h, Ordering.swap_eq_gt]
exact compareOfLessAndEq_eq_lt
theorem compareOfLessAndEq_eq_gt
{α : Type u} [LT α] [LE α] [DecidableLT α] [DecidableEq α]
(antisymm : {x y : α}, x y y x x = y)
(total : (x y : α), x y y x) (not_le : {x y : α}, ¬ x y y < x) (x y : α) :
compareOfLessAndEq x y = .gt y < x := by
apply compareOfLessAndEq_eq_gt_of_lt_iff_not_gt_and_ne
exact lt_iff_not_gt_and_ne_of_antisymm_of_total_of_not_le antisymm total not_le
theorem isLE_compareOfLessAndEq
{α : Type u} [LT α] [LE α] [DecidableLT α] [DecidableLE α] [DecidableEq α]
(antisymm : {x y : α}, x y y x x = y)
(not_le : {x y : α}, ¬ x y y < x) (total : (x y : α), x y y x) {x y : α} :
(compareOfLessAndEq x y).isLE x y := by
have refl (a : α) := by cases total a a <;> assumption
rw [Ordering.isLE_iff_eq_lt_or_eq_eq, compareOfLessAndEq_eq_lt,
compareOfLessAndEq_eq_eq refl not_le]
constructor
· rintro (h | rfl)
· rw [ not_le] at h
exact total _ _ |>.resolve_left h
· exact refl x
· intro hle
by_cases hge : x y
· exact Or.inr <| antisymm hle hge
· exact Or.inl <| not_le.mp hge
end Lemmas
/--
`Ord α` provides a computable total order on `α`, in terms of the
`compare : αα → Ordering` function.
Typically instances will be transitive, reflexive, and antisymmetric,
but this is not enforced by the typeclass.
There is a derive handler, so appending `deriving Ord` to an inductive type or structure
will attempt to create an `Ord` instance.
-/
@[ext]
class Ord (α : Type u) where
/-- Compare two elements in `α` using the comparator contained in an `[Ord α]` instance. -/
compare : α α Ordering
export Ord (compare)
/--
Compares two values by comparing the results of applying a function.
In particular, `x` is compared to `y` by comparing `f x` and `f y`.
Examples:
* `compareOn (·.length) "apple" "banana" = .lt`
* `compareOn (· % 3) 5 6 = .gt`
* `compareOn (·.foldl max 0) [1, 2, 3] [3, 2, 1] = .eq`
-/
@[inline, expose] def compareOn [ord : Ord β] (f : α β) (x y : α) : Ordering :=
compare (f x) (f y)
instance : Ord Nat where
compare x y := compareOfLessAndEq x y
instance : Ord Int where
compare x y := compareOfLessAndEq x y
instance : Ord Bool where
compare
| false, true => Ordering.lt
| true, false => Ordering.gt
| _, _ => Ordering.eq
instance : Ord String where
compare x y := compareOfLessAndEq x y
instance (n : Nat) : Ord (Fin n) where
compare x y := compare x.val y.val
instance : Ord UInt8 where
compare x y := compareOfLessAndEq x y
instance : Ord UInt16 where
compare x y := compareOfLessAndEq x y
instance : Ord UInt32 where
compare x y := compareOfLessAndEq x y
instance : Ord UInt64 where
compare x y := compareOfLessAndEq x y
instance : Ord USize where
compare x y := compareOfLessAndEq x y
instance : Ord Char where
compare x y := compareOfLessAndEq x y
instance : Ord Int8 where
compare x y := compareOfLessAndEq x y
instance : Ord Int16 where
compare x y := compareOfLessAndEq x y
instance : Ord Int32 where
compare x y := compareOfLessAndEq x y
instance : Ord Int64 where
compare x y := compareOfLessAndEq x y
instance : Ord ISize where
compare x y := compareOfLessAndEq x y
instance {n} : Ord (BitVec n) where
compare x y := compareOfLessAndEq x y
instance [Ord α] : Ord (Option α) where
compare
| none, none => .eq
| none, some _ => .lt
| some _, none => .gt
| some x, some y => compare x y
instance : Ord Ordering where
compare := compareOn (·.toCtorIdx)
namespace List
@[specialize, expose]
protected def compareLex {α} (cmp : α α Ordering) :
List α List α Ordering
| [], [] => .eq
| [], _ => .lt
| _, [] => .gt
| x :: xs, y :: ys => match cmp x y with
| .lt => .lt
| .eq => xs.compareLex cmp ys
| .gt => .gt
instance {α} [Ord α] : Ord (List α) where
compare := List.compareLex compare
protected theorem compare_eq_compareLex {α} [Ord α] :
compare (α := List α) = List.compareLex compare := rfl
protected theorem compareLex_cons_cons {α} {cmp} {x y : α} {xs ys : List α} :
(x :: xs).compareLex cmp (y :: ys) = (cmp x y).then (xs.compareLex cmp ys) := by
rw [List.compareLex]
split <;> simp_all
@[simp]
protected theorem compare_cons_cons {α} [Ord α] {x y : α} {xs ys : List α} :
compare (x :: xs) (y :: ys) = (compare x y).then (compare xs ys) :=
List.compareLex_cons_cons
protected theorem compareLex_nil_cons {α} {cmp} {x : α} {xs : List α} :
[].compareLex cmp (x :: xs) = .lt :=
rfl
@[simp]
protected theorem compare_nil_cons {α} [Ord α] {x : α} {xs : List α} :
compare [] (x :: xs) = .lt :=
rfl
protected theorem compareLex_cons_nil {α} {cmp} {x : α} {xs : List α} :
(x :: xs).compareLex cmp [] = .gt :=
rfl
@[simp]
protected theorem compare_cons_nil {α} [Ord α] {x : α} {xs : List α} :
compare (x :: xs) [] = .gt :=
rfl
protected theorem compareLex_nil_nil {α} {cmp} :
[].compareLex (α := α) cmp [] = .eq :=
rfl
@[simp]
protected theorem compare_nil_nil {α} [Ord α] :
compare (α := List α) [] [] = .eq :=
rfl
protected theorem isLE_compareLex_nil_left {α} {cmp} {xs : List α} :
(List.compareLex (cmp := cmp) [] xs).isLE := by
cases xs <;> simp [List.compareLex_nil_nil, List.compareLex_nil_cons]
protected theorem isLE_compare_nil_left {α} [Ord α] {xs : List α} :
(compare [] xs).isLE :=
List.isLE_compareLex_nil_left
protected theorem isLE_compareLex_nil_right {α} {cmp} {xs : List α} :
(List.compareLex (cmp := cmp) xs []).isLE xs = [] := by
cases xs <;> simp [List.compareLex_nil_nil, List.compareLex_cons_nil]
@[simp]
protected theorem isLE_compare_nil_right {α} [Ord α] {xs : List α} :
(compare xs []).isLE xs = [] :=
List.isLE_compareLex_nil_right
protected theorem isGE_compareLex_nil_left {α} {cmp} {xs : List α} :
(List.compareLex (cmp := cmp) [] xs).isGE xs = [] := by
cases xs <;> simp [List.compareLex_nil_nil, List.compareLex_nil_cons]
@[simp]
protected theorem isGE_compare_nil_left {α} [Ord α] {xs : List α} :
(compare [] xs).isGE xs = [] :=
List.isGE_compareLex_nil_left
protected theorem isGE_compareLex_nil_right {α} {cmp} {xs : List α} :
(List.compareLex (cmp := cmp) xs []).isGE := by
cases xs <;> simp [List.compareLex_nil_nil, List.compareLex_cons_nil]
protected theorem isGE_compare_nil_right {α} [Ord α] {xs : List α} :
(compare xs []).isGE :=
List.isGE_compareLex_nil_right
protected theorem compareLex_nil_left_eq_eq {α} {cmp} {xs : List α} :
List.compareLex cmp [] xs = .eq xs = [] := by
cases xs <;> simp [List.compareLex_nil_nil, List.compareLex_nil_cons]
@[simp]
protected theorem compare_nil_left_eq_eq {α} [Ord α] {xs : List α} :
compare [] xs = .eq xs = [] :=
List.compareLex_nil_left_eq_eq
protected theorem compareLex_nil_right_eq_eq {α} {cmp} {xs : List α} :
xs.compareLex cmp [] = .eq xs = [] := by
cases xs <;> simp [List.compareLex_nil_nil, List.compareLex_cons_nil]
@[simp]
protected theorem compare_nil_right_eq_eq {α} [Ord α] {xs : List α} :
compare xs [] = .eq xs = [] :=
List.compareLex_nil_right_eq_eq
end List
namespace Array
@[specialize]
protected def compareLex {α} (cmp : α α Ordering) (a₁ a₂ : Array α) : Ordering :=
go 0
where go i :=
if h₁ : a₁.size <= i then
if a₂.size <= i then .eq else .lt
else
if h₂ : a₂.size <= i then
.gt
else match cmp a₁[i] a₂[i] with
| .lt => .lt
| .eq => go (i + 1)
| .gt => .gt
termination_by a₁.size - i
instance {α} [Ord α] : Ord (Array α) where
compare := Array.compareLex compare
protected theorem compare_eq_compareLex {α} [Ord α] :
compare (α := Array α) = Array.compareLex compare := rfl
private theorem compareLex.go_succ {α} {cmp} {x₁ x₂} {a₁ a₂ : List α} {i} :
compareLex.go cmp (x₁ :: a₁).toArray (x₂ :: a₂).toArray (i + 1) =
compareLex.go cmp a₁.toArray a₂.toArray i := by
induction i using Array.compareLex.go.induct cmp a₁.toArray a₂.toArray
all_goals try
conv => congr <;> rw [compareLex.go]
simp
repeat' split <;> (try simp_all; done)
protected theorem _root_.List.compareLex_eq_compareLex_toArray {α} {cmp} {l₁ l₂ : List α} :
List.compareLex cmp l₁ l₂ = Array.compareLex cmp l₁.toArray l₂.toArray := by
simp only [Array.compareLex]
induction l₁ generalizing l₂ with
| nil =>
cases l₂
· simp [Array.compareLex.go, List.compareLex_nil_nil]
· simp [Array.compareLex.go, List.compareLex_nil_cons]
| cons x xs ih =>
cases l₂
· simp [Array.compareLex.go, List.compareLex_cons_nil]
· rw [Array.compareLex.go, List.compareLex_cons_cons]
simp only [List.size_toArray, List.length_cons, Nat.le_zero_eq, Nat.add_one_ne_zero,
reduceDIte, List.getElem_toArray, List.getElem_cons_zero, Nat.zero_add]
split <;> simp_all [compareLex.go_succ]
protected theorem _root_.List.compare_eq_compare_toArray {α} [Ord α] {l₁ l₂ : List α} :
compare l₁ l₂ = compare l₁.toArray l₂.toArray :=
List.compareLex_eq_compareLex_toArray
protected theorem compareLex_eq_compareLex_toList {α} {cmp} {a₁ a₂ : Array α} :
Array.compareLex cmp a₁ a₂ = List.compareLex cmp a₁.toList a₂.toList := by
rw [List.compareLex_eq_compareLex_toArray]
protected theorem compare_eq_compare_toList {α} [Ord α] {a₁ a₂ : Array α} :
compare a₁ a₂ = compare a₁.toList a₂.toList :=
Array.compareLex_eq_compareLex_toList
end Array
namespace Vector
@[expose]
protected def compareLex {α n} (cmp : α α Ordering) (a b : Vector α n) : Ordering :=
Array.compareLex cmp a.toArray b.toArray
instance {α n} [Ord α] : Ord (Vector α n) where
compare := Vector.compareLex compare
protected theorem compareLex_eq_compareLex_toArray {α n cmp} {a b : Vector α n} :
Vector.compareLex cmp a b = Array.compareLex cmp a.toArray b.toArray :=
rfl
protected theorem compareLex_eq_compareLex_toList {α n cmp} {a b : Vector α n} :
Vector.compareLex cmp a b = List.compareLex cmp a.toList b.toList :=
Array.compareLex_eq_compareLex_toList
protected theorem compare_eq_compare_toArray {α n} [Ord α] {a b : Vector α n} :
compare a b = compare a.toArray b.toArray :=
rfl
protected theorem compare_eq_compare_toList {α n} [Ord α] {a b : Vector α n} :
compare a b = compare a.toList b.toList :=
Array.compare_eq_compare_toList
end Vector
/-- The lexicographic order on pairs. -/
@[expose] def lexOrd [Ord α] [Ord β] : Ord (α × β) where
compare := compareLex (compareOn (·.1)) (compareOn (·.2))
/--
Constructs an `BEq` instance from an `Ord` instance that asserts that the result of `compare` is
`Ordering.eq`.
-/
@[expose] def beqOfOrd [Ord α] : BEq α where
beq a b := (compare a b).isEq
/--
Constructs an `LT` instance from an `Ord` instance that asserts that the result of `compare` is
`Ordering.lt`.
-/
@[expose] def ltOfOrd [Ord α] : LT α where
lt a b := compare a b = Ordering.lt
@[inline]
instance [Ord α] : DecidableRel (@LT.lt α ltOfOrd) := fun a b =>
decidable_of_bool (compare a b).isLT Ordering.isLT_iff_eq_lt
/--
Constructs an `LT` instance from an `Ord` instance that asserts that the result of `compare`
satisfies `Ordering.isLE`.
-/
@[expose] def leOfOrd [Ord α] : LE α where
le a b := (compare a b).isLE
@[inline]
instance [Ord α] : DecidableRel (@LE.le α leOfOrd) := fun _ _ => instDecidableEqBool ..
namespace Ord
/--
Constructs a `BEq` instance from an `Ord` instance.
-/
@[expose] protected abbrev toBEq (ord : Ord α) : BEq α :=
beqOfOrd
/--
Constructs an `LT` instance from an `Ord` instance.
-/
@[expose] protected abbrev toLT (ord : Ord α) : LT α :=
ltOfOrd
/--
Constructs an `LE` instance from an `Ord` instance.
-/
@[expose] protected abbrev toLE (ord : Ord α) : LE α :=
leOfOrd
/--
Inverts the order of an `Ord` instance.
The result is an `Ord α` instance that returns `Ordering.lt` when `ord` would return `Ordering.gt`
and that returns `Ordering.gt` when `ord` would return `Ordering.lt`.
-/
@[expose] protected def opposite (ord : Ord α) : Ord α where
compare x y := ord.compare y x
/--
Constructs an `Ord` instance that compares values according to the results of `f`.
In particular, `ord.on f` compares `x` and `y` by comparing `f x` and `f y` according to `ord`.
The function `compareOn` can be used to perform this comparison without constructing an intermediate
`Ord` instance.
-/
@[expose] protected def on (_ : Ord β) (f : α β) : Ord α where
compare := compareOn f
/--
Constructs the lexicographic order on products `α × β` from orders for `α` and `β`.
-/
@[expose] protected abbrev lex (_ : Ord α) (_ : Ord β) : Ord (α × β) :=
lexOrd
/--
Constructs an `Ord` instance from two existing instances by combining them lexicographically.
The resulting instance compares elements first by `ord₁` and then, if this returns `Ordering.eq`, by
`ord₂`.
The function `compareLex` can be used to perform this comparison without constructing an
intermediate `Ord` instance. `Ordering.then` can be used to lexicographically combine the results of
comparisons.
-/
@[expose] protected def lex' (ord₁ ord₂ : Ord α) : Ord α where
compare := compareLex ord₁.compare ord₂.compare
end Ord
public import Init.Data.Ord.Basic
public import Init.Data.Ord.BitVec
public import Init.Data.Ord.SInt
public import Init.Data.Ord.String
public import Init.Data.Ord.UInt
public import Init.Data.Ord.Vector

View File

@@ -0,0 +1,825 @@
/-
Copyright (c) 2021 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Dany Fabian, Sebastian Ullrich
-/
module
prelude
public import Init.Data.String.Basic
public import Init.Data.Array.Basic
public import Init.Data.SInt.Basic
public import all Init.Data.Vector.Basic
public section
/--
The result of a comparison according to a total order.
The relationship between the compared items may be:
* `Ordering.lt`: less than
* `Ordering.eq`: equal
* `Ordering.gt`: greater than
-/
inductive Ordering where
/-- Less than. -/
| lt
/-- Equal. -/
| eq
/-- Greater than. -/
| gt
deriving Inhabited, DecidableEq, Repr
namespace Ordering
/--
Swaps less-than and greater-than ordering results.
Examples:
* `Ordering.lt.swap = Ordering.gt`
* `Ordering.eq.swap = Ordering.eq`
* `Ordering.gt.swap = Ordering.lt`
-/
@[inline, expose]
def swap : Ordering Ordering
| .lt => .gt
| .eq => .eq
| .gt => .lt
/--
If `a` and `b` are `Ordering`, then `a.then b` returns `a` unless it is `.eq`, in which case it
returns `b`. Additionally, it has “short-circuiting” behavior similar to boolean `&&`: if `a` is not
`.eq` then the expression for `b` is not evaluated.
This is a useful primitive for constructing lexicographic comparator functions. The `deriving Ord`
syntax on a structure uses the `Ord` instance to compare each field in order, combining the results
equivalently to `Ordering.then`.
Use `compareLex` to lexicographically combine two comparison functions.
Examples:
```lean example
structure Person where
name : String
age : Nat
-- Sort people first by name (in ascending order), and people with the same name by age (in
-- descending order)
instance : Ord Person where
compare a b := (compare a.name b.name).then (compare b.age a.age)
```
```lean example
#eval Ord.compare (⟨"Gert", 33⟩ : Person) ⟨"Dana", 50⟩
```
```output
Ordering.gt
```
```lean example
#eval Ord.compare (⟨"Gert", 33⟩ : Person) ⟨"Gert", 50⟩
```
```output
Ordering.gt
```
```lean example
#eval Ord.compare (⟨"Gert", 33⟩ : Person) ⟨"Gert", 20⟩
```
```output
Ordering.lt
```
-/
@[macro_inline, expose] def «then» (a b : Ordering) : Ordering :=
match a with
| .eq => b
| a => a
/-- Version of `Ordering.then'` for proof by reflection. -/
@[expose] noncomputable def then' (a b : Ordering) : Ordering :=
Ordering.rec a b a a
/--
Checks whether the ordering is `eq`.
-/
@[inline, expose]
def isEq : Ordering Bool
| eq => true
| _ => false
/--
Checks whether the ordering is not `eq`.
-/
@[inline, expose]
def isNe : Ordering Bool
| eq => false
| _ => true
/--
Checks whether the ordering is `lt` or `eq`.
-/
@[inline, expose]
def isLE : Ordering Bool
| gt => false
| _ => true
/--
Checks whether the ordering is `lt`.
-/
@[inline, expose]
def isLT : Ordering Bool
| lt => true
| _ => false
/--
Checks whether the ordering is `gt`.
-/
@[inline, expose]
def isGT : Ordering Bool
| gt => true
| _ => false
/--
Checks whether the ordering is `gt` or `eq`.
-/
@[inline, expose]
def isGE : Ordering Bool
| lt => false
| _ => true
section Lemmas
protected theorem «forall» {p : Ordering Prop} : ( o, p o) p .lt p .eq p .gt := by
constructor
· intro h
exact h _, h _, h _
· rintro h₁, h₂, h₃ (_ | _ | _) <;> assumption
protected theorem «exists» {p : Ordering Prop} : ( o, p o) p .lt p .eq p .gt := by
constructor
· rintro (_ | _ | _), h
· exact .inl h
· exact .inr (.inl h)
· exact .inr (.inr h)
· rintro (h | h | h) <;> exact _, h
instance [DecidablePred p] : Decidable ( o : Ordering, p o) :=
decidable_of_decidable_of_iff Ordering.«forall».symm
instance [DecidablePred p] : Decidable ( o : Ordering, p o) :=
decidable_of_decidable_of_iff Ordering.«exists».symm
@[simp] theorem isLT_lt : lt.isLT := rfl
@[simp] theorem isLE_lt : lt.isLE := rfl
@[simp] theorem isEq_lt : lt.isEq = false := rfl
@[simp] theorem isNe_lt : lt.isNe = true := rfl
@[simp] theorem isGE_lt : lt.isGE = false := rfl
@[simp] theorem isGT_lt : lt.isGT = false := rfl
@[simp] theorem isLT_eq : eq.isLT = false := rfl
@[simp] theorem isLE_eq : eq.isLE := rfl
@[simp] theorem isEq_eq : eq.isEq := rfl
@[simp] theorem isNe_eq : eq.isNe = false := rfl
@[simp] theorem isGE_eq : eq.isGE := rfl
@[simp] theorem isGT_eq : eq.isGT = false := rfl
@[simp] theorem isLT_gt : gt.isLT = false := rfl
@[simp] theorem isLE_gt : gt.isLE = false := rfl
@[simp] theorem isEq_gt : gt.isEq = false := rfl
@[simp] theorem isNe_gt : gt.isNe = true := rfl
@[simp] theorem isGE_gt : gt.isGE := rfl
@[simp] theorem isGT_gt : gt.isGT := rfl
@[simp] theorem lt_beq_eq : (lt == eq) = false := rfl
@[simp] theorem lt_beq_gt : (lt == gt) = false := rfl
@[simp] theorem eq_beq_lt : (eq == lt) = false := rfl
@[simp] theorem eq_beq_gt : (eq == gt) = false := rfl
@[simp] theorem gt_beq_lt : (gt == lt) = false := rfl
@[simp] theorem gt_beq_eq : (gt == eq) = false := rfl
@[simp] theorem swap_lt : lt.swap = .gt := rfl
@[simp] theorem swap_eq : eq.swap = .eq := rfl
@[simp] theorem swap_gt : gt.swap = .lt := rfl
theorem eq_eq_of_isLE_of_isLE_swap : {o : Ordering}, o.isLE o.swap.isLE o = .eq := by decide
theorem eq_eq_of_isGE_of_isGE_swap : {o : Ordering}, o.isGE o.swap.isGE o = .eq := by decide
theorem eq_eq_of_isLE_of_isGE : {o : Ordering}, o.isLE o.isGE o = .eq := by decide
theorem eq_eq_iff_isLE_and_isGE : {o : Ordering}, o = .eq o.isLE o.isGE := by decide
theorem eq_swap_iff_eq_eq : {o : Ordering}, o = o.swap o = .eq := by decide
theorem eq_eq_of_eq_swap : {o : Ordering}, o = o.swap o = .eq := eq_swap_iff_eq_eq.mp
@[simp] theorem isLE_eq_false : {o : Ordering}, o.isLE = false o = .gt := by decide
@[simp] theorem isGE_eq_false : {o : Ordering}, o.isGE = false o = .lt := by decide
@[simp] theorem isNe_eq_false : {o : Ordering}, o.isNe = false o = .eq := by decide
@[simp] theorem isEq_eq_false : {o : Ordering}, o.isEq = false ¬o = .eq := by decide
@[simp] theorem swap_eq_gt : {o : Ordering}, o.swap = .gt o = .lt := by decide
@[simp] theorem swap_eq_lt : {o : Ordering}, o.swap = .lt o = .gt := by decide
@[simp] theorem swap_eq_eq : {o : Ordering}, o.swap = .eq o = .eq := by decide
@[simp] theorem isLT_swap : {o : Ordering}, o.swap.isLT = o.isGT := by decide
@[simp] theorem isLE_swap : {o : Ordering}, o.swap.isLE = o.isGE := by decide
@[simp] theorem isEq_swap : {o : Ordering}, o.swap.isEq = o.isEq := by decide
@[simp] theorem isNe_swap : {o : Ordering}, o.swap.isNe = o.isNe := by decide
@[simp] theorem isGE_swap : {o : Ordering}, o.swap.isGE = o.isLE := by decide
@[simp] theorem isGT_swap : {o : Ordering}, o.swap.isGT = o.isLT := by decide
theorem isLE_of_eq_lt : {o : Ordering}, o = .lt o.isLE := by decide
theorem isLE_of_eq_eq : {o : Ordering}, o = .eq o.isLE := by decide
theorem isGE_of_eq_gt : {o : Ordering}, o = .gt o.isGE := by decide
theorem isGE_of_eq_eq : {o : Ordering}, o = .eq o.isGE := by decide
theorem ne_eq_of_eq_lt : {o : Ordering}, o = .lt o .eq := by decide
theorem ne_eq_of_eq_gt : {o : Ordering}, o = .gt o .eq := by decide
@[simp] theorem isLT_iff_eq_lt : {o : Ordering}, o.isLT o = .lt := by decide
@[simp] theorem isGT_iff_eq_gt : {o : Ordering}, o.isGT o = .gt := by decide
@[simp] theorem isEq_iff_eq_eq : {o : Ordering}, o.isEq o = .eq := by decide
@[simp] theorem isNe_iff_ne_eq : {o : Ordering}, o.isNe ¬o = .eq := by decide
theorem isLE_iff_ne_gt : {o : Ordering}, o.isLE ¬o = .gt := by decide
theorem isGE_iff_ne_lt : {o : Ordering}, o.isGE ¬o = .lt := by decide
theorem isLE_iff_eq_lt_or_eq_eq : {o : Ordering}, o.isLE o = .lt o = .eq := by decide
theorem isGE_iff_eq_gt_or_eq_eq : {o : Ordering}, o.isGE o = .gt o = .eq := by decide
theorem isLT_eq_beq_lt : {o : Ordering}, o.isLT = (o == .lt) := by decide
theorem isLE_eq_not_beq_gt : {o : Ordering}, o.isLE = (!o == .gt) := by decide
theorem isLE_eq_isLT_or_isEq : {o : Ordering}, o.isLE = (o.isLT || o.isEq) := by decide
theorem isGT_eq_beq_gt : {o : Ordering}, o.isGT = (o == .gt) := by decide
theorem isGE_eq_not_beq_lt : {o : Ordering}, o.isGE = (!o == .lt) := by decide
theorem isGE_eq_isGT_or_isEq : {o : Ordering}, o.isGE = (o.isGT || o.isEq) := by decide
theorem isEq_eq_beq_eq : {o : Ordering}, o.isEq = (o == .eq) := by decide
theorem isNe_eq_not_beq_eq : {o : Ordering}, o.isNe = (!o == .eq) := by decide
theorem isNe_eq_isLT_or_isGT : {o : Ordering}, o.isNe = (o.isLT || o.isGT) := by decide
@[simp] theorem not_isLT_eq_isGE : {o : Ordering}, (!o.isLT) = o.isGE := by decide
@[simp] theorem not_isLE_eq_isGT : {o : Ordering}, (!o.isLE) = o.isGT := by decide
@[simp] theorem not_isGT_eq_isLE : {o : Ordering}, (!o.isGT) = o.isLE := by decide
@[simp] theorem not_isGE_eq_isLT : {o : Ordering}, (!o.isGE) = o.isLT := by decide
@[simp] theorem not_isNe_eq_isEq : {o : Ordering}, (!o.isNe) = o.isEq := by decide
theorem not_isEq_eq_isNe : {o : Ordering}, (!o.isEq) = o.isNe := by decide
theorem ne_lt_iff_isGE : {o : Ordering}, ¬o = .lt o.isGE := by decide
theorem ne_gt_iff_isLE : {o : Ordering}, ¬o = .gt o.isLE := by decide
@[simp] theorem swap_swap : {o : Ordering}, o.swap.swap = o := by decide
@[simp] theorem swap_inj : {o₁ o₂ : Ordering}, o₁.swap = o₂.swap o₁ = o₂ := by decide
theorem swap_then : (o₁ o₂ : Ordering), (o₁.then o₂).swap = o₁.swap.then o₂.swap := by decide
theorem then_eq_lt : {o₁ o₂ : Ordering}, o₁.then o₂ = lt o₁ = lt o₁ = eq o₂ = lt := by decide
theorem then_eq_gt : {o₁ o₂ : Ordering}, o₁.then o₂ = gt o₁ = gt o₁ = eq o₂ = gt := by decide
@[simp] theorem then_eq_eq : {o₁ o₂ : Ordering}, o₁.then o₂ = eq o₁ = eq o₂ = eq := by decide
theorem isLT_then : {o₁ o₂ : Ordering}, (o₁.then o₂).isLT = (o₁.isLT || o₁.isEq && o₂.isLT) := by decide
theorem isEq_then : {o₁ o₂ : Ordering}, (o₁.then o₂).isEq = (o₁.isEq && o₂.isEq) := by decide
theorem isNe_then : {o₁ o₂ : Ordering}, (o₁.then o₂).isNe = (o₁.isNe || o₂.isNe) := by decide
theorem isGT_then : {o₁ o₂ : Ordering}, (o₁.then o₂).isGT = (o₁.isGT || o₁.isEq && o₂.isGT) := by decide
@[simp] theorem lt_then {o : Ordering} : lt.then o = lt := rfl
@[simp] theorem gt_then {o : Ordering} : gt.then o = gt := rfl
@[simp] theorem eq_then {o : Ordering} : eq.then o = o := rfl
@[simp] theorem then_eq : {o : Ordering}, o.then eq = o := by decide
@[simp] theorem then_self : {o : Ordering}, o.then o = o := by decide
theorem then_assoc : (o₁ o₂ o₃ : Ordering), (o₁.then o₂).then o₃ = o₁.then (o₂.then o₃) := by decide
theorem isLE_then_iff_or : {o₁ o₂ : Ordering}, (o₁.then o₂).isLE o₁ = lt (o₁ = eq o₂.isLE) := by decide
theorem isLE_then_iff_and : {o₁ o₂ : Ordering}, (o₁.then o₂).isLE o₁.isLE (o₁ = lt o₂.isLE) := by decide
theorem isLE_left_of_isLE_then : {o₁ o₂ : Ordering}, (o₁.then o₂).isLE o₁.isLE := by decide
theorem isGE_left_of_isGE_then : {o₁ o₂ : Ordering}, (o₁.then o₂).isGE o₁.isGE := by decide
instance : Std.Associative Ordering.then := then_assoc
instance : Std.IdempotentOp Ordering.then := fun _ => then_self
instance : Std.LawfulIdentity Ordering.then eq where
left_id _ := eq_then
right_id _ := then_eq
theorem then'_eq_then (a b : Ordering) : a.then' b = a.then b := by
cases a <;> simp [Ordering.then', Ordering.then]
end Lemmas
end Ordering
/--
Uses decidable less-than and equality relations to find an `Ordering`.
In particular, if `x < y` then the result is `Ordering.lt`. If `x = y` then the result is
`Ordering.eq`. Otherwise, it is `Ordering.gt`.
`compareOfLessAndBEq` uses `BEq` instead of `DecidableEq`.
-/
@[inline, expose] def compareOfLessAndEq {α} (x y : α) [LT α] [Decidable (x < y)] [DecidableEq α] : Ordering :=
if x < y then Ordering.lt
else if x = y then Ordering.eq
else Ordering.gt
/--
Uses a decidable less-than relation and Boolean equality to find an `Ordering`.
In particular, if `x < y` then the result is `Ordering.lt`. If `x == y` then the result is
`Ordering.eq`. Otherwise, it is `Ordering.gt`.
`compareOfLessAndEq` uses `DecidableEq` instead of `BEq`.
-/
@[inline] def compareOfLessAndBEq {α} (x y : α) [LT α] [Decidable (x < y)] [BEq α] : Ordering :=
if x < y then .lt
else if x == y then .eq
else .gt
/--
Compares `a` and `b` lexicographically by `cmp₁` and `cmp₂`.
`a` and `b` are first compared by `cmp₁`. If this returns `Ordering.eq`, `a` and `b` are compared
by `cmp₂` to break the tie.
To lexicographically combine two `Ordering`s, use `Ordering.then`.
-/
@[inline, expose] def compareLex (cmp₁ cmp₂ : α β Ordering) (a : α) (b : β) : Ordering :=
(cmp₁ a b).then (cmp₂ a b)
section Lemmas
@[simp]
theorem compareLex_eq_eq {α} {cmp₁ cmp₂} {a b : α} :
compareLex cmp₁ cmp₂ a b = .eq cmp₁ a b = .eq cmp₂ a b = .eq := by
simp [compareLex]
theorem compareOfLessAndEq_eq_swap_of_lt_iff_not_gt_and_ne {α : Type u} [LT α] [DecidableLT α] [DecidableEq α]
(h : x y : α, x < y ¬ y < x x y) {x y : α} :
compareOfLessAndEq x y = (compareOfLessAndEq y x).swap := by
simp only [compareOfLessAndEq]
split
· rename_i h'
rw [h] at h'
simp only [h'.1, h'.2.symm, reduceIte, Ordering.swap_gt]
· split
· rename_i h'
have : ¬ y < y := Not.imp (·.2 rfl) <| (h y y).mp
simp only [h', this, reduceIte, Ordering.swap_eq]
· rename_i h' h''
replace h' := (h y x).mpr h', Ne.symm h''
simp only [h', reduceIte, Ordering.swap_lt]
theorem lt_iff_not_gt_and_ne_of_antisymm_of_total_of_not_le
{α : Type u} [LT α] [LE α] [DecidableLT α] [DecidableEq α]
(antisymm : {x y : α}, x y y x x = y)
(total : (x y : α), x y y x) (not_le : {x y : α}, ¬ x y y < x) (x y : α) :
x < y ¬ y < x x y := by
simp only [ not_le, Classical.not_not]
constructor
· intro h
have refl := by cases total y y <;> assumption
exact (total _ _).resolve_left h, fun h' => (h' h) refl
· intro h₁, h₂ h₃
exact h₂ (antisymm h₁ h₃)
theorem compareOfLessAndEq_eq_swap
{α : Type u} [LT α] [LE α] [DecidableLT α] [DecidableEq α]
(antisymm : {x y : α}, x y y x x = y)
(total : (x y : α), x y y x) (not_le : {x y : α}, ¬ x y y < x) {x y : α} :
compareOfLessAndEq x y = (compareOfLessAndEq y x).swap := by
apply compareOfLessAndEq_eq_swap_of_lt_iff_not_gt_and_ne
exact lt_iff_not_gt_and_ne_of_antisymm_of_total_of_not_le antisymm total not_le
@[simp]
theorem compareOfLessAndEq_eq_lt
{α : Type u} [LT α] [LE α] [DecidableLT α] [DecidableEq α] {x y : α} :
compareOfLessAndEq x y = .lt x < y := by
rw [compareOfLessAndEq]
repeat' split <;> simp_all
theorem compareOfLessAndEq_eq_eq
{α : Type u} [LT α] [LE α] [DecidableLT α] [DecidableLE α] [DecidableEq α]
(refl : (x : α), x x) (not_le : {x y : α}, ¬ x y y < x) {x y : α} :
compareOfLessAndEq x y = .eq x = y := by
rw [compareOfLessAndEq]
repeat' split <;> try (simp_all; done)
simp only [reduceCtorEq, false_iff]
rintro rfl
rename_i hlt
simp [ not_le] at hlt
exact hlt (refl x)
theorem compareOfLessAndEq_eq_gt_of_lt_iff_not_gt_and_ne
{α : Type u} [LT α] [LE α] [DecidableLT α] [DecidableEq α] {x y : α}
(h : x y : α, x < y ¬ y < x x y) :
compareOfLessAndEq x y = .gt y < x := by
rw [compareOfLessAndEq_eq_swap_of_lt_iff_not_gt_and_ne h, Ordering.swap_eq_gt]
exact compareOfLessAndEq_eq_lt
theorem compareOfLessAndEq_eq_gt
{α : Type u} [LT α] [LE α] [DecidableLT α] [DecidableEq α]
(antisymm : {x y : α}, x y y x x = y)
(total : (x y : α), x y y x) (not_le : {x y : α}, ¬ x y y < x) (x y : α) :
compareOfLessAndEq x y = .gt y < x := by
apply compareOfLessAndEq_eq_gt_of_lt_iff_not_gt_and_ne
exact lt_iff_not_gt_and_ne_of_antisymm_of_total_of_not_le antisymm total not_le
theorem isLE_compareOfLessAndEq
{α : Type u} [LT α] [LE α] [DecidableLT α] [DecidableLE α] [DecidableEq α]
(antisymm : {x y : α}, x y y x x = y)
(not_le : {x y : α}, ¬ x y y < x) (total : (x y : α), x y y x) {x y : α} :
(compareOfLessAndEq x y).isLE x y := by
have refl (a : α) := by cases total a a <;> assumption
rw [Ordering.isLE_iff_eq_lt_or_eq_eq, compareOfLessAndEq_eq_lt,
compareOfLessAndEq_eq_eq refl not_le]
constructor
· rintro (h | rfl)
· rw [ not_le] at h
exact total _ _ |>.resolve_left h
· exact refl x
· intro hle
by_cases hge : x y
· exact Or.inr <| antisymm hle hge
· exact Or.inl <| not_le.mp hge
end Lemmas
/--
`Ord α` provides a computable total order on `α`, in terms of the
`compare : αα → Ordering` function.
Typically instances will be transitive, reflexive, and antisymmetric,
but this is not enforced by the typeclass.
There is a derive handler, so appending `deriving Ord` to an inductive type or structure
will attempt to create an `Ord` instance.
-/
@[ext]
class Ord (α : Type u) where
/-- Compare two elements in `α` using the comparator contained in an `[Ord α]` instance. -/
compare : α α Ordering
export Ord (compare)
/--
Compares two values by comparing the results of applying a function.
In particular, `x` is compared to `y` by comparing `f x` and `f y`.
Examples:
* `compareOn (·.length) "apple" "banana" = .lt`
* `compareOn (· % 3) 5 6 = .gt`
* `compareOn (·.foldl max 0) [1, 2, 3] [3, 2, 1] = .eq`
-/
@[inline, expose] def compareOn [ord : Ord β] (f : α β) (x y : α) : Ordering :=
compare (f x) (f y)
instance : Ord Nat where
compare x y := compareOfLessAndEq x y
instance : Ord Int where
compare x y := compareOfLessAndEq x y
instance : Ord Bool where
compare
| false, true => Ordering.lt
| true, false => Ordering.gt
| _, _ => Ordering.eq
instance : Ord String where
compare x y := compareOfLessAndEq x y
instance (n : Nat) : Ord (Fin n) where
compare x y := compare x.val y.val
instance : Ord UInt8 where
compare x y := compareOfLessAndEq x y
instance : Ord UInt16 where
compare x y := compareOfLessAndEq x y
instance : Ord UInt32 where
compare x y := compareOfLessAndEq x y
instance : Ord UInt64 where
compare x y := compareOfLessAndEq x y
instance : Ord USize where
compare x y := compareOfLessAndEq x y
instance : Ord Char where
compare x y := compareOfLessAndEq x y
instance : Ord Int8 where
compare x y := compareOfLessAndEq x y
instance : Ord Int16 where
compare x y := compareOfLessAndEq x y
instance : Ord Int32 where
compare x y := compareOfLessAndEq x y
instance : Ord Int64 where
compare x y := compareOfLessAndEq x y
instance : Ord ISize where
compare x y := compareOfLessAndEq x y
instance {n} : Ord (BitVec n) where
compare x y := compareOfLessAndEq x y
instance [Ord α] : Ord (Option α) where
compare
| none, none => .eq
| none, some _ => .lt
| some _, none => .gt
| some x, some y => compare x y
instance : Ord Ordering where
compare := compareOn (·.toCtorIdx)
namespace List
@[specialize, expose]
protected def compareLex {α} (cmp : α α Ordering) :
List α List α Ordering
| [], [] => .eq
| [], _ => .lt
| _, [] => .gt
| x :: xs, y :: ys => match cmp x y with
| .lt => .lt
| .eq => xs.compareLex cmp ys
| .gt => .gt
instance {α} [Ord α] : Ord (List α) where
compare := List.compareLex compare
protected theorem compare_eq_compareLex {α} [Ord α] :
compare (α := List α) = List.compareLex compare := rfl
protected theorem compareLex_cons_cons {α} {cmp} {x y : α} {xs ys : List α} :
(x :: xs).compareLex cmp (y :: ys) = (cmp x y).then (xs.compareLex cmp ys) := by
rw [List.compareLex]
split <;> simp_all
@[simp]
protected theorem compare_cons_cons {α} [Ord α] {x y : α} {xs ys : List α} :
compare (x :: xs) (y :: ys) = (compare x y).then (compare xs ys) :=
List.compareLex_cons_cons
protected theorem compareLex_nil_cons {α} {cmp} {x : α} {xs : List α} :
[].compareLex cmp (x :: xs) = .lt :=
rfl
@[simp]
protected theorem compare_nil_cons {α} [Ord α] {x : α} {xs : List α} :
compare [] (x :: xs) = .lt :=
rfl
protected theorem compareLex_cons_nil {α} {cmp} {x : α} {xs : List α} :
(x :: xs).compareLex cmp [] = .gt :=
rfl
@[simp]
protected theorem compare_cons_nil {α} [Ord α] {x : α} {xs : List α} :
compare (x :: xs) [] = .gt :=
rfl
protected theorem compareLex_nil_nil {α} {cmp} :
[].compareLex (α := α) cmp [] = .eq :=
rfl
@[simp]
protected theorem compare_nil_nil {α} [Ord α] :
compare (α := List α) [] [] = .eq :=
rfl
protected theorem isLE_compareLex_nil_left {α} {cmp} {xs : List α} :
(List.compareLex (cmp := cmp) [] xs).isLE := by
cases xs <;> simp [List.compareLex_nil_nil, List.compareLex_nil_cons]
protected theorem isLE_compare_nil_left {α} [Ord α] {xs : List α} :
(compare [] xs).isLE :=
List.isLE_compareLex_nil_left
protected theorem isLE_compareLex_nil_right {α} {cmp} {xs : List α} :
(List.compareLex (cmp := cmp) xs []).isLE xs = [] := by
cases xs <;> simp [List.compareLex_nil_nil, List.compareLex_cons_nil]
@[simp]
protected theorem isLE_compare_nil_right {α} [Ord α] {xs : List α} :
(compare xs []).isLE xs = [] :=
List.isLE_compareLex_nil_right
protected theorem isGE_compareLex_nil_left {α} {cmp} {xs : List α} :
(List.compareLex (cmp := cmp) [] xs).isGE xs = [] := by
cases xs <;> simp [List.compareLex_nil_nil, List.compareLex_nil_cons]
@[simp]
protected theorem isGE_compare_nil_left {α} [Ord α] {xs : List α} :
(compare [] xs).isGE xs = [] :=
List.isGE_compareLex_nil_left
protected theorem isGE_compareLex_nil_right {α} {cmp} {xs : List α} :
(List.compareLex (cmp := cmp) xs []).isGE := by
cases xs <;> simp [List.compareLex_nil_nil, List.compareLex_cons_nil]
protected theorem isGE_compare_nil_right {α} [Ord α] {xs : List α} :
(compare xs []).isGE :=
List.isGE_compareLex_nil_right
protected theorem compareLex_nil_left_eq_eq {α} {cmp} {xs : List α} :
List.compareLex cmp [] xs = .eq xs = [] := by
cases xs <;> simp [List.compareLex_nil_nil, List.compareLex_nil_cons]
@[simp]
protected theorem compare_nil_left_eq_eq {α} [Ord α] {xs : List α} :
compare [] xs = .eq xs = [] :=
List.compareLex_nil_left_eq_eq
protected theorem compareLex_nil_right_eq_eq {α} {cmp} {xs : List α} :
xs.compareLex cmp [] = .eq xs = [] := by
cases xs <;> simp [List.compareLex_nil_nil, List.compareLex_cons_nil]
@[simp]
protected theorem compare_nil_right_eq_eq {α} [Ord α] {xs : List α} :
compare xs [] = .eq xs = [] :=
List.compareLex_nil_right_eq_eq
end List
namespace Array
@[specialize]
protected def compareLex {α} (cmp : α α Ordering) (a₁ a₂ : Array α) : Ordering :=
go 0
where go i :=
if h₁ : a₁.size <= i then
if a₂.size <= i then .eq else .lt
else
if h₂ : a₂.size <= i then
.gt
else match cmp a₁[i] a₂[i] with
| .lt => .lt
| .eq => go (i + 1)
| .gt => .gt
termination_by a₁.size - i
instance {α} [Ord α] : Ord (Array α) where
compare := Array.compareLex compare
protected theorem compare_eq_compareLex {α} [Ord α] :
compare (α := Array α) = Array.compareLex compare := rfl
private theorem compareLex.go_succ {α} {cmp} {x₁ x₂} {a₁ a₂ : List α} {i} :
compareLex.go cmp (x₁ :: a₁).toArray (x₂ :: a₂).toArray (i + 1) =
compareLex.go cmp a₁.toArray a₂.toArray i := by
induction i using Array.compareLex.go.induct cmp a₁.toArray a₂.toArray
all_goals try
conv => congr <;> rw [compareLex.go]
simp
repeat' split <;> (try simp_all; done)
protected theorem _root_.List.compareLex_eq_compareLex_toArray {α} {cmp} {l₁ l₂ : List α} :
List.compareLex cmp l₁ l₂ = Array.compareLex cmp l₁.toArray l₂.toArray := by
simp only [Array.compareLex]
induction l₁ generalizing l₂ with
| nil =>
cases l₂
· simp [Array.compareLex.go, List.compareLex_nil_nil]
· simp [Array.compareLex.go, List.compareLex_nil_cons]
| cons x xs ih =>
cases l₂
· simp [Array.compareLex.go, List.compareLex_cons_nil]
· rw [Array.compareLex.go, List.compareLex_cons_cons]
simp only [List.size_toArray, List.length_cons, Nat.le_zero_eq, Nat.add_one_ne_zero,
reduceDIte, List.getElem_toArray, List.getElem_cons_zero, Nat.zero_add]
split <;> simp_all [compareLex.go_succ]
protected theorem _root_.List.compare_eq_compare_toArray {α} [Ord α] {l₁ l₂ : List α} :
compare l₁ l₂ = compare l₁.toArray l₂.toArray :=
List.compareLex_eq_compareLex_toArray
protected theorem compareLex_eq_compareLex_toList {α} {cmp} {a₁ a₂ : Array α} :
Array.compareLex cmp a₁ a₂ = List.compareLex cmp a₁.toList a₂.toList := by
rw [List.compareLex_eq_compareLex_toArray]
protected theorem compare_eq_compare_toList {α} [Ord α] {a₁ a₂ : Array α} :
compare a₁ a₂ = compare a₁.toList a₂.toList :=
Array.compareLex_eq_compareLex_toList
end Array
namespace Vector
@[expose]
protected def compareLex {α n} (cmp : α α Ordering) (a b : Vector α n) : Ordering :=
Array.compareLex cmp a.toArray b.toArray
instance {α n} [Ord α] : Ord (Vector α n) where
compare := Vector.compareLex compare
protected theorem compareLex_eq_compareLex_toArray {α n cmp} {a b : Vector α n} :
Vector.compareLex cmp a b = Array.compareLex cmp a.toArray b.toArray :=
rfl
protected theorem compareLex_eq_compareLex_toList {α n cmp} {a b : Vector α n} :
Vector.compareLex cmp a b = List.compareLex cmp a.toList b.toList :=
Array.compareLex_eq_compareLex_toList
protected theorem compare_eq_compare_toArray {α n} [Ord α] {a b : Vector α n} :
compare a b = compare a.toArray b.toArray :=
rfl
protected theorem compare_eq_compare_toList {α n} [Ord α] {a b : Vector α n} :
compare a b = compare a.toList b.toList :=
Array.compare_eq_compare_toList
end Vector
/-- The lexicographic order on pairs. -/
@[expose] def lexOrd [Ord α] [Ord β] : Ord (α × β) where
compare := compareLex (compareOn (·.1)) (compareOn (·.2))
/--
Constructs an `BEq` instance from an `Ord` instance that asserts that the result of `compare` is
`Ordering.eq`.
-/
@[expose] def beqOfOrd [Ord α] : BEq α where
beq a b := (compare a b).isEq
/--
Constructs an `LT` instance from an `Ord` instance that asserts that the result of `compare` is
`Ordering.lt`.
-/
@[expose] def ltOfOrd [Ord α] : LT α where
lt a b := compare a b = Ordering.lt
@[inline]
instance [Ord α] : DecidableRel (@LT.lt α ltOfOrd) := fun a b =>
decidable_of_bool (compare a b).isLT Ordering.isLT_iff_eq_lt
/--
Constructs an `LT` instance from an `Ord` instance that asserts that the result of `compare`
satisfies `Ordering.isLE`.
-/
@[expose] def leOfOrd [Ord α] : LE α where
le a b := (compare a b).isLE
@[inline]
instance [Ord α] : DecidableRel (@LE.le α leOfOrd) := fun _ _ => instDecidableEqBool ..
namespace Ord
/--
Constructs a `BEq` instance from an `Ord` instance.
-/
@[expose] protected abbrev toBEq (ord : Ord α) : BEq α :=
beqOfOrd
/--
Constructs an `LT` instance from an `Ord` instance.
-/
@[expose] protected abbrev toLT (ord : Ord α) : LT α :=
ltOfOrd
/--
Constructs an `LE` instance from an `Ord` instance.
-/
@[expose] protected abbrev toLE (ord : Ord α) : LE α :=
leOfOrd
/--
Inverts the order of an `Ord` instance.
The result is an `Ord α` instance that returns `Ordering.lt` when `ord` would return `Ordering.gt`
and that returns `Ordering.gt` when `ord` would return `Ordering.lt`.
-/
@[expose] protected def opposite (ord : Ord α) : Ord α where
compare x y := ord.compare y x
/--
Constructs an `Ord` instance that compares values according to the results of `f`.
In particular, `ord.on f` compares `x` and `y` by comparing `f x` and `f y` according to `ord`.
The function `compareOn` can be used to perform this comparison without constructing an intermediate
`Ord` instance.
-/
@[expose] protected def on (_ : Ord β) (f : α β) : Ord α where
compare := compareOn f
/--
Constructs the lexicographic order on products `α × β` from orders for `α` and `β`.
-/
@[expose] protected abbrev lex (_ : Ord α) (_ : Ord β) : Ord (α × β) :=
lexOrd
/--
Constructs an `Ord` instance from two existing instances by combining them lexicographically.
The resulting instance compares elements first by `ord₁` and then, if this returns `Ordering.eq`, by
`ord₂`.
The function `compareLex` can be used to perform this comparison without constructing an
intermediate `Ord` instance. `Ordering.then` can be used to lexicographically combine the results of
comparisons.
-/
@[expose] protected def lex' (ord₁ ord₂ : Ord α) : Ord α where
compare := compareLex ord₁.compare ord₂.compare
end Ord

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