Compare commits

..

197 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
2068 changed files with 21344 additions and 6024 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
@@ -205,7 +221,7 @@ jobs:
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,22 +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 }}
- name: Upload Build Artifact
if: always() && matrix.name == 'Linux Lake (cached)'
uses: actions/upload-artifact@v4
with:
path: build

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

@@ -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

@@ -53,11 +53,6 @@ There are also two alternative presets that combine some of these options you ca
Select the C/C++ compilers to use. Official Lean releases currently use Clang;
see also `.github/workflows/ci.yml` for the CI config.
* `-DUSE_LAKE=ON`\
Experimental option to build the core libraries using Lake instead of `lean.mk`. Caveats:
* As native code compilation is still handled by cmake, changes to stage0/ (such as from `git pull`) are picked up only when invoking the build via `make`, not via `Refresh Dependencies` in the editor.
* `USE_LAKE` is not yet compatible with `LAKE_ARTIFACT_CACHE`
Lean will automatically use [CCache](https://ccache.dev/) if available to avoid
redundant builds, especially after stage 0 has been updated.

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,7 +1,7 @@
#!/usr/bin/env bash
set -euxo pipefail
cmake --preset release -DUSE_LAKE=ON 1>&2
cmake --preset release 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

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`")

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.
@@ -2499,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

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

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} :

View File

@@ -312,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
@@ -838,9 +838,10 @@ theorem mem_of_contains_eq_true [BEq α] [LawfulBEq α] {a : α} {as : Array α}
cases as
simp
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
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
@@ -2893,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]
@@ -2954,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

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

@@ -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

@@ -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

@@ -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}
@@ -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
@@ -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

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

@@ -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

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

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

@@ -361,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

@@ -1280,7 +1280,7 @@ noncomputable def diseq_eq_subst_cert (x : Var) (p₁ : Poly) (p₂ : Poly) (p
theorem eq_diseq_subst (ctx : Context) (x : Var) (p₁ : Poly) (p₂ : Poly) (p₃ : Poly)
: diseq_eq_subst_cert x p₁ p₂ p₃ p₁.denote' ctx = 0 p₂.denote' ctx 0 p₃.denote' ctx 0 := by
simp [diseq_eq_subst_cert]
intros _ _; subst p₃
intro _ _; subst p₃
intro h₁ h₂
simp [*]
@@ -2121,6 +2121,81 @@ theorem not_le_of_le (ctx : Context) (p₁ p₂ : Poly) : not_le_of_le_cert p₁
have := not_le_of_le' ctx p₁ p₂ h 0 h₁; simp at this
simp [*]
theorem natCast_sub (x y : Nat)
: (NatCast.natCast (x - y) : Int)
=
if (NatCast.natCast y : Int) + (-1)*NatCast.natCast x 0 then
(NatCast.natCast x : Int) + -1*NatCast.natCast y
else
(0 : Int) := by
change ((x - y) : Int) = if (y : Int) + (-1)*x 0 then (x : Int) + (-1)*y else 0
rw [Int.neg_mul, Int.sub_eq_add_neg, Int.one_mul]
rw [Int.neg_mul, Int.sub_eq_add_neg, Int.one_mul]
split
next h =>
replace h := Int.le_of_sub_nonpos h
rw [Int.ofNat_le] at h
rw [Int.ofNat_sub h]
next h =>
have : ¬ (y : Int) x := by
intro h
replace h := Int.sub_nonpos_of_le h
contradiction
rw [Int.ofNat_le] at this
rw [Lean.Omega.Int.ofNat_sub_eq_zero this]
/-! Helper theorem for linearizing nonlinear terms -/
@[expose] noncomputable def var_eq_cert (x : Var) (k : Int) (p : Poly) : Bool :=
Poly.rec (fun _ => false)
(fun k₁ x' p' _ => Poly.rec (fun k₂ => k₁ != 0 && x == x' && k == -k₂/k₁) (fun _ _ _ _ => false) p')
p
theorem var_eq (ctx : Context) (x : Var) (k : Int) (p : Poly) : var_eq_cert x k p p.denote' ctx = 0 x.denote ctx = k := by
simp [var_eq_cert]; cases p <;> simp; next k₁ x' p' =>
cases p' <;> simp; next k₂ =>
intro h₁ _ _; subst x' k; intro h₂
replace h₂ := Int.neg_eq_of_add_eq_zero h₂
rw [Int.neg_eq_comm] at h₂
rw [ h₂]; clear h₂; simp; rw [Int.mul_comm, Int.mul_ediv_cancel]
assumption
@[expose] noncomputable def of_var_eq_mul_cert (x : Var) (k : Int) (y : Var) (p : Poly) : Bool :=
p.beq' (.add 1 x (.add (-k) y (.num 0)))
theorem of_var_eq_mul (ctx : Context) (x : Var) (k : Int) (y : Var) (p : Poly) : of_var_eq_mul_cert x k y p x.denote ctx = k * y.denote ctx p.denote' ctx = 0 := by
simp [of_var_eq_mul_cert]; intro _ h; subst p; simp [h]
rw [Int.neg_mul, Int.sub_eq_add_neg, Int.sub_self]
@[expose] noncomputable def of_var_eq_var_cert (x : Var) (y : Var) (p : Poly) : Bool :=
p.beq' (.add 1 x (.add (-1) y (.num 0)))
theorem of_var_eq_var (ctx : Context) (x : Var) (y : Var) (p : Poly) : of_var_eq_var_cert x y p x.denote ctx = y.denote ctx p.denote' ctx = 0 := by
simp [of_var_eq_var_cert]; intro _ h; subst p; simp [h]
rw [ Int.sub_eq_add_neg, Int.sub_self]
@[expose] noncomputable def of_var_eq_cert (x : Var) (k : Int) (p : Poly) : Bool :=
p.beq' (.add 1 x (.num (-k)))
theorem of_var_eq (ctx : Context) (x : Var) (k : Int) (p : Poly) : of_var_eq_cert x k p x.denote ctx = k p.denote' ctx = 0 := by
simp [of_var_eq_cert]; intro _ h; subst p; simp [h]
rw [ Int.sub_eq_add_neg, Int.sub_self]
theorem eq_one_mul (a : Int) : a = 1*a := by simp
theorem mul_eq_kk (a b k₁ k₂ k : Int) (h₁ : a = k₁) (h₂ : b = k₂) (h₃ : k₁*k₂ == k) : a*b = k := by simp_all
theorem mul_eq_kkx (a b k₁ k₂ c k : Int) (h₁ : a = k₁) (h₂ : b = k₂*c) (h₃ : k₁*k₂ == k) : a*b = k*c := by
simp at h₃; rw [h₁, h₂, Int.mul_assoc, h₃]
theorem mul_eq_kxk (a b k₁ c k₂ k : Int) (h₁ : a = k₁*c) (h₂ : b = k₂) (h₃ : k₁*k₂ == k) : a*b = k*c := by
simp at h₃; rw [h₁, h₂, Int.mul_comm, Int.mul_assoc, Int.mul_comm k₂, h₃]
theorem mul_eq_zero_left (a b : Int) (h : a = 0) : a*b = 0 := by simp [*]
theorem mul_eq_zero_right (a b : Int) (h : b = 0) : a*b = 0 := by simp [*]
theorem div_eq (a b k : Int) (h : b = k) : a / b = a / k := by simp [*]
theorem mod_eq (a b k : Int) (h : b = k) : a % b = a % k := by simp [*]
theorem div_eq' (a b b' k : Int) (h₁ : b = b') (h₂ : k == a/b') : a / b = k := by simp_all
theorem mod_eq' (a b b' k : Int) (h₁ : b = b') (h₂ : k == a%b') : a % b = k := by simp_all
end Int.Linear
theorem Int.not_le_eq (a b : Int) : (¬a b) = (b + 1 a) := by

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

@@ -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

@@ -2108,6 +2108,11 @@ def range' : (start len : Nat) → (step : Nat := 1) → List Nat
| _, 0, _ => []
| s, n+1, step => s :: range' (s+step) n step
@[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 -/
/--

View File

@@ -427,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 α} :

View File

@@ -1241,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
@@ -1262,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
@@ -1298,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

@@ -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 -/
@@ -1851,6 +1857,10 @@ theorem append_eq_map_iff {f : α → β} :
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.

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)]
@@ -165,11 +176,7 @@ instance [LT α] [Trans (· < · : αα → Prop) (· < ·) (· < ·)] :
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
@@ -179,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
@@ -193,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₂
@@ -247,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
@@ -262,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'
@@ -272,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
@@ -456,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₂
@@ -480,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₃) :
@@ -514,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 β)

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

@@ -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,28 +90,27 @@ theorem map_sub_range' {a s : Nat} (h : a ≤ s) (n : Nat) :
rintro rfl
omega
theorem range'_eq_append_iff : range' s n = xs ++ ys k, k n xs = range' s k ys = range' (s + k) (n - k) := by
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
@@ -178,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)
@@ -355,9 +394,7 @@ 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, ?_

View File

@@ -29,30 +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']
theorem range'_ne_nil_iff (s : Nat) {n step : Nat} : range' s n step [] n 0 := by
cases n <;> simp
@[simp] theorem range'_zero : range' s 0 step = [] := by
simp
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'_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
@@ -81,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
@@ -107,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 :=
@@ -129,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
@@ -152,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
@@ -162,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]
@@ -196,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]
@@ -208,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]

View File

@@ -352,7 +352,7 @@ 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

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

@@ -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

@@ -583,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]
@@ -674,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]

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

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

@@ -1164,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)
@@ -1177,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)
@@ -1331,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]

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,824 +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
/-- Version of `Ordering.then'` for proof by reflection. -/
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_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
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

View File

@@ -6,7 +6,7 @@ Authors: Markus Himmel, Paul Reichert, Robin Arnez
module
prelude
public import Std.Classes.Ord.Basic
public import Init.Data.Order.Ord
public import Init.Data.BitVec.Lemmas
public section

View File

@@ -6,7 +6,7 @@ Authors: Markus Himmel, Paul Reichert, Robin Arnez
module
prelude
public import Std.Classes.Ord.Basic
public import Init.Data.Order.Ord
public import Init.Data.SInt.Lemmas
public section

View File

@@ -6,7 +6,7 @@ Authors: Markus Himmel, Paul Reichert, Robin Arnez
module
prelude
public import Std.Classes.Ord.Basic
public import Init.Data.Order.Ord
public import Init.Data.String.Lemmas
public section

View File

@@ -6,7 +6,7 @@ Authors: Markus Himmel, Paul Reichert, Robin Arnez
module
prelude
public import Std.Classes.Ord.Basic
public import Init.Data.Order.Ord
public import Init.Data.UInt.Lemmas
public section

View File

@@ -6,7 +6,7 @@ Authors: Markus Himmel, Paul Reichert, Robin Arnez
module
prelude
public import Std.Classes.Ord.Basic
public import Init.Data.Order.Ord
public import Init.Data.Vector.Lemmas
public section

16
src/Init/Data/Order.lean Normal file
View File

@@ -0,0 +1,16 @@
/-
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.Order.Ord
public import Init.Data.Order.Classes
public import Init.Data.Order.ClassesExtra
public import Init.Data.Order.Lemmas
public import Init.Data.Order.LemmasExtra
public import Init.Data.Order.Factories
public import Init.Data.Order.FactoriesExtra
public import Init.Data.Order.PackageFactories

View File

@@ -0,0 +1,194 @@
/-
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.Core
namespace Std
/-!
# Order-related typeclasses
This module provides the typeclasses used to state that basic operations on some type `α`
reflect a certain well-behaved order structure on `α`.
The basic operations are provided by the typeclasses `LE α`, `LT α`, `BEq α`, `Ord α`, `Min α` and
`Max α`.
All of them describe at least some way to compare elements in `α`. Usually, any subset of them
is available and one can/must show that these comparisons are well-behaved in some sense.
For example, one could merely require that the available operations reflect a preorder
(where the less-or-equal relation only needs to be reflexive and transitive). Alternatively,
one could require a full linear order (additionally requiring antisymmetry and totality of the
less-or-equal relation).
There are many ways to characterize, say, linear orders:
* `(· ≤ ·)` is reflexive, transitive, antisymmetric and total.
* `(· ≤ ·)` is antisymmetric, `a < b ↔ ¬ b ≤ a` and `(· < ·)` is irreflexive, transitive and asymmetric.
* `min a b` is either `a` or `b`, is symmetric and satisfies the
following property: `min c (min a b) = c` if and only if `min c a = c` and `min c b = c`.
It is desirable that lemmas about linear orders state this hypothesis in a canonical way.
Therefore, the classes defining preorders, partial orders, linear preorders and linear orders
are all formulated purely in terms of `LE`. For other operations, there are
classes for compatibility of `LE` with other operations. Hence, a lemma may look like:
```lean
theorem lt_trans {α : Type u} [LE α] [LT α]
[IsPreorder α] -- The order on `α` induced by `LE α` is, among other things, transitive.
[LawfulOrderLT α] -- `<` is the less-than relation induced by `LE α`.
{a b : α} : a < b → b < c → a < c := by
sorry
```
-/
/--
This typeclass states that the order structure on `α`, represented by an `LE α` instance,
is a preorder. In other words, the less-or-equal relation is reflexive and transitive.
-/
public class IsPreorder (α : Type u) [LE α] where
le_refl : a : α, a a
le_trans : a b c : α, a b b c a c
/--
This typeclass states that the order structure on `α`, represented by an `LE α` instance,
is a partial order.
In other words, the less-or-equal relation is reflexive, transitive and antisymmetric.
-/
public class IsPartialOrder (α : Type u) [LE α] extends IsPreorder α where
le_antisymm : a b : α, a b b a a = b
/--
This typeclass states that the order structure on `α`, represented by an `LE α` instance,
is a linear preorder.
In other words, the less-or-equal relation is reflexive, transitive and total.
-/
public class IsLinearPreorder (α : Type u) [LE α] extends IsPreorder α where
le_total : a b : α, a b b a
/--
This typeclass states that the order structure on `α`, represented by an `LE α` instance,
is a linear order.
In other words, the less-or-equal relation is reflexive, transitive, antisymmetric and total.
-/
public class IsLinearOrder (α : Type u) [LE α] extends IsPartialOrder α, IsLinearPreorder α
section LT
/--
This typeclass states that the synthesized `LT α` instance is compatible with the `LE α`
instance. This means that `LT.lt a b` holds if and only if `a` is less or equal to `b` according
to the `LE α` instance, but `b` is not less or equal to `a`.
`LawfulOrderLT α` automatically entails that `LT α` is asymmetric: `a < b` and `b < a` can never
be true simultaneously.
`LT α` does not uniquely determine the `LE α`: There can be only one compatible order data
instance that is total, but there can be others that are not total.
-/
public class LawfulOrderLT (α : Type u) [LT α] [LE α] where
lt_iff : a b : α, a < b a b ¬ b a
end LT
section BEq
public class LawfulOrderBEq (α : Type u) [BEq α] [LE α] where
beq_iff_le_and_ge : a b : α, a == b a b b a
end BEq
section Min
/--
This typeclass states that `Min.min a b` returns one of its arguments, either `a` or `b`.
-/
public class MinEqOr (α : Type u) [Min α] where
min_eq_or : a b : α, min a b = a min a b = b
/--
If both `a` and `b` satisfy some property `P`, then so does `min a b`, because it is equal to
either `a` or `b`.
-/
public def MinEqOr.elim {α : Type u} [Min α] [MinEqOr α] {P : α Prop} {a b : α} (ha : P a) (hb : P b) :
P (min a b) := by
cases MinEqOr.min_eq_or a b <;> rename_i h
case inl => exact h.symm ha
case inr => exact h.symm hb
/--
This typeclass states that being less or equal to `min a b` is equivalent to being less or
equal to both `a` and `b`..
-/
public class LawfulOrderInf (α : Type u) [Min α] [LE α] where
le_min_iff : a b c : α, a (min b c) a b a c
/--
This typeclass bundles `MinEqOr α` and `LawfulOrderInf α`. It characterizes when a `Min α`
instance reasonably computes minima in some type `α` that has an `LE α` instance.
As long as `α` is a preorder (see `IsPreorder α`), this typeclass implies that the order on
`α` is total and that `Min.min a b` returns either `a` or `b`, whichever is less or equal to
the other.
-/
public class LawfulOrderMin (α : Type u) [Min α] [LE α] extends MinEqOr α, LawfulOrderInf α
/--
This typeclass states that `min a b = if a ≤ b then a else b` (for any `DecidableLE α` instance).
-/
public class LawfulOrderLeftLeaningMin (α : Type u) [Min α] [LE α] where
min_eq_left : a b : α, a b min a b = a
min_eq_right : a b : α, ¬ a b min a b = b
end Min
section Max
/--
This typeclass states that `Max.max a b` returns one of its arguments, either `a` or `b`.
-/
public class MaxEqOr (α : Type u) [Max α] where
max_eq_or : a b : α, max a b = a max a b = b
/--
If both `a` and `b` satisfy some property `P`, then so does `max a b`, because it is equal to
either `a` or `b`.
-/
public def MaxEqOr.elim {α : Type u} [Max α] [MaxEqOr α] {P : α Prop} {a b : α} (ha : P a) (hb : P b) :
P (max a b) := by
cases MaxEqOr.max_eq_or a b <;> rename_i h
case inl => exact h.symm ha
case inr => exact h.symm hb
/--
This typeclass states that being less or equal to `Max.max a b` is equivalent to being less or
equal to both `a` and `b`.
-/
public class LawfulOrderSup (α : Type u) [Max α] [LE α] where
max_le_iff : a b c : α, (max a b) c a c b c
/--
This typeclass bundles `MaxEqOr α` and `LawfulOrderSup α`. It characterizes when a `Max α`
instance reasonably computes maxima in some type `α` that has an `LE α` instance.
As long as `α` is a preorder (see `IsPreorder α`), this typeclass implies that the order on
`α` is total and that `Min.min a b` returns either `a` or `b`, whichever is greater or equal to
the other.
-/
public class LawfulOrderMax (α : Type u) [Max α] [LE α] extends MaxEqOr α, LawfulOrderSup α
/--
This typeclass states that `max a b = if b ≤ a then a else b` (for any `DecidableLE α` instance).
-/
public class LawfulOrderLeftLeaningMax (α : Type u) [Max α] [LE α] where
max_eq_left : a b : α, b a max a b = a
max_eq_right : a b : α, ¬ b a max a b = b
end Max
end Std

View File

@@ -0,0 +1,39 @@
/-
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.Order.Classes
public import Init.Data.Ord.Basic
namespace Std
/--
This typeclass states that the synthesized `Ord α` instance is compatible with the `LE α`
instance. This means that according to `compare`, the following are equivalent:
* `a` is less than or equal to `b` according to `compare`.
* `b` is greater than or equal to `b` according to `compare`.
* `a ≤ b` holds.
`LawfulOrderOrd α` automatically entails that `Ord α` is oriented (see `OrientedOrd α`)
and that `LE α` is total.
`Ord α` and `LE α` mutually determine each other in the presence of `LawfulOrderOrd α`.
-/
public class LawfulOrderOrd (α : Type u) [Ord α] [LE α] where
isLE_compare : a b : α, (compare a b).isLE a b
isGE_compare : a b : α, (compare a b).isGE b a
public theorem LawfulOrderOrd.isLE_compare_eq_false {α : Type u} [Ord α] [LE α] [LawfulOrderOrd α] {a b : α} :
(compare a b).isLE = false ¬ a b := by
simp [ isLE_compare]
public theorem LawfulOrderOrd.isGE_compare_eq_false {α : Type u} [Ord α] [LE α] [LawfulOrderOrd α] {a b : α} :
(compare a b).isGE = false ¬ b a := by
simp [ isGE_compare]
end Std

View File

@@ -0,0 +1,305 @@
/-
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.Order.Classes
import Init.Classical
namespace Std
/-!
This module provides utilities for the creation of order-related typeclass instances.
-/
section OfLE
/--
Creates a `Min α` instance from `LE α` and `DecidableLE α` so that `min a b` is either `a` or `b`,
preferring `a` over `b` when in doubt.
Has a `LawfulOrderLeftLeaningMin α` instance.
-/
@[inline]
public def _root_.Min.leftLeaningOfLE (α : Type u) [LE α] [DecidableLE α] : Min α where
min a b := if a b then a else b
/--
Creates a `Max α` instance from `LE α` and `DecidableLE α` so that `max a b` is either `a` or `b`,
preferring `a` over `b` when in doubt.
Has a `LawfulOrderLeftLeaningMax α` instance.
-/
@[inline]
public def _root_.Max.leftLeaningOfLE (α : Type u) [LE α] [DecidableLE α] : Max α where
max a b := if b a then a else b
/--
This instance is only publicly defined in `Init.Data.Order.Lemmas`.
-/
instance {r : α α Prop} [Total r] : Refl r where
refl a := by simpa using Total.total a a
/--
If an `LE α` instance is reflexive and transitive, then it represents a preorder.
-/
public theorem IsPreorder.of_le {α : Type u} [LE α]
(le_refl : Std.Refl (α := α) (· ·) := by exact inferInstance)
(le_trans : Trans (α := α) (· ·) (· ·) (· ·) := by exact inferInstance) :
IsPreorder α where
le_refl := le_refl.refl
le_trans _ _ _ := le_trans.trans
/--
If an `LE α` instance is transitive and total, then it represents a linear preorder.
-/
public theorem IsLinearPreorder.of_le {α : Type u} [LE α]
(le_trans : Trans (α := α) (· ·) (· ·) (· ·) := by exact inferInstance)
(le_total : Total (α := α) (· ·) := by exact inferInstance) :
IsLinearPreorder α where
toIsPreorder := .of_le
le_total := le_total.total
/--
If an `LE α` is reflexive, antisymmetric and transitive, then it represents a partial order.
-/
public theorem IsPartialOrder.of_le {α : Type u} [LE α]
(le_refl : Std.Refl (α := α) (· ·) := by exact inferInstance)
(le_antisymm : Std.Antisymm (α := α) (· ·) := by exact inferInstance)
(le_trans : Trans (α := α) (· ·) (· ·) (· ·) := by exact inferInstance) :
IsPartialOrder α where
toIsPreorder := .of_le
le_antisymm := le_antisymm.antisymm
/--
If an `LE α` instance is antisymmetric, transitive and total, then it represents a linear order.
-/
public theorem IsLinearOrder.of_le {α : Type u} [LE α]
(le_antisymm : Std.Antisymm (α := α) (· ·) := by exact inferInstance)
(le_trans : Trans (α := α) (· ·) (· ·) (· ·) := by exact inferInstance)
(le_total : Total (α := α) (· ·) := by exact inferInstance) :
IsLinearOrder α where
toIsLinearPreorder := .of_le
le_antisymm := le_antisymm.antisymm
/--
This lemma derives a `LawfulOrderLT α` instance from a property involving an `LE α` instance.
-/
public theorem LawfulOrderLT.of_le {α : Type u} [LT α] [LE α]
(lt_iff : a b : α, a < b a b ¬ b a) : LawfulOrderLT α where
lt_iff := lt_iff
/--
This lemma derives a `LawfulOrderBEq α` instance from a property involving an `LE α` instance.
-/
public theorem LawfulOrderBEq.of_le {α : Type u} [BEq α] [LE α]
(beq_iff : a b : α, a == b a b b a) : LawfulOrderBEq α where
beq_iff_le_and_ge := by simp [beq_iff]
/--
This lemma characterizes in terms of `LE α` when a `Min α` instance "behaves like an infimum
operator".
-/
public theorem LawfulOrderInf.of_le {α : Type u} [Min α] [LE α]
(le_min_iff : a b c : α, a min b c a b a c) : LawfulOrderInf α where
le_min_iff := le_min_iff
/--
Returns a `LawfulOrderMin α` instance given certain properties.
This lemma derives a `LawfulOrderMin α` instance from two properties involving `LE α` and `Min α`
instances.
The produced instance entails `LawfulOrderInf α` and `MinEqOr α`.
-/
public theorem LawfulOrderMin.of_le_min_iff {α : Type u} [Min α] [LE α]
(le_min_iff : a b c : α, a min b c a b a c := by exact LawfulOrderInf.le_min_iff)
(min_eq_or : a b : α, min a b = a min a b = b := by exact MinEqOr.min_eq_or) :
LawfulOrderMin α where
toLawfulOrderInf := .of_le le_min_iff
toMinEqOr := min_eq_or
/--
Returns a `LawfulOrderMin α` instance given that `max a b` returns either `a` or `b` and that
it is less than or equal to both of them. The `≤` relation needs to be transitive.
The produced instance entails `LawfulOrderInf α` and `MinEqOr α`.
-/
public theorem LawfulOrderMin.of_min_le {α : Type u} [Min α] [LE α]
[i : Trans (α := α) (· ·) (· ·) (· ·)]
(min_le_left : a b : α, min a b a) (min_le_right : a b : α, min a b b)
(min_eq_or : a b : α, min a b = a min a b = b := by exact MinEqOr.min_eq_or) :
LawfulOrderMin α := by
apply LawfulOrderMin.of_le_min_iff
· simp [autoParam]
intro a b c
constructor
· intro h
exact i.trans h (min_le_left b c), i.trans h (min_le_right b c)
· intro h
cases min_eq_or b c <;> simp [*]
· exact min_eq_or
/--
This lemma characterizes in terms of `LE α` when a `Max α` instance "behaves like a supremum
operator".
-/
public def LawfulOrderSup.of_le {α : Type u} [Max α] [LE α]
(max_le_iff : a b c : α, max a b c a c b c) : LawfulOrderSup α where
max_le_iff := max_le_iff
/--
Returns a `LawfulOrderMax α` instance given certain properties.
This lemma derives a `LawfulOrderMax α` instance from two properties involving `LE α` and `Max α`
instances.
The produced instance entails `LawfulOrderSup α` and `MaxEqOr α`.
-/
public def LawfulOrderMax.of_max_le_iff {α : Type u} [Max α] [LE α]
(max_le_iff : a b c : α, max a b c a c b c := by exact LawfulOrderInf.le_min_iff)
(max_eq_or : a b : α, max a b = a max a b = b := by exact MaxEqOr.max_eq_or) :
LawfulOrderMax α where
toLawfulOrderSup := .of_le max_le_iff
toMaxEqOr := max_eq_or
/--
Returns a `LawfulOrderMax α` instance given that `max a b` returns either `a` or `b` and that
it is larger than or equal to both of them. The `≤` relation needs to be transitive.
The produced instance entails `LawfulOrderSup α` and `MaxEqOr α`.
-/
public theorem LawfulOrderMax.of_le_max {α : Type u} [Max α] [LE α]
[i : Trans (α := α) (· ·) (· ·) (· ·)]
(left_le_max : a b : α, a max a b) (right_le_max : a b : α, b max a b)
(max_eq_or : a b : α, max a b = a max a b = b := by exact MaxEqOr.max_eq_or) :
LawfulOrderMax α := by
apply LawfulOrderMax.of_max_le_iff
· simp [autoParam]
intro a b c
constructor
· intro h
exact i.trans (left_le_max a b) h, i.trans (right_le_max a b) h
· intro h
cases max_eq_or a b <;> simp [*]
· exact max_eq_or
end OfLE
section OfLT
/--
Creates a *total* `LE α` instance from an `LT α` instance.
This only makes sense for asymmetric `LT α` instances (see `Std.Asymm`).
-/
@[inline]
public def _root_.LE.ofLT (α : Type u) [LT α] : LE α where
le a b := ¬ b < a
/--
The `LE α` instance obtained from an asymmetric `LT α` instance is compatible with said
`LT α` instance.
-/
public instance LawfulOrderLT.of_lt {α : Type u} [LT α] [i : Asymm (α := α) (· < ·)] :
haveI := LE.ofLT α
LawfulOrderLT α :=
letI := LE.ofLT α
{ lt_iff a b := by simpa [LE.ofLT, Classical.not_not] using i.asymm a b }
/--
If an `LT α` instance is asymmetric and its negation is transitive, then `LE.ofLT α` represents a
linear preorder.
-/
public theorem IsLinearPreorder.of_lt {α : Type u} [LT α]
(lt_asymm : Asymm (α := α) (· < ·) := by exact inferInstance)
(not_lt_trans : Trans (α := α) (¬ · < ·) (¬ · < ·) (¬ · < ·) := by exact inferInstance) :
haveI := LE.ofLT α
IsLinearPreorder α :=
letI := LE.ofLT α
{ le_trans := by simpa [LE.ofLT] using fun a b c hab hbc => not_lt_trans.trans hbc hab
le_total a b := by
apply Or.symm
open Classical in simpa [LE.ofLT, Decidable.imp_iff_not_or] using lt_asymm.asymm a b
le_refl a := by
open Classical in simpa [LE.ofLT] using lt_asymm.asymm a a }
/--
If an `LT α` instance is asymmetric and its negation is transitive and antisymmetric, then
`LE.ofLT α` represents a linear order.
-/
public theorem IsLinearOrder.of_lt {α : Type u} [LT α]
(lt_asymm : Asymm (α := α) (· < ·) := by exact inferInstance)
(not_lt_trans : Trans (α := α) (¬ · < ·) (¬ · < ·) (¬ · < ·) := by exact inferInstance)
(not_lt_antisymm : Antisymm (α := α) (¬ · < ·) := by exact inferInstance) :
haveI := LE.ofLT α
IsLinearOrder α :=
letI := LE.ofLT α
haveI : IsLinearPreorder α := .of_lt
{ le_antisymm := by
simpa [LE.ofLT] using fun a b hab hba => not_lt_antisymm.antisymm a b hba hab }
/--
This lemma characterizes in terms of `LT α` when a `Min α` instance
"behaves like an infimum operator" with respect to `LE.ofLT α`.
-/
public theorem LawfulOrderInf.of_lt {α : Type u} [Min α] [LT α]
(min_lt_iff : a b c : α, min b c < a b < a c < a) :
haveI := LE.ofLT α
LawfulOrderInf α :=
letI := LE.ofLT α
{ le_min_iff a b c := by
open Classical in
simp only [LE.ofLT, Decidable.not_iff_not (a := ¬ min b c < a)]
simpa [Decidable.imp_iff_not_or] using min_lt_iff a b c }
/--
Derives a `LawfulOrderMin α` instance for `LE.ofLT` from two properties involving
`LT α` and `Min α` instances.
The produced instance entails `LawfulOrderInf α` and `MinEqOr α`.
-/
public theorem LawfulOrderMin.of_lt {α : Type u} [Min α] [LT α]
(min_lt_iff : a b c : α, min b c < a b < a c < a)
(min_eq_or : a b : α, min a b = a min a b = b) :
haveI := LE.ofLT α
LawfulOrderMin α :=
letI := LE.ofLT α
{ toLawfulOrderInf := .of_lt min_lt_iff
toMinEqOr := min_eq_or }
/--
This lemma characterizes in terms of `LT α` when a `Max α` instance
"behaves like an supremum operator" with respect to `LE.ofLT α`.
-/
public def LawfulOrderSup.of_lt {α : Type u} [Max α] [LT α]
(lt_max_iff : a b c : α, c < max a b c < a c < b) :
haveI := LE.ofLT α
LawfulOrderSup α :=
letI := LE.ofLT α
{ max_le_iff a b c := by
open Classical in
simp only [LE.ofLT, Decidable.not_iff_not ( a := ¬ c < max a b)]
simpa [Decidable.imp_iff_not_or] using lt_max_iff a b c }
/--
Derives a `LawfulOrderMax α` instance for `LE.ofLT` from two properties involving `LT α` and
`Max α` instances.
The produced instance entails `LawfulOrderSup α` and `MaxEqOr α`.
-/
public def LawfulOrderMax.of_lt {α : Type u} [Max α] [LT α]
(lt_max_iff : a b c : α, c < max a b c < a c < b)
(max_eq_or : a b : α, max a b = a max a b = b) :
haveI := LE.ofLT α
LawfulOrderMax α :=
letI := LE.ofLT α
{ toLawfulOrderSup := .of_lt lt_max_iff
toMaxEqOr := max_eq_or }
end OfLT
end Std

View File

@@ -0,0 +1,122 @@
/-
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.Order.ClassesExtra
public import Init.Data.Order.Ord
namespace Std
/--
Creates an `LE α` instance from an `Ord α` instance.
`OrientedOrd α` must be satisfied so that the resulting `LE α` instance faithfully represents
the `Ord α` instance.
-/
@[inline, expose]
public def _root_.LE.ofOrd (α : Type u) [Ord α] : LE α where
le a b := (compare a b).isLE
/--
Creates an `DecidableLE α` instance using a well-behaved `Ord α` instance.
-/
@[inline, expose]
public def _root_.DecidableLE.ofOrd (α : Type u) [LE α] [Ord α] [LawfulOrderOrd α] :
DecidableLE α :=
fun a b => match h : (compare a b).isLE with
| true => isTrue (by simpa only [LawfulOrderOrd.isLE_compare] using h)
| false => isFalse (by simpa only [LawfulOrderOrd.isLE_compare_eq_false] using h)
/--
Creates an `LT α` instance from an `Ord α` instance.
`OrientedOrd α` must be satisfied so that the resulting `LT α` instance faithfully represents
the `Ord α` instance.
-/
@[inline, expose]
public def _root_.LT.ofOrd (α : Type u) [Ord α] :
LT α where
lt a b := compare a b = .lt
public theorem isLE_compare {α : Type u} [Ord α] [LE α] [LawfulOrderOrd α]
{a b : α} : (compare a b).isLE a b := by
simp [ LawfulOrderOrd.isLE_compare]
public theorem isGE_compare {α : Type u} [Ord α] [LE α] [LawfulOrderOrd α]
{a b : α} : (compare a b).isGE b a := by
simp [ LawfulOrderOrd.isGE_compare]
-- We need to define `compare_eq_lt` and `compare_eq_gt` here instead of in `LemmasExtra.lean`
-- because they are needed for `DecidableLT.ofOrd`.
public theorem compare_eq_lt {α : Type u} [Ord α] [LT α] [LE α] [LawfulOrderOrd α] [LawfulOrderLT α]
{a b : α} : compare a b = .lt a < b := by
rw [LawfulOrderLT.lt_iff, LawfulOrderOrd.isLE_compare, LawfulOrderOrd.isGE_compare]
cases compare a b <;> simp
public theorem compare_eq_gt {α : Type u} [Ord α] [LT α] [LE α] [LawfulOrderOrd α] [LawfulOrderLT α]
{a b : α} : compare a b = .gt b < a := by
rw [LawfulOrderLT.lt_iff, LawfulOrderOrd.isGE_compare, LawfulOrderOrd.isLE_compare]
cases compare a b <;> simp
public theorem compare_eq_eq {α : Type u} [Ord α] [BEq α] [LE α] [LawfulOrderOrd α] [LawfulOrderBEq α]
{a b : α} : compare a b = .eq a == b := by
open Classical.Order in
rw [LawfulOrderBEq.beq_iff_le_and_ge, isLE_compare, isGE_compare]
cases compare a b <;> simp
/--
Creates a `DecidableLT α` instance using a well-behaved `Ord α` instance.
-/
@[inline, expose]
public def _root_.DecidableLT.ofOrd (α : Type u) [LE α] [LT α] [Ord α] [LawfulOrderOrd α]
[LawfulOrderLT α] :
DecidableLT α :=
fun a b => if h : compare a b = .lt then
isTrue (by simpa only [compare_eq_lt] using h)
else
isFalse (by simpa only [compare_eq_lt] using h)
/--
Creates a `BEq α` instance from an `Ord α` instance. -/
@[inline, expose]
public def _root_.BEq.ofOrd (α : Type u) [Ord α] :
BEq α where
beq a b := compare a b = .eq
/--
The `LE α` instance obtained from an `Ord α` instance is compatible with said `Ord α`
instance if `compare` is oriented, i.e., `compare a b = .lt ↔ compare b a = .gt`.
-/
public instance instLawfulOrderOrd_ofOrd (α : Type u) [Ord α] [OrientedOrd α] :
haveI := LE.ofOrd α
LawfulOrderOrd α :=
letI := LE.ofOrd α
{ isLE_compare := by simp [LE.ofOrd]
isGE_compare := by simp [LE.ofOrd, OrientedCmp.isGE_eq_isLE] }
attribute [local instance] LT.ofOrd in
/--
The `LT α` instance obtained from an `Ord α` instance is compatible with the `LE α` instance
instance if `Ord α` is compatible with it.
-/
public instance instLawfulOrderLT_ofOrd {α : Type u} [Ord α] [LE α] [LawfulOrderOrd α] :
LawfulOrderLT α where
lt_iff {a b} := by
simp +contextual [LT.lt, Std.isLE_compare (a := a), Std.isGE_compare (a := a)]
attribute [local instance] BEq.ofOrd in
/--
The `BEq α` instance obtained from an `Ord α` instance is compatible with the `LE α` instance
instance if `Ord α` is compatible with it.
-/
public instance instLawfulOrderBEq_ofOrd {α : Type u} [Ord α] [LE α] [LawfulOrderOrd α] :
LawfulOrderBEq α where
beq_iff_le_and_ge {a b} := by
simp +contextual [BEq.beq, Std.isLE_compare (a := a), Std.isGE_compare (a := a),
Ordering.eq_eq_iff_isLE_and_isGE]
end Std

View File

@@ -0,0 +1,502 @@
/-
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.Order.Classes
public import Init.Data.Order.Factories
import all Init.Data.Order.Factories
import Init.SimpLemmas
public import Init.Classical
public import Init.Data.BEq
namespace Std
/-!
This module provides typeclass instances and lemmas about order-related typeclasses.
-/
section AxiomaticInstances
public instance (r : α α Prop) [Asymm r] : Irrefl r where
irrefl a h := Asymm.asymm a a h h
public instance {r : α α Prop} [Total r] : Refl r where
refl a := by simpa using Total.total a a
public instance Total.asymm_of_total_not {r : α α Prop} [i : Total (¬ r · ·)] : Asymm r where
asymm a b h := by cases i.total a b <;> trivial
public theorem Asymm.total_not {r : α α Prop} [i : Asymm r] : Total (¬ r · ·) where
total a b := by
apply Classical.byCases (p := r a b) <;> intro hab
· exact Or.inr <| i.asymm a b hab
· exact Or.inl hab
public instance {α : Type u} [LE α] [IsPartialOrder α] :
Antisymm (α := α) (· ·) where
antisymm := IsPartialOrder.le_antisymm
public instance {α : Type u} [LE α] [IsPreorder α] :
Trans (α := α) (· ·) (· ·) (· ·) where
trans := IsPreorder.le_trans _ _ _
public instance {α : Type u} [LE α] [IsPreorder α] :
Refl (α := α) (· ·) where
refl := IsPreorder.le_refl
public instance {α : Type u} [LE α] [IsLinearPreorder α] :
Total (α := α) (· ·) where
total := IsLinearPreorder.le_total
end AxiomaticInstances
section LE
public theorem le_refl {α : Type u} [LE α] [Refl (α := α) (· ·)] (a : α) : a a := by
simp [Refl.refl]
public theorem le_antisymm {α : Type u} [LE α] [Std.Antisymm (α := α) (· ·)] {a b : α}
(hab : a b) (hba : b a) : a = b :=
Antisymm.antisymm _ _ hab hba
public theorem le_antisymm_iff {α : Type u} [LE α] [Antisymm (α := α) (· ·)]
[Refl (α := α) (· ·)] {a b : α} : a b b a a = b :=
fun | hab, hba => le_antisymm hab hba, by simp +contextual [le_refl]
public theorem le_trans {α : Type u} [LE α] [Trans (α := α) (· ·) (· ·) (· ·)] {a b c : α}
(hab : a b) (hbc : b c) : a c :=
Trans.trans hab hbc
public theorem le_total {α : Type u} [LE α] [Std.Total (α := α) (· ·)] {a b : α} :
a b b a :=
Std.Total.total a b
public theorem le_of_not_ge {α : Type u} [LE α] [Std.Total (α := α) (· ·)] {a b : α} :
¬ b a a b := by
intro h
simpa [h] using Std.Total.total a b (r := (· ·))
end LE
section LT
public theorem lt_iff_le_and_not_ge {α : Type u} [LT α] [LE α] [LawfulOrderLT α] {a b : α} :
a < b a b ¬ b a :=
LawfulOrderLT.lt_iff a b
public theorem not_lt {α : Type u} [LT α] [LE α] [Std.Total (α := α) (· ·)] [LawfulOrderLT α]
{a b : α} : ¬ a < b b a := by
simp [lt_iff_le_and_not_ge, Classical.not_not, Std.Total.total]
public theorem not_gt_of_lt {α : Type u} [LT α] [i : Std.Asymm (α := α) (· < ·)] {a b : α}
(h : a < b) : ¬ b < a :=
i.asymm a b h
public theorem le_of_lt {α : Type u} [LT α] [LE α] [LawfulOrderLT α] {a b : α} (h : a < b) :
a b := by
simp only [LawfulOrderLT.lt_iff] at h
exact h.1
public instance {α : Type u} [LT α] [LE α] [LawfulOrderLT α] :
Std.Asymm (α := α) (· < ·) where
asymm a b := by
simp only [LawfulOrderLT.lt_iff]
intro h h'
exact h.2.elim h'.1
public instance {α : Type u} [LT α] [LE α] [IsPreorder α] [LawfulOrderLT α] :
Std.Irrefl (α := α) (· < ·) := inferInstance
public instance {α : Type u} [LT α] [LE α] [Trans (α := α) (· ·) (· ·) (· ·) ]
[LawfulOrderLT α] : Trans (α := α) (· < ·) (· < ·) (· < ·) where
trans {a b c} hab hbc := by
simp only [lt_iff_le_and_not_ge] at hab hbc
apply And.intro
· exact le_trans hab.1 hbc.1
· intro hca
exact hab.2.elim (le_trans hbc.1 hca)
public instance {α : Type u} {_ : LT α} [LE α] [LawfulOrderLT α]
[Total (α := α) (· ·)] [Antisymm (α := α) (· ·)] :
Antisymm (α := α) (¬ · < ·) where
antisymm a b hab hba := by
simp only [not_lt] at hab hba
exact Antisymm.antisymm (r := (· ·)) a b hba hab
public instance {α : Type u} {_ : LT α} [LE α] [LawfulOrderLT α]
[Total (α := α) (· ·)] [Trans (α := α) (· ·) (· ·) (· ·)] :
Trans (α := α) (¬ · < ·) (¬ · < ·) (¬ · < ·) where
trans {a b c} hab hbc := by
simp only [not_lt] at hab hbc
exact le_trans hbc hab
public instance {α : Type u} {_ : LT α} [LE α] [LawfulOrderLT α] [Total (α := α) (· ·)] :
Total (α := α) (¬ · < ·) where
total a b := by simp [not_lt, Std.Total.total]
public theorem lt_of_le_of_lt {α : Type u} [LE α] [LT α]
[Trans (α := α) (· ·) (· ·) (· ·)] [LawfulOrderLT α] {a b c : α} (hab : a b)
(hbc : b < c) : a < c := by
simp only [lt_iff_le_and_not_ge] at hbc
apply And.intro
· exact le_trans hab hbc.1
· intro hca
exact hbc.2.elim (le_trans hca hab)
public theorem lt_of_lt_of_le {α : Type u} [LE α] [LT α]
[Trans (α := α) (· ·) (· ·) (· ·)] [LawfulOrderLT α] {a b c : α} (hab : a < b)
(hbc : b c) : a < c := by
simp only [lt_iff_le_and_not_ge] at hab
apply And.intro
· exact le_trans hab.1 hbc
· intro hca
exact hab.2.elim (le_trans hbc hca)
public theorem lt_of_le_of_ne {α : Type u} [LE α] [LT α]
[Std.Antisymm (α := α) (· ·)] [LawfulOrderLT α] {a b : α}
(hle : a b) (hne : a b) : a < b := by
apply Classical.byContradiction
simp only [lt_iff_le_and_not_ge, hle, true_and, Classical.not_not, imp_false]
intro hge
exact hne.elim <| Std.Antisymm.antisymm a b hle hge
end LT
end Std
namespace Classical.Order
open Std
public scoped instance instLT {α : Type u} [LE α] :
LT α where
lt a b := a b ¬ b a
public instance instLawfulOrderLT {α : Type u} [LE α] :
LawfulOrderLT α where
lt_iff _ _ := Iff.rfl
end Classical.Order
namespace Std
section BEq
public theorem beq_iff_le_and_ge {α : Type u} [BEq α] [LE α] [LawfulOrderBEq α]
{a b : α} : a == b a b b a := by
simp [LawfulOrderBEq.beq_iff_le_and_ge]
public instance {α : Type u} [BEq α] [LE α] [LawfulOrderBEq α] :
Symm (α := α) (· == ·) where
symm := by simp_all [beq_iff_le_and_ge]
public instance {α : Type u} [BEq α] [LE α] [LawfulOrderBEq α] [IsPreorder α] : EquivBEq α where
rfl := by simp [beq_iff_le_and_ge, le_refl]
symm := Symm.symm (r := (· == ·)) _ _
trans hab hbc := by
simp only [beq_iff_le_and_ge] at hab hbc
exact le_trans hab.1 hbc.1, le_trans hbc.2 hab.2
public instance {α : Type u} [BEq α] [LE α] [LawfulOrderBEq α] [IsPartialOrder α] :
LawfulBEq α where
eq_of_beq := by
simp only [beq_iff_le_and_ge, and_imp]
apply le_antisymm
end BEq
end Std
namespace Classical.Order
open Std
public noncomputable scoped instance instBEq {α : Type u} [LE α] : BEq α where
beq a b := a b b a
public instance instLawfulOrderBEq {α : Type u} [LE α] :
LawfulOrderBEq α where
beq_iff_le_and_ge a b := by simp [BEq.beq]
end Classical.Order
namespace Std
section Min
public theorem min_self {α : Type u} [Min α] [Std.IdempotentOp (min : α α α)] {a : α} :
min a a = a :=
Std.IdempotentOp.idempotent a
public theorem le_min_iff {α : Type u} [Min α] [LE α]
[LawfulOrderInf α] {a b c : α} :
a min b c a b a c :=
LawfulOrderInf.le_min_iff a b c
public theorem min_le_left {α : Type u} [Min α] [LE α] [Refl (α := α) (· ·)] [LawfulOrderInf α]
{a b : α} : min a b a :=
le_min_iff.mp (le_refl _) |>.1
public theorem min_le_right {α : Type u} [Min α] [LE α] [Refl (α := α) (· ·)] [LawfulOrderInf α]
{a b : α} : min a b b :=
le_min_iff.mp (le_refl _) |>.2
public theorem min_le {α : Type u} [Min α] [LE α] [IsPreorder α] [LawfulOrderMin α] {a b c : α} :
min a b c a c b c := by
cases MinEqOr.min_eq_or a b <;> rename_i h
· simpa [h] using le_trans (h min_le_right (a := a) (b := b))
· simpa [h] using le_trans (h min_le_left (a := a) (b := b))
public theorem min_eq_or {α : Type u} [Min α] [MinEqOr α] {a b : α} :
min a b = a min a b = b :=
MinEqOr.min_eq_or a b
public instance {α : Type u} [LE α] [Min α] [IsLinearOrder α] [LawfulOrderInf α] :
MinEqOr α where
min_eq_or a b := by
cases le_total (a := a) (b := b)
· apply Or.inl
apply le_antisymm
· apply min_le_left
· rw [le_min_iff]
exact le_refl a, _
· apply Or.inr
apply le_antisymm
· apply min_le_right
· rw [le_min_iff]
exact _, le_refl b
/--
If a `Min α` instance satisfies typical properties in terms of a reflexive and antisymmetric `LE α`
instance, then the `LE α` instance represents a linear order.
-/
public theorem IsLinearOrder.of_refl_of_antisymm_of_lawfulOrderMin {α : Type u} [LE α]
[LE α] [Min α] [Refl (α := α) (· ·)] [Antisymm (α := α) (· ·)] [LawfulOrderMin α] :
IsLinearOrder α := by
apply IsLinearOrder.of_le
· infer_instance
· constructor
intro a b c hab hbc
have : b = min b c := by
apply le_antisymm
· rw [le_min_iff]
exact le_refl b, hbc
· apply min_le_left
rw [this, le_min_iff] at hab
exact hab.2
· constructor
intro a b
cases min_eq_or (a := a) (b := b) <;> rename_i h
· exact Or.inl (h min_le_right)
· exact Or.inr (h min_le_left)
public instance {α : Type u} [Min α] [MinEqOr α] :
Std.IdempotentOp (min : α α α) where
idempotent a := by cases MinEqOr.min_eq_or a a <;> assumption
public instance {α : Type u} [LE α] [Min α] [IsLinearOrder α] [LawfulOrderMin α] :
Std.Associative (min : α α α) where
assoc a b c := by apply le_antisymm <;> simp [min_le, le_min_iff, le_refl]
public theorem min_eq_if {α : Type u} [LE α] [DecidableLE α] {_ : Min α}
[LawfulOrderLeftLeaningMin α] {a b : α} :
min a b = if a b then a else b := by
split <;> rename_i h
· simp [LawfulOrderLeftLeaningMin.min_eq_left _ _ h]
· simp [LawfulOrderLeftLeaningMin.min_eq_right _ _ h]
public theorem max_eq_if {α : Type u} [LE α] [DecidableLE α] {_ : Max α}
[LawfulOrderLeftLeaningMax α] {a b : α} :
max a b = if b a then a else b := by
split <;> rename_i h
· simp [LawfulOrderLeftLeaningMax.max_eq_left _ _ h]
· simp [LawfulOrderLeftLeaningMax.max_eq_right _ _ h]
public instance {α : Type u} [LE α] [Min α] [IsLinearOrder α] [LawfulOrderInf α] :
LawfulOrderLeftLeaningMin α where
min_eq_left a b hab := by
apply le_antisymm
· apply min_le_left
· simp [le_min_iff, le_refl, hab]
min_eq_right a b hab := by
apply le_antisymm
· apply min_le_right
· simp [le_min_iff, le_refl, le_of_not_ge hab]
public theorem LawfulOrderLeftLeaningMin.of_eq {α : Type u} [LE α] [Min α] [DecidableLE α]
(min_eq : a b : α, min a b = if a b then a else b) : LawfulOrderLeftLeaningMin α where
min_eq_left a b := by simp +contextual [min_eq]
min_eq_right a b := by simp +contextual [min_eq]
attribute [local instance] Min.leftLeaningOfLE
public instance [LE α] [DecidableLE α] : LawfulOrderLeftLeaningMin α :=
.of_eq (fun a b => by simp [min])
public instance {α : Type u} [LE α] [Min α] [LawfulOrderLeftLeaningMin α] :
MinEqOr α where
min_eq_or a b := by
open scoped Classical in
suffices min_eq : min a b = if a b then a else b by
rw [min_eq]
split <;> simp
split <;> simp [*, LawfulOrderLeftLeaningMin.min_eq_left, LawfulOrderLeftLeaningMin.min_eq_right]
public instance {α : Type u} [LE α] [Min α] [IsLinearPreorder α] [LawfulOrderLeftLeaningMin α] :
LawfulOrderMin α where
toMinEqOr := inferInstance
le_min_iff a b c := by
open scoped Classical in
suffices min_eq : min b c = if b c then b else c by
rw [min_eq]
split <;> rename_i hbc
· simp only [iff_self_and]
exact fun hab => le_trans hab hbc
· simp only [iff_and_self]
exact fun hac => le_trans hac (by simpa [hbc] using Std.le_total (a := b) (b := c))
split <;> simp [*, LawfulOrderLeftLeaningMin.min_eq_left, LawfulOrderLeftLeaningMin.min_eq_right]
end Min
end Std
namespace Classical.Order
open Std
public noncomputable scoped instance instMin {α : Type u} [LE α] : Min α :=
.leftLeaningOfLE α
end Classical.Order
namespace Std
section Max
public theorem max_self {α : Type u} [Max α] [Std.IdempotentOp (max : α α α)] {a : α} :
max a a = a :=
Std.IdempotentOp.idempotent a
public theorem max_le_iff {α : Type u} [Max α] [LE α] [LawfulOrderSup α] {a b c : α} :
max a b c a c b c :=
LawfulOrderSup.max_le_iff a b c
public theorem left_le_max {α : Type u} [Max α] [LE α] [Refl (α := α) (· ·)] [LawfulOrderSup α]
{a b : α} : a max a b :=
max_le_iff.mp (le_refl _) |>.1
public theorem right_le_max {α : Type u} [Max α] [LE α] [Refl (α := α) (· ·)]
[LawfulOrderSup α] {a b : α} : b max a b :=
max_le_iff.mp (le_refl _) |>.2
public theorem le_max {α : Type u} [Max α] [LE α] [IsPreorder α] [LawfulOrderMax α] {a b c : α} :
a max b c a b a c := by
cases MaxEqOr.max_eq_or b c <;> rename_i h
· simpa [h] using (le_trans · (h right_le_max))
· simpa [h] using (le_trans · (h left_le_max))
public theorem max_eq_or {α : Type u} [Max α] [MaxEqOr α] {a b : α} :
max a b = a max a b = b :=
MaxEqOr.max_eq_or a b
public instance {α : Type u} [LE α] [Max α] [IsLinearOrder α] [LawfulOrderSup α] :
MaxEqOr α where
max_eq_or a b := by
cases le_total (a := a) (b := b)
· apply Or.inr
apply le_antisymm
· rw [max_le_iff]
exact _, le_refl b
· apply right_le_max
· apply Or.inl
apply le_antisymm
· rw [max_le_iff]
exact le_refl a, _
· apply left_le_max
/--
If a `Max α` instance satisfies typical properties in terms of a reflexive and antisymmetric `LE α`
instance, then the `LE α` instance represents a linear order.
-/
public theorem IsLinearOrder.of_refl_of_antisymm_of_lawfulOrderMax {α : Type u} [LE α] [Max α]
[Refl (α := α) (· ·)] [Antisymm (α := α) (· ·)] [LawfulOrderMax α] :
IsLinearOrder α := by
apply IsLinearOrder.of_le
· infer_instance
· constructor
intro a b c hab hbc
have : b = max a b := by
apply le_antisymm
· exact right_le_max
· rw [max_le_iff]
exact hab, le_refl b
rw [this, max_le_iff] at hbc
exact hbc.1
· constructor
intro a b
cases max_eq_or (a := a) (b := b) <;> rename_i h
· exact Or.inr (h right_le_max)
· exact Or.inl (h left_le_max)
public instance {α : Type u} [Max α] [MaxEqOr α] {P : α Prop} : Max (Subtype P) where
max a b := Max.max a.val b.val, MaxEqOr.elim a.property b.property
public instance {α : Type u} [Max α] [MaxEqOr α] :
Std.IdempotentOp (max : α α α) where
idempotent a := by cases MaxEqOr.max_eq_or a a <;> assumption
public instance {α : Type u} [LE α] [Max α] [IsLinearOrder α] [LawfulOrderMax α] :
Std.Associative (max : α α α) where
assoc a b c := by
apply le_antisymm
all_goals
simp only [max_le_iff]
simp [le_max, le_refl]
public instance {α : Type u} [LE α] [Max α] [IsLinearOrder α] [LawfulOrderSup α] :
LawfulOrderLeftLeaningMax α where
max_eq_left a b hab := by
apply le_antisymm
· simp [max_le_iff, le_refl, hab]
· apply left_le_max
max_eq_right a b hab := by
apply le_antisymm
· simp [max_le_iff, le_refl, le_of_not_ge hab]
· apply right_le_max
public theorem LawfulOrderLeftLeaningMax.of_eq {α : Type u} [LE α] [Max α] [DecidableLE α]
(min_eq : a b : α, max a b = if b a then a else b) : LawfulOrderLeftLeaningMax α where
max_eq_left a b := by simp +contextual [min_eq]
max_eq_right a b := by simp +contextual [min_eq]
attribute [local instance] Max.leftLeaningOfLE
public instance [LE α] [DecidableLE α] : LawfulOrderLeftLeaningMax α :=
.of_eq (fun a b => by simp [max])
public instance {α : Type u} [LE α] [Max α] [LawfulOrderLeftLeaningMax α] :
MaxEqOr α where
max_eq_or a b := by
open scoped Classical in
suffices min_eq : max a b = if b a then a else b by
rw [min_eq]
split <;> simp
split <;> simp [*, LawfulOrderLeftLeaningMax.max_eq_left, LawfulOrderLeftLeaningMax.max_eq_right]
public instance {α : Type u} [LE α] [Max α] [IsLinearPreorder α] [LawfulOrderLeftLeaningMax α] :
LawfulOrderMax α where
toMaxEqOr := inferInstance
max_le_iff a b c := by
open scoped Classical in
suffices max_eq : max a b = if b a then a else b by
rw [max_eq]
split <;> rename_i hba
· simp only [iff_self_and]
exact fun hac => le_trans hba hac
· simp only [iff_and_self]
exact fun hbc => le_trans (by simpa [hba] using Std.le_total (a := b) (b := a)) hbc
split <;> simp [*, LawfulOrderLeftLeaningMax.max_eq_left, LawfulOrderLeftLeaningMax.max_eq_right]
end Max
end Std
namespace Classical.Order
open Std
public noncomputable scoped instance instMax {α : Type u} [LE α] : Max α :=
.leftLeaningOfLE α
end Classical.Order

View File

@@ -0,0 +1,180 @@
/-
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
import Init.Data.Order.Lemmas
public import Init.Data.Order.FactoriesExtra
public import Init.Data.Order.Ord
namespace Std
public instance {α : Type u} [LE α] [Ord α] [LawfulOrderOrd α] : OrientedOrd α where
eq_swap := by
open Classical.Order in
intro a b
apply Eq.symm
cases h : compare a b
· rw [compare_eq_lt] at h
simpa [Ordering.swap_eq_lt, compare_eq_gt] using h
· rw [compare_eq_eq] at h
simpa [Ordering.swap_eq_eq, compare_eq_eq] using BEq.symm h
· rw [compare_eq_gt] at h
simpa [Ordering.swap_eq_gt, compare_eq_lt] using h
public instance {α : Type u} [Ord α] [LE α] [LawfulOrderOrd α] [IsPreorder α] : TransOrd α where
isLE_trans := by
simp only [isLE_compare]
apply le_trans
public instance {α : Type u} [Ord α] [BEq α] [LE α] [LawfulOrderOrd α] [LawfulOrderBEq α] :
LawfulBEqOrd α where
compare_eq_iff_beq := by
simp [Ordering.eq_eq_iff_isLE_and_isGE, isLE_compare, isGE_compare, beq_iff_le_and_ge]
public instance {α : Type u} [Ord α] [LE α] [LawfulOrderOrd α] [IsPartialOrder α] : LawfulEqOrd α where
eq_of_compare {a b} := by
intro h
apply le_antisymm
· simp [ isLE_compare, h]
· simp [ isGE_compare, h]
public instance [Ord α] [LE α] [LawfulOrderOrd α] :
Total (α := α) (· ·) where
total a b := by
rw [ isLE_compare, isGE_compare]
cases compare a b <;> simp
public instance {α : Type u} [Ord α] [LE α] [LawfulOrderOrd α] [Antisymm (α := α) (· ·)] :
LawfulEqOrd α where
eq_of_compare := by
open Classical.Order in
simp [Ordering.eq_eq_iff_isLE_and_isGE, isLE_compare, isGE_compare, le_antisymm_iff]
public instance {α : Type u} [Ord α] [LE α] [LawfulOrderOrd α] [LawfulEqOrd α] :
Antisymm (α := α) (· ·) where
antisymm a b := by
simp [ and_imp, isLE_compare (a := a), isGE_compare (a := a),
Ordering.eq_eq_iff_isLE_and_isGE]
public theorem compare_eq_eq_iff_eq {α : Type u} [Ord α] [LawfulEqOrd α] {a b : α} :
compare a b = .eq a = b :=
LawfulEqOrd.compare_eq_iff_eq
public theorem IsLinearPreorder.of_ord {α : Type u} [LE α] [Ord α] [LawfulOrderOrd α]
[TransOrd α] : IsLinearPreorder α where
le_refl a := by simp [ isLE_compare]
le_trans a b c := by simpa [ isLE_compare] using TransOrd.isLE_trans
le_total a b := Total.total a b
public theorem IsLinearOrder.of_ord {α : Type u} [LE α] [Ord α] [LawfulOrderOrd α]
[TransOrd α] [LawfulEqOrd α] : IsLinearOrder α where
toIsLinearPreorder := .of_ord
le_antisymm a b hab hba := by
apply LawfulEqOrd.eq_of_compare
rw [ isLE_compare] at hab
rw [ isGE_compare] at hba
rw [Ordering.eq_eq_iff_isLE_and_isGE, hab, hba, and_self]
/--
This lemma derives a `LawfulOrderLT α` instance from a property involving an `Ord α` instance.
-/
public instance LawfulOrderLT.of_ord (α : Type u) [Ord α] [LT α] [LE α] [LawfulOrderOrd α]
(lt_iff_compare_eq_lt : a b : α, a < b compare a b = .lt) :
LawfulOrderLT α where
lt_iff a b := by
simp +contextual [lt_iff_compare_eq_lt, isLE_compare (a := a), isGE_compare (a := a)]
/--
This lemma derives a `LawfulOrderBEq α` instance from a property involving an `Ord α` instance.
-/
public instance LawfulOrderBEq.of_ord (α : Type u) [Ord α] [BEq α] [LE α] [LawfulOrderOrd α]
(beq_iff_compare_eq_eq : a b : α, a == b compare a b = .eq) :
LawfulOrderBEq α where
beq_iff_le_and_ge := by
simp [beq_iff_compare_eq_eq, Ordering.eq_eq_iff_isLE_and_isGE, isLE_compare, isGE_compare]
/--
This lemma derives a `LawfulOrderInf α` instance from a property involving an `Ord α` instance.
-/
public instance LawfulOrderInf.of_ord (α : Type u) [Ord α] [Min α] [LE α] [LawfulOrderOrd α]
(compare_min_isLE_iff : a b c : α,
(compare a (min b c)).isLE (compare a b).isLE (compare a c).isLE) :
LawfulOrderInf α where
le_min_iff := by simpa [isLE_compare] using compare_min_isLE_iff
/--
This lemma derives a `LawfulOrderMin α` instance from a property involving an `Ord α` instance.
-/
public instance LawfulOrderMin.of_ord (α : Type u) [Ord α] [Min α] [LE α] [LawfulOrderOrd α]
(compare_min_isLE_iff : a b c : α,
(compare a (min b c)).isLE (compare a b).isLE (compare a c).isLE)
(min_eq_or : a b : α, min a b = a min a b = b) :
LawfulOrderMin α where
toLawfulOrderInf := .of_ord α compare_min_isLE_iff
min_eq_or := min_eq_or
/--
This lemma derives a `LawfulOrderSup α` instance from a property involving an `Ord α` instance.
-/
public instance LawfulOrderSup.of_ord (α : Type u) [Ord α] [Max α] [LE α] [LawfulOrderOrd α]
(compare_max_isLE_iff : a b c : α,
(compare (max a b) c).isLE (compare a c).isLE (compare b c).isLE) :
LawfulOrderSup α where
max_le_iff := by simpa [isLE_compare] using compare_max_isLE_iff
/--
This lemma derives a `LawfulOrderMax α` instance from a property involving an `Ord α` instance.
-/
public instance LawfulOrderMax.of_ord (α : Type u) [Ord α] [Max α] [LE α] [LawfulOrderOrd α]
(compare_max_isLE_iff : a b c : α,
(compare (max a b) c).isLE (compare a c).isLE (compare b c).isLE)
(max_eq_or : a b : α, max a b = a max a b = b) :
LawfulOrderMax α where
toLawfulOrderSup := .of_ord α compare_max_isLE_iff
max_eq_or := max_eq_or
public theorem min_eq_if_isLE_compare {α : Type u} [Ord α] [LE α] {_ : Min α}
[LawfulOrderOrd α] [LawfulOrderLeftLeaningMin α] {a b : α} :
min a b = if (compare a b).isLE then a else b := by
open Classical in simp [min_eq_if, isLE_compare]
public theorem max_eq_if_isGE_compare {α : Type u} [Ord α] [LE α] {_ : Max α}
[LawfulOrderOrd α] [LawfulOrderLeftLeaningMax α]
{a b : α} : max a b = if (compare a b).isGE then a else b := by
open Classical in simp [max_eq_if, isGE_compare]
end Std
namespace Classical.Order
open Std
/--
Derives an `Ord α` instance from an `LE α` instance. Because all elements are comparable with
`compare`, the resulting `Ord α` instance only makes sense if `LE α` is total.
-/
public noncomputable scoped instance instOrd {α : Type u} [LE α] :
Ord α where
compare a b := if a b then if b a then .eq else .lt else .gt
public instance instLawfulOrderOrd {α : Type u} [LE α]
[Total (α := α) (· ·)] :
LawfulOrderOrd α where
isLE_compare a b := by
simp only [compare]
by_cases a b <;> rename_i h
· simp only [h, reduceIte, iff_true]
split <;> simp
· simp [h]
isGE_compare a b := by
simp only [compare]
cases Total.total (r := (· ·)) a b <;> rename_i h
· simp only [h, reduceIte]
split <;> simp [*]
· simp only [h, reduceIte, iff_true]
split <;> simp
end Classical.Order

View File

@@ -6,7 +6,7 @@ Authors: Markus Himmel, Paul Reichert, Robin Arnez
module
prelude
public import Init.Data.Ord
public import Init.Data.Ord.Basic
public section

View File

@@ -0,0 +1,811 @@
/-
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.Order.FactoriesExtra
public import Init.Data.Order.LemmasExtra
namespace Std
/-!
## Instance packages and factories for them
Instance packages are classes with the sole purpose to bundle together multiple smaller classes.
They should not be used as hypotheses, but they make it more convenient to define multiple instances
at once.
-/
section Preorder
/--
This class entails `LE α`, `LT α` and `BEq α` instances as well as proofs that these operations
represent the same preorder structure on `α`.
-/
public class PreorderPackage (α : Type u) extends
LE α, LT α, BEq α, LawfulOrderLT α, LawfulOrderBEq α, IsPreorder α where
decidableLE : DecidableLE α
decidableLT : DecidableLT α
public instance [PreorderPackage α] : DecidableLE α := PreorderPackage.decidableLE
public instance [PreorderPackage α] : DecidableLT α := PreorderPackage.decidableLT
namespace FactoryInstances
public scoped instance beqOfDecidableLE {α : Type u} [LE α] [DecidableLE α] :
BEq α where
beq a b := a b b a
public instance instLawfulOrderBEqOfDecidableLE {α : Type u} [LE α] [DecidableLE α] :
LawfulOrderBEq α where
beq_iff_le_and_ge := by simp [BEq.beq]
/-- If `LT` can be characterized in terms of a decidable `LE`, then `LT` is decidable either. -/
@[expose]
public def decidableLTOfLE {α : Type u} [LE α] {_ : LT α} [DecidableLE α] [LawfulOrderLT α] :
DecidableLT α :=
fun a b =>
haveI := iff_iff_eq.mp <| LawfulOrderLT.lt_iff a b
if h : a b ¬ b a then .isTrue (this h) else .isFalse (this h)
end FactoryInstances
/--
This structure contains all the data needed to create a `PreorderPackage α` instance. Its fields
are automatically provided if possible. For the detailed rules how the fields are inferred, see
`PreorderPackage.ofLE`.
-/
public structure Packages.PreorderOfLEArgs (α : Type u) where
le : LE α := by infer_instance
decidableLE : DecidableLE α := by infer_instance
lt :
let := le
LT α := by
extract_lets
first
| infer_instance
| exact Classical.Order.instLT
beq :
let := le; let := decidableLE
BEq α := by
extract_lets
first
| infer_instance
| exact FactoryInstances.beqOfDecidableLE
lt_iff :
let := le; let := lt
a b : α, a < b a b ¬ b a := by
extract_lets
first
| exact LawfulOrderLT.lt_iff
| fail "Failed to automatically prove that the `LE` and `LT` instances are compatible. \
Please ensure that a `LawfulOrderLT` instance can be synthesized or \
manually provide the field `lt_iff`."
decidableLT :
let := le; let := decidableLE; let := lt; haveI := lt_iff
have := lt_iff
DecidableLT α := by
extract_lets
haveI := @LawfulOrderLT.mk (lt_iff := by assumption) ..
first
| infer_instance
| exact FactoryInstances.decidableLTOfLE
| fail "Failed to automatically derive that `LT` is decidable. \
Please ensure that a `DecidableLT` instance can be synthesized or \
manually provide the field `decidableLT`."
beq_iff_le_and_ge :
let := le; let := decidableLE; let := beq
a b : α, a == b a b b a := by
extract_lets
first
| exact LawfulOrderBEq.beq_iff_le_and_ge
| fail "Failed to automatically prove that the `LE` and `BEq` instances are compatible. \
Please ensure that a `LawfulOrderBEq` instance can be synthesized or \
manually provide the field `beq_iff_le_and_ge`."
le_refl :
let := le
a : α, a a := by
extract_lets
first
| exact Std.Refl.refl (r := (· ·))
| fail "Failed to automatically prove that the `LE` instance is reflexive. \
Please ensure that a `Refl` instance can be synthesized or \
manually provide the field `le_refl`."
le_trans :
let := le
a b c : α, a b b c a c := by
extract_lets
first
| exact fun _ _ _ hab hbc => Trans.trans (r := (· ·)) (s := (· ·)) (t := (· ·)) hab hbc
| fail "Failed to automatically prove that the `LE` instance is transitive. \
Please ensure that a `Trans` instance can be synthesized or \
manually provide the field `le_trans`."
/--
Use this factory to conveniently define a preorder on a type `α` and all the associated operations
and instances given an `LE α` instance.
Creates a `PreorderPackage α` instance. Such an instance entails `LE α`, `LT α` and
`BEq α` instances as well as an `IsPreorder α` instance and `LawfulOrder*` instances proving the
compatibility of the operations with the preorder.
In the presence of `LE α`, `DecidableLE α`, `Refl (· ≤ ·)` and `Trans (· ≤ ·) (· ≤ ·) (· ≤ ·)`
instances, no arguments are required and the factory can be used as in this example:
```lean
public instance : PreorderPackage X := .ofLE X
```
If not all of these instances are available via typeclass synthesis, it is necessary to explicitly
provide some arguments:
```lean
public instance : PreorderPackage X := .ofLE X {
le_refl := sorry
le_trans := sorry }
```
It is also possible to do all of this by hand, without resorting to `PreorderPackage`. This can
be useful if, say, one wants to avoid specifying an `LT α` instance, which is not possible with
`PreorderPackage`.
**How the arguments are filled**
Lean tries to fill all of the fields of the `args : Packages.PreorderOfLEArgs α` parameter
automatically. If it fails, it is necessary to provide some of the fields manually.
* For the data-carrying typeclasses `LE`, `LT` and `BEq`, existing instances are always preferred.
If no existing instances can be synthesized, it is attempted to derive an instance from the `LE`
instance.
* Some proof obligations can be filled automatically if the data-carrying typeclasses have been
derived from the `LE` instance. For example: If the `beq` field is omitted and no `BEq α` instance
can be synthesized, it is derived from the `LE α` instance. In this case, `beq_iff_le_and_ge` can
be omitted because Lean can infer that `BEq α` and `LE α` are compatible.
* Other proof obligations, namely `le_refl` and `le_trans`, can be omitted if `Refl` and `Trans`
instances can be synthesized.
-/
@[expose]
public def PreorderPackage.ofLE (α : Type u)
(args : Packages.PreorderOfLEArgs α := by exact {}) : PreorderPackage α where
toLE := args.le
decidableLE := args.decidableLE
toLT := args.lt
toBEq := args.beq
toLawfulOrderLT := @LawfulOrderLT.mk (lt_iff := args.lt_iff)
decidableLT := args.decidableLT
toLawfulOrderBEq := @LawfulOrderBEq.mk (beq_iff_le_and_ge := args.beq_iff_le_and_ge)
le_refl := args.le_refl
le_trans := args.le_trans
end Preorder
section PartialOrder
/--
This class entails `LE α`, `LT α` and `BEq α` instances as well as proofs that these operations
represent the same partial order structure on `α`.
-/
public class PartialOrderPackage (α : Type u) extends
PreorderPackage α, IsPartialOrder α
/--
This structure contains all the data needed to create a `PartialOrderPakckage α` instance. Its
fields are automatically provided if possible. For the detailed rules how the fields are inferred,
see `PartialOrderPackage.ofLE`.
-/
public structure Packages.PartialOrderOfLEArgs (α : Type u) extends Packages.PreorderOfLEArgs α where
le_antisymm :
let := le
a b : α, a b b a a = b := by
extract_lets
first
| exact Antisymm.antisymm
| fail "Failed to automatically prove that the `LE` instance is antisymmetric. \
Please ensure that a `Antisymm` instance can be synthesized or \
manually provide the field `le_antisymm`."
/-
Use this factory to conveniently define a partial order on a type `α` and all the associated
operations and instances given an `LE α` instance.
Creates a `PartialOrderPackage α` instance. Such an instance entails `LE α`, `LT α` and
`BEq α` instances as well as an `IsPartialOrder α` instance and `LawfulOrder*` instances proving the
compatibility of the operations with the preorder.
In the presence of `LE α`, `DecidableLE α`, `Refl (· ≤ ·)`, `Trans (· ≤ ·) (· ≤ ·) (· ≤ ·)`
and `Antisymm (· ≤ ·)` instances, no arguments are required and the factory can be used as in this
example:
```lean
public instance : PartialOrderPackage X := .ofLE X
```
If not all of these instances are available via typeclass synthesis, it is necessary to explicitly
provide some arguments:
```lean
public instance : PartialOrderPackage X := .ofLE X {
le_refl := sorry
le_trans := sorry
le_antisymm := sorry }
```
It is also possible to do all of this by hand, without resorting to `PartialOrderPackage`. This can
be useful if, say, one wants to avoid specifying an `LT α` instance, which is not possible with
`PartialOrderPackage`.
**How the arguments are filled**
Lean tries to fill all of the fields of the `args : Packages.PartialOrderOfLEArgs α` parameter
automatically. If it fails, it is necessary to provide some of the fields manually.
* For the data-carrying typeclasses `LE`, `LT` and `BEq`, existing instances are always preferred.
If no existing instances can be synthesized, it is attempted to derive an instance from the `LE`
instance.
* Some proof obligations can be filled automatically if the data-carrying typeclasses have been
derived from the `LE` instance. For example: If the `beq` field is omitted and no `BEq α` instance
can be synthesized, it is derived from the `LE α` instance. In this case, `beq_iff_le_and_ge` can be
omitted because Lean can infer that `BEq α` and `LE α` are compatible.
* Other proof obligations, namely `le_refl`, `le_trans` and `le_antisymm`, can be omitted if `Refl`,
`Trans` and `Antisymm` instances can be synthesized.
-/
@[expose]
public def PartialOrderPackage.ofLE (α : Type u)
(args : Packages.PartialOrderOfLEArgs α := by exact {}) : PartialOrderPackage α where
toPreorderPackage := .ofLE α args.toPreorderOfLEArgs
le_antisymm := args.le_antisymm
end PartialOrder
namespace FactoryInstances
public scoped instance instOrdOfDecidableLE {α : Type u} [LE α] [DecidableLE α] :
Ord α where
compare a b := if a b then if b a then .eq else .lt else .gt
theorem isLE_compare {α : Type u} [LE α] [DecidableLE α] {a b : α} :
(compare a b).isLE a b := by
simp only [compare]
split
· split <;> simp_all
· simp_all
theorem isGE_compare {α : Type u} [LE α] [DecidableLE α]
(le_total : a b : α, a b b a) {a b : α} :
(compare a b).isGE b a := by
simp only [compare]
split
· split <;> simp_all
· specialize le_total a b
simp_all
public instance instLawfulOrderOrdOfDecidableLE {α : Type u} [LE α] [DecidableLE α]
[Total (α := α) (· ·)] :
LawfulOrderOrd α where
isLE_compare _ _ := by exact isLE_compare
isGE_compare _ _ := by exact isGE_compare (le_total := Total.total)
end FactoryInstances
/--
This class entails `LE α`, `LT α`, `BEq α` and `Ord α` instances as well as proofs that these
operations represent the same linear preorder structure on `α`.
-/
public class LinearPreorderPackage (α : Type u) extends
PreorderPackage α, Ord α, LawfulOrderOrd α, IsLinearPreorder α
/--
This structure contains all the data needed to create a `LinearPreorderPackage α` instance. Its fields
are automatically provided if possible. For the detailed rules how the fields are inferred, see
`LinearPreorderPackage.ofLE`.
-/
public structure Packages.LinearPreorderOfLEArgs (α : Type u) extends
PreorderOfLEArgs α where
ord :
let := le; let := decidableLE
Ord α := by
extract_lets
first
| infer_instance
| exact FactoryInstances.instOrdOfDecidableLE
le_total :
a b : α, a b b a := by
first
| exact Total.total
| fail "Failed to automatically prove that the `LE` instance is total. \
Please ensure that a `Total` instance can be synthesized or \
manually provide the field `le_total`."
le_refl a := (by simpa using le_total a a)
isLE_compare :
let := le; let := decidableLE; let := ord
a b : α, (compare a b).isLE a b := by
extract_lets
first
| exact LawfulOrderOrd.isLE_compare
| fail "Failed to automatically prove that `(compare a b).isLE` is equivalent to `a ≤ b`. \
Please ensure that a `LawfulOrderOrd` instance can be synthesized or \
manually provide the field `isLE_compare`."
isGE_compare :
let := le; let := decidableLE; have := le_total; let := ord
a b : α, (compare a b).isGE b a := by
extract_lets
first
| exact LawfulOrderOrd.isGE_compare
| fail "Failed to automatically prove that `(compare a b).isGE` is equivalent to `b ≤ a`. \
Please ensure that a `LawfulOrderOrd` instance can be synthesized or \
manually provide the field `isGE_compare`."
/--
Use this factory to conveniently define a linear preorder on a type `α` and all the associated
operations and instances given an `LE α` instance.
Creates a `LinearPreorderPackage α` instance. Such an instance entails `LE α`, `LT α`, `BEq α` and
`Ord α` instances as well as an `IsLinearPreorder α` instance and `LawfulOrder*` instances proving
the compatibility of the operations with the linear preorder.
In the presence of `LE α`, `DecidableLE α`, `Total (· ≤ ·)` and `Trans (· ≤ ·) (· ≤ ·) (· ≤ ·)`
instances, no arguments are required and the factory can be used as in this example:
```lean
public instance : LinearPreorderPackage X := .ofLE X
```
If not all of these instances are available via typeclass synthesis, it is necessary to explicitly
provide some arguments:
```lean
public instance : LinearPreorderPackage X := .ofLE X {
le_total := sorry
le_trans := sorry }
```
It is also possible to do all of this by hand, without resorting to `LinearPreorderPackage`. This
can be useful if, say, one wants to avoid specifying an `LT α` instance, which is not possible with
`LinearPreorderPackage`.
**How the arguments are filled**
Lean tries to fill all of the fields of the `args : Packages.LinearPreorderOfLEArgs α` parameter
automatically. If it fails, it is necessary to provide some of the fields manually.
* For the data-carrying typeclasses `LE`, `LT`, `BEq` and `Ord`, existing instances are always
preferred. If no existing instances can be synthesized, it is attempted to derive an instance from
the `LE` instance.
* Some proof obligations can be filled automatically if the data-carrying typeclasses have been
derived from the `LE` instance. For example: If the `beq` field is omitted and no `BEq α` instance
can be synthesized, it is derived from the `LE α` instance. In this case, `beq_iff_le_and_ge` can be
omitted because Lean can infer that `BEq α` and `LE α` are compatible.
* Other proof obligations, namely `le_total` and `le_trans`, can be omitted if `Total` and `Trans`
instances can be synthesized.
-/
@[expose]
public def LinearPreorderPackage.ofLE (α : Type u)
(args : Packages.LinearPreorderOfLEArgs α := by exact {}) : LinearPreorderPackage α where
toPreorderPackage := .ofLE α args.toPreorderOfLEArgs
toOrd := letI := args.le; args.ord
le_total := args.le_total
isLE_compare := args.isLE_compare
isGE_compare := args.isGE_compare
/--
This class entails `LE α`, `LT α`, `BEq α`, `Ord α`, `Min α` and `Max α` instances as well as proofs
that these operations represent the same linear order structure on `α`.
-/
public class LinearOrderPackage (α : Type u) extends
LinearPreorderPackage α, PartialOrderPackage α, Min α, Max α,
LawfulOrderMin α, LawfulOrderMax α, IsLinearOrder α
/--
This structure contains all the data needed to create a `LinearOrderPackage α` instance. Its fields
are automatically provided if possible. For the detailed rules how the fields are inferred, see
`LinearOrderPackage.ofLE`.
-/
public structure Packages.LinearOrderOfLEArgs (α : Type u) extends
LinearPreorderOfLEArgs α, PartialOrderOfLEArgs α where
min :
let := le; let := decidableLE
Min α := by
extract_lets
first
| infer_instance
| exact Min.leftLeaningOfLE _
max :
let := le; let := decidableLE
Max α := by
extract_lets
first
| infer_instance
| exact Max.leftLeaningOfLE _
min_eq :
let := le; let := decidableLE; let := min
a b : α, Min.min a b = if a b then a else b := by
extract_lets
first
| exact fun a b => Std.min_eq_if (a := a) (b := b)
| fail "Failed to automatically prove that `min` is left-leaning. \
Please ensure that a `LawfulOrderLeftLeaningMin` instance can be synthesized or \
manually provide the field `min_eq`."
max_eq :
let := le; let := decidableLE; let := max
a b : α, Max.max a b = if b a then a else b := by
extract_lets
first
| exact fun a b => Std.max_eq_if (a := a) (b := b)
| fail "Failed to automatically prove that `max` is left-leaning. \
Please ensure that a `LawfulOrderLeftLeaningMax` instance can be synthesized or \
manually provide the field `max_eq`."
/--
Use this factory to conveniently define a linear order on a type `α` and all the associated
operations and instances given an `LE α` instance.
Creates a `LinearOrderPackage α` instance. Such an instance entails `LE α`, `LT α`, `BEq α`,
`Ord α`, `Min α` and `Max α` instances as well as an `IsLinearOrder α` instance and `LawfulOrder*`
instances proving the compatibility of the operations with the linear order.
In the presence of `LE α`, `DecidableLE α`, `Total (· ≤ ·)`, `Trans (· ≤ ·) (· ≤ ·) (· ≤ ·)` and
`Antisymm (· ≤ ·)` instances, no arguments are required and the factory can be used as in this
example:
```lean
public instance : LinearOrderPackage X := .ofLE X
```
If not all of these instances are available via typeclass synthesis, it is necessary to explicitly
provide some arguments:
```lean
public instance : LinearOrderPackage X := .ofLE X {
le_total := sorry
le_trans := sorry
le_antisymm := sorry }
```
It is also possible to do all of this by hand, without resorting to `LinearOrderPackage`. This
can be useful if, say, one wants to avoid specifying an `LT α` instance, which is not possible with
`LinearOrderPackage`.
**How the arguments are filled**
Lean tries to fill all of the fields of the `args : Packages.LinearOrderOfLEArgs α` parameter
automatically. If it fails, it is necessary to provide some of the fields manually.
* For the data-carrying typeclasses `LE`, `LT`, `BEq`, `Ord`, `Min` and `Max`, existing instances
are always preferred. If no existing instances can be synthesized, it is attempted to derive an
instance from the `LE` instance.
* Some proof obligations can be filled automatically if the data-carrying typeclasses have been
derived from the `LE` instance. For example: If the `beq` field is omitted and no `BEq α` instance
can be synthesized, it is derived from the `LE α` instance. In this case, `beq_iff_le_and_ge` can be
omitted because Lean can infer that `BEq α` and `LE α` are compatible.
* Other proof obligations, namely `le_total`, `le_trans` and `le_antisymm`, can be omitted if
`Total`, `Trans` and `Antisymm` instances can be synthesized.
-/
@[expose]
public def LinearOrderPackage.ofLE (α : Type u)
(args : Packages.LinearOrderOfLEArgs α := by exact {}) : LinearOrderPackage α where
toLinearPreorderPackage := .ofLE α args.toLinearPreorderOfLEArgs
le_antisymm := (PartialOrderPackage.ofLE α args.toPartialOrderOfLEArgs).le_antisymm
toMin := args.min
toMax := args.max
toLawfulOrderMin := by
letI := LinearPreorderPackage.ofLE α args.toLinearPreorderOfLEArgs
letI := args.decidableLE; letI := args.min
haveI : LawfulOrderLeftLeaningMin α := .of_eq args.min_eq
infer_instance
toLawfulOrderMax := by
letI := LinearPreorderPackage.ofLE α args.toLinearPreorderOfLEArgs
letI := args.decidableLE; letI := args.max
haveI : LawfulOrderLeftLeaningMax α := .of_eq args.max_eq
infer_instance
section LinearPreorder
namespace FactoryInstances
attribute [scoped instance] LE.ofOrd
attribute [scoped instance] LT.ofOrd
attribute [scoped instance] BEq.ofOrd
public theorem _root_.Std.OrientedCmp.of_gt_iff_lt {α : Type u} {cmp : α α Ordering}
(h : a b : α, cmp a b = .gt cmp b a = .lt) : OrientedCmp cmp where
eq_swap {a b} := by
cases h' : cmp a b
· apply Eq.symm
simp [h, h']
· cases h'' : cmp b a
· simp [ h, h'] at h''
· simp
· simp [h, h'] at h''
· apply Eq.symm
simp [ h, h']
end FactoryInstances
/--
This structure contains all the data needed to create a `LinearPreorderPackage α` instance.
Its fields are automatically provided if possible. For the detailed rules how the fields are
inferred, see `LinearPreorderPackage.ofOrd`.
-/
public structure Packages.LinearPreorderOfOrdArgs (α : Type u) where
ord : Ord α := by infer_instance
transOrd : TransOrd α := by infer_instance
le :
let := ord
LE α := by
extract_lets
first
| infer_instance
| exact LE.ofOrd _
lawfulOrderOrd :
let := ord; let := transOrd; let := le
LawfulOrderOrd α := by
extract_lets
first
| infer_instance
| fail "Failed to automatically derive a `LawfulOrderOrd` instance. \
Please ensure that the instance can be synthesized or \
manually provide the field `lawfulOrderOrd`."
decidableLE :
let := ord; let := le; have := lawfulOrderOrd
DecidableLE α := by
extract_lets
first
| infer_instance
| exact DecidableLE.ofOrd _
| fail "Failed to automatically derive that `LE` is decidable.\
Please ensure that a `DecidableLE` instance can be synthesized or \
manually provide the field `decidableLE`."
lt :
let := ord
LT α := by
extract_lets
first
| infer_instance
| exact LT.ofOrd _
lt_iff :
let := ord; let := le; have := lawfulOrderOrd; let := lt
a b : α, a < b compare a b = .lt := by
extract_lets
first
| exact fun _ _ => Std.compare_eq_lt.symm
| fail "Failed to automatically derive that `LT` and `Ord` are compatible. \
Please ensure that a `LawfulOrderLT` instance can be synthesized or \
manually provide the field `lt_iff`."
decidableLT :
let := ord; let := lt; let := le; have := lawfulOrderOrd; have := lt_iff
DecidableLT α := by
extract_lets
first
| infer_instance
| exact DecidableLT.ofOrd _
| fail "Failed to automatically derive that `LT` is decidable. \
Please ensure that a `DecidableLT` instance can be synthesized or \
manually provide the field `decidableLT`."
beq :
let := ord; BEq α := by
extract_lets
first
| infer_instance
| exact BEq.ofOrd _
beq_iff :
let := ord; let := le; have := lawfulOrderOrd; let := beq
a b : α, a == b compare a b = .eq := by
extract_lets
first
| exact fun _ _ => Std.compare_eq_eq.symm
| fail "Failed to automatically derive that `BEq` and `Ord` are compatible. \
Please ensure that a `LawfulOrderBEq` instance can be synthesized or \
manually provide the field `beq_iff`."
/--
Use this factory to conveniently define a linear preorder on a type `α` and all the associated
operations and instances given an `Ord α` instance.
Creates a `LinearPreorderPackage α` instance. Such an instance entails `LE α`, `LT α`, `BEq α` and
`Ord α` instances as well as an `IsLinearPreorder α` instance and `LawfulOrder*` instances proving
the compatibility of the operations with the linear preorder.
In the presence of `Ord α` and `TransOrd α` instances, no arguments are required and the factory can
be used as in this example:
```lean
public instance : LinearPreorderPackage X := .ofOrd X
```
If not all of these instances are available via typeclass synthesis, it is necessary to explicitly
provide some arguments:
```lean
public instance : LinearPreorderPackage X := .ofOrd X {
ord := sorry
transOrd := sorry }
```
It is also possible to do all of this by hand, without resorting to `LinearPreorderPackage`. This
can be useful if, say, one wants to avoid specifying an `LT α` instance, which is not possible with
`LinearPreorderPackage`.
**How the arguments are filled**
Lean tries to fill all of the fields of the `args : Packages.LinearPreorderOfOrdArgs α` parameter
automatically. If it fails, it is necessary to provide some of the fields manually.
* For the data-carrying typeclasses `LE`, `LT`, `BEq` and `Ord`, existing instances are always
preferred. If no existing instances can be synthesized, it is attempted to derive an instance from
the `Ord` instance.
* Some proof obligations can be filled automatically if the data-carrying typeclasses have been
derived from the `Ord` instance. For example: If the `beq` field is omitted and no `BEq α` instance
can be synthesized, it is derived from the `Ord α` instance. In this case, `beq_iff`
can be omitted because Lean can infer that `BEq α` and `Ord α` are compatible.
* Other proof obligations, for example `transOrd`, can be omitted if a matching instance can be
synthesized.
-/
@[expose]
public def LinearPreorderPackage.ofOrd (α : Type u)
(args : Packages.LinearPreorderOfOrdArgs α := by exact {}) : LinearPreorderPackage α :=
letI := args.ord
haveI := args.transOrd
letI := args.le
haveI := args.lawfulOrderOrd
{ toOrd := args.ord
toLE := args.le
toLT := args.lt
toBEq := args.beq
toLawfulOrderOrd := args.lawfulOrderOrd
lt_iff a b := by
cases h : compare a b
all_goals simp [h, args.lawfulOrderOrd.isLE_compare a _, args.lawfulOrderOrd.isGE_compare a _,
args.lt_iff]
beq_iff_le_and_ge a b := by
simp [args.beq_iff, Ordering.eq_eq_iff_isLE_and_isGE, isLE_compare,
isGE_compare]
decidableLE := args.decidableLE
decidableLT := args.decidableLT
le_refl a := by simp [ isLE_compare]
le_total a b := by cases h : compare a b <;> simp [h, isLE_compare (a := a), isGE_compare (a := a)]
le_trans a b c := by simpa [ isLE_compare] using TransOrd.isLE_trans }
end LinearPreorder
section LinearOrder
namespace FactoryInstances
public scoped instance instMinOfOrd {α : Type u} [Ord α] :
Min α where
min a b := if (compare a b).isLE then a else b
public scoped instance instMaxOfOrd {α : Type u} [Ord α] :
Max α where
max a b := if (compare b a).isLE then a else b
public instance instLawfulOrderLeftLeaningMinOfOrd {α : Type u} [Ord α] [LE α] [LawfulOrderOrd α] :
LawfulOrderLeftLeaningMin α where
min_eq_left a b := by simp +contextual only [ Std.isLE_compare, min, reduceIte, implies_true]
min_eq_right a b := by
simp +contextual only [ Std.isLE_compare, min, Bool.false_eq_true, reduceIte, implies_true]
public instance instLawfulOrderLeftLeaningMaxOfOrd {α : Type u} [Ord α] [LE α] [LawfulOrderOrd α] :
LawfulOrderLeftLeaningMax α where
max_eq_left a b := by simp +contextual only [ Std.isLE_compare, max, reduceIte, implies_true]
max_eq_right a b := by
simp +contextual only [ Std.isLE_compare, max, Bool.false_eq_true, reduceIte, implies_true]
end FactoryInstances
/--
This structure contains all the data needed to create a `LinearOrderPackage α` instance.
Its fields are automatically provided if possible. For the detailed rules how the fields are
inferred, see `LinearOrderPackage.ofOrd`.
-/
public structure Packages.LinearOrderOfOrdArgs (α : Type u) extends
LinearPreorderOfOrdArgs α where
eq_of_compare :
let := ord; let := le
a b : α, compare a b = .eq a = b := by
extract_lets
first
| exact LawfulEqOrd.eq_of_compare
| fail "Failed to derive a `LawfulEqOrd` instance. \
Please make sure that it can be synthesized or \
manually provide the field `eq_of_compare`."
min :
let := ord
Min α := by
extract_lets
first
| infer_instance
| exact FactoryInstances.instMinOfOrd
max :
let := ord
Max α := by
extract_lets
first
| infer_instance
| exact FactoryInstances.instMaxOfOrd
min_eq :
let := ord; let := le; let := min; have := lawfulOrderOrd
a b : α, Min.min a b = if (compare a b).isLE then a else b := by
extract_lets
first
| exact fun a b => Std.min_eq_if_isLE_compare (a := a) (b := b)
| fail "Failed to automatically prove that `min` is left-leaning. \
Please ensure that a `LawfulOrderLeftLeaningMin` instance can be synthesized or \
manually provide the field `min_eq`."
max_eq :
let := ord; let := le; let := max; have := lawfulOrderOrd
a b : α, Max.max a b = if (compare a b).isGE then a else b := by
extract_lets
first
| exact fun a b => Std.max_eq_if_isGE_compare (a := a) (b := b)
| fail "Failed to automatically prove that `max` is left-leaning. \
Please ensure that a `LawfulOrderLeftLeaningMax` instance can be synthesized or \
manually provide the field `max_eq`."
/--
Use this factory to conveniently define a linear order on a type `α` and all the associated
operations and instances given an `Ord α` instance.
Creates a `LinearOrderPackage α` instance. Such an instance entails `LE α`, `LT α`, `BEq α`,
`Ord α`, `Min α` and `Max α` instances as well as an `IsLinearOrder α` instance and `LawfulOrder*`
instances proving the compatibility of the operations with the linear order.
In the presence of `Ord α`, `TransOrd α` and `LawfulEqOrd α` instances, no arguments are required
and the factory can be used as in this
example:
```lean
public instance : LinearOrderPackage X := .ofLE X
```
If not all of these instances are available via typeclass synthesis, it is necessary to explicitly
provide some arguments:
```lean
public instance : LinearOrderPackage X := .ofLE X {
transOrd := sorry
eq_of_compare := sorry }
```
It is also possible to do all of this by hand, without resorting to `LinearOrderPackage`. This
can be useful if, say, one wants to avoid specifying an `LT α` instance, which is not possible with
`LinearOrderPackage`.
**How the arguments are filled**
Lean tries to fill all of the fields of the `args : Packages.LinearOrderOfLEArgs α` parameter
automatically. If it fails, it is necessary to provide some of the fields manually.
* For the data-carrying typeclasses `LE`, `LT`, `BEq`, `Ord`, `Min` and `Max`, existing instances
are always preferred. If no existing instances can be synthesized, it is attempted to derive an
instance from the `Ord` instance.
* Some proof obligations can be filled automatically if the data-carrying typeclasses have been
derived from the `Ord` instance. For example: If the `beq` field is omitted and no `BEq α` instance
can be synthesized, it is derived from the `LE α` instance. In this case, `beq_iff`
can be omitted because Lean can infer that `BEq α` and `Ord α` are compatible.
* Other proof obligations, such as `transOrd`, can be omitted if matching instances can be
synthesized.
-/
@[expose]
public def LinearOrderPackage.ofOrd (α : Type u)
(args : Packages.LinearOrderOfOrdArgs α := by exact {}) : LinearOrderPackage α :=
letI := LinearPreorderPackage.ofOrd α args.toLinearPreorderOfOrdArgs
haveI : LawfulEqOrd α := args.eq_of_compare _ _
letI : Min α := args.min
letI : Max α := args.max
{ toMin := args.min
toMax := args.max,
le_antisymm := Antisymm.antisymm
toLawfulOrderMin := by
haveI : LawfulOrderLeftLeaningMin α := .of_eq (by simpa [isLE_compare] using args.min_eq)
infer_instance
toLawfulOrderMax := by
haveI : LawfulOrderLeftLeaningMax α := .of_eq (by simpa [isGE_compare] using args.max_eq)
infer_instance }
end LinearOrder
end Std

View File

@@ -36,7 +36,14 @@ structure StdGen where
s1 : Nat
s2 : Nat
instance : Inhabited StdGen := { s1 := 0, s2 := 0 }
/-- Returns a standard number generator. -/
def mkStdGen (s : Nat := 0) : StdGen :=
let q := s / 2147483562
let s1 := s % 2147483562
let s2 := q % 2147483398
s1 + 1, s2 + 1
instance : Inhabited StdGen := mkStdGen
/-- The range of values returned by `StdGen` -/
def stdRange := (1, 2147483562)
@@ -77,13 +84,6 @@ instance : RandomGen StdGen := {
split := stdSplit
}
/-- Returns a standard number generator. -/
def mkStdGen (s : Nat := 0) : StdGen :=
let q := s / 2147483562
let s1 := s % 2147483562
let s2 := q % 2147483398
s1 + 1, s2 + 1
/--
Auxiliary function for randomNatVal.
Generate random values until we exceed the target magnitude.

View File

@@ -12,6 +12,7 @@ public import Init.Data.Range.Polymorphic.Stream
public import Init.Data.Range.Polymorphic.Lemmas
public import Init.Data.Range.Polymorphic.Nat
public import Init.Data.Range.Polymorphic.NatLemmas
public import Init.Data.Range.Polymorphic.GetElemTactic
public section

View File

@@ -26,10 +26,9 @@ class RangeSize (shape : BoundShape) (α : Type u) where
This typeclass ensures that a `RangeSize` instance returns the correct size for all ranges.
-/
class LawfulRangeSize (su : BoundShape) (α : Type u) [UpwardEnumerable α]
[SupportsUpperBound su α] [RangeSize su α]
[LawfulUpwardEnumerable α] [HasFiniteRanges su α] where
[SupportsUpperBound su α] [RangeSize su α] where
/-- If the smallest value in the range is beyond the upper bound, the size is zero. -/
size_eq_zero_of_not_satisfied (upperBound : Bound su α) (init : α)
size_eq_zero_of_not_isSatisfied (upperBound : Bound su α) (init : α)
(h : ¬ SupportsUpperBound.IsSatisfied upperBound init) :
RangeSize.size upperBound init = 0
/--
@@ -43,11 +42,38 @@ class LawfulRangeSize (su : BoundShape) (α : Type u) [UpwardEnumerable α]
/--
If the smallest value in the range satisfies the upper bound and has a successor, the size is
one larger than the size of the range starting at the successor. -/
size_eq_succ_of_succ?_eq_some (upperBound : Bound su α) (init : α)
size_eq_succ_of_succ?_eq_some (upperBound : Bound su α) (init a : α)
(h : SupportsUpperBound.IsSatisfied upperBound init)
(h' : UpwardEnumerable.succ? init = some a) :
RangeSize.size upperBound init = RangeSize.size upperBound a + 1
theorem LawfulRangeSize.size_eq_zero_iff_not_isSatisfied {su : BoundShape} {α : Type u}
[UpwardEnumerable α] [SupportsUpperBound su α] [RangeSize su α] [LawfulRangeSize su α]
{bound : Bound su α} {init : α} :
RangeSize.size bound init = 0 ¬ SupportsUpperBound.IsSatisfied bound init := by
match h : decide (SupportsUpperBound.IsSatisfied bound init) with
| true =>
simp only [decide_eq_true_eq] at h
match hs : UpwardEnumerable.succ? init with
| none => simp [h, LawfulRangeSize.size_eq_one_of_succ?_eq_none (h := h) (h' := hs)]
| some b => simp [h, LawfulRangeSize.size_eq_succ_of_succ?_eq_some (h := h) (h' := hs)]
| false =>
simp only [decide_eq_false_iff_not] at h
simp [h, LawfulRangeSize.size_eq_zero_of_not_isSatisfied]
theorem LawfulRangeSize.size_pos_iff_isSatisfied {su : BoundShape} {α : Type u}
[UpwardEnumerable α] [SupportsUpperBound su α] [RangeSize su α] [LawfulRangeSize su α]
{bound : Bound su α} {init : α} :
0 < RangeSize.size bound init SupportsUpperBound.IsSatisfied bound init := by
have := size_eq_zero_iff_not_isSatisfied (bound := bound) (init := init)
match h : decide (SupportsUpperBound.IsSatisfied bound init) with
| true =>
simp only [decide_eq_true_eq] at h
simp_all [Nat.pos_iff_ne_zero]
| false =>
simp only [decide_eq_false_iff_not] at h
simp_all
/--
Checks whether the range contains any value.
@@ -60,6 +86,11 @@ def isEmpty {sl su α} [UpwardEnumerable α] [BoundedUpwardEnumerable sl α]
[SupportsUpperBound su α] (r : PRange sl, su α) : Bool :=
(BoundedUpwardEnumerable.init? r.lower).all (! SupportsUpperBound.IsSatisfied r.upper ·)
theorem mem_iff_isSatisfied [SupportsLowerBound sl α] [SupportsUpperBound su α]
{x : α} {r : Std.PRange sl, su α} :
x r SupportsLowerBound.IsSatisfied r.lower x SupportsUpperBound.IsSatisfied r.upper x := by
simp [Membership.mem]
theorem le_upper_of_mem {sl α} [LE α] [DecidableLE α] [SupportsLowerBound sl α]
{a : α} {r : PRange sl, .closed α} (h : a r) : a r.upper :=
h.2
@@ -84,6 +115,11 @@ macro_rules
| `(tactic| get_elem_tactic_extensible) =>
`(tactic|
first
-- This is a relatively rigid check. See `Init.Data.Range.Polymorphic.GetElemTactic`
-- for a second one that is more flexible.
-- Note: This one is not *strictly* inferior. This one is better able to look under
-- reducible terms. The other tactic needs special handling for `Vector.size` to work
-- around that fact that `omega` does not reduce terms.
| apply Std.PRange.Internal.get_elem_helper_upper_open _ (by trivial)
| done)

View File

@@ -0,0 +1,34 @@
/-
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.Range.Polymorphic.Basic
public import Init.Data.Vector.Basic
public section
/-!
This module contains an advanced `GetElem` tactic extension for polymorphic ranges.
While `Init.Data.Range.Polymorphic.Basic` already defines one, it only works in very
basic cases where the open upper bound of the range is exactly the size of the collection.
This tactic is using `omega` to be more powerful, but it needs special handling for `Vector`,
which is impossible in `Init.Data.Range.Polymorphic.Basic` for bootstrapping reasons.
-/
macro_rules
| `(tactic| get_elem_tactic_extensible) =>
`(tactic|
first
| rw [Std.PRange.mem_iff_isSatisfied] at *
dsimp +zetaDelta only [Std.PRange.SupportsLowerBound.IsSatisfied, Std.PRange.SupportsUpperBound.IsSatisfied,
-- `Vector.size` needs to be unfolded because for `xs : Vector α n`, one needs to prove
-- `i < n` instead of `i < xs.size`. Although `Vector.size` is reducible, this is
-- not enough for `omega`.
Vector.size] at *
omega
| done)

View File

@@ -0,0 +1,178 @@
/-
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.Order.Classes
public import Init.Data.Range.Polymorphic.Basic
import Init.Data.Nat.Lemmas
import Init.Data.Order.Lemmas
/-!
This module provides instances that reduce the amount of code necessary to make a type compatible
with the polymorphic ranges. For an example of the required work, take a look at
`Init.Data.Range.Polymorphic.Nat`.
-/
public section
namespace Std.PRange
instance [LE α] [LT α] [UpwardEnumerable α] [LawfulUpwardEnumerable α]
[LawfulUpwardEnumerableLE α] [LawfulOrderLT α] : LawfulUpwardEnumerableLT α where
lt_iff a b := by
simp only [LawfulOrderLT.lt_iff, LawfulUpwardEnumerableLE.le_iff]
constructor
· intro h
obtain n, hn := h.1
cases n
· apply h.2.elim
refine 0, ?_
simpa [UpwardEnumerable.succMany?_zero] using hn.symm
exact _, hn
· intro h
constructor
· match h with | _, hn => exact _, hn
· exact UpwardEnumerable.not_ge_of_lt h
instance [LE α] [DecidableLE α] [UpwardEnumerable α] [LawfulUpwardEnumerableLE α] :
LawfulUpwardEnumerableLowerBound .closed α where
isSatisfied_iff a l := by
simp [SupportsLowerBound.IsSatisfied, BoundedUpwardEnumerable.init?,
LawfulUpwardEnumerableLE.le_iff]
instance [LE α] [DecidableLE α] [UpwardEnumerable α] [LawfulUpwardEnumerableLE α]
[Trans (α := α) (· ·) (· ·) (· ·)]:
LawfulUpwardEnumerableUpperBound .closed α where
isSatisfied_of_le u a b hub hab := by
simp only [SupportsUpperBound.IsSatisfied, LawfulUpwardEnumerableLE.le_iff] at hub hab
exact Trans.trans hab hub
instance [LT α] [DecidableLT α] [UpwardEnumerable α] [LawfulUpwardEnumerable α]
[LawfulUpwardEnumerableLT α] :
LawfulUpwardEnumerableLowerBound .open α where
isSatisfied_iff a l := by
simp only [SupportsLowerBound.IsSatisfied, BoundedUpwardEnumerable.init?,
LawfulUpwardEnumerableLT.lt_iff]
constructor
· rintro n, hn
simp only [LawfulUpwardEnumerable.succMany?_succ_eq_succ?_bind_succMany?] at hn
cases h : UpwardEnumerable.succ? l
· simp [h] at hn
· exact _, rfl, n, by simpa [h] using hn
· rintro init, hi, n, hn
exact n, by simpa [LawfulUpwardEnumerable.succMany?_succ_eq_succ?_bind_succMany?, hi] using hn
instance [LT α] [DecidableLT α] [UpwardEnumerable α] [LawfulUpwardEnumerable α]
[LawfulUpwardEnumerableLT α] :
LawfulUpwardEnumerableUpperBound .open α where
isSatisfied_of_le u a b hub hab := by
simp only [SupportsUpperBound.IsSatisfied, LawfulUpwardEnumerableLT.lt_iff] at hub
exact UpwardEnumerable.lt_of_le_of_lt hab hub
instance [UpwardEnumerable α] [Least? α] [LawfulUpwardEnumerableLeast? α] :
LawfulUpwardEnumerableLowerBound .unbounded α where
isSatisfied_iff a l := by
simpa [SupportsLowerBound.IsSatisfied, BoundedUpwardEnumerable.init?] using
LawfulUpwardEnumerableLeast?.eq_succMany?_least? a
instance [LE α] [Total (α := α) (· ·)] [UpwardEnumerable α] [LawfulUpwardEnumerable α]
[LawfulUpwardEnumerableLE α] :
LinearlyUpwardEnumerable α where
eq_of_succ?_eq a b hab := by
cases Total.total (α := α) (r := (· ·)) a b <;> rename_i h <;>
simp only [LawfulUpwardEnumerableLE.le_iff] at h
· obtain n, hn := h
cases n
· simpa [UpwardEnumerable.succMany?_zero] using hn
· exfalso
rw [LawfulUpwardEnumerable.succMany?_succ_eq_succ?_bind_succMany?, hab,
LawfulUpwardEnumerable.succMany?_succ_eq_succ?_bind_succMany?] at hn
exact UpwardEnumerable.lt_irrefl _, hn
· obtain n, hn := h
cases n
· simpa [UpwardEnumerable.succMany?_zero] using hn.symm
· exfalso
rw [LawfulUpwardEnumerable.succMany?_succ_eq_succ?_bind_succMany?, hab.symm,
LawfulUpwardEnumerable.succMany?_succ_eq_succ?_bind_succMany?] at hn
exact UpwardEnumerable.lt_irrefl _, hn
instance [UpwardEnumerable α] : LawfulUpwardEnumerableUpperBound .unbounded α where
isSatisfied_of_le _ _ _ _ _ := .intro
/--
Creates a `RangeSize .open α` from a `RangeSize .closed α` instance. If the latter is lawful
and certain other conditions hold, then the former is also lawful by
`LawfulRangeSize.open_of_closed`.
-/
@[inline]
abbrev RangeSize.openOfClosed [RangeSize .closed α] : RangeSize .open α where
size bound a := RangeSize.size (shape := .closed) bound a - 1
attribute [local instance] RangeSize.openOfClosed in
instance LawfulRangeSize.open_of_closed [UpwardEnumerable α] [LE α] [DecidableLE α]
[LT α] [DecidableLT α] [LawfulOrderLT α] [IsPartialOrder α]
[LawfulUpwardEnumerable α] [LawfulUpwardEnumerableLE α]
[RangeSize .closed α] [LawfulRangeSize .closed α] :
LawfulRangeSize .open α where
size_eq_zero_of_not_isSatisfied bound a h := by
simp only [SupportsUpperBound.IsSatisfied] at h
simp only [RangeSize.size]
by_cases h' : a bound
· match hs : UpwardEnumerable.succ? a with
| none => rw [LawfulRangeSize.size_eq_one_of_succ?_eq_none (h := h') (h' := by omega)]
| some b =>
rw [LawfulRangeSize.size_eq_succ_of_succ?_eq_some (h := h') (h' := hs)]
have : ¬ b bound := by
intro hb
have : a < b := by
rw [LawfulUpwardEnumerableLT.lt_iff]
exact 0, by simpa [UpwardEnumerable.succMany?_one] using hs
exact h (lt_of_lt_of_le this hb)
rw [LawfulRangeSize.size_eq_zero_of_not_isSatisfied (h := this)]
· suffices RangeSize.size (shape := .closed) bound a = 0 by omega
exact LawfulRangeSize.size_eq_zero_of_not_isSatisfied _ _ h'
size_eq_one_of_succ?_eq_none bound a h h' := by
exfalso
simp only [SupportsUpperBound.IsSatisfied, LawfulUpwardEnumerableLT.lt_iff] at h
obtain n, hn := h
simp [LawfulUpwardEnumerable.succMany?_succ_eq_succ?_bind_succMany?, h'] at hn
size_eq_succ_of_succ?_eq_some bound a a' h h' := by
simp only [SupportsUpperBound.IsSatisfied] at h
simp only [RangeSize.size, Nat.pred_eq_succ_iff]
rw [LawfulRangeSize.size_eq_succ_of_succ?_eq_some (h := le_of_lt h) (h' := h')]
rw [ Nat.sub_add_comm]
· omega
· simp only [Nat.succ_le_iff, LawfulRangeSize.size_pos_iff_isSatisfied,
SupportsUpperBound.IsSatisfied]
rw [LawfulUpwardEnumerableLE.le_iff]
rw [LawfulUpwardEnumerableLT.lt_iff] at h
refine h.choose, ?_
simpa [LawfulUpwardEnumerable.succMany?_succ_eq_succ?_bind_succMany?, h'] using h.choose_spec
instance LawfulRangeSize.instHasFiniteRanges [UpwardEnumerable α] [LawfulUpwardEnumerable α]
[RangeSize su α] [SupportsUpperBound su α] [LawfulRangeSize su α] : HasFiniteRanges su α where
finite init bound := by
refine RangeSize.size bound init, ?_
generalize hn : RangeSize.size bound init = n
induction n generalizing init with
| zero =>
simp only [LawfulRangeSize.size_eq_zero_iff_not_isSatisfied] at hn
simp [UpwardEnumerable.succMany?_zero, hn]
| succ =>
rename_i n ih
rw [LawfulUpwardEnumerable.succMany?_succ_eq_succ?_bind_succMany?]
match hs : UpwardEnumerable.succ? init with
| none => simp
| some a =>
simp only [Option.bind_some]
apply ih
have : SupportsUpperBound.IsSatisfied bound init :=
LawfulRangeSize.size_pos_iff_isSatisfied.mp (by omega)
rw [LawfulRangeSize.size_eq_succ_of_succ?_eq_some (h := this) (h' := hs)] at hn
omega
end Std.PRange

View File

@@ -376,7 +376,7 @@ instance {su} [UpwardEnumerable α] [SupportsUpperBound su α] [RangeSize su α]
cases hn : UpwardEnumerable.succ? next
· have := LawfulRangeSize.size_eq_one_of_succ?_eq_none _ _ h hn
simp [*] at this
· have := LawfulRangeSize.size_eq_succ_of_succ?_eq_some _ _ h hn
· have := LawfulRangeSize.size_eq_succ_of_succ?_eq_some _ _ _ h hn
simp [*] at this
· simp [h]
· rename_i ih
@@ -389,11 +389,11 @@ instance {su} [UpwardEnumerable α] [SupportsUpperBound su α] [RangeSize su α]
simp only [RangeIterator.step, Array.size_empty]
simp_all [LawfulRangeSize.size_eq_one_of_succ?_eq_none _ _ h' hn]
· rename_i next'
have := LawfulRangeSize.size_eq_succ_of_succ?_eq_some _ _ h' hn
have := LawfulRangeSize.size_eq_succ_of_succ?_eq_some _ _ _ h' hn
simp only [this, Nat.add_right_cancel_iff] at h
specialize ih (it := some next', it.internalState.upperBound) next' rfl h
rw [ih, Nat.add_comm]
· have := LawfulRangeSize.size_eq_zero_of_not_satisfied _ _ h'
· have := LawfulRangeSize.size_eq_zero_of_not_isSatisfied _ _ h'
simp [*] at this
theorem isEmpty_iff_forall_not_mem {sl su} [UpwardEnumerable α] [LawfulUpwardEnumerable α]

View File

@@ -6,12 +6,18 @@ Authors: Paul Reichert
module
prelude
public import Init.Data.Nat.Lemmas
public import Init.Data.Range.Polymorphic.Basic
import Init.Data.Nat.Lemmas
public import Init.Data.Nat.Order
public import Init.Data.Range.Polymorphic.Instances
public import Init.Data.Order.Classes
import Init.Data.Order.Lemmas
public section
namespace Std.PRange
open Std
-- induce LawfulUpwardEnumerable from antisymmetry and transitivity?
instance : UpwardEnumerable Nat where
succ? n := some (n + 1)
@@ -20,6 +26,10 @@ instance : UpwardEnumerable Nat where
instance : Least? Nat where
least? := some 0
instance : LawfulUpwardEnumerableLeast? Nat where
eq_succMany?_least? a := by
simpa [Least?.least?] using a, by simp [UpwardEnumerable.succMany?]
instance : LawfulUpwardEnumerableLE Nat where
le_iff a b := by
constructor
@@ -30,101 +40,24 @@ instance : LawfulUpwardEnumerableLE Nat where
rw [ hn]
exact Nat.le_add_right _ _
instance : LawfulUpwardEnumerableLT Nat where
lt_iff a b := by
constructor
· intro h
refine b - a - 1, ?_
simp [UpwardEnumerable.succMany?]
rw [Nat.sub_add_cancel, Nat.add_sub_cancel']
· exact Nat.le_of_lt h
· rwa [Nat.lt_iff_add_one_le, Nat.le_sub_iff_add_le'] at h
exact Nat.le_trans (Nat.le_succ _) h
· rintro n, hn
simp only [UpwardEnumerable.succMany?, Option.some.injEq] at hn
rw [ hn]
apply Nat.lt_add_of_pos_right
apply Nat.zero_lt_succ
instance : LawfulUpwardEnumerable Nat where
succMany?_zero := by simp [UpwardEnumerable.succMany?]
succMany?_succ := by simp [UpwardEnumerable.succMany?, UpwardEnumerable.succ?, Nat.add_assoc]
ne_of_lt a b hlt := by
rw [ LawfulUpwardEnumerableLT.lt_iff] at hlt
exact Nat.ne_of_lt hlt
instance : LawfulUpwardEnumerableLowerBound .closed Nat where
isSatisfied_iff a l := by
simp [ LawfulUpwardEnumerableLE.le_iff, BoundedUpwardEnumerable.init?,
SupportsLowerBound.IsSatisfied]
instance : LawfulUpwardEnumerableUpperBound .closed Nat where
isSatisfied_of_le u a b hub hab := by
rw [ LawfulUpwardEnumerableLE.le_iff] at hab
exact Nat.le_trans hab hub
instance : LawfulUpwardEnumerableLowerBound .open Nat where
isSatisfied_iff a l := by
simp [ LawfulUpwardEnumerableLE.le_iff, BoundedUpwardEnumerable.init?,
SupportsLowerBound.IsSatisfied, UpwardEnumerable.succ?, Nat.lt_iff_add_one_le]
instance : LawfulUpwardEnumerableUpperBound .open Nat where
isSatisfied_of_le u a b hub hab := by
rw [ LawfulUpwardEnumerableLE.le_iff] at hab
exact Nat.lt_of_le_of_lt hab hub
instance : LawfulUpwardEnumerableLowerBound .unbounded Nat where
isSatisfied_iff a l := by
simp [ LawfulUpwardEnumerableLE.le_iff, BoundedUpwardEnumerable.init?,
SupportsLowerBound.IsSatisfied, Least?.least?]
instance : LawfulUpwardEnumerableUpperBound .unbounded Nat where
isSatisfied_of_le _ _ _ _ _ := .intro
instance : LinearlyUpwardEnumerable Nat where
eq_of_succ?_eq a b := by simp [UpwardEnumerable.succ?]
have hn := hlt.choose_spec
simp only [UpwardEnumerable.succMany?, Option.some.injEq] at hn
omega
instance : InfinitelyUpwardEnumerable Nat where
isSome_succ? a := by simp [UpwardEnumerable.succ?]
private def rangeRev (k : Nat) :=
match k with
| 0 => []
| k + 1 => k :: rangeRev k
private theorem mem_rangeRev {k l : Nat} (h : l < k) : l rangeRev k := by
induction k
case zero => cases h
case succ k ih =>
rw [rangeRev]
by_cases hl : l = k
· simp [hl]
· apply List.mem_cons_of_mem
exact ih (Nat.lt_of_le_of_ne (Nat.le_of_lt_succ h) hl)
@[no_expose]
instance : HasFiniteRanges .closed Nat where
mem_of_satisfiesUpperBound upperBound := by
refine rangeRev (upperBound + 1), fun a h => ?_
simp only [SupportsUpperBound.IsSatisfied] at h
exact mem_rangeRev (Nat.lt_succ_of_le h)
@[no_expose]
instance : HasFiniteRanges .open Nat where
mem_of_satisfiesUpperBound upperBound := by
refine rangeRev (upperBound + 1), fun a h => ?_
simp only [SupportsUpperBound.IsSatisfied] at h
apply mem_rangeRev
exact Nat.lt_succ_of_lt h
instance : RangeSize .closed Nat where
size bound a := bound + 1 - a
instance : RangeSize .open Nat where
size bound a := bound - a
instance : RangeSize .open Nat := .openOfClosed
instance : LawfulRangeSize .closed Nat where
size_eq_zero_of_not_satisfied upperBound init hu := by
size_eq_zero_of_not_isSatisfied upperBound init hu := by
simp only [SupportsUpperBound.IsSatisfied, RangeSize.size] at hu
omega
size_eq_one_of_succ?_eq_none upperBound init hu h := by
@@ -135,17 +68,10 @@ instance : LawfulRangeSize .closed Nat where
Option.some.injEq] at hu h
omega
instance : LawfulRangeSize .open Nat where
size_eq_zero_of_not_satisfied upperBound init hu := by
simp only [SupportsUpperBound.IsSatisfied, RangeSize.size] at hu
omega
size_eq_one_of_succ?_eq_none upperBound init hu h := by
simp only [UpwardEnumerable.succ?] at h
cases h
size_eq_succ_of_succ?_eq_some upperBound init hu h := by
simp only [SupportsUpperBound.IsSatisfied, RangeSize.size, UpwardEnumerable.succ?,
Option.some.injEq] at hu h
omega
/-!
The following instances are used for the implementation of array slices a.k.a. `Subarray`.
See also `Init.Data.Slice.Array`.
-/
instance : ClosedOpenIntersection .open, .open Nat where
intersection r s := PRange.mk (max (r.lower + 1) s.lower) (min r.upper s.upper)

View File

@@ -175,9 +175,9 @@ instance {sl su α a} [SupportsLowerBound sl α] [SupportsUpperBound su α] (r :
This typeclass ensures that ranges with the given shape of upper bounds are always finite.
This is a prerequisite for many functions and instances, such as `PRange.toList` or `ForIn'`.
-/
class HasFiniteRanges (shape α) [SupportsUpperBound shape α] : Prop where
mem_of_satisfiesUpperBound (u : Bound shape α) :
enumeration : List α, (a : α) SupportsUpperBound.IsSatisfied u a a enumeration
class HasFiniteRanges (shape α) [UpwardEnumerable α] [SupportsUpperBound shape α] : Prop where
finite (init : α) (u : Bound shape α) :
n, (UpwardEnumerable.succMany? n init).elim True (¬ SupportsUpperBound.IsSatisfied u ·)
/--
This typeclass will usually be used together with `UpwardEnumerable α`. It provides the starting

View File

@@ -232,36 +232,49 @@ private def List.length_filter_strict_mono {l : List α} {P Q : α → Bool} {a
private def RangeIterator.instFinitenessRelation [UpwardEnumerable α] [SupportsUpperBound su α]
[LawfulUpwardEnumerable α] [HasFiniteRanges su α] :
FinitenessRelation (RangeIterator su α) Id where
rel :=
open Classical in
InvImage WellFoundedRelation.rel
(fun it => (HasFiniteRanges.mem_of_satisfiesUpperBound it.internalState.upperBound).choose
|>.filter ( a, it.internalState.next = some a UpwardEnumerable.LE a ·)
|>.length)
wf := InvImage.wf _ WellFoundedRelation.wf
subrelation {it it'} h := by
simp_wf
rw [Monadic.isPlausibleSuccessorOf_iff] at h
obtain a, hn, hu, hn', hu' := h
rw [hu']
apply List.length_filter_strict_mono (a := a)
· intro u h
simp only [decide_eq_true_eq] at h
obtain a', ha', hle := h
refine a, hn, UpwardEnumerable.le_trans 1, ?_ hle
rw [ha'] at hn'
rw [UpwardEnumerable.succMany?_succ, LawfulUpwardEnumerable.succMany?_zero,
Option.bind_some, hn']
· exact (HasFiniteRanges.mem_of_satisfiesUpperBound _).choose_spec _ hu
· intro h
simp only [decide_eq_true_eq] at h
obtain x, hx, h := h
rw [hx] at hn'
have hlt : UpwardEnumerable.LT a x :=
0, by simp [UpwardEnumerable.succMany?_succ, UpwardEnumerable.succMany?_zero, hn']
exact UpwardEnumerable.not_gt_of_le h hlt
· simp only [decide_eq_true_eq]
exact a, hn, UpwardEnumerable.le_refl _
rel it' it := it'.IsPlausibleSuccessorOf it
wf := by
constructor
intro it
have hnone : bound, Acc (fun it' it : IterM (α := RangeIterator su α) Id α => it'.IsPlausibleSuccessorOf it)
none, bound := by
intro bound
constructor
intro it' step, hs₁, hs₂
simp only [IterM.IsPlausibleStep, Iterator.IsPlausibleStep, Monadic.step] at hs₂
simp [hs₂, IterStep.successor] at hs₁
simp only [IterM.IsPlausibleSuccessorOf, IterM.IsPlausibleStep, Iterator.IsPlausibleStep,
Monadic.step, exists_eq_right] at hnone
match it with
| none, _ => apply hnone
| some init, bound =>
obtain n, hn := HasFiniteRanges.finite init bound
induction n generalizing init with
| zero =>
simp only [UpwardEnumerable.succMany?_zero, Option.elim_some] at hn
constructor
simp [hn, IterStep.successor]
| succ n ih =>
constructor
rintro it'
simp only [LawfulUpwardEnumerable.succMany?_succ_eq_succ?_bind_succMany?] at hn
match hs : UpwardEnumerable.succ? init with
| none =>
simp only [hs]
intro h
split at h
· cases h
apply hnone
· cases h
| some a =>
intro h
simp only [hs] at h hn
specialize ih _ hn
split at h
· cases h
exact ih
· cases h
subrelation := id
@[no_expose]
instance RangeIterator.instFinite {su} [UpwardEnumerable α] [SupportsUpperBound su α]
@@ -441,7 +454,7 @@ instance RangeIterator.instIteratorLoop {su} [UpwardEnumerable α] [SupportsUppe
(f : (out : α) UpwardEnumerable.LE least out SupportsUpperBound.IsSatisfied upperBound out (c : γ) n (Subtype (fun s : ForInStep γ => Pl out c s)))
(next : α) (hl : UpwardEnumerable.LE least next) (hu : SupportsUpperBound.IsSatisfied upperBound next) : n γ := do
match f next hl hu acc with
| .yield acc', h =>
| .yield acc', _ =>
match hs : UpwardEnumerable.succ? next with
| some next' =>
if hu : SupportsUpperBound.IsSatisfied upperBound next' then

View File

@@ -129,6 +129,10 @@ theorem UpwardEnumerable.le_refl {α : Type u} [UpwardEnumerable α] [LawfulUpwa
(a : α) : UpwardEnumerable.LE a a :=
0, LawfulUpwardEnumerable.succMany?_zero a
theorem UpwardEnumerable.lt_irrefl {α : Type u} [UpwardEnumerable α] [LawfulUpwardEnumerable α]
{a : α} : ¬ UpwardEnumerable.LT a a :=
fun h => LawfulUpwardEnumerable.ne_of_lt a a h rfl
theorem UpwardEnumerable.le_trans {α : Type u} [UpwardEnumerable α] [LawfulUpwardEnumerable α]
{a b c : α} (hab : UpwardEnumerable.LE a b) (hbc : UpwardEnumerable.LE b c) :
UpwardEnumerable.LE a c := by
@@ -145,6 +149,12 @@ theorem UpwardEnumerable.lt_of_lt_of_le {α : Type u} [UpwardEnumerable α] [Law
refine hab.choose + hbc.choose, ?_
rw [Nat.add_right_comm, succMany?_add, hab.choose_spec, Option.bind_some, hbc.choose_spec]
theorem UpwardEnumerable.lt_of_le_of_lt {α : Type u} [UpwardEnumerable α] [LawfulUpwardEnumerable α]
{a b c : α} (hab : UpwardEnumerable.LE a b) (hbc : UpwardEnumerable.LT b c) :
UpwardEnumerable.LT a c := by
refine hab.choose + hbc.choose, ?_
rw [Nat.add_assoc, succMany?_add, hab.choose_spec, Option.bind_some, hbc.choose_spec]
theorem UpwardEnumerable.not_gt_of_le {α : Type u} [UpwardEnumerable α] [LawfulUpwardEnumerable α]
{a b : α} :
UpwardEnumerable.LE a b ¬ UpwardEnumerable.LT b a := by
@@ -154,6 +164,11 @@ theorem UpwardEnumerable.not_gt_of_le {α : Type u} [UpwardEnumerable α] [Lawfu
rw [Nat.add_assoc, UpwardEnumerable.succMany?_add, hle, Option.bind_some, hgt]
exact LawfulUpwardEnumerable.ne_of_lt _ _ this rfl
theorem UpwardEnumerable.not_ge_of_lt {α : Type u} [UpwardEnumerable α] [LawfulUpwardEnumerable α]
{a b : α} :
UpwardEnumerable.LT a b ¬ UpwardEnumerable.LE b a :=
flip not_gt_of_le
theorem UpwardEnumerable.not_gt_of_lt {α : Type u} [UpwardEnumerable α] [LawfulUpwardEnumerable α]
{a b : α} (h : UpwardEnumerable.LT a b) : ¬ UpwardEnumerable.LT b a :=
not_gt_of_le (le_of_lt h)

View File

@@ -1,11 +1,10 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Paul Reichert
Authors: Kim Morrison
-/
module
prelude
public import Std.Classes.Ord
public section
public import Init.Data.Rat.Basic
public import Init.Data.Rat.Lemmas

View File

@@ -0,0 +1,295 @@
/-
Copyright (c) 2022 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro
-/
module
prelude
public import Init.Data.Nat.Coprime
public import Init.Data.Hashable
public import Init.Data.OfScientific
@[expose] public section
/-! # Basics for the Rational Numbers -/
/--
Rational numbers, implemented as a pair of integers `num / den` such that the
denominator is positive and the numerator and denominator are coprime.
-/
-- `Rat` is not tagged with the `ext` attribute, since this is more often than not undesirable
structure Rat where
/-- Constructs a rational number from components.
We rename the constructor to `mk'` to avoid a clash with the smart constructor. -/
mk' ::
/-- The numerator of the rational number is an integer. -/
num : Int
/-- The denominator of the rational number is a natural number. -/
den : Nat := 1
/-- The denominator is nonzero. -/
den_nz : den 0 := by decide
/-- The numerator and denominator are coprime: it is in "reduced form". -/
reduced : num.natAbs.Coprime den := by decide
deriving DecidableEq, Hashable
instance : Inhabited Rat := { num := 0 }
instance : ToString Rat where
toString a := if a.den = 1 then toString a.num else s!"{a.num}/{a.den}"
instance : Repr Rat where
reprPrec a _ := if a.den = 1 then repr a.num else s!"({a.num} : Rat)/{a.den}"
theorem Rat.den_pos (self : Rat) : 0 < self.den := Nat.pos_of_ne_zero self.den_nz
/--
Auxiliary definition for `Rat.normalize`. Constructs `num / den` as a rational number,
dividing both `num` and `den` by `g` (which is the gcd of the two) if it is not 1.
-/
@[inline] def Rat.maybeNormalize (num : Int) (den g : Nat)
(dvd_num : g num) (dvd_den : g den) (den_nz : den / g 0)
(reduced : (num / g).natAbs.Coprime (den / g)) : Rat :=
if hg : g = 1 then
{ num, den
den_nz := by simp [hg] at den_nz; exact den_nz
reduced := by simp [hg] at reduced; exact reduced }
else { num := num.divExact g dvd_num, den := den.divExact g dvd_den, den_nz, reduced }
theorem Rat.normalize.dvd_num {num : Int} {den g : Nat}
(e : g = num.natAbs.gcd den) : g num := by
rw [e, Int.dvd_natAbs, Int.ofNat_dvd]
exact Nat.gcd_dvd_left num.natAbs den
theorem Rat.normalize.dvd_den {num : Int} {den g : Nat}
(e : g = num.natAbs.gcd den) : g den :=
e Nat.gcd_dvd_right ..
theorem Rat.normalize.den_nz {num : Int} {den g : Nat} (den_nz : den 0)
(e : g = num.natAbs.gcd den) : den / g 0 :=
e Nat.ne_of_gt (Nat.div_gcd_pos_of_pos_right _ (Nat.pos_of_ne_zero den_nz))
theorem Rat.normalize.reduced {num : Int} {den g : Nat} (den_nz : den 0)
(e : g = num.natAbs.gcd den) : (num / g).natAbs.Coprime (den / g) :=
have : Int.natAbs (num / g) = num.natAbs / g := by
rw [Int.natAbs_ediv_of_dvd (dvd_num e), Int.natAbs_natCast]
this e Nat.coprime_div_gcd_div_gcd (Nat.gcd_pos_of_pos_right _ (Nat.pos_of_ne_zero den_nz))
/--
Construct a normalized `Rat` from a numerator and nonzero denominator.
This is a "smart constructor" that divides the numerator and denominator by
the gcd to ensure that the resulting rational number is normalized.
-/
@[inline] def Rat.normalize (num : Int) (den : Nat := 1) (den_nz : den 0 := by decide) : Rat :=
Rat.maybeNormalize num den (num.natAbs.gcd den)
(normalize.dvd_num rfl) (normalize.dvd_den rfl)
(normalize.den_nz den_nz rfl) (normalize.reduced den_nz rfl)
/--
Construct a rational number from a numerator and denominator.
This is a "smart constructor" that divides the numerator and denominator by
the gcd to ensure that the resulting rational number is normalized, and returns
zero if `den` is zero.
-/
def mkRat (num : Int) (den : Nat) : Rat :=
if den_nz : den = 0 then { num := 0 } else Rat.normalize num den den_nz
namespace Rat
/-- Embedding of `Int` in the rational numbers. -/
def ofInt (num : Int) : Rat := { num, reduced := Nat.coprime_one_right _ }
instance : NatCast Rat where
natCast n := ofInt n
instance : IntCast Rat := ofInt
instance : OfNat Rat n := n
/-- Is this rational number integral? -/
@[inline] protected def isInt (a : Rat) : Bool := a.den == 1
/-- Form the quotient `n / d` where `n d : Int`. -/
def divInt : Int Int Rat
| n, .ofNat d => inline (mkRat n d)
| n, .negSucc d => normalize (-n) d.succ nofun
@[inherit_doc] scoped infixl:70 " /. " => Rat.divInt
/-- Implements "scientific notation" `123.4e-5` for rational numbers. (This definition is
`@[irreducible]` because you don't want to unfold it. Use `Rat.ofScientific_def`,
`Rat.ofScientific_true_def`, or `Rat.ofScientific_false_def` instead.) -/
@[irreducible] protected def ofScientific (m : Nat) (s : Bool) (e : Nat) : Rat :=
if s then
Rat.normalize m (10 ^ e) <| Nat.ne_of_gt <| Nat.pow_pos (by decide)
else
(m * 10 ^ e : Nat)
instance : OfScientific Rat where
ofScientific m s e := Rat.ofScientific (OfNat.ofNat m) s (OfNat.ofNat e)
/-- Rational number strictly less than relation, as a `Bool`. -/
protected def blt (a b : Rat) : Bool :=
if a.num < 0 && 0 b.num then
true
else if a.num = 0 then
0 < b.num
else if 0 < a.num && b.num 0 then
false
else
-- `a` and `b` must have the same sign
a.num * b.den < b.num * a.den
instance : LT Rat := (·.blt ·)
instance (a b : Rat) : Decidable (a < b) :=
inferInstanceAs (Decidable (_ = true))
instance : LE Rat := fun a b => b.blt a = false
instance (a b : Rat) : Decidable (a b) :=
inferInstanceAs (Decidable (_ = false))
/-- Multiplication of rational numbers. (This definition is `@[irreducible]` because you don't
want to unfold it. Use `Rat.mul_def` instead.) -/
@[irreducible] protected def mul (a b : Rat) : Rat :=
let g1 := Nat.gcd a.num.natAbs b.den
let g2 := Nat.gcd b.num.natAbs a.den
{ num := a.num.divExact g1 (normalize.dvd_num rfl) * b.num.divExact g2 (normalize.dvd_num rfl)
den := a.den.divExact g2 (normalize.dvd_den rfl) * b.den.divExact g1 (normalize.dvd_den rfl)
den_nz := Nat.ne_of_gt <| Nat.mul_pos
(Nat.div_gcd_pos_of_pos_right _ a.den_pos) (Nat.div_gcd_pos_of_pos_right _ b.den_pos)
reduced := by
simp only [Int.divExact_eq_tdiv, Int.natAbs_mul, Int.natAbs_tdiv, Nat.coprime_mul_iff_left]
refine Nat.coprime_mul_iff_right.2 ?_, ?_, Nat.coprime_mul_iff_right.2 ?_, ?_
· exact a.reduced.coprime_div_left (Nat.gcd_dvd_left ..)
|>.coprime_div_right (Nat.gcd_dvd_right ..)
· exact Nat.coprime_div_gcd_div_gcd (Nat.gcd_pos_of_pos_right _ b.den_pos)
· exact Nat.coprime_div_gcd_div_gcd (Nat.gcd_pos_of_pos_right _ a.den_pos)
· exact b.reduced.coprime_div_left (Nat.gcd_dvd_left ..)
|>.coprime_div_right (Nat.gcd_dvd_right ..) }
instance : Mul Rat := Rat.mul
/--
The inverse of a rational number. Note: `inv 0 = 0`. (This definition is `@[irreducible]`
because you don't want to unfold it. Use `Rat.inv_def` instead.)
-/
@[irreducible] protected def inv (a : Rat) : Rat :=
if h : a.num < 0 then
{ num := -a.den, den := a.num.natAbs
den_nz := Nat.ne_of_gt (Int.natAbs_pos.2 (Int.ne_of_lt h))
reduced := Int.natAbs_neg a.den a.reduced.symm }
else if h : a.num > 0 then
{ num := a.den, den := a.num.natAbs
den_nz := Nat.ne_of_gt (Int.natAbs_pos.2 (Int.ne_of_gt h))
reduced := a.reduced.symm }
else
a
/-- Division of rational numbers. Note: `div a 0 = 0`. -/
protected def div : Rat Rat Rat := (· * ·.inv)
/-- Division of rational numbers. Note: `div a 0 = 0`. Written with a separate function `Rat.div`
as a wrapper so that the definition is not unfolded at `.instance` transparency. -/
instance : Div Rat := Rat.div
theorem add.aux (a b : Rat) {g ad bd} (hg : g = a.den.gcd b.den)
(had : ad = a.den / g) (hbd : bd = b.den / g) :
let den := ad * b.den; let num := a.num * bd + b.num * ad
num.natAbs.gcd g = num.natAbs.gcd den := by
intro den num
have ae : ad * g = a.den := had Nat.div_mul_cancel (hg Nat.gcd_dvd_left ..)
have be : bd * g = b.den := hbd Nat.div_mul_cancel (hg Nat.gcd_dvd_right ..)
have hden : den = ad * bd * g := by rw [Nat.mul_assoc, be]
rw [hden, Nat.Coprime.gcd_mul_left_cancel_right]
have cop : ad.Coprime bd := had hbd hg
Nat.coprime_div_gcd_div_gcd (Nat.gcd_pos_of_pos_left _ a.den_pos)
have H1 (d : Nat) :
d.gcd num.natAbs a.num.natAbs * bd d.gcd num.natAbs b.num.natAbs * ad := by
have := d.gcd_dvd_right num.natAbs
rw [ Int.ofNat_dvd, Int.dvd_natAbs] at this
have := Int.dvd_iff_dvd_of_dvd_add this
rwa [ Int.dvd_natAbs, Int.ofNat_dvd, Int.natAbs_mul,
Int.dvd_natAbs, Int.ofNat_dvd, Int.natAbs_mul] at this
apply Nat.Coprime.mul_left
· have := (H1 ad).2 <| Nat.dvd_trans (Nat.gcd_dvd_left ..) (Nat.dvd_mul_left ..)
have := (cop.coprime_dvd_left <| Nat.gcd_dvd_left ..).dvd_of_dvd_mul_right this
exact Nat.eq_one_of_dvd_one <| a.reduced.gcd_eq_one Nat.dvd_gcd this <|
Nat.dvd_trans (Nat.gcd_dvd_left ..) (ae Nat.dvd_mul_right ..)
· have := (H1 bd).1 <| Nat.dvd_trans (Nat.gcd_dvd_left ..) (Nat.dvd_mul_left ..)
have := (cop.symm.coprime_dvd_left <| Nat.gcd_dvd_left ..).dvd_of_dvd_mul_right this
exact Nat.eq_one_of_dvd_one <| b.reduced.gcd_eq_one Nat.dvd_gcd this <|
Nat.dvd_trans (Nat.gcd_dvd_left ..) (be Nat.dvd_mul_right ..)
/--
Addition of rational numbers. (This definition is `@[irreducible]` because you don't want to
unfold it. Use `Rat.add_def` instead.)
-/
@[irreducible] protected def add (a b : Rat) : Rat :=
let g := a.den.gcd b.den
if hg : g = 1 then
have den_nz := Nat.ne_of_gt <| Nat.mul_pos a.den_pos b.den_pos
have reduced := add.aux a b hg.symm (Nat.div_one _).symm (Nat.div_one _).symm
|>.symm.trans (Nat.gcd_one_right _)
{ num := a.num * b.den + b.num * a.den, den := a.den * b.den, den_nz, reduced }
else
let den := (a.den / g) * b.den
let num := a.num * (b.den / g) + b.num * (a.den / g)
let g1 := num.natAbs.gcd g
have den_nz := Nat.ne_of_gt <| Nat.mul_pos (Nat.div_gcd_pos_of_pos_left _ a.den_pos) b.den_pos
have e : g1 = num.natAbs.gcd den := add.aux a b rfl rfl rfl
Rat.maybeNormalize num den g1 (normalize.dvd_num e) (normalize.dvd_den e)
(normalize.den_nz den_nz e) (normalize.reduced den_nz e)
instance : Add Rat := Rat.add
/-- Negation of rational numbers. -/
protected def neg (a : Rat) : Rat :=
{ a with num := -a.num, reduced := by rw [Int.natAbs_neg]; exact a.reduced }
instance : Neg Rat := Rat.neg
theorem sub.aux (a b : Rat) {g ad bd} (hg : g = a.den.gcd b.den)
(had : ad = a.den / g) (hbd : bd = b.den / g) :
let den := ad * b.den; let num := a.num * bd - b.num * ad
num.natAbs.gcd g = num.natAbs.gcd den := by
have := add.aux a (-b) hg had hbd
simp only [show (-b).num = -b.num from rfl, Int.neg_mul] at this
exact this
/-- Subtraction of rational numbers. (This definition is `@[irreducible]` because you don't want to
unfold it. Use `Rat.sub_def` instead.)
-/
@[irreducible] protected def sub (a b : Rat) : Rat :=
let g := a.den.gcd b.den
if hg : g = 1 then
have den_nz := Nat.ne_of_gt <| Nat.mul_pos a.den_pos b.den_pos
have reduced := sub.aux a b hg.symm (Nat.div_one _).symm (Nat.div_one _).symm
|>.symm.trans (Nat.gcd_one_right _)
{ num := a.num * b.den - b.num * a.den, den := a.den * b.den, den_nz, reduced }
else
let den := (a.den / g) * b.den
let num := a.num * (b.den / g) - b.num * (a.den / g)
let g1 := num.natAbs.gcd g
have den_nz := Nat.ne_of_gt <| Nat.mul_pos (Nat.div_gcd_pos_of_pos_left _ a.den_pos) b.den_pos
have e : g1 = num.natAbs.gcd den := sub.aux a b rfl rfl rfl
Rat.maybeNormalize num den g1 (normalize.dvd_num e) (normalize.dvd_den e)
(normalize.den_nz den_nz e) (normalize.reduced den_nz e)
instance : Sub Rat := Rat.sub
/-- The floor of a rational number `a` is the largest integer less than or equal to `a`. -/
protected def floor (a : Rat) : Int :=
if a.den = 1 then
a.num
else
a.num / a.den
/-- The ceiling of a rational number `a` is the smallest integer greater than or equal to `a`. -/
protected def ceil (a : Rat) : Int :=
if a.den = 1 then
a.num
else
a.num / a.den + 1
end Rat

View File

@@ -0,0 +1,391 @@
/-
Copyright (c) 2022 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro
-/
module
prelude
public import Init.Data.Rat.Basic
@[expose] public section
/-! # Additional lemmas about the Rational Numbers -/
namespace Rat
-- This is not marked as an `@[ext]` lemma as this is rarely useful for rational numbers.
theorem ext : {p q : Rat} p.num = q.num p.den = q.den p = q
| _,_,_,_, _,_,_,_, rfl, rfl => rfl
@[simp] theorem mk_den_one {r : Int} :
r, 1, Nat.one_ne_zero, (Nat.coprime_one_right _) = (r : Rat) := rfl
@[simp] theorem zero_num : (0 : Rat).num = 0 := rfl
@[simp] theorem zero_den : (0 : Rat).den = 1 := rfl
@[simp] theorem one_num : (1 : Rat).num = 1 := rfl
@[simp] theorem one_den : (1 : Rat).den = 1 := rfl
@[simp] theorem maybeNormalize_eq {num den g} (dvd_num dvd_den den_nz reduced) :
maybeNormalize num den g dvd_num dvd_den den_nz reduced =
{ num := num.divExact g dvd_num, den := den / g, den_nz, reduced } := by
unfold maybeNormalize; split
· subst g; simp
· rfl
theorem normalize_eq {num den} (den_nz) : normalize num den den_nz =
{ num := num / num.natAbs.gcd den
den := den / num.natAbs.gcd den
den_nz := normalize.den_nz den_nz rfl
reduced := normalize.reduced den_nz rfl } := by
simp only [normalize, maybeNormalize_eq, Int.divExact_eq_ediv]
@[simp] theorem normalize_zero (nz) : normalize 0 d nz = 0 := by
simp [normalize, Int.natAbs_zero, Nat.div_self (Nat.pos_of_ne_zero nz)]; rfl
theorem mk_eq_normalize (num den nz c) : num, den, nz, c = normalize num den nz := by
simp [normalize_eq, c.gcd_eq_one]
theorem normalize_self (r : Rat) : normalize r.num r.den r.den_nz = r := (mk_eq_normalize ..).symm
theorem normalize_mul_left {a : Nat} (d0 : d 0) (a0 : a 0) :
normalize (a * n) (a * d) (Nat.mul_ne_zero a0 d0) = normalize n d d0 := by
simp [normalize_eq, Int.natAbs_mul, Nat.gcd_mul_left,
Nat.mul_div_mul_left _ _ (Nat.pos_of_ne_zero a0), Int.natCast_mul,
Int.mul_ediv_mul_of_pos _ _ (Int.natCast_pos.2 <| Nat.pos_of_ne_zero a0)]
theorem normalize_mul_right {a : Nat} (d0 : d 0) (a0 : a 0) :
normalize (n * a) (d * a) (Nat.mul_ne_zero d0 a0) = normalize n d d0 := by
rw [ normalize_mul_left (d0 := d0) a0]
congr 1
· apply Int.mul_comm
· apply Nat.mul_comm
theorem normalize_eq_iff (z₁ : d₁ 0) (z₂ : d₂ 0) :
normalize n₁ d₁ z₁ = normalize n₂ d₂ z₂ n₁ * d₂ = n₂ * d₁ := by
constructor <;> intro h
· simp only [normalize_eq, mk'.injEq] at h
have hn₁ := Int.ofNat_dvd_left.2 <| Nat.gcd_dvd_left n₁.natAbs d₁
have hn₂ := Int.ofNat_dvd_left.2 <| Nat.gcd_dvd_left n₂.natAbs d₂
have hd₁ := Int.ofNat_dvd.2 <| Nat.gcd_dvd_right n₁.natAbs d₁
have hd₂ := Int.ofNat_dvd.2 <| Nat.gcd_dvd_right n₂.natAbs d₂
rw [ Int.ediv_mul_cancel (Int.dvd_trans hd₂ (Int.dvd_mul_left ..)),
Int.mul_ediv_assoc _ hd₂, Int.natCast_ediv, h.2, Int.natCast_ediv,
Int.mul_ediv_assoc _ hd₁, Int.mul_ediv_assoc' _ hn₁,
Int.mul_right_comm, h.1, Int.ediv_mul_cancel hn₂]
· rw [ normalize_mul_right _ z₂, normalize_mul_left z₂ z₁, Int.mul_comm d₁, h]
theorem maybeNormalize_eq_normalize {num : Int} {den g : Nat} (dvd_num dvd_den den_nz reduced)
(hn : g num) (hd : g den) :
maybeNormalize num den g dvd_num dvd_den den_nz reduced =
normalize num den (mt (by simp [·]) den_nz) := by
simp only [maybeNormalize_eq, mk_eq_normalize, Int.divExact_eq_ediv]
have : g 0 := mt (by simp [·]) den_nz
rw [ normalize_mul_right _ this, Int.ediv_mul_cancel hn]
congr 1; exact Nat.div_mul_cancel hd
@[simp] theorem normalize_eq_zero (d0 : d 0) : normalize n d d0 = 0 n = 0 := by
have' := normalize_eq_iff d0 Nat.one_ne_zero
rw [normalize_zero (d := 1)] at this; rw [this]; simp
theorem normalize_num_den' (num den nz) : d : Nat, d 0
num = (normalize num den nz).num * d den = (normalize num den nz).den * d := by
refine num.natAbs.gcd den, Nat.gcd_ne_zero_right nz, ?_
simp [normalize_eq, Int.ediv_mul_cancel (Int.ofNat_dvd_left.2 <| Nat.gcd_dvd_left ..),
Nat.div_mul_cancel (Nat.gcd_dvd_right ..)]
theorem normalize_num_den (h : normalize n d z = n', d', z', c) :
m : Nat, m 0 n = n' * m d = d' * m := by
have := normalize_num_den' n d z; rwa [h] at this
theorem normalize_eq_mkRat {num den} (den_nz) : normalize num den den_nz = mkRat num den := by
simp [mkRat, den_nz]
theorem mkRat_num_den (z : d 0) (h : mkRat n d = n', d', z', c) :
m : Nat, m 0 n = n' * m d = d' * m :=
normalize_num_den ((normalize_eq_mkRat z).symm h)
theorem mkRat_def (n d) : mkRat n d = if d0 : d = 0 then 0 else normalize n d d0 := rfl
theorem mkRat_self (a : Rat) : mkRat a.num a.den = a := by
rw [ normalize_eq_mkRat a.den_nz, normalize_self]
theorem mk_eq_mkRat (num den nz c) : num, den, nz, c = mkRat num den := by
simp [mk_eq_normalize, normalize_eq_mkRat]
@[simp] theorem zero_mkRat (n) : mkRat 0 n = 0 := by simp [mkRat_def]
@[simp] theorem mkRat_zero (n) : mkRat n 0 = 0 := by simp [mkRat_def]
theorem mkRat_eq_zero (d0 : d 0) : mkRat n d = 0 n = 0 := by simp [mkRat_def, d0]
theorem mkRat_ne_zero (d0 : d 0) : mkRat n d 0 n 0 := not_congr (mkRat_eq_zero d0)
theorem mkRat_mul_left {a : Nat} (a0 : a 0) : mkRat (a * n) (a * d) = mkRat n d := by
if d0 : d = 0 then simp [d0] else
rw [ normalize_eq_mkRat d0, normalize_mul_left d0 a0, normalize_eq_mkRat]
theorem mkRat_mul_right {a : Nat} (a0 : a 0) : mkRat (n * a) (d * a) = mkRat n d := by
rw [ mkRat_mul_left (d := d) a0]
congr 1
· apply Int.mul_comm
· apply Nat.mul_comm
theorem mkRat_eq_iff (z₁ : d₁ 0) (z₂ : d₂ 0) :
mkRat n₁ d₁ = mkRat n₂ d₂ n₁ * d₂ = n₂ * d₁ := by
rw [ normalize_eq_mkRat z₁, normalize_eq_mkRat z₂, normalize_eq_iff]
@[simp] theorem divInt_ofNat (num den) : num /. (den : Nat) = mkRat num den := by
simp [divInt]
theorem mk_eq_divInt (num den nz c) : num, den, nz, c = num /. (den : Nat) := by
simp [mk_eq_mkRat]
theorem divInt_self (a : Rat) : a.num /. a.den = a := by rw [divInt_ofNat, mkRat_self]
@[simp] theorem zero_divInt (n) : 0 /. n = 0 := by cases n <;> simp [divInt]
@[simp] theorem divInt_zero (n) : n /. 0 = 0 := mkRat_zero n
theorem neg_divInt_neg (num den) : -num /. -den = num /. den := by
match den with
| Nat.succ n =>
simp only [divInt, Int.neg_ofNat_succ]
simp [normalize_eq_mkRat, Int.neg_neg]
| 0 => rfl
| Int.negSucc n =>
simp only [divInt, Int.neg_negSucc]
simp [normalize_eq_mkRat]
theorem divInt_neg' (num den) : num /. -den = -num /. den := by rw [ neg_divInt_neg, Int.neg_neg]
theorem divInt_eq_iff (z₁ : d₁ 0) (z₂ : d₂ 0) :
n₁ /. d₁ = n₂ /. d₂ n₁ * d₂ = n₂ * d₁ := by
rcases Int.eq_nat_or_neg d₁ with _, rfl | rfl <;>
rcases Int.eq_nat_or_neg d₂ with _, rfl | rfl <;>
simp_all [divInt_neg', Int.neg_eq_zero,
mkRat_eq_iff, Int.neg_mul, Int.mul_neg, Int.eq_neg_comm, eq_comm]
theorem divInt_mul_left {a : Int} (a0 : a 0) : (a * n) /. (a * d) = n /. d := by
if d0 : d = 0 then simp [d0] else
simp [divInt_eq_iff (Int.mul_ne_zero a0 d0) d0, Int.mul_assoc, Int.mul_left_comm]
theorem divInt_mul_right {a : Int} (a0 : a 0) : (n * a) /. (d * a) = n /. d := by
simp [ divInt_mul_left (d := d) a0, Int.mul_comm]
theorem divInt_num_den (z : d 0) (h : n /. d = n', d', z', c) :
m, m 0 n = n' * m d = d' * m := by
rcases Int.eq_nat_or_neg d with _, rfl | rfl <;>
simp_all [divInt_neg', Int.neg_eq_zero]
· have m, h₁, h₂ := mkRat_num_den z h; exists m
simp [Int.natCast_mul, h₁, h₂]
· have m, h₁, h₂ := mkRat_num_den z h; exists -m
rw [ Int.neg_inj, Int.neg_neg] at h₂
simp [Int.natCast_mul, h₁, h₂, Int.mul_neg, Int.neg_eq_zero]
@[simp] theorem ofInt_ofNat : ofInt (OfNat.ofNat n) = OfNat.ofNat n := rfl
@[simp] theorem ofInt_num : (ofInt n : Rat).num = n := rfl
@[simp] theorem ofInt_den : (ofInt n : Rat).den = 1 := rfl
@[simp] theorem ofNat_num : (OfNat.ofNat n : Rat).num = OfNat.ofNat n := rfl
@[simp] theorem ofNat_den : (OfNat.ofNat n : Rat).den = 1 := rfl
theorem add_def (a b : Rat) :
a + b = normalize (a.num * b.den + b.num * a.den) (a.den * b.den)
(Nat.mul_ne_zero a.den_nz b.den_nz) := by
show Rat.add .. = _; delta Rat.add; dsimp only; split
· exact (normalize_self _).symm
· have : a.den.gcd b.den 0 := Nat.gcd_ne_zero_left a.den_nz
rw [maybeNormalize_eq_normalize _ _ _ _
(Int.ofNat_dvd_left.2 <| Nat.gcd_dvd_left ..)
(Nat.dvd_trans (Nat.gcd_dvd_right ..) <|
Nat.dvd_trans (Nat.gcd_dvd_right ..) (Nat.dvd_mul_left ..)),
normalize_mul_right _ this]; congr 1
· simp only [Int.add_mul, Int.mul_assoc, Int.ofNat_mul_ofNat,
Nat.div_mul_cancel (Nat.gcd_dvd_left ..), Nat.div_mul_cancel (Nat.gcd_dvd_right ..)]
· rw [Nat.mul_right_comm, Nat.div_mul_cancel (Nat.gcd_dvd_left ..)]
theorem add_def' (a b : Rat) : a + b = mkRat (a.num * b.den + b.num * a.den) (a.den * b.den) := by
rw [add_def, normalize_eq_mkRat]
theorem add_zero (a : Rat) : a + 0 = a := by simp [add_def', mkRat_self]
theorem zero_add (a : Rat) : 0 + a = a := by simp [add_def', mkRat_self]
theorem normalize_add_normalize (n₁ n₂) {d₁ d₂} (z₁ z₂) :
normalize n₁ d₁ z₁ + normalize n₂ d₂ z₂ =
normalize (n₁ * d₂ + n₂ * d₁) (d₁ * d₂) (Nat.mul_ne_zero z₁ z₂) := by
cases e₁ : normalize n₁ d₁ z₁; rcases normalize_num_den e₁ with g₁, zg₁, rfl, rfl
cases e₂ : normalize n₂ d₂ z₂; rcases normalize_num_den e₂ with g₂, zg₂, rfl, rfl
simp only [add_def]; rw [ normalize_mul_right _ (Nat.mul_ne_zero zg₁ zg₂)]; congr 1
· rw [Int.add_mul]; simp [Int.natCast_mul, Int.mul_assoc, Int.mul_left_comm, Int.mul_comm]
· simp [Nat.mul_left_comm, Nat.mul_comm]
theorem mkRat_add_mkRat (n₁ n₂ : Int) {d₁ d₂} (z₁ : d₁ 0) (z₂ : d₂ 0) :
mkRat n₁ d₁ + mkRat n₂ d₂ = mkRat (n₁ * d₂ + n₂ * d₁) (d₁ * d₂) := by
rw [ normalize_eq_mkRat z₁, normalize_eq_mkRat z₂, normalize_add_normalize, normalize_eq_mkRat]
theorem divInt_add_divInt (n₁ n₂ : Int) {d₁ d₂} (z₁ : d₁ 0) (z₂ : d₂ 0) :
n₁ /. d₁ + n₂ /. d₂ = (n₁ * d₂ + n₂ * d₁) /. (d₁ * d₂) := by
rcases Int.eq_nat_or_neg d₁ with _, rfl | rfl <;>
rcases Int.eq_nat_or_neg d₂ with _, rfl | rfl <;>
simp_all [ Int.natCast_mul, Int.neg_eq_zero, divInt_neg', Int.mul_neg,
Int.neg_add, Int.neg_mul, mkRat_add_mkRat]
@[simp] theorem neg_num (a : Rat) : (-a).num = -a.num := rfl
@[simp] theorem neg_den (a : Rat) : (-a).den = a.den := rfl
theorem neg_normalize (n d z) : -normalize n d z = normalize (-n) d z := by
simp only [normalize, maybeNormalize_eq, Int.divExact_eq_tdiv, Int.natAbs_neg, Int.neg_tdiv]
rfl
theorem neg_mkRat (n d) : -mkRat n d = mkRat (-n) d := by
if z : d = 0 then simp [z]; rfl else simp [ normalize_eq_mkRat z, neg_normalize]
theorem neg_divInt (n d) : -(n /. d) = -n /. d := by
rcases Int.eq_nat_or_neg d with _, rfl | rfl <;> simp [divInt_neg', neg_mkRat]
theorem sub_def (a b : Rat) :
a - b = normalize (a.num * b.den - b.num * a.den) (a.den * b.den)
(Nat.mul_ne_zero a.den_nz b.den_nz) := by
show Rat.sub .. = _; delta Rat.sub; dsimp only; split
· exact (normalize_self _).symm
· have : a.den.gcd b.den 0 := Nat.gcd_ne_zero_left a.den_nz
rw [maybeNormalize_eq_normalize _ _ _ _
(Int.ofNat_dvd_left.2 <| Nat.gcd_dvd_left ..)
(Nat.dvd_trans (Nat.gcd_dvd_right ..) <|
Nat.dvd_trans (Nat.gcd_dvd_right ..) (Nat.dvd_mul_left ..)),
normalize_mul_right _ this]; congr 1
· simp only [Int.sub_mul, Int.mul_assoc, Int.natCast_mul,
Nat.div_mul_cancel (Nat.gcd_dvd_left ..), Nat.div_mul_cancel (Nat.gcd_dvd_right ..)]
· rw [Nat.mul_right_comm, Nat.div_mul_cancel (Nat.gcd_dvd_left ..)]
theorem sub_def' (a b : Rat) : a - b = mkRat (a.num * b.den - b.num * a.den) (a.den * b.den) := by
rw [sub_def, normalize_eq_mkRat]
protected theorem sub_eq_add_neg (a b : Rat) : a - b = a + -b := by
simp [add_def, sub_def, Int.neg_mul, Int.sub_eq_add_neg]
theorem divInt_sub_divInt (n₁ n₂ : Int) {d₁ d₂} (z₁ : d₁ 0) (z₂ : d₂ 0) :
n₁ /. d₁ - n₂ /. d₂ = (n₁ * d₂ - n₂ * d₁) /. (d₁ * d₂) := by
simp only [Rat.sub_eq_add_neg, neg_divInt,
divInt_add_divInt _ _ z₁ z₂, Int.neg_mul, Int.sub_eq_add_neg]
theorem mul_def (a b : Rat) :
a * b = normalize (a.num * b.num) (a.den * b.den) (Nat.mul_ne_zero a.den_nz b.den_nz) := by
show Rat.mul .. = _; delta Rat.mul; dsimp only
have H1 : a.num.natAbs.gcd b.den 0 := Nat.gcd_ne_zero_right b.den_nz
have H2 : b.num.natAbs.gcd a.den 0 := Nat.gcd_ne_zero_right a.den_nz
simp only [Int.divExact_eq_tdiv, Nat.divExact_eq_div]
rw [mk_eq_normalize, normalize_mul_right _ (Nat.mul_ne_zero H1 H2)]; congr 1
· rw [Int.natCast_mul, Int.mul_assoc, Int.mul_right_comm (Int.tdiv ..),
Int.tdiv_mul_cancel (Int.ofNat_dvd_left.2 <| Nat.gcd_dvd_left ..), Int.mul_assoc,
Int.tdiv_mul_cancel (Int.ofNat_dvd_left.2 <| Nat.gcd_dvd_left ..)]
· rw [ Nat.mul_assoc, Nat.mul_right_comm, Nat.mul_right_comm (_/_),
Nat.div_mul_cancel (Nat.gcd_dvd_right ..), Nat.mul_assoc,
Nat.div_mul_cancel (Nat.gcd_dvd_right ..)]
theorem mul_def' (a b : Rat) : a * b = mkRat (a.num * b.num) (a.den * b.den) := by
rw [mul_def, normalize_eq_mkRat]
protected theorem mul_comm (a b : Rat) : a * b = b * a := by
simp [mul_def, normalize_eq_mkRat, Int.mul_comm, Nat.mul_comm]
@[simp] protected theorem zero_mul (a : Rat) : 0 * a = 0 := by simp [mul_def]
@[simp] protected theorem mul_zero (a : Rat) : a * 0 = 0 := by simp [mul_def]
@[simp] protected theorem one_mul (a : Rat) : 1 * a = a := by simp [mul_def, normalize_self]
@[simp] protected theorem mul_one (a : Rat) : a * 1 = a := by simp [mul_def, normalize_self]
theorem normalize_mul_normalize (n₁ n₂) {d₁ d₂} (z₁ z₂) :
normalize n₁ d₁ z₁ * normalize n₂ d₂ z₂ =
normalize (n₁ * n₂) (d₁ * d₂) (Nat.mul_ne_zero z₁ z₂) := by
cases e₁ : normalize n₁ d₁ z₁; rcases normalize_num_den e₁ with g₁, zg₁, rfl, rfl
cases e₂ : normalize n₂ d₂ z₂; rcases normalize_num_den e₂ with g₂, zg₂, rfl, rfl
simp only [mul_def]; rw [ normalize_mul_right _ (Nat.mul_ne_zero zg₁ zg₂)]; congr 1
· simp [Int.natCast_mul, Int.mul_assoc, Int.mul_left_comm]
· simp [Nat.mul_left_comm, Nat.mul_comm]
theorem mkRat_mul_mkRat (n₁ n₂ : Int) (d₁ d₂) :
mkRat n₁ d₁ * mkRat n₂ d₂ = mkRat (n₁ * n₂) (d₁ * d₂) := by
if z₁ : d₁ = 0 then simp [z₁] else if z₂ : d₂ = 0 then simp [z₂] else
rw [ normalize_eq_mkRat z₁, normalize_eq_mkRat z₂, normalize_mul_normalize, normalize_eq_mkRat]
theorem divInt_mul_divInt (n₁ n₂ : Int) {d₁ d₂} (z₁ : d₁ 0) (z₂ : d₂ 0) :
(n₁ /. d₁) * (n₂ /. d₂) = (n₁ * n₂) /. (d₁ * d₂) := by
rcases Int.eq_nat_or_neg d₁ with _, rfl | rfl <;>
rcases Int.eq_nat_or_neg d₂ with _, rfl | rfl <;>
simp_all [ Int.natCast_mul, divInt_neg', Int.mul_neg, Int.neg_mul, mkRat_mul_mkRat]
theorem inv_def (a : Rat) : a.inv = (a.den : Int) /. a.num := by
unfold Rat.inv; split
· next h => rw [mk_eq_divInt, Int.natAbs_neg,
Int.natAbs_of_nonneg (Int.le_of_lt <| Int.neg_pos_of_neg h), neg_divInt_neg]
split
· next h => rw [mk_eq_divInt, Int.natAbs_of_nonneg (Int.le_of_lt h)]
· next h₁ h₂ =>
apply (divInt_self _).symm.trans
simp [Int.le_antisymm (Int.not_lt.1 h₂) (Int.not_lt.1 h₁)]
@[simp] protected theorem inv_zero : (0 : Rat).inv = 0 := by unfold Rat.inv; rfl
@[simp] theorem inv_divInt (n d : Int) : (n /. d).inv = d /. n := by
if z : d = 0 then simp [z] else
cases e : n /. d; rcases divInt_num_den z e with g, zg, rfl, rfl
simp [inv_def, divInt_mul_right zg]
theorem div_def (a b : Rat) : a / b = a * b.inv := rfl
/-! ### `ofScientific` -/
theorem ofScientific_true_def : Rat.ofScientific m true e = mkRat m (10 ^ e) := by
unfold Rat.ofScientific; rw [normalize_eq_mkRat]; rfl
theorem ofScientific_false_def : Rat.ofScientific m false e = (m * 10 ^ e : Nat) := by
unfold Rat.ofScientific; rfl
theorem ofScientific_def : Rat.ofScientific m s e =
if s then mkRat m (10 ^ e) else (m * 10 ^ e : Nat) := by
cases s; exact ofScientific_false_def; exact ofScientific_true_def
/-- `Rat.ofScientific` applied to numeric literals is the same as a scientific literal. -/
@[simp]
theorem ofScientific_ofNat_ofNat :
Rat.ofScientific (no_index (OfNat.ofNat m)) s (no_index (OfNat.ofNat e))
= OfScientific.ofScientific m s e := rfl
/-! ### `intCast` -/
@[simp] theorem intCast_den (a : Int) : (a : Rat).den = 1 := rfl
@[simp] theorem intCast_num (a : Int) : (a : Rat).num = a := rfl
/-!
The following lemmas are later subsumed by e.g. `Int.cast_add` and `Int.cast_mul` in Mathlib
but it is convenient to have these earlier, for users who only need `Int` and `Rat`.
-/
@[simp, norm_cast] theorem intCast_inj {a b : Int} : (a : Rat) = (b : Rat) a = b := by
constructor
· rintro ; rfl
· simp_all
theorem intCast_zero : ((0 : Int) : Rat) = (0 : Rat) := rfl
theorem intCast_one : ((1 : Int) : Rat) = (1 : Rat) := rfl
@[simp, norm_cast] theorem intCast_add (a b : Int) :
((a + b : Int) : Rat) = (a : Rat) + (b : Rat) := by
rw [add_def]
simp [normalize_eq]
@[simp, norm_cast] theorem intCast_neg (a : Int) : ((-a : Int) : Rat) = -(a : Rat) := rfl
@[simp, norm_cast] theorem intCast_sub (a b : Int) :
((a - b : Int) : Rat) = (a : Rat) - (b : Rat) := by
rw [sub_def]
simp [normalize_eq]
@[simp, norm_cast] theorem intCast_mul (a b : Int) :
((a * b : Int) : Rat) = (a : Rat) * (b : Rat) := by
rw [mul_def]
simp [normalize_eq]

View File

@@ -15,9 +15,12 @@ public import Init.Data.Int.LemmasAux
public import all Init.Data.UInt.Basic
public import Init.Data.UInt.Lemmas
public import Init.System.Platform
import Init.Data.Order.Lemmas
public section
open Std
open Lean in
set_option hygiene false in
macro "declare_int_theorems" typeName:ident _bits:term:arg : command => do
@@ -3025,6 +3028,56 @@ protected theorem Int64.lt_asymm {a b : Int64} : a < b → ¬b < a :=
protected theorem ISize.lt_asymm {a b : ISize} : a < b ¬b < a :=
fun hab hba => ISize.lt_irrefl (ISize.lt_trans hab hba)
instance Int8.instIsLinearOrder : IsLinearOrder Int8 := by
apply IsLinearOrder.of_le
case le_antisymm => constructor; apply Int8.le_antisymm
case le_total => constructor; apply Int8.le_total
case le_trans => constructor; apply Int8.le_trans
instance : LawfulOrderLT Int8 where
lt_iff := by
simp [ Int8.not_le, Decidable.imp_iff_not_or, Std.Total.total]
instance Int16.instIsLinearOrder : IsLinearOrder Int16 := by
apply IsLinearOrder.of_le
case le_antisymm => constructor; apply Int16.le_antisymm
case le_total => constructor; apply Int16.le_total
case le_trans => constructor; apply Int16.le_trans
instance : LawfulOrderLT Int16 where
lt_iff := by
simp [ Int16.not_le, Decidable.imp_iff_not_or, Std.Total.total]
instance Int32.instIsLinearOrder : IsLinearOrder Int32 := by
apply IsLinearOrder.of_le
case le_antisymm => constructor; apply Int32.le_antisymm
case le_total => constructor; apply Int32.le_total
case le_trans => constructor; apply Int32.le_trans
instance : LawfulOrderLT Int32 where
lt_iff := by
simp [ Int32.not_le, Decidable.imp_iff_not_or, Std.Total.total]
instance Int64.instIsLinearOrder : IsLinearOrder Int64 := by
apply IsLinearOrder.of_le
case le_antisymm => constructor; apply Int64.le_antisymm
case le_total => constructor; apply Int64.le_total
case le_trans => constructor; apply Int64.le_trans
instance : LawfulOrderLT Int64 where
lt_iff := by
simp [ Int64.not_le, Decidable.imp_iff_not_or, Std.Total.total]
instance ISize.instIsLinearOrder : IsLinearOrder ISize := by
apply IsLinearOrder.of_le
case le_antisymm => constructor; apply ISize.le_antisymm
case le_total => constructor; apply ISize.le_total
case le_trans => constructor; apply ISize.le_trans
instance : LawfulOrderLT ISize where
lt_iff := by
simp [ ISize.not_le, Decidable.imp_iff_not_or, Std.Total.total]
protected theorem Int8.add_neg_eq_sub {a b : Int8} : a + -b = a - b := Int8.toBitVec_inj.1 BitVec.add_neg_eq_sub
protected theorem Int16.add_neg_eq_sub {a b : Int16} : a + -b = a - b := Int16.toBitVec_inj.1 BitVec.add_neg_eq_sub
protected theorem Int32.add_neg_eq_sub {a b : Int32} : a + -b = a - b := Int32.toBitVec_inj.1 BitVec.add_neg_eq_sub

View File

@@ -17,6 +17,7 @@ public import all Init.Data.Range.Polymorphic.Basic
public import Init.Data.Range.Polymorphic.Nat
public import Init.Data.Range.Polymorphic.Iterators
public import Init.Data.Slice.Operations
import Init.Omega
public section

View File

@@ -26,6 +26,33 @@ def List.asString (s : List Char) : String :=
namespace String
instance : HAdd String.Pos String.Pos String.Pos where
hAdd p₁ p₂ := { byteIdx := p₁.byteIdx + p₂.byteIdx }
instance : HSub String.Pos String.Pos String.Pos where
hSub p₁ p₂ := { byteIdx := p₁.byteIdx - p₂.byteIdx }
instance : HAdd String.Pos Char String.Pos where
hAdd p c := { byteIdx := p.byteIdx + c.utf8Size }
instance : HAdd String.Pos String String.Pos where
hAdd p s := { byteIdx := p.byteIdx + s.utf8ByteSize }
instance : LE String.Pos where
le p₁ p₂ := p₁.byteIdx p₂.byteIdx
instance : LT String.Pos where
lt p₁ p₂ := p₁.byteIdx < p₂.byteIdx
instance (p₁ p₂ : String.Pos) : Decidable (LE.le p₁ p₂) :=
inferInstanceAs (Decidable (p₁.byteIdx p₂.byteIdx))
instance (p₁ p₂ : String.Pos) : Decidable (LT.lt p₁ p₂) :=
inferInstanceAs (Decidable (p₁.byteIdx < p₂.byteIdx))
instance : Min String.Pos := minOfLe
instance : Max String.Pos := maxOfLe
instance : OfNat String.Pos (nat_lit 0) where
ofNat := {}
@@ -485,6 +512,7 @@ Examples:
* `"tea".firstDiffPos "teas" = ⟨3⟩`
* `"teas".firstDiffPos "tea" = ⟨3⟩`
-/
@[expose]
def firstDiffPos (a b : String) : Pos :=
let stopPos := a.endPos.min b.endPos
let rec loop (i : Pos) : Pos :=
@@ -511,7 +539,7 @@ Examples:
* `"red green blue".extract ⟨4⟩ ⟨100⟩ = "green blue"`
* `"L∃∀N".extract ⟨2⟩ ⟨100⟩ = "green blue"`
-/
@[extern "lean_string_utf8_extract"]
@[extern "lean_string_utf8_extract", expose]
def extract : (@& String) (@& Pos) (@& Pos) String
| s, b, e => if b.byteIdx e.byteIdx then "" else go₁ s 0 b e
where

View File

@@ -6,11 +6,15 @@ Authors: Leonardo de Moura
module
prelude
public import Init.Data.Char.Order
public import Init.Data.Char.Lemmas
public import Init.Data.List.Lex
import Init.Data.Order.Lemmas
public section
open Std
namespace String
protected theorem data_eq_of_eq {a b : String} (h : a = b) : a.data = b.data :=
@@ -34,4 +38,14 @@ protected theorem ne_of_lt {a b : String} (h : a < b) : a ≠ b := by
have := String.lt_irrefl a
intro h; subst h; contradiction
instance instIsLinearOrder : IsLinearOrder String := by
apply IsLinearOrder.of_le
case le_antisymm => constructor; apply String.le_antisymm
case le_trans => constructor; apply String.le_trans
case le_total => constructor; apply String.le_total
instance : LawfulOrderLT String where
lt_iff a b := by
simp [ String.not_le, Decidable.imp_iff_not_or, Std.Total.total]
end String

View File

@@ -1,32 +1,11 @@
/-
Copyright (c) 2017 Johannes Hölzl. 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: Johannes Hölzl
Authors: Paul Reichert
-/
module
prelude
public import Init.Ext
public import Init.Core
public section
namespace Subtype
universe u
variable {α : Sort u} {p q : α Prop}
@[ext]
protected theorem ext : {a1 a2 : { x // p x }}, (a1 : α) = (a2 : α) a1 = a2
| _, _, _, _, rfl => rfl
@[simp]
protected theorem «forall» {q : { a // p a } Prop} : ( x, q x) a b, q a, b :=
fun h a b h a, b, fun h a, b h a b
@[simp]
protected theorem «exists» {q : { a // p a } Prop} :
(Exists fun x => q x) Exists fun a => Exists fun b => q a, b :=
fun a, b, h a, b, h, fun a, b, h a, b, h
end Subtype
public import Init.Data.Subtype.Basic
public import Init.Data.Subtype.Order
public import Init.Data.Subtype.OrderExtra

View File

@@ -0,0 +1,32 @@
/-
Copyright (c) 2017 Johannes Hölzl. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johannes Hölzl
-/
module
prelude
public import Init.Ext
public import Init.Core
public section
namespace Subtype
universe u
variable {α : Sort u} {p q : α Prop}
@[ext]
protected theorem ext : {a1 a2 : { x // p x }}, (a1 : α) = (a2 : α) a1 = a2
| _, _, _, _, rfl => rfl
@[simp]
protected theorem «forall» {q : { a // p a } Prop} : ( x, q x) a b, q a, b :=
fun h a b h a, b, fun h a, b h a b
@[simp]
protected theorem «exists» {q : { a // p a } Prop} :
(Exists fun x => q x) Exists fun a => Exists fun b => q a, b :=
fun a, b, h a, b, h, fun a, b, h a, b, h
end Subtype

View File

@@ -0,0 +1,92 @@
/-
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
import Init.SimpLemmas
public import Init.Data.Order.Classes
public import Init.Data.Order.Lemmas
import Init.Data.Order.Factories
import Init.Data.Subtype.Basic
namespace Subtype
open Std
public instance instLE {α : Type u} [LE α] {P : α Prop} : LE (Subtype P) where
le a b := a.val b.val
public instance instLT {α : Type u} [LT α] {P : α Prop} : LT (Subtype P) where
lt a b := a.val < b.val
public instance instLawfulOrderLT {α : Type u} [LT α] [LE α] [LawfulOrderLT α]
{P : α Prop} : LawfulOrderLT (Subtype P) where
lt_iff a b := by simp [LT.lt, LE.le, LawfulOrderLT.lt_iff]
public instance instMin {α : Type u} [Min α] [MinEqOr α] {P : α Prop} : Min (Subtype P) where
min a b := Min.min a.val b.val, MinEqOr.elim a.property b.property
public instance instMax {α : Type u} [Max α] [MaxEqOr α] {P : α Prop} : Max (Subtype P) where
max a b := max a.val b.val, MaxEqOr.elim a.property b.property
public instance instReflLE {α : Type u} [LE α] [i : Refl (α := α) (· ·)] {P : α Prop} :
Refl (α := Subtype P) (· ·) where
refl a := i.refl a.val
public instance instAntisymmLE {α : Type u} [LE α] [i : Antisymm (α := α) (· ·)] {P : α Prop} :
Antisymm (α := Subtype P) (· ·) where
antisymm a b hab hba := private Subtype.ext <| i.antisymm a.val b.val hab hba
public instance instTotalLE {α : Type u} [LE α] [i : Total (α := α) (· ·)] {P : α Prop} :
Total (α := Subtype P) (· ·) where
total a b := i.total a.val b.val
public instance instTransLE {α : Type u} [LE α] [i : Trans (α := α) (· ·) (· ·) (· ·)]
{P : α Prop} :
Trans (α := Subtype P) (· ·) (· ·) (· ·) where
trans := i.trans
public instance instMinEqOr {α : Type u} [Min α] [MinEqOr α] {P : α Prop} :
MinEqOr (Subtype P) where
min_eq_or a b := by
cases min_eq_or (a := a.val) (b := b.val) <;> rename_i h
· exact Or.inl <| Subtype.ext h
· exact Or.inr <| Subtype.ext h
public instance instLawfulOrderMin {α : Type u} [LE α] [Min α] [LawfulOrderMin α] {P : α Prop} :
LawfulOrderMin (Subtype P) where
le_min_iff _ _ _ := by
exact le_min_iff (α := α)
public instance instMaxEqOr {α : Type u} [Max α] [MaxEqOr α] {P : α Prop} :
MaxEqOr (Subtype P) where
max_eq_or a b := by
cases max_eq_or (a := a.val) (b := b.val) <;> rename_i h
· exact Or.inl <| Subtype.ext h
· exact Or.inr <| Subtype.ext h
public instance instLawfulOrderMax {α : Type u} [LE α] [Max α] [LawfulOrderMax α] {P : α Prop} :
LawfulOrderMax (Subtype P) where
max_le_iff _ _ _ := by
open Classical.Order in
exact max_le_iff (α := α)
public instance instIsPreorder {α : Type u} [LE α] [IsPreorder α] {P : α Prop} :
IsPreorder (Subtype P) :=
IsPreorder.of_le
public instance instIsLinearPreorder {α : Type u} [LE α] [IsLinearPreorder α] {P : α Prop} :
IsLinearPreorder (Subtype P) :=
IsLinearPreorder.of_le
public instance instIsPartialOrder {α : Type u} [LE α] [IsPartialOrder α] {P : α Prop} :
IsPartialOrder (Subtype P) :=
IsPartialOrder.of_le
public instance instIsLinearOrder {α : Type u} [LE α] [IsLinearOrder α] {P : α Prop} :
IsLinearOrder (Subtype P) :=
IsLinearOrder.of_le
end Subtype

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