Compare commits

..

187 Commits

Author SHA1 Message Date
Leonardo de Moura
9c8cb61b5c chore: add module keyword to grind tests
This PR also fixes missing `@[expose]` in grind support definitions.
2025-08-21 14:50:41 -07: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
2016 changed files with 19905 additions and 4988 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 commited 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

@@ -25,7 +25,7 @@ public import Init.Data.SInt
public import Init.Data.Float
public import Init.Data.Float32
public import Init.Data.Option
public import Init.Data.Ord
public import Init.Data.OrdRoot
public import Init.Data.Random
public import Init.Data.ToString
public import Init.Data.Range
@@ -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

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

@@ -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_le (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)

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
· apply Nat.le_min
· intro a b
simp only [Nat.min_def]
split <;> simp
public instance : LawfulOrderMax Nat := by
apply LawfulOrderMax.of_le
· 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

@@ -96,7 +96,7 @@ Ordering.lt
| a => a
/-- Version of `Ordering.then'` for proof by reflection. -/
noncomputable def then' (a b : Ordering) : Ordering :=
@[expose] noncomputable def then' (a b : Ordering) : Ordering :=
Ordering.rec a b a a
/--
@@ -204,6 +204,7 @@ instance [DecidablePred p] : Decidable (∃ o : Ordering, p o) :=
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

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

View File

@@ -0,0 +1,14 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
module
prelude
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

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,240 @@
/-
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
/--
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 {α : Type u} [Min α] [LE α]
(le_min_iff : a b c : α, a min b c a b a c)
(min_eq_or : a b : α, min a b = a min a b = b) : LawfulOrderMin α where
toLawfulOrderInf := .of_le le_min_iff
toMinEqOr := 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_le {α : Type u} [Max α] [LE α]
(max_le_iff : a b c : α, max a b c a c b c)
(max_eq_or : a b : α, max a b = a max a b = b) : LawfulOrderMax α where
toLawfulOrderSup := .of_le max_le_iff
toMaxEqOr := 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`).
-/
public def 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,406 @@
/-
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 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 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_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]
end Min
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
end Max
end Std

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,857 @@
/-
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
namespace FactoryInstances
public scoped instance instMinOfDecidableLE {α : Type u} [LE α] [DecidableLE α] : Min α where
min a b := if a b then a else b
public scoped instance instMaxOfDecidableLE {α : Type u} [LE α] [DecidableLE α] : Max α where
max a b := if b a then a else b
public instance instLawfulOrderLeftLeaningMinOfDecidableLE {α : Type u} [LE α] [DecidableLE α] :
LawfulOrderLeftLeaningMin α where
min_eq_left a b := by simp +contextual [min]
min_eq_right a b := by simp +contextual [min]
public instance instLawfulOrderLeftLeaningMaxOfDecidableLE {α : Type u} [LE α] [DecidableLE α] :
LawfulOrderLeftLeaningMax α where
max_eq_left a b := by simp +contextual [max]
max_eq_right a b := by simp +contextual [max]
end FactoryInstances
/--
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 FactoryInstances.instMinOfDecidableLE
max :
let := le; let := decidableLE
Max α := by
extract_lets
first
| infer_instance
| exact FactoryInstances.instMaxOfDecidableLE
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`."
public theorem IsLinearPreorder.lawfulOrderMin_of_min_eq {α : Type u} [LE α]
[DecidableLE α] [Min α] [IsLinearPreorder α]
(min_eq : a b : α, min a b = if a b then a else b) :
LawfulOrderMin α where
min_eq_or a b := by
rw [min_eq]
split <;> simp
le_min_iff a b c := by
simp only [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))
public theorem IsLinearPreorder.lawfulOrderMax_of_max_eq {α : Type u} [LE α]
[DecidableLE α] [Max α] [IsLinearPreorder α]
(max_eq : a b : α, max a b = if b a then a else b) :
LawfulOrderMax α where
max_eq_or a b := by
rw [max_eq]
split <;> simp
max_le_iff a b c := by
simp only [max_eq]
split <;> rename_i hab
· simp only [iff_self_and]
exact fun hbc => le_trans hab hbc
· simp only [iff_and_self]
exact fun hac => le_trans (by simpa [hab] using Std.le_total (a := a) (b := b)) hac
/--
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 :=
letI := LinearPreorderPackage.ofLE α args.toLinearPreorderOfLEArgs
letI := args.decidableLE; letI := args.min
IsLinearPreorder.lawfulOrderMin_of_min_eq args.min_eq
toLawfulOrderMax :=
letI := LinearPreorderPackage.ofLE α args.toLinearPreorderOfLEArgs
letI := args.decidableLE; letI := args.max
IsLinearPreorder.lawfulOrderMax_of_max_eq args.max_eq
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 _
lawful_le :
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 `lawful_le`."
decidableLE :
let := ord; let := le; have := lawful_le
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 := lawful_le; 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 := lawful_le; 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 := lawful_le; 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.lawful_le
{ toOrd := args.ord
toLE := args.le
toLT := args.lt
toBEq := args.beq
toLawfulOrderOrd := args.lawful_le
lt_iff a b := by
cases h : compare a b
all_goals simp [h, args.lawful_le.isLE_compare a _, args.lawful_le.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 := lawful_le
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 \
manuelly provide the field `min_eq`."
max_eq :
let := ord; let := le; let := max; have := lawful_le
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 := IsLinearPreorder.lawfulOrderMin_of_min_eq
(by simpa [isLE_compare] using args.min_eq)
toLawfulOrderMax := IsLinearPreorder.lawfulOrderMax_of_max_eq
(by simpa [isGE_compare] using args.max_eq) }
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

@@ -60,6 +60,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 +89,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

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

@@ -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,388 @@
/-
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 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

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

View File

@@ -0,0 +1,13 @@
/-
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.Subtype.Order
public import Init.Data.Ord.Basic
public instance {α : Type u} [Ord α] {P : α Prop} : Ord (Subtype P) where
compare a b := compare a.val b.val

View File

@@ -8,9 +8,13 @@ module
prelude
public import Init.Data.UInt.BasicAux
public import Init.Data.BitVec.Basic
public import Init.Data.Order.Classes
import Init.Data.Order.Factories
@[expose] public section
open Std
set_option linter.missingDocs true
open Nat

View File

@@ -15,9 +15,13 @@ public import all Init.Data.BitVec.Basic
public import Init.Data.BitVec.Lemmas
public import Init.Data.Nat.Div.Lemmas
public import Init.System.Platform
public import Init.Data.Order.Factories
import Init.Data.Order.Lemmas
public section
open Std
open Lean in
set_option hygiene false in
macro "declare_uint_theorems" typeName:ident bits:term:arg : command => do
@@ -206,6 +210,19 @@ macro "declare_uint_theorems" typeName:ident bits:term:arg : command => do
protected theorem le_antisymm {a b : $typeName} (h₁ : a b) (h₂ : b a) : a = b :=
le_antisymm_iff.2 h₁, h₂
open $typeName renaming
le_refl le_refl', le_antisymm le_antisymm', le_total le_total', le_trans le_trans' in
instance instIsLinearOrder : IsLinearOrder $typeName := by
apply IsLinearOrder.of_le
case le_antisymm => constructor; apply le_antisymm'
case le_total => constructor; apply le_total'
case le_trans => constructor; apply le_trans'
open $typeName renaming not_le not_le'
instance : LawfulOrderLT $typeName where
lt_iff _ _ := by
simp [ not_le', Decidable.imp_iff_not_or, Std.Total.total]
@[simp] protected theorem ofNat_one : ofNat 1 = 1 := (rfl)
@[simp] protected theorem ofNat_toNat {x : $typeName} : ofNat x.toNat = x := by

View File

@@ -11,15 +11,17 @@ public import Init.Data.Vector.Lemmas
public import all Init.Data.Array.Lex.Basic
public import Init.Data.Array.Lex.Lemmas
import Init.Data.Range.Polymorphic.Lemmas
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.
namespace Vector
/-! ### Lexicographic ordering -/
@[simp] theorem lt_toArray [LT α] {xs ys : Vector α n} : xs.toArray < ys.toArray xs < ys := Iff.rfl
@@ -96,27 +98,35 @@ instance [LT α]
Trans (· < · : Vector α n Vector α n Prop) (· < ·) (· < ·) where
trans h₁ h₂ := Vector.lt_trans h₁ h₂
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 α] [LawfulOrderLT α] [IsLinearOrder α]
{xs ys zs : Vector α n} (h₁ : xs ys) (h₂ : ys < zs) : xs < zs :=
Array.lt_of_le_of_lt h₁ h₂
protected theorem le_trans [LT α]
[Std.Irrefl (· < · : α α Prop)]
@[deprecated Vector.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) (¬ · < ·) (¬ · < ·)]
{xs ys zs : Vector α n} (h₁ : xs ys) (h₂ : ys < zs) : xs < zs :=
letI := LE.ofLT α
haveI : IsLinearOrder α := IsLinearOrder.of_lt
Array.lt_of_le_of_lt h₁ h₂
protected theorem le_trans [LT α] [LE α] [LawfulOrderLT α] [IsLinearOrder α]
{xs ys zs : Vector α n} (h₁ : xs ys) (h₂ : ys zs) : xs zs :=
fun h₃ => h₁ (Vector.lt_of_le_of_lt h₂ h₃)
@[deprecated Vector.le_trans (since := "2025-08-01")]
protected theorem le_trans' [LT α]
[Std.Asymm (· < · : α α Prop)]
[Std.Antisymm (¬ · < · : α α Prop)]
[Trans (¬ · < · : α α Prop) (¬ · < ·) (¬ · < ·)]
{xs ys zs : Vector α n} (h₁ : xs ys) (h₂ : ys zs) : xs zs :=
fun h₃ => h₁ (Vector.lt_of_le_of_lt h₂ h₃)
letI := LE.ofLT α
haveI : IsLinearOrder α := IsLinearOrder.of_lt
Array.le_trans h₁ h₂
instance [LT α]
[Std.Irrefl (· < · : α α Prop)]
[Std.Asymm (· < · : α α Prop)]
[Std.Antisymm (¬ · < · : α α Prop)]
[Trans (¬ · < · : α α Prop) (¬ · < ·) (¬ · < ·)] :
instance [LT α] [LE α] [LawfulOrderLT α] [IsLinearOrder α] :
Trans (· · : Vector α n Vector α n Prop) (· ·) (· ·) where
trans h₁ h₂ := Vector.le_trans h₁ h₂
@@ -129,30 +139,44 @@ instance [LT α]
Std.Asymm (· < · : Vector α n Vector α n Prop) where
asymm _ _ := Vector.lt_asymm
protected theorem le_total [LT α]
[i : Std.Total (¬ · < · : α α Prop)] (xs ys : Vector α n) : xs ys ys xs :=
protected theorem le_total [LT α] [i : Std.Asymm (· < · : α α Prop)] (xs ys : Vector α n) :
xs ys ys xs :=
Array.le_total _ _
instance [LT α]
[Std.Total (¬ · < · : α α Prop)] :
protected theorem le_antisymm [LT α] [LE α] [IsLinearOrder α] [LawfulOrderLT α]
{xs ys : Vector α n} (h₁ : xs ys) (h₂ : ys xs) : xs = ys :=
Vector.toArray_inj.mp <| Array.le_antisymm h₁ h₂
instance [LT α] [Std.Asymm (· < · : α α Prop)] :
Std.Total (· · : Vector α n Vector α n Prop) where
total := Vector.le_total
instance [LT α] [LE α] [IsLinearOrder α] [LawfulOrderLT α] :
IsLinearOrder (Vector α n) := by
apply IsLinearOrder.of_le
case le_antisymm => constructor; apply Vector.le_antisymm
case le_total => constructor; apply Vector.le_total
case le_trans => constructor; apply Vector.le_trans
@[simp] protected theorem not_lt [LT α]
{xs ys : Vector α n} : ¬ xs < ys ys xs := Iff.rfl
@[simp] protected theorem not_le [LT α]
{xs ys : Vector α n} : ¬ ys xs xs < ys := Classical.not_not
instance [LT α] [Std.Asymm (· < · : α α Prop)] : LawfulOrderLT (Vector α n) where
lt_iff _ _ := by
open Classical in
simp [ Vector.not_le, Decidable.imp_iff_not_or, Std.Total.total]
protected theorem le_of_lt [LT α]
[i : Std.Total (¬ · < · : α α Prop)]
[i : Std.Asymm (· < · : α α Prop)]
{xs ys : Vector α n} (h : xs < ys) : xs ys :=
Array.le_of_lt h
protected theorem le_iff_lt_or_eq [LT α]
[Std.Irrefl (· < · : α α Prop)]
[Std.Asymm (· < · : α α Prop)]
[Std.Antisymm (¬ · < · : α α Prop)]
[Std.Total (¬ · < · : α α Prop)]
{xs ys : Vector α n} : xs ys xs < ys xs = ys := by
simpa using Array.le_iff_lt_or_eq (xs := xs.toArray) (ys := ys.toArray)
@@ -222,7 +246,6 @@ protected theorem lt_iff_exists [LT α] {xs ys : Vector α n} :
simp_all [Array.lt_iff_exists]
protected theorem le_iff_exists [LT α]
[Std.Irrefl (· < · : α α Prop)]
[Std.Asymm (· < · : α α Prop)]
[Std.Antisymm (¬ · < · : α α Prop)] {xs ys : Vector α n} :
xs ys
@@ -237,7 +260,6 @@ theorem append_left_lt [LT α] {xs : Vector α n} {ys ys' : Vector α m} (h : ys
simpa using Array.append_left_lt h
theorem append_left_le [LT α]
[Std.Irrefl (· < · : α α Prop)]
[Std.Asymm (· < · : α α Prop)]
[Std.Antisymm (¬ · < · : α α Prop)]
{xs : Vector α n} {ys ys' : Vector α m} (h : ys ys') :
@@ -250,10 +272,8 @@ protected theorem map_lt [LT α] [LT β]
simpa using Array.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 : Vector α n} {f : α β} (w : x y, x < y f x < f y) (h : xs ys) :

View File

@@ -20,6 +20,9 @@ class AddRightCancel (M : Type u) [Add M] where
/-- Addition is right-cancellative. -/
add_right_cancel : a b c : M, a + c = b + c a = b
/-- A type with zero and addition,
where addition is commutative and associative,
and the zero is the right identity for addition. -/
class AddCommMonoid (M : Type u) extends Zero M, Add M where
/-- Zero is the right identity for addition. -/
add_zero : a : M, a + 0 = a
@@ -30,6 +33,9 @@ class AddCommMonoid (M : Type u) extends Zero M, Add M where
attribute [instance 100] AddCommMonoid.toZero AddCommMonoid.toAdd
/-- A type with zero, addition, negation, and subtraction,
where addition is commutative and associative,
and negation is the left inverse of addition. -/
class AddCommGroup (M : Type u) extends AddCommMonoid M, Neg M, Sub M where
/-- Negation is the left inverse of addition. -/
neg_add_cancel : a : M, -a + a = 0

View File

@@ -267,7 +267,7 @@ instance [NatModule α] [AddRightCancel α] [NoNatZeroDivisors α] : NoNatZeroDi
replace h₂ := NoNatZeroDivisors.no_nat_zero_divisors k (a₁ + b₂) (a₂ + b₁) h₁ h₂
apply Quot.sound; simp [r]; exists 0; simp [h₂]
instance [Preorder α] [OrderedAdd α] : LE (OfNatModule.Q α) where
instance [LE α] [LT α] [Preorder α] [OrderedAdd α] : LE (OfNatModule.Q α) where
le a b := Q.liftOn₂ a b (fun (a, b) (c, d) => a + d b + c)
(by intro (a₁, b₁) (a₂, b₂) (a₃, b₃) (a₄, b₄)
simp; intro k₁ h₁ k₂ h₂
@@ -283,11 +283,14 @@ instance [Preorder α] [OrderedAdd α] : LE (OfNatModule.Q α) where
rw [this]; clear this
rw [ OrderedAdd.add_le_left_iff])
@[local simp] theorem mk_le_mk [Preorder α] [OrderedAdd α] {a₁ a₂ b₁ b₂ : α} :
instance [LE α] [LT α] [Preorder α] [OrderedAdd α] : LT (OfNatModule.Q α) where
lt a b := a b ¬b a
@[local simp] theorem mk_le_mk [LE α] [LT α] [Preorder α] [OrderedAdd α] {a₁ a₂ b₁ b₂ : α} :
Q.mk (a₁, a₂) Q.mk (b₁, b₂) a₁ + b₂ a₂ + b₁ := by
rfl
instance [Preorder α] [OrderedAdd α] : Preorder (OfNatModule.Q α) where
instance [LE α] [LT α] [Preorder α] [OrderedAdd α] : Preorder (OfNatModule.Q α) where
le_refl a := by
obtain a₁, a₂ := a
change Q.mk _ Q.mk _
@@ -308,24 +311,24 @@ instance [Preorder α] [OrderedAdd α] : Preorder (OfNatModule.Q α) where
attribute [-simp] Q.mk
@[local simp] private theorem mk_lt_mk [Preorder α] [OrderedAdd α] {a₁ a₂ b₁ b₂ : α} :
@[local simp] private theorem mk_lt_mk [LE α] [LT α] [Preorder α] [OrderedAdd α] {a₁ a₂ b₁ b₂ : α} :
Q.mk (a₁, a₂) < Q.mk (b₁, b₂) a₁ + b₂ < a₂ + b₁ := by
simp [Preorder.lt_iff_le_not_le, AddCommMonoid.add_comm]
@[local simp] private theorem mk_pos [Preorder α] [OrderedAdd α] {a₁ a₂ : α} :
@[local simp] private theorem mk_pos [LE α] [LT α] [Preorder α] [OrderedAdd α] {a₁ a₂ : α} :
0 < Q.mk (a₁, a₂) a₂ < a₁ := by
change Q.mk (0,0) < _ _
simp [mk_lt_mk, AddCommMonoid.zero_add]
@[local simp]
theorem toQ_le [Preorder α] [OrderedAdd α] {a b : α} : toQ a toQ b a b := by
theorem toQ_le [LE α] [LT α] [Preorder α] [OrderedAdd α] {a b : α} : toQ a toQ b a b := by
simp
@[local simp]
theorem toQ_lt [Preorder α] [OrderedAdd α] {a b : α} : toQ a < toQ b a < b := by
theorem toQ_lt [LE α] [LT α] [Preorder α] [OrderedAdd α] {a b : α} : toQ a < toQ b a < b := by
simp [Preorder.lt_iff_le_not_le]
instance [Preorder α] [OrderedAdd α] : OrderedAdd (OfNatModule.Q α) where
instance [LE α] [LT α] [Preorder α] [OrderedAdd α] : OrderedAdd (OfNatModule.Q α) where
add_le_left_iff := by
intro a b c
obtain a₁, a₂ := a

View File

@@ -15,7 +15,7 @@ namespace Lean.Grind
namespace Field.IsOrdered
variable {R : Type u} [Field R] [LinearOrder R] [OrderedRing R]
variable {R : Type u} [Field R] [LE R] [LT R] [LinearOrder R] [OrderedRing R]
open OrderedAdd
open OrderedRing

View File

@@ -8,11 +8,11 @@ prelude
public import Init.Grind.Ordered.Module
public import Init.Grind.Ordered.Ring
public import Init.Grind.Ring.Field
public import all Init.Data.Ord
public import all Init.Data.Ord.Basic
public import all Init.Data.AC
public import Init.Data.RArray
public section
@[expose] public section
/-!
Support for the linear arithmetic module for `IntModule` in `grind`
@@ -254,17 +254,17 @@ open OrderedAdd
Helper theorems for conflict resolution during model construction.
-/
private theorem le_add_le {α} [IntModule α] [Preorder α] [OrderedAdd α] {a b : α}
private theorem le_add_le {α} [IntModule α] [LE α] [LT α] [Preorder α] [OrderedAdd α] {a b : α}
(h₁ : a 0) (h₂ : b 0) : a + b 0 := by
replace h₁ := add_le_left h₁ b; simp at h₁
exact Preorder.le_trans h₁ h₂
private theorem le_add_lt {α} [IntModule α] [Preorder α] [OrderedAdd α] {a b : α}
private theorem le_add_lt {α} [IntModule α] [LE α] [LT α] [Preorder α] [OrderedAdd α] {a b : α}
(h₁ : a 0) (h₂ : b < 0) : a + b < 0 := by
replace h₁ := add_le_left h₁ b; simp at h₁
exact Preorder.lt_of_le_of_lt h₁ h₂
private theorem lt_add_lt {α} [IntModule α] [Preorder α] [OrderedAdd α] {a b : α}
private theorem lt_add_lt {α} [IntModule α] [LE α] [LT α] [Preorder α] [OrderedAdd α] {a b : α}
(h₁ : a < 0) (h₂ : b < 0) : a + b < 0 := by
replace h₁ := add_lt_left h₁ b; simp at h₁
exact Preorder.lt_trans h₁ h₂
@@ -277,7 +277,7 @@ def le_le_combine_cert (p₁ p₂ p₃ : Poly) : Bool :=
let a₂ := p₂.leadCoeff.natAbs
p₃ == (p₁.mul a₂ |>.combine (p₂.mul a₁))
theorem le_le_combine {α} [IntModule α] [Preorder α] [OrderedAdd α] (ctx : Context α) (p₁ p₂ p₃ : Poly)
theorem le_le_combine {α} [IntModule α] [LE α] [LT α] [Preorder α] [OrderedAdd α] (ctx : Context α) (p₁ p₂ p₃ : Poly)
: le_le_combine_cert p₁ p₂ p₃ p₁.denote' ctx 0 p₂.denote' ctx 0 p₃.denote' ctx 0 := by
simp [le_le_combine_cert]; intro _ h₁ h₂; subst p₃; simp
replace h₁ := zsmul_nonpos (coe_natAbs_nonneg p₂.leadCoeff) h₁
@@ -289,7 +289,7 @@ def le_lt_combine_cert (p₁ p₂ p₃ : Poly) : Bool :=
let a₂ := p₂.leadCoeff.natAbs
a₁ > (0 : Int) && p₃ == (p₁.mul a₂ |>.combine (p₂.mul a₁))
theorem le_lt_combine {α} [IntModule α] [Preorder α] [OrderedAdd α] (ctx : Context α) (p₁ p₂ p₃ : Poly)
theorem le_lt_combine {α} [IntModule α] [LE α] [LT α] [Preorder α] [OrderedAdd α] (ctx : Context α) (p₁ p₂ p₃ : Poly)
: le_lt_combine_cert p₁ p₂ p₃ p₁.denote' ctx 0 p₂.denote' ctx < 0 p₃.denote' ctx < 0 := by
simp [-Int.natAbs_pos, -Int.ofNat_pos, le_lt_combine_cert]; intro hp _ h₁ h₂; subst p₃; simp
replace h₁ := zsmul_nonpos (coe_natAbs_nonneg p₂.leadCoeff) h₁
@@ -301,7 +301,7 @@ def lt_lt_combine_cert (p₁ p₂ p₃ : Poly) : Bool :=
let a₂ := p₂.leadCoeff.natAbs
a₂ > (0 : Int) && a₁ > (0 : Int) && p₃ == (p₁.mul a₂ |>.combine (p₂.mul a₁))
theorem lt_lt_combine {α} [IntModule α] [Preorder α] [OrderedAdd α] (ctx : Context α) (p₁ p₂ p₃ : Poly)
theorem lt_lt_combine {α} [IntModule α] [LE α] [LT α] [Preorder α] [OrderedAdd α] (ctx : Context α) (p₁ p₂ p₃ : Poly)
: lt_lt_combine_cert p₁ p₂ p₃ p₁.denote' ctx < 0 p₂.denote' ctx < 0 p₃.denote' ctx < 0 := by
simp [-Int.natAbs_pos, -Int.ofNat_pos, lt_lt_combine_cert]; intro hp₁ hp₂ _ h₁ h₂; subst p₃; simp
replace h₁ := zsmul_neg_iff (p₂.leadCoeff.natAbs) h₁ |>.mpr hp₁
@@ -312,7 +312,7 @@ def diseq_split_cert (p₁ p₂ : Poly) : Bool :=
p₂ == p₁.mul (-1)
-- We need `LinearOrder` to use `trichotomy`
theorem diseq_split {α} [IntModule α] [LinearOrder α] [OrderedAdd α] (ctx : Context α) (p₁ p₂ : Poly)
theorem diseq_split {α} [IntModule α] [LE α] [LT α] [LinearOrder α] [OrderedAdd α] (ctx : Context α) (p₁ p₂ : Poly)
: diseq_split_cert p₁ p₂ p₁.denote' ctx 0 p₁.denote' ctx < 0 p₂.denote' ctx < 0 := by
simp [diseq_split_cert]; intro _ h₁; subst p₂; simp
cases LinearOrder.trichotomy (p₁.denote ctx) 0
@@ -322,7 +322,7 @@ theorem diseq_split {α} [IntModule α] [LinearOrder α] [OrderedAdd α] (ctx :
simp [h₁] at h
rw [ neg_pos_iff, neg_zsmul, neg_neg, one_zsmul]; assumption
theorem diseq_split_resolve {α} [IntModule α] [LinearOrder α] [OrderedAdd α] (ctx : Context α) (p₁ p₂ : Poly)
theorem diseq_split_resolve {α} [IntModule α] [LE α] [LT α] [LinearOrder α] [OrderedAdd α] (ctx : Context α) (p₁ p₂ : Poly)
: diseq_split_cert p₁ p₂ p₁.denote' ctx 0 ¬p₁.denote' ctx < 0 p₂.denote' ctx < 0 := by
intro h₁ h₂ h₃
exact (diseq_split ctx p₁ p₂ h₁ h₂).resolve_left h₃
@@ -338,7 +338,7 @@ theorem eq_norm {α} [IntModule α] (ctx : Context α) (lhs rhs : Expr) (p : Pol
: norm_cert lhs rhs p lhs.denote ctx = rhs.denote ctx p.denote' ctx = 0 := by
simp [norm_cert]; intro _ h₁; subst p; simp [Expr.denote, h₁, sub_self]
theorem le_of_eq {α} [IntModule α] [Preorder α] [OrderedAdd α] (ctx : Context α) (lhs rhs : Expr) (p : Poly)
theorem le_of_eq {α} [IntModule α] [LE α] [LT α] [Preorder α] [OrderedAdd α] (ctx : Context α) (lhs rhs : Expr) (p : Poly)
: norm_cert lhs rhs p lhs.denote ctx = rhs.denote ctx p.denote' ctx 0 := by
simp [norm_cert]; intro _ h₁; subst p; simp [Expr.denote, h₁, sub_self]
apply Preorder.le_refl
@@ -351,21 +351,21 @@ theorem diseq_norm {α} [IntModule α] (ctx : Context α) (lhs rhs : Expr) (p :
rw [add_left_comm, sub_eq_add_neg, sub_self, add_zero] at h
contradiction
theorem le_norm {α} [IntModule α] [Preorder α] [OrderedAdd α] (ctx : Context α) (lhs rhs : Expr) (p : Poly)
theorem le_norm {α} [IntModule α] [LE α] [LT α] [Preorder α] [OrderedAdd α] (ctx : Context α) (lhs rhs : Expr) (p : Poly)
: norm_cert lhs rhs p lhs.denote ctx rhs.denote ctx p.denote' ctx 0 := by
simp [norm_cert]; intro _ h₁; subst p; simp [Expr.denote]
replace h₁ := add_le_left h₁ (-rhs.denote ctx)
simp [ sub_eq_add_neg, sub_self] at h₁
assumption
theorem lt_norm {α} [IntModule α] [Preorder α] [OrderedAdd α] (ctx : Context α) (lhs rhs : Expr) (p : Poly)
theorem lt_norm {α} [IntModule α] [LE α] [LT α] [Preorder α] [OrderedAdd α] (ctx : Context α) (lhs rhs : Expr) (p : Poly)
: norm_cert lhs rhs p lhs.denote ctx < rhs.denote ctx p.denote' ctx < 0 := by
simp [norm_cert]; intro _ h₁; subst p; simp [Expr.denote]
replace h₁ := add_lt_left h₁ (-rhs.denote ctx)
simp [ sub_eq_add_neg, sub_self] at h₁
assumption
theorem not_le_norm {α} [IntModule α] [LinearOrder α] [OrderedAdd α] (ctx : Context α) (lhs rhs : Expr) (p : Poly)
theorem not_le_norm {α} [IntModule α] [LE α] [LT α] [LinearOrder α] [OrderedAdd α] (ctx : Context α) (lhs rhs : Expr) (p : Poly)
: norm_cert rhs lhs p ¬ lhs.denote ctx rhs.denote ctx p.denote' ctx < 0 := by
simp [norm_cert]; intro _ h₁; subst p; simp [Expr.denote]
replace h₁ := LinearOrder.lt_of_not_le h₁
@@ -373,7 +373,7 @@ theorem not_le_norm {α} [IntModule α] [LinearOrder α] [OrderedAdd α] (ctx :
simp [ sub_eq_add_neg, sub_self] at h₁
assumption
theorem not_lt_norm {α} [IntModule α] [LinearOrder α] [OrderedAdd α] (ctx : Context α) (lhs rhs : Expr) (p : Poly)
theorem not_lt_norm {α} [IntModule α] [LE α] [LT α] [LinearOrder α] [OrderedAdd α] (ctx : Context α) (lhs rhs : Expr) (p : Poly)
: norm_cert rhs lhs p ¬ lhs.denote ctx < rhs.denote ctx p.denote' ctx 0 := by
simp [norm_cert]; intro _ h₁; subst p; simp [Expr.denote]
replace h₁ := LinearOrder.le_of_not_lt h₁
@@ -383,14 +383,14 @@ theorem not_lt_norm {α} [IntModule α] [LinearOrder α] [OrderedAdd α] (ctx :
-- If the module does not have a linear order, we can still put the expressions in polynomial forms
theorem not_le_norm' {α} [IntModule α] [Preorder α] [OrderedAdd α] (ctx : Context α) (lhs rhs : Expr) (p : Poly)
theorem not_le_norm' {α} [IntModule α] [LE α] [LT α] [Preorder α] [OrderedAdd α] (ctx : Context α) (lhs rhs : Expr) (p : Poly)
: norm_cert lhs rhs p ¬ lhs.denote ctx rhs.denote ctx ¬ p.denote' ctx 0 := by
simp [norm_cert]; intro _ h₁; subst p; simp [Expr.denote]; intro h
replace h := add_le_right (rhs.denote ctx) h
rw [sub_eq_add_neg, add_left_comm, sub_eq_add_neg, sub_self] at h; simp at h
contradiction
theorem not_lt_norm' {α} [IntModule α] [Preorder α] [OrderedAdd α] (ctx : Context α) (lhs rhs : Expr) (p : Poly)
theorem not_lt_norm' {α} [IntModule α] [LE α] [LT α] [Preorder α] [OrderedAdd α] (ctx : Context α) (lhs rhs : Expr) (p : Poly)
: norm_cert lhs rhs p ¬ lhs.denote ctx < rhs.denote ctx ¬ p.denote' ctx < 0 := by
simp [norm_cert]; intro _ h₁; subst p; simp [Expr.denote]; intro h
replace h := add_lt_right (rhs.denote ctx) h
@@ -403,7 +403,7 @@ Equality detection
def eq_of_le_ge_cert (p₁ p₂ : Poly) : Bool :=
p₂ == p₁.mul (-1)
theorem eq_of_le_ge {α} [IntModule α] [PartialOrder α] [OrderedAdd α] (ctx : Context α) (p₁ : Poly) (p₂ : Poly)
theorem eq_of_le_ge {α} [IntModule α] [LE α] [LT α] [PartialOrder α] [OrderedAdd α] (ctx : Context α) (p₁ : Poly) (p₂ : Poly)
: eq_of_le_ge_cert p₁ p₂ p₁.denote' ctx 0 p₂.denote' ctx 0 p₁.denote' ctx = 0 := by
simp [eq_of_le_ge_cert]
intro; subst p₂; simp
@@ -419,7 +419,7 @@ Helper theorems for closing the goal
theorem diseq_unsat {α} [IntModule α] (ctx : Context α) : (Poly.nil).denote ctx 0 False := by
simp [Poly.denote]
theorem lt_unsat {α} [IntModule α] [Preorder α] (ctx : Context α) : (Poly.nil).denote ctx < 0 False := by
theorem lt_unsat {α} [IntModule α] [LE α] [LT α] [Preorder α] (ctx : Context α) : (Poly.nil).denote ctx < 0 False := by
simp [Poly.denote]; intro h
have := Preorder.lt_iff_le_not_le.mp h
simp at this
@@ -427,7 +427,7 @@ theorem lt_unsat {α} [IntModule α] [Preorder α] (ctx : Context α) : (Poly.ni
def zero_lt_one_cert (p : Poly) : Bool :=
p == .add (-1) 0 .nil
theorem zero_lt_one {α} [Ring α] [Preorder α] [OrderedRing α] (ctx : Context α) (p : Poly)
theorem zero_lt_one {α} [Ring α] [LE α] [LT α] [Preorder α] [OrderedRing α] (ctx : Context α) (p : Poly)
: zero_lt_one_cert p (0 : Var).denote ctx = One.one p.denote' ctx < 0 := by
simp [zero_lt_one_cert]; intro _ h; subst p; simp [Poly.denote, h, One.one, neg_zsmul]
rw [neg_lt_iff, neg_zero]; apply OrderedRing.zero_lt_one
@@ -435,7 +435,7 @@ theorem zero_lt_one {α} [Ring α] [Preorder α] [OrderedRing α] (ctx : Context
def zero_ne_one_cert (p : Poly) : Bool :=
p == .add 1 0 .nil
theorem zero_ne_one_of_ord_ring {α} [Ring α] [Preorder α] [OrderedRing α] (ctx : Context α) (p : Poly)
theorem zero_ne_one_of_ord_ring {α} [Ring α] [LE α] [LT α] [Preorder α] [OrderedRing α] (ctx : Context α) (p : Poly)
: zero_ne_one_cert p (0 : Var).denote ctx = One.one p.denote' ctx 0 := by
simp [zero_ne_one_cert]; intro _ h; subst p; simp [Poly.denote, h, One.one]
intro h; have := OrderedRing.zero_lt_one (R := α); simp [h, Preorder.lt_irrefl] at this
@@ -484,7 +484,7 @@ theorem eq_coeff {α} [IntModule α] [NoNatZeroDivisors α] (ctx : Context α) (
def coeff_cert (p₁ p₂ : Poly) (k : Nat) :=
k > 0 && p₁ == p₂.mul k
theorem le_coeff {α} [IntModule α] [LinearOrder α] [OrderedAdd α] (ctx : Context α) (p₁ p₂ : Poly) (k : Nat)
theorem le_coeff {α} [IntModule α] [LE α] [LT α] [LinearOrder α] [OrderedAdd α] (ctx : Context α) (p₁ p₂ : Poly) (k : Nat)
: coeff_cert p₁ p₂ k p₁.denote' ctx 0 p₂.denote' ctx 0 := by
simp [coeff_cert]; intro h _; subst p₁; simp
have : k > (0 : Int) := Int.natCast_pos.mpr h
@@ -493,7 +493,7 @@ theorem le_coeff {α} [IntModule α] [LinearOrder α] [OrderedAdd α] (ctx : Con
replace h₂ := zsmul_pos_iff (k) h₂ |>.mpr this
exact Preorder.lt_irrefl 0 (Preorder.lt_of_lt_of_le h₂ h₁)
theorem lt_coeff {α} [IntModule α] [LinearOrder α] [OrderedAdd α] (ctx : Context α) (p₁ p₂ : Poly) (k : Nat)
theorem lt_coeff {α} [IntModule α] [LE α] [LT α] [LinearOrder α] [OrderedAdd α] (ctx : Context α) (p₁ p₂ : Poly) (k : Nat)
: coeff_cert p₁ p₂ k p₁.denote' ctx < 0 p₂.denote' ctx < 0 := by
simp [coeff_cert]; intro h _; subst p₁; simp
have : k > (0 : Int) := Int.natCast_pos.mpr h
@@ -544,7 +544,7 @@ def eq_le_subst_cert (x : Var) (p₁ p₂ p₃ : Poly) :=
let b := p₂.coeff x
a 0 && p₃ == (p₂.mul a |>.combine (p₁.mul (-b)))
theorem eq_le_subst {α} [IntModule α] [Preorder α] [OrderedAdd α] (ctx : Context α) (x : Var) (p₁ p₂ p₃ : Poly)
theorem eq_le_subst {α} [IntModule α] [LE α] [LT α] [Preorder α] [OrderedAdd α] (ctx : Context α) (x : Var) (p₁ p₂ p₃ : Poly)
: eq_le_subst_cert x p₁ p₂ p₃ p₁.denote' ctx = 0 p₂.denote' ctx 0 p₃.denote' ctx 0 := by
simp [eq_le_subst_cert]; intro h _ h₁ h₂; subst p₃; simp [h₁]
exact zsmul_nonpos h h₂
@@ -554,7 +554,7 @@ def eq_lt_subst_cert (x : Var) (p₁ p₂ p₃ : Poly) :=
let b := p₂.coeff x
a > 0 && p₃ == (p₂.mul a |>.combine (p₁.mul (-b)))
theorem eq_lt_subst {α} [IntModule α] [Preorder α] [OrderedAdd α] (ctx : Context α) (x : Var) (p₁ p₂ p₃ : Poly)
theorem eq_lt_subst {α} [IntModule α] [LE α] [LT α] [Preorder α] [OrderedAdd α] (ctx : Context α) (x : Var) (p₁ p₂ p₃ : Poly)
: eq_lt_subst_cert x p₁ p₂ p₃ p₁.denote' ctx = 0 p₂.denote' ctx < 0 p₃.denote' ctx < 0 := by
simp [eq_lt_subst_cert]; intro h _ h₁ h₂; subst p₃; simp [h₁]
exact zsmul_neg_iff (p₁.coeff x) h₂ |>.mpr h

View File

@@ -17,7 +17,7 @@ namespace Lean.Grind
/--
Addition is compatible with a preorder if `a ≤ b ↔ a + c ≤ b + c`.
-/
class OrderedAdd (M : Type u) [HAdd M M M] [Preorder M] where
class OrderedAdd (M : Type u) [HAdd M M M] [LE M] [LT M] [Preorder M] where
/-- `a + c ≤ b + c` iff `a ≤ b`. -/
add_le_left_iff : {a b : M} (c : M), a b a + c b + c
@@ -30,7 +30,7 @@ open AddCommMonoid NatModule
section
variable {M : Type u} [Preorder M] [AddCommMonoid M] [OrderedAdd M]
variable {M : Type u} [LE M] [LT M] [Preorder M] [AddCommMonoid M] [OrderedAdd M]
theorem add_le_right_iff {a b : M} (c : M) : a b c + a c + b := by
rw [add_comm c a, add_comm c b, add_le_left_iff]
@@ -73,7 +73,7 @@ end
section
variable {M : Type u} [Preorder M] [NatModule M] [OrderedAdd M]
variable {M : Type u} [LE M] [LT M] [Preorder M] [NatModule M] [OrderedAdd M]
theorem nsmul_le_nsmul {k : Nat} {a b : M} (h : a b) : k * a k * b := by
induction k with
@@ -117,7 +117,7 @@ end
section
open AddCommGroup
variable {M : Type u} [Preorder M] [AddCommGroup M] [OrderedAdd M]
variable {M : Type u} [LE M] [LT M] [Preorder M] [AddCommGroup M] [OrderedAdd M]
theorem neg_le_iff {a b : M} : -a b -b a := by
rw [OrderedAdd.add_le_left_iff a, neg_add_cancel]
@@ -127,7 +127,7 @@ theorem neg_le_iff {a b : M} : -a ≤ b ↔ -b ≤ a := by
end
section
variable {M : Type u} [Preorder M] [IntModule M] [OrderedAdd M]
variable {M : Type u} [LE M] [LT M] [Preorder M] [IntModule M] [OrderedAdd M]
open AddCommGroup IntModule
theorem zsmul_pos_iff (k : Int) {x : M} (h : 0 < x) : 0 < k * x 0 < k :=
@@ -154,7 +154,7 @@ theorem zsmul_nonneg {k : Int} {x : M} (h : 0 ≤ k) (hx : 0 ≤ x) : 0 ≤ k *
end
section
variable {M : Type u} [Preorder M] [AddCommGroup M] [OrderedAdd M]
variable {M : Type u} [LE M] [LT M] [Preorder M] [AddCommGroup M] [OrderedAdd M]
open AddCommGroup
@@ -186,7 +186,7 @@ end
section
variable {M : Type u} [Preorder M] [IntModule M] [OrderedAdd M]
variable {M : Type u} [LE M] [LT M] [Preorder M] [IntModule M] [OrderedAdd M]
open IntModule
theorem zsmul_neg_iff (k : Int) {a : M} (h : a < 0) : k * a < 0 0 < k := by

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