Compare commits

..

178 Commits

Author SHA1 Message Date
Leonardo de Moura
2d1a86edf5 fix: panic in grind ring
This PR fixes a panic in `grind ring` exposed by #10242. `grind ring`
should not assume that all normalizations have been applied, because
some subterms cannot be rewritten by `simp` due to typing constraints.
Moreover, `grind` uses `preprocessLight` in a few places, and it skips
the simplifier/normalizer.

Closes #10242
2025-09-05 09:09:43 -07:00
Sebastian Ullrich
974c649e2e fix: meta structure/inductive should create meta ctor (#10263) 2025-09-05 14:00:55 +00:00
Paul Reichert
184f716da1 refactor: improve names in the range API (#10059)
This PR improves the names of definitions and lemmas in the polymorphic
range API. It also introduces a recommended spelling. For example, a
left-closed, right-open range is spelled `Rco` in analogy with Mathlib's
`Ico` intervals.
2025-09-05 13:10:05 +00:00
Lean stage0 autoupdater
3f7f1c87f6 chore: update stage0 2025-09-05 11:21:02 +00:00
Marc Huisinga
7ba0ae1f72 feat: improve auto-completion performance (#10249)
This PR speeds up auto-completion by a factor of ~3.5x through various
performance improvements in the language server. On one machine, with
`import Mathlib`, completing `i` used to take 3200ms and now instead
yields a result in 920ms.

Specifically, the following improvements are made:
- The watchdog process no longer de-serializes and re-serializes most
messages from the file worker before passing them on to the user - a
fast partial de-serialization procedure is now used to determine whether
the message needs to be de-serialized in full or not.
- `escapePart` is optimized to perform better on ASCII strings that do
not need escaping.
- `Json.compress` is optimized to allocate fewer objects.
- A faster JSON compression specifically for completion responses is
implemented that skips allocating `Json` altogether.
- The JSON compression has been moved to the task where we convert a
request response to `Json` so that converting to a string won't block
the output task of the FileWorker and so the `Json` value is not marked
as multi-threaded when we compress is, which drastically increases the
cost of reference-counting.
- The JSON representation of the `data?` field of each completion item
is optimized.
- Both the completion kind and the set of completion tags for each
imported completion item is now cached.
- The filtering of duplicate completion items is optimized.

Other adjustments:
- `LT UInt8` and `LE UInt8` are moved to Prelude so that they can be
used in `Init.Meta` for the name part escaping fast path.
- `Array.usize` is exposed since it was marked as `@[simp]`.
2025-09-05 08:55:49 +00:00
Cameron Zwarich
9923a8d9f8 chore: remove special case for extern constructors (#10257)
This is subsumed by the fix in #10256.
2025-09-05 06:08:45 +00:00
Cameron Zwarich
de38a16fa9 fix: use IR decls in toIR for applications without mono decls (#10256)
This PR corrects a mistake in `toIR` where it could over-apply a
function that has an IR decl but no mono decl.

Fixes #10181.
2025-09-05 05:32:19 +00:00
Cameron Zwarich
c0238e396c refactor: inline tryIrDecl? into its only caller (#10255)
This helper function was actually incorrectly named anyways.
2025-09-05 04:41:34 +00:00
Cameron Zwarich
c7cc398935 refactor: create a mkApplication helper for toIR (#10254) 2025-09-05 01:42:36 +00:00
Mac Malone
849bb770fd refactor: lake: split PackageConfig from Config.Package (#10253)
This PR moves the `PackageConfig` definition from `Lake.Config.Package`
into its own module. This enables a significant reduction in the `meta
import` tree of the `Lake.CLI.Translate` modules.
2025-09-04 23:15:37 +00:00
Leonardo de Moura
6cefbc4bb0 chore: fix typo (#10251) 2025-09-04 16:05:00 +00:00
Paul Reichert
9b6a4a7588 fix: solve two problems with LinearOrderPackage factories (#10250)
This PR fixes a bug in the `LinearOrderPackage.ofOrd` factory. If there
is a `LawfulEqOrd` instance available, it should automatically use it
instead of requiring the user to provide the `eq_of_compare` argument to
the factory. The PR also solves a hygiene-related problem making the
factories fail when `Std` is not open.
2025-09-04 15:27:09 +00:00
Sebastian Ullrich
47787dc1cb perf: rebuild leak on private match (#10246)
This PR prevents downstream rebuilds on changes to private `match`es
under the module system
2025-09-04 12:51:42 +00:00
Lean stage0 autoupdater
25ab3dd93d chore: update stage0 2025-09-04 08:22:20 +00:00
Kim Morrison
bbd45b13f4 chore: move omega internals to a namespace (#10243)
This PR moves some internal implementation details of `omega` out of the
`List` namespace. See [#mathlib4 > Naming: ne_zero vs nonzero @
💬](https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/Naming.3A.20ne_zero.20vs.20nonzero/near/537424328).
2025-09-04 06:32:02 +00:00
Kim Morrison
85f168bbd0 chore: add test cases for grind on Fin lemmas (#10241)
This PR adds some test cases for `grind` working with `Fin`. There are
many still failing tests in `tests/lean/grind/grind_fin.lean` which I'm
intending to triage and work on.
2025-09-04 04:28:29 +00:00
Marcus Rossel
89aed0931e feat: improve error message when passing local hypotheses to grind (#8891)
This PR improves the error message produced when passing (automatically
redundant) local hypotheses to `grind`.
2025-09-04 03:00:21 +00:00
Sebastian Ullrich
92d24e1c40 fix: Environment.realizeConst to replay realization map (#10238)
This PR fixes an issue with retrieving realized declarations after use
of Aesop uncovered by #10229
2025-09-03 22:16:40 +00:00
Leonardo de Moura
c15ee8a9f0 fix: universe polymorphic E-matching (#10239)
This PR fixes the E-matching procedure for theorems that contain
universe parameters not referenced by any regular parameter. This kind
of theorem seldom happens in practice, but we do have instances in the
standard library. Example:
```
@[simp, grind =] theorem Std.Do.SPred.down_pure {φ : Prop} : (⌜φ⌝ : SPred []).down = φ := rfl
```

closes #10233
2025-09-03 22:14:58 +00:00
Leonardo de Moura
320b02108b fix: grind canonicalizer (#10237)
This PR fixes a missing case in the `grind` canonicalizer. Some types
may include terms or propositions that are internalized later in the
`grind` state.

closes #10232
2025-09-03 18:08:48 +00:00
Rob23oba
80df86dfdd feat: add more MonoBind instances for monad transformers (#10230)
This PR adds `MonoBind` for more monad transformers. This allows using
`partial_fixpoint` for more complicated monads based on `Option` and
`EIO`. Example:
```lean-4
abbrev M := ReaderT String (StateT String.Pos Option)

def parseAll (x : M α) : M (List α) := do
  if (← read).atEnd (← get) then
    return []
  let val ← x
  let list ← parseAll x
  return val :: list
partial_fixpoint
```
2025-09-03 17:15:41 +00:00
Paul Reichert
fef390df08 perf: improve iterator/range benchmarks, use shortcut instances for Int ranges (#10197)
This PR is the result of analyzing the elaborator performance regression
introduced by #10005. It makes the `workspaceSymboldNewRanges` and
`iterators` benchmarks less noisy. It also replaces some range-related
instances for `Nat` with shortcuts to the general-purpose instances.
This is a trade-off between the ergonomics and the synthesis cost of
having general-purpose instances.
2025-09-03 15:47:52 +00:00
Sebastian Ullrich
37be918c50 perf: do not export EqnInfo for non-exposed defs (#10229) 2025-09-03 10:03:52 +00:00
Sebastian Ullrich
2efbe4ac36 feat: support visibility modifiers on syntax abbrevs (#10228)
Closes #10068
2025-09-03 07:53:29 +00:00
Eric Wieser
6d68aab56a feat: generalize universes in monadic operators for collections (#10224)
This PR generalizes the monadic operations for `HashMap`, `TreeMap`, and
`HashSet` to work for `m : Type u → Type v`.

This upstreams [a workaround from
Aesop](66a992130e/Aesop/Util/Basic.lean (L57-L66)),
and seems to continue a pattern already established in other files, such
as:
```lean
Array.forM.{u, v, w} {α : Type u} {m : Type v → Type w} [Monad m] (f : α → m PUnit) (as : Array α) (start : Nat := 0)
  (stop : Nat := as.size) : m PUnit
```
2025-09-03 07:24:14 +00:00
Joachim Breitner
ccb8568756 feat: linear-size DecidableEq instance (#10152)
This PR introduces an alternative construction for `DecidableEq`
instances that avoids the quadratic overhead of the default
construction.

The usual construction uses a `match` statement that looks at each pair
of constructors, and thus is necessarily quadratic in size. For
inductive data type with dozens of constructors or more, this quickly
becomes slow to process.

The new construction first compares the constructor tags (using the
`.ctorIdx` introduced in #9951), and handles the case of a differing
constructor tag quickly. If the constructor tags match, it uses the
per-constructor-eliminators (#9952) to create a linear-size instance. It
does so by creating a custom “matcher” for a parallel match on the data
types and the `h : x1.ctorIdx = x2.ctorIdx` assumption; this behaves
(and delaborates) like a normal `match` statement, but is implemented in
a bespoke way. This same-constructor-matcher will be useful for
implementing other instances as well.

The new construction produces less efficient code at the moment, so we
use it only for inductive types with 10 or more constructors by default.
The option `deriving.decEq.linear_construction_threshold` can be used to
adjust the threshold; set it to 0 to always use the new construction.
2025-09-03 06:31:49 +00:00
Leonardo de Moura
a4f6f391fe feat: equality propagation from AC module to grind core (#10223)
This PR implements equality propagation from the new AC module into the
`grind` core. Examples:

```lean
example {α β : Sort u} (f : α → β) (op : α → α → α) [Std.Associative op] [Std.Commutative op] 
    (a b c d : α) : op a (op b b) = op d c → f (op (op b a) (op b c)) = f (op c (op d c)) := by
  grind only

example (a b c : Nat) : min a (max b (max c 0)) = min (max c b) a := by
  grind -cutsat only

example {α β : Sort u} (bar : α → β) (op : α → α → α) [Std.Associative op] [Std.IdempotentOp op]
    (a b c d e f x y w : α) :
    op d (op x c) = op a b →
    op e (op f (op y w)) = op (op d a) (op b c) →
    bar (op d (op x c)) = bar (op e (op f (op y w))) := by
  grind only
```
2025-09-02 23:02:25 +00:00
Leonardo de Moura
dac61c406f feat: extra critical pairs for associative + idempotent operators in grind ac (#10221)
This PR adds the extra critical pairs to ensure the `grind ac` procedure
is complete when the operator is associative and idempotent, but not
commutative. Example:
```lean
example {α : Sort u} (op : α → α → α) [Std.Associative op] [Std.IdempotentOp op] (a b c d e f x y w : α)
    : op d (op x c) = op a b →
      op e (op f (op y w)) = op a (op b c) →
      op d (op x c) = op e (op f (op y w)) := by
  grind only

example {α : Sort u} (op : α → α → α) [Std.Associative op] [Std.IdempotentOp op] (a b c d e f x y w : α)
    : op a (op d x) = op b c →
      op e (op f (op y w)) = op a (op b c) →
      op a (op d x) = op e (op f (op y w)) := by
  grind only
```
2025-09-02 15:52:56 +00:00
Henrik Böving
db35f98b26 fix: make csimp equivalence criteria more strict (#10214)
This PR fixes #10213.
2025-09-02 14:36:08 +00:00
Leonardo de Moura
e6f50b0181 perf: EqCnstr.superposeWith (#10218)
This PR adds a small optimization for `EqCnstr.superposeWith`
It also adds a new test unrelated to the optimization.
2025-09-02 13:50:47 +00:00
Dax Fohl
2877196656 doc: fix broken "quickstart" and "supported editors" link (#8785)
The "supported editors" link in
https://github.com/leanprover/lean4/blob/master/doc/dev/index.md is
broken, as `setup.md` no longer exists in the repo. This PR changes the
link to point to the live Lean docs setup page at
https://docs.lean-lang.org/lean4/doc/setup.html#editing.

A similar fix for quickstart is included.

---------

Co-authored-by: Sebastian Ullrich <sebasti@nullri.ch>
2025-09-02 12:45:04 +00:00
Aaron Liu
f748d1c4ef doc: fix typo in docstring for fieldIdxKind (#8814)
This PR fixes a typo in the docstring for `Lean.fieldIdxKind`, which was
missing a backtick.
2025-09-02 12:30:07 +00:00
Eric Wieser
848832dd61 chore: demote a panic to an exception in saveModuleData (#9127)
This PR makes `saveModuleData` throw an IO.Error instead of panicking,
if given something that cannot be serialized. This doesn't really matter
for saving modules, but is handy when writing tools to save auxiliary
date in olean files via Batteries' `pickle`.

The caller of this C++ function already is guarded in a `try`/`catch`
that promotes from a `lean::exception` to an `IO.userError`.

A simple test of this in the web editor is
```
import Batteries

#eval pickle "/tmp/foo.txt" fun x : Nat => x
```
which crashes before this change.

---------

Co-authored-by: Laurent Sartran <lsartran@google.com>
2025-09-02 12:25:45 +00:00
Henrik Böving
c5f2c192d6 fix: Selectable.one does not panic on empty array (#10216)
This PR fixes #10193.
2025-09-02 11:55:36 +00:00
Sebastian Ullrich
96c42b95fa chore: CI: reintroduce lost CTEST_OPTIONS (#10211) 2025-09-02 09:26:29 +00:00
Leonardo de Moura
d826474b14 feat: extra critical pairs for AC + idempotent operators in grind ac (#10208)
This PR adds the extra critical pairs to ensure the `grind ac` procedure
is complete when the operator is AC and idempotent. Example:
```lean
example {α : Sort u} (op : α → α → α) [Std.Associative op] [Std.Commutative op] [Std.IdempotentOp op] 
      (a b c d : α) : op a (op b b) = op d c → op (op b a) (op b c) = op c (op d c)  := by
  grind only
```
2025-09-02 04:24:22 +00:00
Kim Morrison
8d9d23b5bb feat: (approximate) inverses of dyadic rationals (#10194)
This PR adds the inverse of a dyadic rational, at a given precision, and
characterising lemmas. Also cleans up various parts of the `Int.DivMod`
and `Rat` APIs, and proves some characterising lemmas about
`Rat.toDyadic`.

---------

Co-authored-by: Rob23oba <152706811+Rob23oba@users.noreply.github.com>
2025-09-02 03:43:53 +00:00
Leonardo de Moura
c83237baf7 chore: cleanup superposeAC? (#10207)
This PR ensures `superposeAC?` and `superpose?` have similar signatures.
2025-09-02 01:55:20 +00:00
Leonardo de Moura
11f618ac49 feat: critical pairs (non commutative case) for grind ac (#10206)
This PR adds superposition for associative (but non-commutative)
operators in `grind ac`. Examples:
```lean
example {α} (op : α → α → α) [Std.Associative op] (a b c d : α)
   : op a b = c →
     op b a = d →
     op (op c a) (op b c) = op (op a d) (op d b) := by
  grind

example {α} (a b c d : List α)
   : a ++ b = c →
     b ++ a = d →
     c ++ a ++ b ++ c = a ++ d ++ d ++ b := by
  grind only
```
2025-09-02 00:58:49 +00:00
Leonardo de Moura
708f715efb feat: critical pairs for grind ac (#10205)
This PR adds superposition for associative and commutative operators in
`grind ac`. Examples:

```lean
example (a b c d e f g h : Nat) :
    max a b = max c d → max b e = max d f → max b g = max d h →
    max (max f d) (max c g) = max (max e (max d (max b (max c e)))) h := by
  grind -cutsat only

example {α} (op : α → α → α) [Std.Associative op] [Std.Commutative op] (a b c d : α)
    : op a b = op b c → op c c = op d c →
      op (op d a) (op b d) = op (op a a) (op b d) := by
  grind only
```
2025-09-01 23:17:09 +00:00
Joachim Breitner
b0506ee835 chore: remove bootstrap tricks from #9951 (#10203)
This PR removes bootstrap tricks from #9951.
2025-09-01 13:30:42 +00:00
Joachim Breitner
f1737737f0 perf: use matcher as splitter (#10184)
This PR avoids constructing the splitter if the matcher itself has the
right type. This happens whenever there are no overlaps.
2025-09-01 11:29:43 +00:00
Joachim Breitner
f3b1f054ef perf: prove match equations by rfl if possible (#10183)
This PR lets match equations be proved by `rfl` if possible, instead of
explicitly unfolding the LHS first. May lead to smaller proofs.
2025-09-01 11:19:55 +00:00
Joachim Breitner
94ea5fb3fd test: add test for #10195 (#10200)
This PR adds a test for #10195
2025-09-01 10:14:54 +00:00
Rob23oba
5b9567b144 fix: complete overhaul of structural recursion on inductives predicates (#9995)
This PR almost completely rewrites the inductive predicate recursion
algorithm; in particular `IndPredBelow` to function more consistently.
Historically, the `brecOn` generation through `IndPredBelow` has been
very error-prone -- this should be fixed now since the new algorithm is
very direct and doesn't rely on tactics or meta-variables at all.
Additionally, the new structural recursion procedure for inductive
predicates shares more code with regular structural recursion and thus
allows for mutual and nested recursion in the same way it was possible
with regular structural recursion. For example, the following works now:
```lean-4
mutual

inductive Even : Nat → Prop where
  | zero : Even 0
  | succ (h : Odd n) : Even n.succ

inductive Odd : Nat → Prop where
  | succ (h : Even n) : Odd n.succ

end

mutual

theorem Even.exists (h : Even n) : ∃ a, n = 2 * a :=
  match h with
  | .zero => ⟨0, rfl⟩
  | .succ h =>
    have ⟨a, ha⟩ := h.exists
    ⟨a + 1, congrArg Nat.succ ha⟩
termination_by structural h

theorem Odd.exists (h : Odd n) : ∃ a, n = 2 * a + 1 :=
  match h with
  | .succ h =>
    have ⟨a, ha⟩ := h.exists
    ⟨a, congrArg Nat.succ ha⟩
termination_by structural h

end
```

Closes #1672
Closes #10004
2025-09-01 08:17:58 +00:00
Leonardo de Moura
c4e5f57512 feat: proof terms for grind ac (#10189)
This PR implements the proof terms for the new `grind ac` module.
Examples:
```lean
example {α : Sort u} (op : α → α → α) [Std.Associative op] (a b c d : α)
    : op a (op b b) = op c d → op c (op d c) = op (op a b) (op b c) := by
  grind only

example {α : Sort u} (op : α → α → α) [Std.Associative op] [Std.Commutative op] (a b c d : α)
    : op a (op b b) = op d c → op (op b a) (op b c) = op c (op d c)  := by
  grind only

example {α : Sort u} (op : α → α → α) [Std.Associative op] [Std.Commutative op]
    (one : α) [Std.LawfulIdentity op one] (a b c d : α)
    : op a (op (op b one) b) = op d c → op (op b a) (op (op b one) c) = op (op c one) (op d c)  := by
  grind only
```

The `grind ac` module is not complete yet, we still need to implement
critical pair computation and fix the support for idempotent operators.
2025-08-31 04:10:10 +00:00
Leonardo de Moura
f376fd87d0 feat: AC disequality simplification (#10186)
This PR adds supports for simplifying disequalities in the `grind ac`
module.
2025-08-30 20:42:21 +00:00
Leonardo de Moura
8e7e55f2d5 doc: grind attribute modifiers (#10185)
This PR documents all `grind` attribute modifiers (e.g., `=`, `usr`,
`ext`, etc).
2025-08-30 16:12:50 +00:00
Kim Morrison
8789e5621b feat: missing Nat.fold(Rev)_add lemmas (#10182)
This PR adds lemmas about `Nat.fold` and `Nat.foldRev` on sums, to match
the existing theorems about `dfold` and `dfoldRev`.
2025-08-30 08:54:12 +00:00
Leonardo de Moura
fbf096510d chore: minimize number of public imports in grind (#10180) 2025-08-30 03:38:47 +00:00
Leonardo de Moura
18cc1cec80 fix: grind instance normalization (#10179)
This PR fixes `grind` instance normalization procedure.
Some modules in grind use builtin instances defined directly in core
(e.g., `cutsat`), while others synthesize them using `synthInstance`
(e.g., `ring`). This inconsistency is problematic, as it may introduce
mismatches and result in two different representations for the same
term. This PR fixes the issue.
2025-08-30 02:24:26 +00:00
Leonardo de Moura
404b00a584 fix: grind preprocessor (#10177)
This PR fixes a bug in the `grind` preprocessor exposed by #10160.

Closes #10160
2025-08-29 23:37:52 +00:00
Leonardo de Moura
50ddf85b07 feat: check grind ac invariants (#10176)
This PR adds code for checking invariants in the `grind ac` module, and
fixes the bugs exposed by them.
2025-08-29 22:36:39 +00:00
Sofia Rodrigues
9107d27368 fix: remove extend from async and await (#10173)
This PR removes the `extends Monad` from `MonadAwait` and `MonadAsync`
to avoid underdetermined instances.

The issue was discussed here: [#lean4 > Is
Std.Internal.IO.Async.MonadAsync.toMonad a bad
instance?](https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/Is.20Std.2EInternal.2EIO.2EAsync.2EMonadAsync.2EtoMonad.20a.20bad.20instance.3F)
2025-08-29 15:33:57 +00:00
Wojciech Rozowski
d51a5b920d feat: change delimiting of local attributes in implicit sections (#9968)
This PR modifies macros, which implement non-atomic definitions and
```$cmd1 in $cmd2``` syntax. These macros involve implicit scopes,
introduced through ```section``` and ```namespace``` commands. Since
sections or namespaces are designed to delimit local attributes, this
has led to unintuitive behaviour when applying local attributes to
definitions appearing in the above-mentioned contexts. This has been
causing the following examples to fail:
```lean4
axiom A : Prop

namespace ex1
open Nat in
@[local simp] axiom a : A ↔ True
example : A := by simp
end ex1

namespace ex2
@[local simp] axiom Foo.a : A ↔ True
example : A := by simp
end ex2
```
This PR adds an internal-only piece of syntax,
```InternalSyntax.end_local_scope```, that influences the
```ScopedEnvExtension.addLocalEntry``` used in implementing local
attributes, to avoid delimiting local entries in the current scope. This
command is used in the above-mentioned macros.

Closes [#9445](https://github.com/leanprover/lean4/issues/9445).

---------

Co-authored-by: Joachim Breitner <mail@joachim-breitner.de>
2025-08-28 15:48:42 +00:00
Wojciech Rozowski
eb013fb90d fix: construction of CompleteLattice instance for eta-reduced definitions (#10144)
This PR changes the construction of a `CompleteLattice` instance on
predicates (maps intro `Prop`) inside of
`coinductive_fixpoint`/`inductive_fixpoint` machinery.

Consider a following endomap on predicates of the type ` α → Prop`:
```lean4
def DefFunctor (r : α → α → Prop) (infSeq : α → Prop) : α → Prop :=
   λ x : α => ∃ y, r x y ∧ infSeq y
```
The following eta-reduced expression failed to elaborate:
```lean4
def def1 (r : α → α → Prop) : α → Prop := DefFunctor r (def1 r)
  coinductive_fixpoint monotonicity sorry
```

At the same time, eta-expanded variant would elaborate correctly:
```lean4
def def2 (r : α → α → Prop) : α → Prop := fun x => DefFunctor r (def2 r) x
  coinductive_fixpoint monotonicity sorry
```

This PR fixes the above issue, by changing the way how `CompleteLattice`
instance on the space of predicates is constructed, to allow for the
eta-reduced case, as outlined above.
2025-08-28 12:27:53 +00:00
Kim Morrison
4c44fdb95f chore: remove grind annotations of List/Array/Vector.zip_map_left/right (#10163)
This PR removes some (hopefully) unnecessary `grind` annotations that
cause instantiation explosions.
2025-08-28 10:38:50 +00:00
Sebastian Ullrich
d63d1188cc chore: fix stdlib size benchmarks 2025-08-28 12:07:27 +02:00
Lean stage0 autoupdater
a31d686ed1 chore: update stage0 2025-08-28 09:45:24 +00:00
Kim Morrison
a62dabeb56 feat: nodup_keys theorems for maps (#10159)
This PR adds `nodup_keys` lemmas as corollaries of existing
`distinct_keys` to all `Map` variants.
2025-08-28 06:00:28 +00:00
Kim Morrison
d2eb1bc9f5 chore: review of failing grind tests (#10166)
This PR reviews the expected-to-fail-right-now tests for `grind`, moving
some (now passing) tests to the main test suite, updating some tests,
and adding some tests about normalisation of exponents.
2025-08-28 05:24:31 +00:00
Leonardo de Moura
38608a672e feat: simplify equations in grind AC module (#10165)
This PR adds support for equality simplification helper functions to the
`grind` AC module.
2025-08-28 03:54:09 +00:00
Leonardo de Moura
86425f655a feat: helper AC.Seq functions (#10164)
This PR adds helper functions for the `AC.Seq` type.
2025-08-28 02:16:52 +00:00
Sebastian Ullrich
9757a7be53 perf: do not export opaque bodies (#10119)
In particular, do not export `partial` bodies
2025-08-27 20:59:59 +00:00
Marc Huisinga
3ce69e4edb feat: re-enable Suggestion.messageData? (#10157)
Re-enables `Suggestion.messageData?` after it was deprecated in #9966
since it is needed for the workaround described in #10150. We will
hopefully be able to clean up with API once #10150 is properly fixed.
2025-08-27 16:23:02 +00:00
Leonardo de Moura
2dda33ddb2 chore: remove workaround (#10156) 2025-08-27 15:18:17 +00:00
Sebastian Ullrich
655a39ceb8 chore: improve error message on trying to access an identifier imported privately from the public scope (#10153) 2025-08-27 13:43:56 +00:00
Sebastian Ullrich
8d26a9e8b5 chore: revert public deriving workarounds (#10155) 2025-08-27 13:15:18 +00:00
Joachim Breitner
72e8970848 chore: benchmarks for deriving DecidableEq on large inductives (#10149)
This PR adds benchmarks for deriving `DecidableEq` on inductives with
many constructors. (Although at the moment, many is “many” as we timeout
for more than 30 or 40 constructors.)
2025-08-27 12:05:04 +00:00
Sebastian Ullrich
697ea0bc01 fix: Unicode path support for Lean Windows executables (#10133)
This PR fixes compatibility of Lean-generated executables with Unicode
file system paths on Windows

Fixes #2554
2025-08-27 11:28:55 +00:00
Sebastian Ullrich
4d5fb31dfb fix: where finally should enter the private scope (#10151)
This PR ensures `where finally` tactics can access private data under
the module system even when the corresponding holes are in the public
scope as long as all of them are of proposition types.
2025-08-27 11:27:40 +00:00
Sebastian Ullrich
43dc9f45d1 chore: CI: disable broken test on macOS x64 2025-08-27 13:14:32 +02:00
Lean stage0 autoupdater
dc1ddda473 chore: update stage0 2025-08-27 10:47:56 +00:00
Joachim Breitner
b5555052bd feat: T.ctor.elim single-constructor cases function (#9952)
This PR adds “non-branching case statements”: For each inductive
constructor `T.con` this adds a function `T.con.with` that is similar
`T.casesOn`, but has only one arm (the one for `con`), and an additional
`t.toCtorIdx = 12` assumption.

For example:
```lean
inductive Vec (α : Type) : Nat → Type where
  | nil : Vec α 0
  | cons {n} : α → Vec α n → Vec α (n + 1)

/--
info: @[reducible] protected def Vec.cons.elim.{u} : {α : Type} →
  {motive : (a : Nat) → Vec α a → Sort u} →
    {a : Nat} →
      (t : Vec α a) →
        t.ctorIdx = 1 → ({n : Nat} → (a : α) → (a_1 : Vec α n) → motive (n + 1) (Vec.cons a a_1)) → motive a t
-/
#guard_msgs in
#print sig Vec.cons.elim
```

This is a building block for non-quadratic implementations of `BEq` and
`DecidableEq` etc.

Builds on top of #9951.

The compiled code for a these functions could presumably, without
branching on the inductive value, directly access the fields. Achieving
this optimization (and achieving it without a quadratic compilation
cost) is not in scope for this PR.
2025-08-27 09:40:31 +00:00
Lean stage0 autoupdater
e4ca32174c chore: update stage0 2025-08-27 09:58:40 +00:00
Sebastian Ullrich
d06fff0f13 chore: CI: use restored ccache cache in update-stage0 2025-08-27 11:44:46 +02:00
Sebastian Ullrich
e74e9694fe feat: revamp and unify visibility/exposure handling in deriving handlers (#10148)
Visibility is now handled implicitly for all deriving handlers by
adjusting section visibility according to the presence of private types
while removing exposition on presence of private constructors can be
opted in on a per-handler level via the new combinator
`withoutExposeFromCtors`.

Fixes #10062 #10063 #10064 #10065
2025-08-27 09:10:24 +00:00
thorimur
5bb7818355 feat: allow position reporting in #guard_msgs (#10125)
This PR allows `#guard_msgs` to report the relative positions of logged
messages with the config option `(positions := true)`.

Closes #8265
2025-08-27 06:47:34 +00:00
Kyle Miller
5bc42bf5ca fix: pretty print dot notation for private definitions on public types (#10122)
This PR adds support for pretty printing using generalized field
notation (dot notation) for private definitions on public types. It also
modifies dot notation elaboration to resolve names after removing the
private prefix, which enables using dot notation for private definitions
on private imported types.

It won't pretty print with dot notation for definitions on inaccessible
private types from other modules.

Closes #7297
2025-08-27 03:30:52 +00:00
Leonardo de Moura
aaec0f584c feat: ac normalization in grind (#10146)
This PR implements the basic infrastructure for the new procedure
handling AC operators in grind. It already supports normalizing
disequalities. Future PRs will add support for simplification using
equalities, and computing critical pairs. Examples:
```lean
example {α : Sort u} (op : α → α → α) [Std.Associative op] (a b c : α)
    : op a (op b c) = op (op a b) c := by
  grind only

example {α : Sort u} (op : α → α → α) (u : α) [Std.Associative op] [Std.LawfulIdentity op u] (a b c : α)
    : op a (op b c) = op (op a b) (op c u) := by
  grind only

example {α : Type u} (op : α → α → α) (u : α) [Std.Associative op] [Std.Commutative op] 
    [Std.IdempotentOp op] [Std.LawfulIdentity op u] (a b c : α)
    : op (op a a) (op b c) = op (op (op b a) (op (op u b) b)) c := by
  grind only

example {α} (as bs cs : List α) : as ++ (bs ++ cs) = ((as ++ []) ++ bs) ++ (cs ++ []) := by
  grind only

example (a b c : Nat) : max a (max b c) = max (max b 0) (max a c) ∧ min a b = min b a := by
  grind only [cases Or]
```
2025-08-27 03:28:30 +00:00
Mac Malone
db3fb47109 refactor: port more of shell.cpp to Lean (#10086)
This PR ports more of the post-initialization C++ shell code to Lean.

All that remains is the initialization of the profiler and task manager.
As initialization tasks rather than main shell code, they were left in
C++ (where the rest of the initialization code currently is).

The `max_memory` and `timeout` Lean options used by the the `--memory`
and `--timeout` command-line options are now properly registered. The
server defaults for max memory and max heartbeats (timeout) were removed
as they were not actually used (because the `server` option that was
checked was neither set nor exists).

This PR also makes better use of the module system in `Shell.lean` and
fixes a minor bug in a previous port where the file name check was
dependent on building the `.ilean` rather than the `.c` file (as was
originally the case).

Fixes #9879.
2025-08-26 20:02:42 +00:00
Joachim Breitner
c83674bdff chore: revert use of macro_inline for ctorIdx (#10141)
This PR reverts the `macro_inline` part of #10135.
2025-08-26 18:07:49 +00:00
Leonardo de Moura
2652cc18b8 chore: error messages consistency (#10143)
This PR standardizes error messages by quoting names with backticks. The
changes were automated, so some cases may still be missing.
2025-08-26 17:55:43 +00:00
Lean stage0 autoupdater
62e00fb5a0 chore: update stage0 2025-08-26 17:42:03 +00:00
Marc Huisinga
2324c0939d chore: add private getUtf8Byte' to Init.Meta (#10140)
This PR adds a private `Lean.Name.getUtf8Byte'` to `Init.Meta` for a
future PR that optimizes `Lean.Name.escapePart`.
`Lean.Name.getUtf8Byte'` should be replaced with `String.getUtf8Byte`
once the string refactor is through.
2025-08-26 16:54:02 +00:00
Sebastian Ullrich
425bebe99e chore: further split libleanshared on Windows to avoid symbol limit (#10136)
Co-authored-by: Markus Himmel <markus@himmel-villmar.de>
2025-08-26 16:01:57 +00:00
Lean stage0 autoupdater
a0613f4d12 chore: update stage0 2025-08-26 16:01:23 +00:00
Sebastian Ullrich
298bd10f54 perf: do not cause compiler.small to export IR bodies unless the Expr body is already being exported (#10002) 2025-08-26 15:12:08 +00:00
Sebastian Ullrich
6810d31602 chore: CI: cache again on failure (#10137) 2025-08-26 14:47:05 +00:00
Luisa Cicolini
3e11f27ff4 feat: add fast circuit for unsigned multiplication overflow detection fastUmulOverflow_eq and surrounding definitions (#7858)
This PR implements the fast circuit for overflow detection in unsigned
multiplication used by Bitwuzla and proposed in:
https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=987767

The theorem is based on three definitions: 
* `uppcRec`: the unsigned parallel prefix circuit for the bits until a
certain `i`
* `aandRec`: the conjunction between the parallel prefix circuit at of
the first operand until a certain `i` and the `i`-th bit in the second
operand
* `resRec`: the preliminary overflow flag computed with these two
definitions
To establish the correspondence between these definitiions and their
meaning in `Nat`, we rely on `clz` and `clzAuxRec` definitions.
Therefore, this PR contains the `clz`- and `clzAuxRec`-related
infrastructure that was necessary to get the proofs through.

An additional change this PR contains is the moving of `### Count
leading zeros` section in `BitVec.Lemmas` downwards. In fact, some of
the proofs I wrote required introducing `Bitvec.toNat_lt_iff` and
`BitVec.le_toNat_iff` which I believe should live in the `Inequalities`
section. Therefore, to put these in the appropriate section, I decided
to move the whole `clz` section downwards (while it's small and
relatively self contained. Specifically, the theorems I moved are:
`clzAuxRec_zero`, `clzAuxRec_succ`, `clzAuxRec_eq_clzAuxRec_of_le`,
`clzAuxRec_eq_clzAuxRec_of_getLsbD_false`.
 
The fast circuit is not yet the default one in the bitblaster, as it's
performance is not yet competitive due to some missing rewrites that
bitwuzla supports but are not in Lean yet.
 
co-authored-by: @bollu

---------

Co-authored-by: Tobias Grosser <tobias@grosser.es>
2025-08-26 13:21:23 +00:00
Kim Morrison
a78a34bbd7 chore: replace Lean.Grind internal preorder classes with the classes from Std (#10129)
This PR replaces the interim order typeclasses used by `Grind` with the
new publicly available classes in `Std`.
2025-08-26 13:18:22 +00:00
Joachim Breitner
0803f1e77e perf: ctorIdx for single-constructor inductives: no casesOn, macro_inline (#10135)
This PR lets the `ctorIdx` definition for single constructor inductives
avoid the pointless `.casesOn`, and uses `macro_inline` to avoid
compiling the function and wasting symbols.
2025-08-26 13:00:10 +00:00
Kim Morrison
9e47edd0df feat: lemmas about rounding dyadics (#10138)
This PR adds lemmas about the `Dyadic.roundUp` and `Dyadic.roundDown`
operations.
2025-08-26 12:31:40 +00:00
Kim Morrison
0f1174d097 chore: use SMul rather than HMul in grind algebra typeclasses (#10095)
This PR modifies the `grind` algebra typeclasses to use `SMul x y`
instead of `HMul x y y`.
2025-08-26 12:23:37 +00:00
Marc Huisinga
f180eee7bf feat: use widget message for "try this" (#9966)
This PR adjusts the "try this" widget to be rendered as a widget message
under 'Messages', not a separate widget under a 'Suggestions' section.
The main benefit of this is that the message of the widget is not
duplicated between 'Messages' and 'Suggestions'.

Since widget message suggestions were already implemented by @jrr6 for
the new hint infrastructure, this PR replaces the old "try this"
implementation with the new hint infrastructure. In doing so, the
`style?` field of suggestions is deprecated, since the hint
infrastructure highlights hints using diff colors, and `style?` also
never saw much use downstream. Additionally, since the message and the
suggestion are now the same component, the `messageData?` field of
suggestions is deprecated as well. Notably, the "Try this:" message
string now also contains a newline and indentation to separate the
suggestion from the rest of the message more clearly and the `postInfo?`
field of the suggestion is now part of the message.

Finally, this PR changes the diff colors used by the hint infrastructure
to be more color-blindness-friendly (insertions are now blue, not green,
and text that remains unchanged is now using the editor foreground color
instead of blue).

### Breaking changes
Tests that use `#guard_msgs` to test the "Try this:" message may need to
be adjusted for the new formatting of the message.
2025-08-26 12:15:32 +00:00
Sebastian Ullrich
6a3fc281ad chore: CI: use Namespace.so checkout action for Linux Lake (#10103) 2025-08-26 09:19:58 +00:00
Lean stage0 autoupdater
06e9f4735a chore: update stage0 2025-08-26 09:46:07 +00:00
Joachim Breitner
0f5f2df11f fix: FunInd: handle let-vars-in-match-better (#10134)
This PR makes the generation of functional induction principles more
robust when the user `let`-binds a variable that is then `match`'ed on.
Fixes #10132.
2025-08-26 08:56:00 +00:00
Joachim Breitner
aa0cf78d93 chore: create .toCtorIdx alias only for enumeration types (#10130)
This PR creates the deprecated `.toCtorIdx` alias only for enumeration
types, which are the types that used to have this function. No need
generating an alias for types that never had it. Should reduce the
number of symbols in the standard library.
2025-08-26 08:33:37 +00:00
Sebastian Ullrich
4f94972ff1 chore: avoid panic in addDocString on partial elaboration (#10131) 2025-08-26 08:16:27 +00:00
Joachim Breitner
37dd26966b fix: rcases: avoid inflating case names with single constructor names (#9918)
This PR prevents `rcases` and `obtain` from creating absurdly long case
tag names when taking single constructor types (like `Exists`) apart.
Fixes #6550

The change does not affect `cases` and `induction`, it seems (where the
user might be surprised to not address the single goal with a name),
because I make the change in Lean/`Meta/Tactic/Induction.lean`, not
`Lean/Elab/Tactic/Induction.lean`. Yes, that's confusing.
2025-08-26 07:56:32 +00:00
Leonardo de Moura
1feac1ae92 chore: simplify grind import graph (#10128) 2025-08-26 06:34:44 +00:00
Leonardo de Moura
3ff195f7b2 refactor: grind build times (#10127) 2025-08-26 06:01:50 +00:00
Leonardo de Moura
5478dcf373 refactor: grind build times (#10126) 2025-08-26 04:06:37 +00:00
Kim Morrison
ad3e975178 feat: dyadic rationals (#9993)
This PR defines the dyadic rationals, showing they are an ordered ring
embedding into the rationals. We will use this for future interval
arithmetic tactics.

Many thanks to @Rob23oba, who did most of the implementation work here.

---------

Co-authored-by: Rob23oba <robin.arnez@web.de>
2025-08-26 03:49:39 +00:00
Leonardo de Moura
cd9865b26b refactor: grind build times (#10124) 2025-08-26 01:05:18 +00:00
Leonardo de Moura
8c4db341dd chore: use ofConstName in error messages (#10121) 2025-08-25 23:20:36 +00:00
Kim Morrison
a6a02fe6b9 chore: reduce Int imports on the critical path (#10123)
This PR shortens the rebuild critical path at
https://speed.lean-lang.org/lean4-out/cbf3814a565f7188f830f365453fb0bdd66d6175/
2025-08-25 23:07:02 +00:00
Kyle Miller
741347281c fix: dot notation for recursive invocation of private definitions (#10120)
This PR fixes an issue where private definitions recursively invoked
using generalized field notation (dot notation) would give an "invalid
field" errors. It also fixes an issue where "invalid field notation"
errors would pretty print the name of the declaration with a `_private`
prefix.

Closes #10044
2025-08-25 22:55:08 +00:00
Kim Morrison
a06e6e7f4d chore: make UInt.Lemmas a private import of String.Extra (#10115)
This PR makes the `Init.Data.UInt.Lemmas` import into
`Init.Data.String.Extra` private; previously this import was on the
rebuild critical path.
2025-08-25 16:46:22 +00:00
Lean stage0 autoupdater
505d5c6013 chore: update stage0 2025-08-25 17:01:52 +00:00
Joachim Breitner
13e8cb5a3a perf: reorder DiscrTree.Key constructors (#10110)
this PR reorders the `DiscrTree.Key` constructors to match the order
given in the manually written `DiscrTree.Key.ctorIdx`. This allows us to
use the auto-generated one, and moreover lets this code benefit from
special compiler support for `.ctorIdx`, once that lands.
2025-08-25 16:13:43 +00:00
Marc Huisinga
2107f45991 chore: revert #10111 (#10118)
Identical to #10052. #10116 fixed the underlying cause of test
flakiness, so this PR should hopefully be good-to-go now.
2025-08-25 15:45:03 +00:00
Marc Huisinga
a72f9429ea test: sort messages (#10116)
This PR normalizes the published diagnostics in the test runner so that
messages published out of order (due to parallelism) cannot cause test
failures. Clients can handle out-of-order messages just fine.
2025-08-25 15:08:11 +00:00
Sebastian Ullrich
321af0e02b fix: public structures with private field types under the module system (#10109)
Fixes #10099
2025-08-25 14:48:23 +00:00
Joachim Breitner
1718ca21cd feat: deprecate .toCtorIdx for .ctorIdx (#10113)
This PR deprecates `.toCtorIdx` for the more naturally named `.ctorIdx`
(and updates the standard library).
2025-08-25 14:32:05 +00:00
Sebastian Ullrich
f4ce319f1b chore: minimize Lean.Expr import (#10112) 2025-08-25 13:35:21 +00:00
Marc Huisinga
340c3da6ae chore: revert #10052 (#10111)
Potential suspect for flaky test failure.
2025-08-25 11:29:21 +00:00
Lean stage0 autoupdater
afbf52896f chore: update stage0 2025-08-25 11:31:26 +00:00
Joachim Breitner
afcf52e623 feat: .ctorIdx for all inductives (#9951)
This PR generates `.ctorIdx` functions for all inductive types, not just
enumeration types. This can be a building block for other constructions
(`BEq`, `noConfusion`) that are size-efficient even for large
inductives.

It also renames it from `.toCtorIdx` to `.ctorIdx`, which is the more
idiomatic naming.
The old name exists as an alias, with a deprecation attribute to be
added after the next
stage0 update.

These functions can arguably compiled down to a rather efficient tag
lookup, rather than a `case` statement. This is future work (but
hopefully near future).

For a fair number of basic types the compiler is not able to compile a
function using `casesOn` until further definitions have been defined.
This therefore (ab)uses the `genInjectivity` flag and
`gen_injective_theorems%` command to also control the generation of this
construct.

For (slightly) more efficient kernel reduction one could use `.rec`
rather than `.casesOn`. I did not do that yet, also because it
complicates compilation.
2025-08-25 10:47:06 +00:00
Sebastian Ullrich
3c40ea2733 chore: revert automatically exposing derived instances (#10101)
Heed surrounding `@[expose]` instead
2025-08-25 08:55:10 +00:00
Marc Huisinga
c95100e8fd fix: de-prioritize PartialTermInfo in hover info selection (#10047)
This PR ensures that hovering over `match` displays the type of the
match.
2025-08-25 08:47:14 +00:00
Marc Huisinga
be4651a772 fix: don't block fileworker with lake setup-file (#10052)
This PR fixes a bug that caused the Lean server process tree to survive
the closing of VS Code.

The cause of this issue was that the file worker main task was blocked
on waiting for the result of `lake setup-file` because the blocking call
was lifted outside of the dedicated server task that was supposed to
contain it by the compiler.
2025-08-25 08:47:01 +00:00
Rob23oba
797985e319 feat: upstream several Rat lemmas from mathlib (#10077)
This PR upstreams lemmas about `Rat` from `Mathlib.Data.Rat.Defs` and
`Mathlib.Algebra.Order.Ring.Unbundled.Rat`, specifically enough to get
`Lean.Grind.Field Rat` and `Lean.Grind.OrderedRing Rat`. In addition to
the lemmas, instances for `Inv Rat`, `Pow Rat Nat` and `Pow Rat Int`
have been upstreamed.

---------

Co-authored-by: Kim Morrison <kim@tqft.net>
2025-08-25 06:02:27 +00:00
Kim Morrison
c9f08de7b3 feat: Lean.Grind.AddCommGroup instance for Rat (#10107)
This PR adds the `Lean.Grind.AddCommGroup` instance for `Rat`.
2025-08-25 05:15:26 +00:00
Leonardo de Moura
9be2eab93d feat: associative operator detection in grind (#10105)
This PR adds support for detecting associative operators in `grind`. The
new AC module also detects whether the operator is commutative,
idempotent, and whether it has a neutral element. The information is
cached.
2025-08-25 03:07:16 +00:00
Leonardo de Moura
cc5ff2afb1 test: grind cutsat (#10106) 2025-08-25 03:04:32 +00:00
Kim Morrison
5651192fa2 chore: fix error in Grind/Arith/Linear/StructId (#10096)
I'm unsure how this was ever working. Is this dead code?
2025-08-25 01:09:23 +00:00
Sebastian Ullrich
5ccea92a09 chore: revert "chore: CI: use Namespace.so checkout action for Linux Lake" (#10102)
Reverts leanprover/lean4#10100 pending a fix for running it on master
2025-08-24 15:58:25 +00:00
Sebastian Ullrich
3fc3f5d240 chore: CI: use Namespace.so checkout action for Linux Lake (#10100)
~1min ~> ~10s
2025-08-24 15:24:50 +00:00
Lean stage0 autoupdater
dca16fb58c chore: update stage0 2025-08-24 15:22:55 +00:00
Sebastian Ullrich
20d66250df feat: optional @[expose] specifier on deriving classes (#10060)
This PR allows for more fine-grained control over what derived instances
have exposed definitions under the module system: handlers should not
expose their implementation unless either the deriving item or a
surrounding section is marked with `@[expose]`. Built-in handlers to be
updated after a stage 0 update.
2025-08-24 14:42:18 +00:00
Kim Morrison
47632f27f6 chore: protect some theorems in the Rat namespace (#10097) 2025-08-24 10:51:47 +00:00
Leonardo de Moura
dfdd682c01 feat: AC theorems for grind (#10093)
This PR adds background theorems for a new solver to be implemented in
`grind` that will support associative and commutative operators.
2025-08-24 05:02:37 +00:00
Cameron Zwarich
8e828216e5 perf: inline dependent instances into specialized decls (#10080) 2025-08-24 00:12:27 +00:00
Lean stage0 autoupdater
92037b5b1b chore: update stage0 2025-08-23 23:33:05 +00:00
Mac Malone
a93e315e72 fix: include all of Lake.Util in core build (#10090)
This PR adds the modules in `Lake.Util` to core's Lake configuration to
ensure all utilities are built. With the module system port, they were
no longer all transitively imported.

Specifically, `Lake.Util.Lock` is unused because Lake does not currently
use a lock file for the build.
2025-08-23 22:55:39 +00:00
Mac Malone
902484988e fix: include Lake.Load in core build (#10087)
This PR adds `Lake.Load` to core's Lake configuration to ensure it is
built. With the module system port, it was no longer transitively
imported.
2025-08-23 21:52:15 +00:00
David Thrane Christiansen
c9727c2d19 feat: add a stop position field to the parser (#10043)
This PR allows Lean's parser to run with a final position prior to the
end of the string, so it can be invoked on a sub-region of the input.

This has applications in Verso proper, which parses Lean syntax in
contexts such as code blocks and docstrings, and it is a prerequisite to
parsing the contents of Lean docstrings.
2025-08-23 18:29:51 +00:00
Kyle Miller
0d9b7fb6b8 feat: adds rawIdent parser alias (#10085)
This PR adds a parser alias for the `rawIdent` parser, so that it can be
used in `syntax` declarations in `Init`.
2025-08-23 17:24:33 +00:00
Kyle Miller
db43de7b9d feat: add enter [in patt] syntax (#10081)
This PR adds `enter [in patt]` syntax. The implementation will come in a
followup PR, and it will stand for `pattern patt`.
2025-08-23 17:16:53 +00:00
Sebastian Ullrich
17f76f3bd7 chore: CI: recover dropped install suffixes (#10076) 2025-08-23 15:15:26 +00:00
Kim Morrison
6f69715f0a feat: replace Std.Internal.Rat (#9979)
This PR replaces `Std.Internal.Rat` with the new public `Rat` upstreamed
from Batteries.

The time library was depending on some defeqs which are no longer true,
so I have inserted some casts.

---------

Co-authored-by: Sebastian Ullrich <sebasti@nullri.ch>
Co-authored-by: Sofia Rodrigues <sofia@algebraic.dev>
2025-08-23 12:07:01 +00:00
Kim Morrison
2b34d5b899 feat: more Int lemmas in preparation for dyadics (#10075)
This PR contains lemmas about `Int` (minor amendments for BitVec and
Nat) that are being used in preparing the dyadics. This is all work of
@Rob23oba, which I'm pulling out of #9993 early to keep that one
manageable.
2025-08-23 04:59:59 +00:00
Cameron Zwarich
0881a8872b chore: use dotted constructor names (#10074) 2025-08-23 04:52:19 +00:00
Cameron Zwarich
91a2de1e1e chore: use forallTelescope rather than forallTelescopeReducing (#10073) 2025-08-23 04:07:23 +00:00
Cameron Zwarich
4a7def9e5f chore: use Expr.fvarId! and FVarId.getType (#10072) 2025-08-23 04:04:57 +00:00
Cameron Zwarich
dc5766d27a fix: eliminate infinite loop in toLCNF's expandNoConfusionMajor (#10070)
This PR fixes the compilation of `noConfusion` by repairing an oversight
made when porting this code from the old compiler. The old compiler only
repeatedly expanded the major for each non-`Prop` field of the inductive
under consideration, mirroring the construction of `noConfusion` itself,
whereas the new compiler erroneously counted all fields.

Fixes #9971.
2025-08-23 02:18:51 +00:00
Leonardo de Moura
a63d483258 feat: pow support in grind cutsat (#10071)
This PR improves support for `a^n` in `grind cutsat`. For example, if
`cutsat` discovers that `a` and `b` are equal to numerals, it now
propagates the equality. This PR is similar to #9996, but `a^b`.
Example:

```lean
example (n : Nat) : n = 2 → 2 ^ (n+1) = 8 := by
  grind
```

With #10022, it also improves the support for `BitVec n` when `n` is not
numeral. Example:

```lean
example {n m : Nat} (x : BitVec n)
    : 2 ≤ n → n ≤ m → m = 2 → x = 0 ∨ x = 1 ∨ x = 2 ∨ x = 3 := by
  grind
```
2025-08-23 01:55:05 +00:00
Mac Malone
1f9bba9d39 refactor: lake: use module (#9749)
This PR refactors the Lake codebase to use the new module system
throughout. Every module in `Lake` is now a `module`.

As this was already a large-scale refactor, a general cleanup of the
code has also been bundled in.

This PR also uses workarounds for currently outstanding module system
issues: #10061, #10062, #10063, #10064, #10065, #10067, and #10068.

**Breaking change:** Since the module system encourages a
`private`-by-default design, the Lake API has switched from its previous
`public`-by-default approach. As such, many definitions that were
previously public are now private. The newly private definitions are not
expected to have had significant user use, Nonetheless, important use
cases could be missed. If a key API is now inaccessible but seems like
it should be public, users are encouraged to report this as an issue on
GitHub.
2025-08-22 23:02:33 +00:00
Leonardo de Moura
5daf65ec56 feat: add helper theorems for NatModule (#10069)
This PR adds helper theorems to support `NatModule` in `grind linarith`.
2025-08-22 20:36:05 +00:00
Paul Reichert
9d4665a0bf perf: quick fix for range elaboration performance (#10066)
This PR reverts parts of #10005 that surprisingly turned out to cause a
performance regression in the benchmarks. The slowdown seems to be
related to elaboration, not inefficiencies in the generated code. This
is just a quick fix. I will take a closer look in a week.
2025-08-22 20:30:13 +00:00
Lean stage0 autoupdater
6df94385c5 chore: update stage0 2025-08-22 17:52:06 +00:00
David Thrane Christiansen
82932ec86a feat: add stop position to parser (#10057)
This PR adds a stop position field to parser input contexts, allowing
the parser to be instructed to stop parsing prior to the end of a file.

This is step 1, prior to a stage0 update, to make run-time data
structures sufficiently compatible to avoid segfaults. After the update,
the actual code to stop parsing can be merged.
2025-08-22 17:04:04 +00:00
Lean stage0 autoupdater
3d7d35b588 chore: update stage0 2025-08-22 16:03:06 +00:00
Sebastian Ullrich
fb23d7b45d chore: make parseQuotWithCurrentStage do what it says under prefer_native (#10058)
Switch on the interpreter when entering quotations under this option
2025-08-22 15:14:43 +00:00
Paul Reichert
f12177d01e feat: introduce Int range notation (#10045)
This PR implements the necessary typeclasses so that range notation
works for integers. For example, `((-2)...3).toList = [-2, -1, 0, 1, 2]
: List Int`.
2025-08-22 14:41:39 +00:00
Sebastian Ullrich
68654c231b doc: fix examples link 2025-08-22 16:28:01 +02:00
Lean stage0 autoupdater
2adc21f28b chore: update stage0 2025-08-22 14:07:57 +00:00
Sebastian Ullrich
0528696bbe fix: deriving DecidableEq under the module system (#10030)
Fixes #9839
2025-08-22 13:18:31 +00:00
Sebastian Ullrich
51bba5338a perf: make macro scope numbering less dependent on surrounding context (#10027)
This PR changes macro scope numbering from per-module to per-command,
ensuring that unrelated changes to other commands do not affect macro
scopes generated by a command, which improves `prefer_native` hit rates
on bootstrapping as well as avoids further rebuilds under the module
system.

In detail, instead of always using the current module name as a macro
scope prefix, each command now introduces a new macro scope prefix
(called "context") of the shape `<main module>._hygCtx_<uniq>` where
`uniq` is a `UInt32` derived from the command but automatically
incremented in case of conflicts (which must be local to the current
module). In the current implementation, `uniq` is the hash of the
declaration name, if any, or else the hash of the full command's syntax.
Thus, it is always independent of syntactic changes to other commands
(except in case of hash conflicts, which should only happen in practice
for syntactically identical commands) and, in the case of declarations,
also independent of syntactic changes to any private parts of the
declaration.
2025-08-22 13:16:02 +00:00
Sebastian Ullrich
561a4510b3 fix: auto params on private structure fields (#10053) 2025-08-22 12:49:37 +00:00
Sebastian Ullrich
0e8838df3b chore: avoid confusing public import all combination (#10051) 2025-08-22 12:04:42 +00:00
Kim Morrison
385daa99a8 chore: cleanup in Data/Rat (#10050)
This PR fixes some naming issues in Data/Rat/Lemmas, and upstreams the
eliminator `numDenCasesOn` and its relatives.
2025-08-22 12:00:19 +00:00
Kim Morrison
7595bc0791 feat: theorems about Nat/Int/Rat needed for dyadics (#10049)
This PR adds some background material needed for introducing the dyadic
rationals in #9993.
2025-08-22 11:45:16 +00:00
Rob23oba
9c6b698227 perf: fast version of Nat.log2 (#10046)
This PR replaces the implementation of `Nat.log2` with a version that
reduces faster.
The new version can handle:
```lean-4
example : Nat.log2 (1 <<< 500) = 500 := rfl
```
2025-08-22 11:32:00 +00:00
Henrik Böving
962ba9649c perf: try to reduce amount of code generated by HashMaps (#9941)
Co-authored-by: Sebastian Ullrich <sebasti@nullri.ch>
2025-08-22 11:24:11 +00:00
Sebastian Ullrich
8c8a6021af perf: use constant macro scope in elabArrow (#10048)
Shrinks .olean size by avoiding variation in this very frequent but
mostly unused name
2025-08-22 11:16:05 +00:00
Lean stage0 autoupdater
584ed5f33e chore: update stage0 2025-08-22 08:30:43 +00:00
Mac Malone
1523ed1cdb chore: allow module in LakeMain (#10039)
This PR enables core's `LakeMain` to be a `module` when core is built
without `USE_LAKE`.

This was a problem when porting Lake to the module system (#9749).
2025-08-22 07:45:42 +00:00
Jason Yuen
facc356a0a chore: fix spelling errors (#10042)
Typos were found with
```
pip install codespell --upgrade
codespell --summary --ignore-words-list enew,forin,fro,happend,hge,ihs,iterm,spred --skip stage0 --check-filenames
codespell --summary --ignore-words-list enew,forin,fro,happend,hge,ihs,iterm,spred --skip stage0 --check-filenames --regex '[A-Z][a-z]*'
codespell --summary --ignore-words-list enew,forin,fro,happend,hge,ihs,iterm,spred --skip stage0 --check-filenames --regex "\b[a-z']*"
```
2025-08-22 07:23:12 +00:00
Paul Reichert
c4b3f303bb feat: more convenient creation of polymorphic range instances (#10005)
This PR shortens the work necessary to make a type compatible with the
polymorphic range notation. In the concrete case of `Nat`, it reduces
the required lines of code from 150 to 70.
2025-08-22 07:08:33 +00:00
Paul Reichert
1448493489 feat: improvements to Min/Max-related classes (#10024)
This PR adds useful declarations to the `LawfulOrderMin/Max` and
`LawfulOrderLeftLeaningMin/Max` API. In particular, it introduces
`.leftLeaningOfLE` factories for `Min` and `Max`. It also renames
`LawfulOrderMin/Max.of_le` to .of_le_min_iff` and `.of_max_le_iff` and
introduces a second variant with different arguments.
2025-08-22 07:08:00 +00:00
Cameron Zwarich
f7a251b75f chore: set experimental.module=true when running grind benchmarks (#10041) 2025-08-22 03:15:36 +00:00
Cameron Zwarich
5aa706435a fix: incorporate info from _redArg decls for noncomputable check (#10040)
This PR changes the `toMono` pass to replace decls with their `_redArg`
equivalent, which has the consequence of not considering arguments
deemed useless by the `reduceArity` pass for the purposes of the
`noncomputable` check.
2025-08-22 01:02:40 +00:00
Leonardo de Moura
a581433d8b fix: grind error messages (#10038)
This PR ensures `grind` error messages use `{.ofConstName declName}`
when referencing declaration names.
2025-08-21 23:28:26 +00:00
Leonardo de Moura
6683d1eb91 chore: add module keyword to grind tests (#10036)
This PR also fixes missing `@[expose]` in grind support definitions.
2025-08-21 22:02:08 +00:00
Cameron Zwarich
504d71f268 chore: remove unnecessary code handling cases of an erased value (#10035)
This is now all handled in `toLCNF`.
2025-08-21 21:55:10 +00:00
2577 changed files with 24821 additions and 13238 deletions

View File

@@ -36,7 +36,7 @@ jobs:
include: ${{fromJson(inputs.config)}}
# complete all jobs
fail-fast: false
runs-on: ${{ matrix.os }}
runs-on: ${{ endsWith(matrix.os, '-with-cache') && fromJSON(format('["{0}", "nscloud-git-mirror-1gb"]', matrix.os)) || matrix.os }}
defaults:
run:
shell: ${{ matrix.shell || 'nix develop -c bash -euxo pipefail {0}' }}
@@ -69,10 +69,16 @@ jobs:
brew install ccache tree zstd coreutils gmp libuv
if: runner.os == 'macOS'
- name: Checkout
if: (!endsWith(matrix.os, '-with-cache'))
uses: actions/checkout@v4
with:
# the default is to use a virtual merge commit between the PR and master: just use the PR
ref: ${{ github.event.pull_request.head.sha }}
- name: Namespace Checkout
if: endsWith(matrix.os, '-with-cache')
uses: namespacelabs/nscloud-checkout-action@v7
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Open Nix shell once
run: true
if: runner.os == 'Linux'
@@ -169,7 +175,9 @@ jobs:
# 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'
# Caching on cancellation created some mysterious issues perhaps related to improper build
# shutdown
if: steps.restore-cache.outputs.cache-hit != 'true' && !cancelled()
uses: actions/cache/save@v4
with:
# NOTE: must be in sync with `restore` above
@@ -215,13 +223,13 @@ jobs:
path: pack/*
- name: Lean stats
run: |
build/stage1/bin/lean --stats src/Lean.lean -Dexperimental.module=true
build/$TARGET_STAGE/bin/lean --stats src/Lean.lean -Dexperimental.module=true
if: ${{ !matrix.cross }}
- name: Test
id: test
run: |
ulimit -c unlimited # coredumps
time ctest --preset ${{ matrix.CMAKE_PRESET || 'release' }} --test-dir build/$TARGET_STAGE -j$NPROC --output-junit test-results.xml
time ctest --preset ${{ matrix.CMAKE_PRESET || 'release' }} --test-dir build/$TARGET_STAGE -j$NPROC --output-junit test-results.xml ${{ matrix.CTEST_OPTIONS }}
if: (matrix.wasm || !matrix.cross) && (inputs.check-level >= 1 || matrix.test)
- name: Test Summary
uses: test-summary/action@v2

View File

@@ -182,12 +182,10 @@ jobs:
"binary-check": "ldd -v",
// foreign code may be linked against more recent glibc
"CTEST_OPTIONS": "-E 'foreign'",
// not compatible with `prepare-llvm` currently
"CMAKE_OPTIONS": "-DUSE_LAKE=OFF",
},
{
"name": "Linux Lake",
"os": large ? "nscloud-ubuntu-22.04-amd64-8x16" : "ubuntu-latest",
"os": large ? "nscloud-ubuntu-22.04-amd64-8x16-with-cache" : "ubuntu-latest",
"check-level": 0,
"test": true,
"check-rebootstrap": level >= 1,
@@ -202,8 +200,6 @@ jobs:
"os": "ubuntu-latest",
"check-level": 2,
"CMAKE_PRESET": "reldebug",
// exclude seriously slow/stackoverflowing tests
"CTEST_OPTIONS": "-E 'interactivetest|leanpkgtest|laketest|benchtest|bv_bitblast_stress|3807'"
},
// TODO: suddenly started failing in CI
/*{
@@ -225,8 +221,7 @@ jobs:
"prepare-llvm": "../script/prepare-llvm-macos.sh lean-llvm*",
"binary-check": "otool -L",
"tar": "gtar", // https://github.com/actions/runner-images/issues/2619
// not compatible with `prepare-llvm` currently
"CMAKE_OPTIONS": "-DUSE_LAKE=OFF",
"CTEST_OPTIONS": "-E 'leanlaketest_hello'", // started failing from unpack
},
{
"name": "macOS aarch64",
@@ -242,8 +237,6 @@ 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",
@@ -252,13 +245,9 @@ jobs:
"check-level": 2,
"shell": "msys2 {0}",
"CMAKE_OPTIONS": "-G \"Unix Makefiles\"",
// for reasons unknown, interactivetests are flaky on Windows
"CTEST_OPTIONS": "--repeat until-pass:2",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/19.1.2/lean-llvm-x86_64-w64-windows-gnu.tar.zst",
"prepare-llvm": "../script/prepare-llvm-mingw.sh lean-llvm*",
"binary-check": "ldd",
// not compatible with `prepare-llvm` currently
"CMAKE_OPTIONS": "-DUSE_LAKE=OFF",
},
{
"name": "Linux aarch64",
@@ -269,8 +258,6 @@ jobs:
"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*",
// 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
//{
@@ -299,6 +286,12 @@ jobs:
// "CTEST_OPTIONS": "-R \"leantest_1007\\.lean|leantest_Format\\.lean|leanruntest\\_1037.lean|leanruntest_ac_rfl\\.lean|leanruntest_tempfile.lean\\.|leanruntest_libuv\\.lean\""
// }
];
for (const job of matrix) {
if (job["prepare-llvm"]) {
// `USE_LAKE` is not compatible with `prepare-llvm` currently
job["CMAKE_OPTIONS"] = (job["CMAKE_OPTIONS"] ? job["CMAKE_OPTIONS"] + " " : "") + "-DUSE_LAKE=OFF";
}
}
console.log(`matrix:\n${JSON.stringify(matrix, null, 2)}`);
matrix = matrix.filter((job) => level >= job["check-level"]);
core.setOutput('matrix', matrix.filter((job) => !job["secondary"]));

View File

@@ -19,6 +19,8 @@ concurrency:
jobs:
update-stage0:
runs-on: nscloud-ubuntu-22.04-amd64-8x16
env:
CCACHE_DIR: ${{ github.workspace }}/.ccache
steps:
# This action should push to an otherwise protected branch, so it
# uses a deploy key with write permissions, as suggested at

View File

@@ -9,7 +9,7 @@ This is the repository for **Lean 4**.
- [Documentation Overview](https://lean-lang.org/documentation/)
- [Language Reference](https://lean-lang.org/doc/reference/latest/)
- [Release notes](RELEASES.md) starting at v4.0.0-m3
- [Examples](https://lean-lang.org/documentation/examples/)
- [Examples](https://lean-lang.org/lean4/doc/examples.html)
- [External Contribution Guidelines](CONTRIBUTING.md)
# Installation

View File

@@ -75,7 +75,7 @@ The github repository will automatically update stage0 on `master` once
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.
The same is true for further stages except that a rebuild of them is retriggered on any committed change, not just to a specific directory.
Thus when debugging e.g. stage 2 failures, you can resume the build from these failures on but may want to explicitly call `clean-stdlib` to either observe changes from `.olean` files of modules that built successfully or to check that you did not break modules that built successfully at some prior point.
If you have write access to the lean4 repository, you can also manually

View File

@@ -8,7 +8,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.
You can use any of the [supported editors](https://lean-lang.org/install/manual/) for editing the Lean source code.
Please see below for specific instructions for VS Code.
### Dev setup using elan

View File

@@ -1,6 +1,6 @@
These are instructions to set up a working development environment for those who wish to make changes to Lean itself. It is part of the [Development Guide](../dev/index.md).
We strongly suggest that new users instead follow the [Quickstart](../quickstart.md) to get started using Lean, since this sets up an environment that can automatically manage multiple Lean toolchain versions, which is necessary when working within the Lean ecosystem.
We strongly suggest that new users instead follow the [Installation Instructions](https://lean-lang.org/install/) to get started using Lean, since this sets up an environment that can automatically manage multiple Lean toolchain versions, which is necessary when working within the Lean ecosystem.
Requirements
------------

View File

@@ -97,5 +97,36 @@ macro "#analyzeEMatchTheorems" : command => `(
#analyzeEMatchTheorems
-- -- We can analyze specific theorems using commands such as
set_option trace.grind.ematch.instance true in
run_meta analyzeEMatchTheorem ``List.filterMap_some {}
set_option trace.grind.ematch.instance true
-- 1. grind immediately sees `(#[] : Array α) = ([] : List α).toArray` but probably this should be hidden.
-- 2. `Vector.toArray_empty` keys on `Array.mk []` rather than `#v[].toArray`
-- I guess we could add `(#[].extract _ _).extract _ _` as a stop pattern.
run_meta analyzeEMatchTheorem ``Array.extract_empty {}
-- Neither `Option.bind_some` nor `Option.bind_fun_some` fire, because the terms appear inside
-- lambdas. So we get crazy things like:
-- `fun x => ((some x).bind some).bind fun x => (some x).bind fun x => (some x).bind some`
-- We could consider replacing `filterMap_some` with
-- `filterMap g (filterMap f xs) = filterMap (f >=> g) xs`
-- to avoid the lambda that `grind` struggles with, but this would require more API around the fish.
run_meta analyzeEMatchTheorem ``Array.filterMap_some {}
-- Not entirely certain what is wrong here, but certainly
-- `eq_empty_of_append_eq_empty` is firing too often.
-- Ideally we could instantiate this is we fine `xs ++ ys` in the same equivalence class,
-- note just as soon as we see `xs ++ ys`.
-- I've tried removing this in https://github.com/leanprover/lean4/pull/10162
run_meta analyzeEMatchTheorem ``Array.range'_succ {}
-- Perhaps the same story here.
run_meta analyzeEMatchTheorem ``Array.range_succ {}
-- `zip_map_left` and `zip_map_right` are bad grind lemmas,
-- checking if they can be removed in https://github.com/leanprover/lean4/pull/10163
run_meta analyzeEMatchTheorem ``Array.zip_map {}
-- It seems crazy to me that as soon as we have `0 >>> n = 0`, we instantiate based on the
-- pattern `0 >>> n >>> m` by substituting `0` into `0 >>> n` to produce the `0 >>> n >>> n`.
-- I don't think any forbidden subterms can help us here. I don't know what to do. :-(
run_meta analyzeEMatchTheorem ``Int.zero_shiftRight {}

View File

@@ -469,6 +469,7 @@ elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
string(APPEND CMAKE_CXX_FLAGS " -ftls-model=initial-exec")
string(APPEND INIT_SHARED_LINKER_FLAGS " -install_name @rpath/libInit_shared.dylib")
string(APPEND LEANSHARED_1_LINKER_FLAGS " -install_name @rpath/libleanshared_1.dylib")
string(APPEND LEANSHARED_2_LINKER_FLAGS " -install_name @rpath/libleanshared_2.dylib")
string(APPEND LEANSHARED_LINKER_FLAGS " -install_name @rpath/libleanshared.dylib")
string(APPEND LAKESHARED_LINKER_FLAGS " -Wl,-force_load,${CMAKE_BINARY_DIR}/lib/lean/libLake.a.export -install_name @rpath/libLake_shared.dylib")
string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,-rpath,@executable_path/../lib -Wl,-rpath,@executable_path/../lib/lean")
@@ -502,7 +503,7 @@ endif()
# are already loaded) and probably fail unless we set up LD_LIBRARY_PATH.
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
# import libraries created by the stdlib.make targets
string(APPEND LEANC_SHARED_LINKER_FLAGS " -lInit_shared -lleanshared_1 -lleanshared")
string(APPEND LEANC_SHARED_LINKER_FLAGS " -lInit_shared -lleanshared_2 -lleanshared_1 -lleanshared")
elseif("${CMAKE_SYSTEM_NAME}" MATCHES "Darwin")
# The second flag is necessary to even *load* dylibs without resolved symbols, as can happen
# if a Lake `extern_lib` depends on a symbols defined by the Lean library but is loaded even
@@ -589,7 +590,7 @@ endif()
add_subdirectory(initialize)
add_subdirectory(shell)
# to be included in `leanshared` but not the smaller `leanshared_1` (as it would pull
# to be included in `leanshared` but not the smaller `leanshared_*` (as it would pull
# in the world)
add_library(leaninitialize STATIC $<TARGET_OBJECTS:initialize>)
set_target_properties(leaninitialize PROPERTIES
@@ -714,6 +715,7 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
)
add_custom_target(leanshared ALL
DEPENDS Init_shared leancpp
COMMAND touch ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libleanshared_2${CMAKE_SHARED_LIBRARY_SUFFIX}
COMMAND touch ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libleanshared_1${CMAKE_SHARED_LIBRARY_SUFFIX}
COMMAND touch ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libleanshared${CMAKE_SHARED_LIBRARY_SUFFIX}
)
@@ -734,7 +736,7 @@ else()
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make leanshared
VERBATIM)
string(APPEND CMAKE_EXE_LINKER_FLAGS " -lInit_shared -lleanshared_1 -lleanshared")
string(APPEND CMAKE_EXE_LINKER_FLAGS " -lInit_shared -lleanshared_2 -lleanshared_1 -lleanshared")
endif()
if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")

View File

@@ -7,8 +7,10 @@ module
prelude
public import Init.Control.Lawful.Basic
public import all Init.Control.Except
public import all Init.Control.State
public import Init.Control.Except
import all Init.Control.Except
public import Init.Control.State
import all Init.Control.State
public import Init.Control.StateRef
public import Init.Ext

View File

@@ -6,12 +6,18 @@ Authors: Quang Dao, Paul Reichert
module
prelude
public import all Init.Control.Option
public import all Init.Control.Except
public import all Init.Control.ExceptCps
public import all Init.Control.StateRef
public import all Init.Control.StateCps
public import all Init.Control.Id
public import Init.Control.Option
import all Init.Control.Option
public import Init.Control.Except
import all Init.Control.Except
public import Init.Control.ExceptCps
import all Init.Control.ExceptCps
public import Init.Control.StateRef
import all Init.Control.StateRef
public import Init.Control.StateCps
import all Init.Control.StateCps
public import Init.Control.Id
import all Init.Control.Id
public import Init.Control.Lawful.MonadLift.Lemmas
public import Init.Control.Lawful.Instances

View File

@@ -290,13 +290,17 @@ macro "right" : conv => `(conv| rhs)
/-- `intro` traverses into binders. Synonym for `ext`. -/
macro "intro" xs:(ppSpace colGt binderIdent)* : conv => `(conv| ext $xs*)
syntax enterArg := binderIdent <|> argArg
syntax enterPattern := "in " (occs)? term
syntax enterArg := binderIdent <|> argArg <|> enterPattern
/-- `enter [arg, ...]` is a compact way to describe a path to a subterm.
It is a shorthand for other conv tactics as follows:
* `enter [i]` is equivalent to `arg i`.
* `enter [@i]` is equivalent to `arg @i`.
* `enter [x]` (where `x` is an identifier) is equivalent to `ext x`.
* `enter [in e]` (where `e` is a term) is equivalent to `pattern e`.
Occurrences can be specified with `enter [in (occs := ...) e]`.
For example, given the target `f (g a (fun x => x b))`, `enter [1, 2, x, 1]`
will traverse to the subterm `b`. -/
syntax (name := enter) "enter" " [" withoutPosition(enterArg,+) "]" : conv

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.OrdRoot
public import Init.Data.Ord
public import Init.Data.Random
public import Init.Data.ToString
public import Init.Data.Range
@@ -51,5 +51,6 @@ public import Init.Data.Range.Polymorphic
public import Init.Data.Slice
public import Init.Data.Order
public import Init.Data.Rat
public import Init.Data.Dyadic
public section

View File

@@ -9,7 +9,8 @@ prelude
public import Init.Data.Array.Mem
public import Init.Data.Array.Lemmas
public import Init.Data.Array.Count
public import all Init.Data.List.Attach
public import Init.Data.List.Attach
import all Init.Data.List.Attach
public section

View File

@@ -10,11 +10,11 @@ public import Init.WFTactics
public import Init.Data.Nat.Basic
public import Init.Data.Fin.Basic
public import Init.Data.UInt.BasicAux
public import Init.Data.Repr
public import Init.Data.ToString.Basic
public import Init.GetElem
public import all Init.Data.List.ToArrayImpl
public import all Init.Data.Array.Set
public import Init.Data.List.ToArrayImpl
import all Init.Data.List.ToArrayImpl
public import Init.Data.Array.Set
import all Init.Data.Array.Set
public section
@@ -162,7 +162,7 @@ This is a low-level version of `Array.size` that directly queries the runtime sy
representation of arrays. While this is not provable, `Array.usize` always returns the exact size of
the array since the implementation only supports arrays of size less than `USize.size`.
-/
@[extern "lean_array_size", simp]
@[extern "lean_array_size", simp, expose]
def usize (xs : @& Array α) : USize := xs.size.toUSize
/--

View File

@@ -6,7 +6,8 @@ Authors: Leonardo de Moura
module
prelude
public import all Init.Data.Array.Basic
public import Init.Data.Array.Basic
import all Init.Data.Array.Basic
public import Init.Data.Nat.Linear
public import Init.NotationExtra

View File

@@ -8,7 +8,8 @@ module
prelude
public import Init.Data.List.TakeDrop
public import all Init.Data.Array.Basic
public import Init.Data.Array.Basic
import all Init.Data.Array.Basic
public section

View File

@@ -6,7 +6,8 @@ Authors: Kim Morrison
module
prelude
public import all Init.Data.Array.Basic
public import Init.Data.Array.Basic
import all Init.Data.Array.Basic
public import Init.Data.Array.Lemmas
public import Init.Data.List.Nat.Count

View File

@@ -6,7 +6,8 @@ Authors: Leonardo de Moura
module
prelude
public import all Init.Data.Array.Basic
public import Init.Data.Array.Basic
import all Init.Data.Array.Basic
public import Init.Data.BEq
public import Init.Data.List.Nat.BEq
public import Init.ByCases

View File

@@ -6,7 +6,8 @@ Authors: Kim Morrison
module
prelude
public import all Init.Data.Array.Basic
public import Init.Data.Array.Basic
import all Init.Data.Array.Basic
public import Init.Data.Array.Lemmas
public import Init.Data.List.Nat.Erase
public import Init.Data.List.Nat.Basic

View File

@@ -7,7 +7,8 @@ module
prelude
public import Init.Data.List.Nat.Find
public import all Init.Data.Array.Basic
public import Init.Data.Array.Basic
import all Init.Data.Array.Basic
public import Init.Data.Array.Lemmas
public import Init.Data.Array.Attach
public import Init.Data.Array.Range
@@ -727,7 +728,7 @@ theorem isNone_findFinIdx? {xs : Array α} {p : α → Bool} :
cases xs
simp only [List.findFinIdx?_toArray, hf, List.findFinIdx?_subtype]
rw [findFinIdx?_congr List.unattach_toArray]
simp only [Option.map_map, Function.comp_def, Fin.cast_trans]
simp only [Option.map_map, Function.comp_def, Fin.cast_cast]
simp [Array.size]
/-! ### idxOf

View File

@@ -70,8 +70,8 @@ private theorem cons_lex_cons [BEq α] {lt : αα → Bool} {a b : α} {xs
rw [cons_lex_cons.forIn'_congr_aux Std.PRange.toList_eq_match rfl (fun _ _ _ => rfl)]
simp only [Std.PRange.SupportsUpperBound.IsSatisfied, bind_pure_comp, map_pure]
rw [cons_lex_cons.forIn'_congr_aux (if_pos (by omega)) rfl (fun _ _ _ => rfl)]
simp only [Std.PRange.toList_open_eq_toList_closed_of_isSome_succ? (lo := 0) (h := rfl),
Std.PRange.UpwardEnumerable.succ?, Nat.add_comm 1, Std.PRange.Nat.ClosedOpen.toList_succ_succ,
simp only [Std.PRange.toList_Rox_eq_toList_Rcx_of_isSome_succ? (lo := 0) (h := rfl),
Std.PRange.UpwardEnumerable.succ?, Nat.add_comm 1, Std.PRange.Nat.toList_Rco_succ_succ,
Option.get_some, List.forIn'_cons, List.size_toArray, List.length_cons, List.length_nil,
Nat.lt_add_one, getElem_append_left, List.getElem_toArray, List.getElem_cons_zero]
cases lt a b

View File

@@ -6,11 +6,13 @@ Authors: Mario Carneiro, Kim Morrison
module
prelude
public import all Init.Data.Array.Basic
public import Init.Data.Array.Basic
import all Init.Data.Array.Basic
public import Init.Data.Array.Lemmas
public import Init.Data.Array.Attach
public import Init.Data.Array.OfFn
public import all Init.Data.List.MapIdx
public import Init.Data.List.MapIdx
import all Init.Data.List.MapIdx
public section

View File

@@ -6,8 +6,10 @@ Authors: Kim Morrison
module
prelude
public import all Init.Data.List.Control
public import all Init.Data.Array.Basic
public import Init.Data.List.Control
import all Init.Data.List.Control
public import Init.Data.Array.Basic
import all Init.Data.Array.Basic
public import Init.Data.Array.Lemmas
public import Init.Data.Array.Attach
public import Init.Data.List.Monadic

View File

@@ -6,7 +6,8 @@ Authors: Kim Morrison
module
prelude
public import all Init.Data.Array.Basic
public import Init.Data.Array.Basic
import all Init.Data.Array.Basic
public import Init.Data.Array.Lemmas
public import Init.Data.Array.Monadic
public import Init.Data.List.OfFn

View File

@@ -7,7 +7,8 @@ module
prelude
public import Init.Data.List.Nat.Perm
public import all Init.Data.Array.Basic
public import Init.Data.Array.Basic
import all Init.Data.Array.Basic
public import Init.Data.Array.Lemmas
public section

View File

@@ -7,8 +7,10 @@ module
prelude
public import Init.Data.Array.Lemmas
public import all Init.Data.Array.Basic
public import all Init.Data.Array.OfFn
public import Init.Data.Array.Basic
import all Init.Data.Array.Basic
public import Init.Data.Array.OfFn
import all Init.Data.Array.OfFn
public import Init.Data.Array.MapIdx
public import Init.Data.Array.Zip
public import Init.Data.List.Nat.Range

View File

@@ -8,7 +8,8 @@ module
prelude
public import Init.Data.Array.Basic
public import all Init.Data.Array.Subarray
public import Init.Data.Array.Subarray
import all Init.Data.Array.Subarray
public import Init.Omega
public section

View File

@@ -6,7 +6,8 @@ Authors: Markus Himmel
module
prelude
public import all Init.Data.Array.Basic
public import Init.Data.Array.Basic
import all Init.Data.Array.Basic
public import Init.Data.Array.Lemmas
public import Init.Data.List.Nat.TakeDrop

View File

@@ -6,7 +6,8 @@ Authors: Kim Morrison
module
prelude
public import all Init.Data.Array.Basic
public import Init.Data.Array.Basic
import all Init.Data.Array.Basic
public import Init.Data.Array.TakeDrop
public import Init.Data.List.Zip
@@ -230,11 +231,9 @@ theorem zip_map {f : αγ} {g : β → δ} {as : Array α} {bs : Array β}
cases bs
simp [List.zip_map]
@[grind _=_]
theorem zip_map_left {f : α γ} {as : Array α} {bs : Array β} :
zip (as.map f) bs = (zip as bs).map (Prod.map f id) := by rw [ zip_map, map_id]
@[grind _=_]
theorem zip_map_right {f : β γ} {as : Array α} {bs : Array β} :
zip as (bs.map f) = (zip as bs).map (Prod.map id f) := by rw [ zip_map, map_id]

View File

@@ -9,7 +9,7 @@ prelude
public import Init.Data.Fin.Basic
public import Init.Data.Nat.Bitwise.Lemmas
public import Init.Data.Nat.Power2
public import Init.Data.Int.Bitwise
public import Init.Data.Int.Bitwise.Basic
public import Init.Data.BitVec.BasicAux
@[expose] public section

View File

@@ -6,11 +6,14 @@ Authors: Harun Khan, Abdalrhman M Mohamed, Joe Hendrix, Siddharth Bhat
module
prelude
public import all Init.Data.Nat.Bitwise.Basic
public import Init.Data.Nat.Bitwise.Basic
import all Init.Data.Nat.Bitwise.Basic
public import Init.Data.Nat.Mod
public import all Init.Data.Int.DivMod
public import Init.Data.Int.DivMod
import all Init.Data.Int.DivMod
public import Init.Data.Int.LemmasAux
public import all Init.Data.BitVec.Basic
public import Init.Data.BitVec.Basic
import all Init.Data.BitVec.Basic
public import Init.Data.BitVec.Decidable
public import Init.Data.BitVec.Lemmas
public import Init.Data.BitVec.Folds
@@ -2152,4 +2155,238 @@ theorem shiftLeft_add_eq_shiftLeft_or {x y : BitVec w} :
(y <<< x) + x = (y <<< x) ||| x := by
rw [BitVec.add_comm, add_shiftLeft_eq_or_shiftLeft, or_comm]
/- ### Fast Circuit For Unsigned Overflow Detection -/
/-!
# Note [Fast Unsigned Multiplication Overflow Detection]
The fast unsigned multiplication overflow detection circuit is described in
`Efficient integer multiplication overflow detection circuits` (https://ieeexplore.ieee.org/abstract/document/987767).
With this circuit, the computation of the overflow flag for the unsigned multiplication of
two bitvectors `x` and `y` with bitwidth `w` requires:
· extending the operands by `1` bit and performing the multiplication with the extended operands,
· computing the preliminary overflow flag, which describes whether `x` and `y` together have at most
`w - 2` leading zeros.
If the most significant bit of the extended operands' multiplication is `true` or if the
preliminary overflow flag is `true`, overflow happens.
In particular, the conditions check two different cases:
· if the most significant bit of the extended operands' multiplication is `true`, the result of the
multiplication 2 ^ w ≤ x.toNat * y.toNat < 2 ^ (w + 1),
· if the preliminary flag is true, then 2 ^ (w + 1) ≤ x.toNat * y.toNat.
The computation of the preliminary overflow flag `resRec` relies on two quantities:
· `uppcRec`: the unsigned parallel prefix circuit for the bits until a certain `i`,
· `aandRec`: the conjunction between the parallel prefix circuit at of the first operand until a certain `i`
and the `i`-th bit in the second operand.
-/
/--
`uppcRec` is the unsigned parallel prefix, `x.uppcRec s = true` iff `x.toNat` is greater or equal
than `2 ^ (w - 1 - (s - 1))`.
-/
def uppcRec {w} (x : BitVec w) (s : Nat) (hs : s < w) : Bool :=
match s with
| 0 => x.msb
| i + 1 => x[w - 1 - i] || uppcRec x i (by omega)
/-- The unsigned parallel prefix of `x` at `s` is `true` if and only if x interpreted
as a natural number is greater or equal than `2 ^ (w - 1 - (s - 1))`. -/
@[simp]
theorem uppcRec_true_iff (x : BitVec w) (s : Nat) (h : s < w) :
uppcRec x s h 2 ^ (w - 1 - (s - 1)) x.toNat := by
rcases w with _|w
· omega
· induction s
· case succ.zero =>
simp only [uppcRec, msb_eq_true_iff_two_mul_ge, Nat.pow_add, Nat.pow_one,
Nat.mul_comm (2 ^ w) 2, ge_iff_le, Nat.add_one_sub_one, zero_le, Nat.sub_eq_zero_of_le,
Nat.sub_zero]
apply Nat.mul_le_mul_left_iff (by omega)
· case succ.succ s ihs =>
simp only [uppcRec, or_eq_true, ihs, Nat.add_one_sub_one]
have := Nat.pow_le_pow_of_le (a := 2) ( n := (w - s)) (m := (w - (s - 1))) (by omega) (by omega)
constructor
· intro h'
rcases h' with h'|h'
· apply ge_two_pow_of_testBit h'
· omega
· intro h'
by_cases hbit: x[w - s]
· simp [hbit]
· have := BitVec.le_toNat_iff_getLsbD_eq_true (x := x) (i := w - s) (by omega)
simp only [h', true_iff] at this
obtain k, hk := this
by_cases hwk : w - s + k < w + 1
· by_cases hk' : 0 < k
· have hle := ge_two_pow_of_testBit hk
have hpowle := Nat.pow_le_pow_of_le (a := 2) ( n := (w - (s - 1))) (m := (w - s + k)) (by omega) (by omega)
omega
· rw [getLsbD_eq_getElem (by omega)] at hk
simp [hbit, show k = 0 by omega] at hk
· simp_all
/--
Conjunction for fast umulOverflow circuit
-/
def aandRec (x y : BitVec w) (s : Nat) (hs : s < w) : Bool :=
y[s] && uppcRec x s (by omega)
/--
Preliminary overflow flag for fast umulOverflow circuit as introduced in
`Efficient integer multiplication overflow detection circuits` (https://ieeexplore.ieee.org/abstract/document/987767).
-/
def resRec (x y : BitVec w) (s : Nat) (hs : s < w) (hslt : 0 < s) : Bool :=
match hs0 : s with
| 0 => by omega
| s' + 1 =>
match hs' : s' with
| 0 => aandRec x y 1 (by omega)
| s'' + 1 =>
(resRec x y s' (by omega) (by omega)) || (aandRec x y s (by omega))
/-- The preliminary overflow flag is true for a certain `s` if and only if the conjunction returns true at
any `k` smaller than or equal to `s`. -/
theorem resRec_true_iff (x y : BitVec w) (s : Nat) (hs : s < w) (hs' : 0 < s) :
resRec x y s hs hs' = true (k : Nat), (h : k s), (_ : 0 < k), aandRec x y k (by omega) := by
unfold resRec
rcases s with _|s
· omega
· rcases s
· case zero =>
constructor
· intro ha
exists 1, by omega, by omega
· intro hr
obtain k, hk, hk', hk'' := hr
simp only [show k = 1 by omega] at hk''
exact hk''
· case succ s =>
induction s
· case zero =>
unfold resRec
simp only [Nat.zero_add, Nat.reduceAdd, or_eq_true]
constructor
· intro h
rcases h with h|h
· exists 1, by omega, by omega
· exists 2, by omega, by omega
· intro h
obtain k, hk, hk', hk'' := h
have h : k = 1 k = 2 := by omega
rcases h with h|h
<;> simp only [h] at hk''
<;> simp [hk'']
· case succ s ihs =>
specialize ihs (by omega) (by omega)
unfold resRec
simp only [or_eq_true, ihs]
constructor
· intro h
rcases h with h|h
· obtain k, hk, hk', hk'' := h
exists k, by omega, by omega
· exists s + 1 + 1 + 1, by omega, by omega
· intro h
obtain k, hk, hk', hk'' := h
by_cases h' : x.aandRec y (s + 1 + 1 + 1) (by omega) = true
· simp [h']
· simp only [h', false_eq_true, _root_.or_false]
by_cases h'' : k s + 1 + 1
· exists k, h'', by omega
· have : k = s + 1 + 1 + 1 := by omega
simp_all
/-- If the sum of the leading zeroes of two bitvecs with bitwidth `w` is less than or equal to
(`w - 2`), then the preliminary overflow flag is true and their unsigned multiplication overflows.
The explanation is in `Efficient integer multiplication overflow detection circuits`
https://ieeexplore.ieee.org/abstract/document/987767
-/
theorem resRec_of_clz_le {x y : BitVec w} (hw : 1 < w) (hx : x 0#w) (hy : y 0#w):
(clz x).toNat + (clz y).toNat w - 2 resRec x y (w - 1) (by omega) (by omega) := by
intro h
rw [resRec_true_iff]
exists (w - 1 - y.clz.toNat), by omega, by omega
simp only [aandRec]
by_cases hw0 : w - 1 - y.clz.toNat = 0
· have := clz_lt_iff_ne_zero.mpr (by omega)
omega
· simp only [and_eq_true, getLsbD_true_clz_of_ne_zero (x := y) (by omega) (by omega),
getElem_of_getLsbD_eq_true, uppcRec_true_iff,
show w - 1 - (w - 1 - y.clz.toNat - 1) = y.clz.toNat + 1 by omega, _root_.true_and]
exact Nat.le_trans (Nat.pow_le_pow_of_le (a := 2) (n := y.clz.toNat + 1)
(m := w - 1 - x.clz.toNat) (by omega) (by omega))
(BitVec.two_pow_sub_clz_le_toNat_of_ne_zero (x := x) (by omega) (by omega))
/--
Complete fast overflow detection circuit for unsigned multiplication.
-/
theorem fastUmulOverflow (x y : BitVec w) :
umulOverflow x y = if hw : w 1 then false
else (setWidth (w + 1) x * setWidth (w + 1) y)[w] || x.resRec y (w - 1) (by omega) (by omega) := by
rcases w with _|_|w
· simp [of_length_zero, umulOverflow]
· have hx : x.toNat 1 := by omega
have hy : y.toNat 1 := by omega
have := Nat.mul_le_mul (n₁ := x.toNat) (m₁ := y.toNat) (n₂ := 1) (m₂ := 1) hx hy
simp [umulOverflow]
omega
· by_cases h : umulOverflow x y
· simp only [h, Nat.reduceLeDiff, reduceDIte, Nat.add_one_sub_one, true_eq, or_eq_true]
simp only [umulOverflow, ge_iff_le, decide_eq_true_eq] at h
by_cases h' : x.toNat * y.toNat < 2 ^ (w + 1 + 1 + 1)
· have hlt := BitVec.getElem_eq_true_of_lt_of_le
(x := (setWidth (w + 1 + 1 + 1) x * setWidth (w + 1 + 1 + 1) y))
(k := w + 1 + 1) (by omega)
simp only [toNat_mul, toNat_setWidth, Nat.lt_add_one, toNat_mod_cancel_of_lt,
Nat.mod_eq_of_lt (a := x.toNat * y.toNat) (b := 2 ^ (w + 1 + 1 + 1)) (by omega), h', h,
forall_const] at hlt
simp [hlt]
· by_cases hsw : (setWidth (w + 1 + 1 + 1) x * setWidth (w + 1 + 1 + 1) y)[w + 1 + 1] = true
· simp [hsw]
· simp only [hsw, false_eq_true, _root_.false_or]
have := Nat.two_pow_pos (w := w + 1 + 1)
have hltx := BitVec.toNat_lt_two_pow_sub_clz (x := x)
have hlty := BitVec.toNat_lt_two_pow_sub_clz (x := y)
have := Nat.mul_ne_zero_iff (m := y.toNat) (n := x.toNat)
simp only [ne_eq, show ¬x.toNat * y.toNat = 0 by omega, not_false_eq_true,
true_iff] at this
obtain hxz,hyz := this
apply resRec_of_clz_le (x := x) (y := y) (by omega) (by simp [toNat_eq]; exact hxz) (by simp [toNat_eq]; exact hyz)
by_cases hzxy : x.clz.toNat + y.clz.toNat w
· omega
· by_cases heq : w + 1 - y.clz.toNat = 0
· by_cases heq' : w + 1 + 1 - y.clz.toNat = 0
· simp [heq', hyz] at hlty
· simp only [show y.clz.toNat = w + 1 by omega, Nat.add_sub_cancel_left,
Nat.pow_one] at hlty
simp only [show y.toNat = 1 by omega, Nat.mul_one, Nat.not_lt] at h'
omega
· by_cases w + 1 < y.clz.toNat
· omega
· simp only [Nat.not_lt] at h'
have := Nat.mul_lt_mul'' (a := x.toNat) (b := y.toNat) (c := 2 ^ (w + 1 + 1 - x.clz.toNat)) (d := 2 ^ (w + 1 + 1 - y.clz.toNat)) hltx hlty
simp only [ Nat.pow_add] at this
have := Nat.pow_le_pow_of_le (a := 2) (n := w + 1 + 1 - x.clz.toNat + (w + 1 + 1 - y.clz.toNat)) (m := w + 1 + 1 + 1)
(by omega) (by omega)
omega
· simp only [h, Nat.reduceLeDiff, reduceDIte, Nat.add_one_sub_one, false_eq, or_eq_false_iff]
simp only [umulOverflow, ge_iff_le, decide_eq_true_eq, Nat.not_le] at h
and_intros
· simp only [ getLsbD_eq_getElem, getLsbD_eq_getMsbD, Nat.lt_add_one, decide_true,
Nat.add_one_sub_one, Nat.sub_self, msb_eq_getMsbD_zero, Bool.true_and,
msb_eq_false_iff_two_mul_lt, toNat_mul, toNat_setWidth, toNat_mod_cancel_of_lt]
rw [Nat.mod_eq_of_lt (by omega),Nat.pow_add (m := w + 1 + 1) (n := 1)]
simp [Nat.mul_comm 2 (x.toNat * y.toNat), h]
· apply Classical.byContradiction
intro hcontra
simp only [not_eq_false, resRec_true_iff, exists_prop, exists_and_left] at hcontra
obtain k,hk,hk',hk'' := hcontra
simp only [aandRec, and_eq_true, uppcRec_true_iff, Nat.add_one_sub_one] at hk''
obtain hky, hkx := hk''
have hyle := two_pow_le_toNat_of_getElem_eq_true (x := y) (i := k) (by omega) hky
have := Nat.mul_le_mul (n₁ := 2 ^ (w + 1 - (k - 1))) (m₁ := 2 ^ k) (n₂ := x.toNat) (m₂ := y.toNat) hkx hyle
simp [ Nat.pow_add, show w + 1 - (k - 1) + k = w + 1 + 1 by omega] at this
omega
end BitVec

View File

@@ -6,7 +6,9 @@ Authors: Joe Hendrix, Harun Khan, Alex Keizer, Abdalrhman M Mohamed, Siddharth B
module
prelude
public import all Init.Data.BitVec.Basic
public import Init.Data.BitVec.Basic
import all Init.Data.BitVec.Basic
import Init.Data.Int.Bitwise.Lemmas
public section

View File

@@ -6,7 +6,8 @@ Authors: Joe Hendrix, Harun Khan
module
prelude
public import all Init.Data.BitVec.Basic
public import Init.Data.BitVec.Basic
import all Init.Data.BitVec.Basic
public import Init.Data.BitVec.Lemmas
public import Init.Data.Nat.Lemmas
public import Init.Data.Fin.Iterate

View File

@@ -7,8 +7,10 @@ module
prelude
public import Init.Data.Bool
public import all Init.Data.BitVec.Basic
public import all Init.Data.BitVec.BasicAux
public import Init.Data.BitVec.Basic
import all Init.Data.BitVec.Basic
public import Init.Data.BitVec.BasicAux
import all Init.Data.BitVec.BasicAux
public import Init.Data.Fin.Lemmas
public import Init.Data.Nat.Lemmas
public import Init.Data.Nat.Div.Lemmas
@@ -508,6 +510,18 @@ theorem getElem_ofBool {b : Bool} {h : i < 1}: (ofBool b)[i] = b := by
@[simp] theorem zero_eq_one_iff (w : Nat) : (0#w = 1#w) (w = 0) := by
rw [ one_eq_zero_iff, eq_comm]
/-- A bitvector is equal to 0#w if and only if all bits are `false` -/
theorem zero_iff_eq_false {x: BitVec w} :
x = 0#w i, x.getLsbD i = false := by
rcases w with _|w
· simp [of_length_zero]
· constructor
· intro hzero
simp [hzero]
· intro hfalse
ext j hj
simp [ getLsbD_eq_getElem, hfalse]
/-! ### msb -/
@[simp] theorem msb_zero : (0#w).msb = false := by simp [BitVec.msb, getMsbD]
@@ -2192,8 +2206,7 @@ theorem sshiftRight_eq_of_msb_false {x : BitVec w} {s : Nat} (h : x.msb = false)
apply BitVec.eq_of_toNat_eq
rw [BitVec.sshiftRight_eq, BitVec.toInt_eq_toNat_cond]
have hxbound : 2 * x.toNat < 2 ^ w := BitVec.msb_eq_false_iff_two_mul_lt.mp h
simp only [hxbound, reduceIte, Int.natCast_shiftRight, ofInt_natCast,
toNat_ofNat, toNat_ushiftRight]
simp only [hxbound, reduceIte, toNat_ushiftRight]
replace hxbound : x.toNat >>> s < 2 ^ w := by
rw [Nat.shiftRight_eq_div_pow]
exact Nat.lt_of_le_of_lt (Nat.div_le_self ..) x.isLt
@@ -5766,40 +5779,6 @@ theorem msb_replicate {n w : Nat} {x : BitVec w} :
simp only [BitVec.msb, getMsbD_replicate, Nat.zero_mod]
cases n <;> cases w <;> simp
/-! ### Count leading zeros -/
theorem clzAuxRec_zero (x : BitVec w) :
x.clzAuxRec 0 = if x.getLsbD 0 then BitVec.ofNat w (w - 1) else BitVec.ofNat w w := by rfl
theorem clzAuxRec_succ (x : BitVec w) :
x.clzAuxRec (n + 1) = if x.getLsbD (n + 1) then BitVec.ofNat w (w - 1 - (n + 1)) else BitVec.clzAuxRec x n := by rfl
theorem clzAuxRec_eq_clzAuxRec_of_le (x : BitVec w) (h : w - 1 n) :
x.clzAuxRec n = x.clzAuxRec (w - 1) := by
let k := n - (w - 1)
rw [show n = (w - 1) + k by omega]
induction k
case zero => simp
case succ k ihk =>
simp [show w - 1 + (k + 1) = (w - 1 + k) + 1 by omega, clzAuxRec_succ, ihk,
show x.getLsbD (w - 1 + k + 1) = false by simp only [show w w - 1 + k + 1 by omega, getLsbD_of_ge]]
theorem clzAuxRec_eq_clzAuxRec_of_getLsbD_false {x : BitVec w} (h : i, n < i x.getLsbD i = false) :
x.clzAuxRec n = x.clzAuxRec (n + k) := by
induction k
case zero => simp
case succ k ihk =>
simp only [show n + (k + 1) = (n + k) + 1 by omega, clzAuxRec_succ]
by_cases hxn : x.getLsbD (n + k + 1)
· have : ¬ (i : Nat), n < i x.getLsbD i = false := by
simp only [Classical.not_forall, Bool.not_eq_false]
exists n + k + 1
simp [show n < n + k + 1 by omega, hxn]
contradiction
· simp only [hxn, Bool.false_eq_true, reduceIte]
exact ihk
/-! ### Inequalities (le / lt) -/
theorem ule_eq_not_ult (x y : BitVec w) : x.ule y = !y.ult x := by
@@ -5848,6 +5827,362 @@ theorem sle_eq_ule {x y : BitVec w} : x.sle y = (x.msb != y.msb ^^ x.ule y) := b
theorem sle_eq_ule_of_msb_eq {x y : BitVec w} (h : x.msb = y.msb) : x.sle y = x.ule y := by
simp [BitVec.sle_eq_ule, h]
/-- A bitvector interpreted as a natural number is greater than or equal to `2 ^ i` if and only if
there exists at least one bit with `true` value at position `i` or higher. -/
theorem le_toNat_iff_getLsbD_eq_true {x : BitVec w} (hi : i < w ) :
(2 ^ i x.toNat) ( k, x.getLsbD (i + k) = true) := by
rcases w with _|w
· simp [of_length_zero]
· constructor
· intro hle
apply Classical.byContradiction
intros hcontra
let x' := setWidth (i + 1) x
have hx' : setWidth (i + 1) x = x' := by rfl
have hcast : w - i + (i + 1) = w + 1 := by omega
simp only [not_exists, Bool.not_eq_true] at hcontra
have hx'' : x = BitVec.cast hcast (0#(w - i) ++ x') := by
ext j
by_cases hj : j < i + 1
· simp only [ hx', getElem_cast, getElem_append, hj, reduceDIte, getElem_setWidth]
rw [getLsbD_eq_getElem]
· simp only [getElem_cast, getElem_append, hj, reduceDIte, getElem_zero]
let j' := j - i
simp only [show j = i + j' by omega]
apply hcontra
have : x'.toNat < 2 ^ i := by
apply Nat.lt_pow_two_of_testBit (n := i) x'.toNat
intro j hj
let j' := j - i
specialize hcontra j'
have : x'.getLsbD (i + j') = x.getLsbD (i + j') := by
subst x'
simp [hcontra]
simp [show j = i + j' by omega, testBit_toNat, this, hcontra]
have : x'.toNat = x.toNat := by
have := BitVec.setWidth_eq_append (w := (w + 1)) (v := i + 1) (x := x')
specialize this (by omega)
rw [toNat_eq, toNat_setWidth, Nat.mod_eq_of_lt (by omega)] at this
simp [hx'']
omega
· intro h
obtain k, hk := h
by_cases hk' : i + k < w + 1
· have := Nat.ge_two_pow_of_testBit hk
have := Nat.pow_le_pow_of_le (a := 2) (n := i) (m := i + k) (by omega) (by omega)
omega
· simp [show w + 1 i + k by omega] at hk
/-- A bitvector interpreted as a natural number is strictly smaller than `2 ^ i` if and only if
all bits at position `i` or higher are false. -/
theorem toNat_lt_iff_getLsbD_eq_false {x : BitVec w} (i : Nat) (hi : i < w) :
x.toNat < 2 ^ i ( k, x.getLsbD (i + k) = false) := by
constructor
· intro h
apply Classical.byContradiction
intro hcontra
simp only [Classical.not_forall, Bool.not_eq_false] at hcontra
obtain k, hk := hcontra
have hle := Nat.ge_two_pow_of_testBit hk
by_cases hlt : i + k < w
· have := Nat.pow_le_pow_of_le (a := 2) (n := i) (m := i + k) (by omega) (by omega)
omega
· simp [show w i + k by omega] at hk
· intro h
apply Classical.byContradiction
intro hcontra
simp [BitVec.le_toNat_iff_getLsbD_eq_true (x := x) (i := i) hi, h] at hcontra
/-- If a bitvector interpreted as a natural number is strictly smaller than `2 ^ (k + 1)` and greater than or
equal to 2 ^ k, then the bit at position `k` must be `true` -/
theorem getElem_eq_true_of_lt_of_le {x : BitVec w} (hk' : k < w) (hlt: x.toNat < 2 ^ (k + 1)) (hle : 2 ^ k x.toNat) :
x[k] = true := by
have := le_toNat_iff_getLsbD_eq_true (x := x) (i := k) hk'
simp only [hle, true_iff] at this
obtain k',hk' := this
by_cases hkk' : k + k' < w
· have := Nat.ge_two_pow_of_testBit hk'
by_cases hzk' : k' = 0
· simp [hzk'] at hk'; exact hk'
· have := Nat.pow_lt_pow_of_lt (a := 2) (n := k) (m := k + k') (by omega) (by omega)
have := Nat.pow_le_pow_of_le (a := 2) (n := k + 1) (m := k + k') (by omega) (by omega)
omega
· simp [show w k + k' by omega] at hk'
/-! ### Count leading zeros -/
theorem clzAuxRec_zero (x : BitVec w) :
x.clzAuxRec 0 = if x.getLsbD 0 then BitVec.ofNat w (w - 1) else BitVec.ofNat w w := by rfl
theorem clzAuxRec_succ (x : BitVec w) :
x.clzAuxRec (n + 1) = if x.getLsbD (n + 1) then BitVec.ofNat w (w - 1 - (n + 1)) else BitVec.clzAuxRec x n := by rfl
theorem clzAuxRec_eq_clzAuxRec_of_le {x : BitVec w} (h : w - 1 n) :
x.clzAuxRec n = x.clzAuxRec (w - 1) := by
let k := n - (w - 1)
rw [show n = (w - 1) + k by omega]
induction k
· case zero => simp
· case succ k ihk =>
simp [show w - 1 + (k + 1) = (w - 1 + k) + 1 by omega, clzAuxRec_succ, ihk,
show x.getLsbD (w - 1 + k + 1) = false by simp only [show w w - 1 + k + 1 by omega, getLsbD_of_ge]]
theorem clzAuxRec_eq_clzAuxRec_of_getLsbD_false {x : BitVec w} (h : i, n < i x.getLsbD i = false) :
x.clzAuxRec n = x.clzAuxRec (n + k) := by
induction k
· case zero => simp
· case succ k ihk =>
simp only [show n + (k + 1) = (n + k) + 1 by omega, clzAuxRec_succ]
by_cases hxn : x.getLsbD (n + k + 1)
· have : ¬ (i : Nat), n < i x.getLsbD i = false := by
simp only [Classical.not_forall, Bool.not_eq_false]
exists n + k + 1
simp [show n < n + k + 1 by omega, hxn]
contradiction
· simp only [hxn, Bool.false_eq_true, reduceIte]
exact ihk
theorem clzAuxRec_le {x : BitVec w} (n : Nat) :
clzAuxRec x n w := by
have := Nat.lt_pow_self (a := 2) (n := w) (by omega)
rcases w with _|w
· simp [of_length_zero]
· induction n
· case zero =>
simp only [clzAuxRec_zero]
by_cases hx0 : x.getLsbD 0
· simp only [hx0, Nat.add_one_sub_one, reduceIte, natCast_eq_ofNat, ofNat_le_ofNat,
Nat.mod_two_pow_self, ge_iff_le, Nat.mod_eq_of_lt (a := w) (b := 2 ^ (w + 1)) (by omega)]
omega
· simp only [hx0, Bool.false_eq_true, reduceIte, natCast_eq_ofNat, BitVec.le_refl]
· case succ n ihn =>
simp only [clzAuxRec_succ, Nat.add_one_sub_one, natCast_eq_ofNat, ge_iff_le]
by_cases hxn : x.getLsbD (n + 1)
· simp [hxn, Nat.mod_eq_of_lt (a := w - (n + 1)) (b := 2 ^(w + 1)) (by omega)]
omega
· simp only [hxn, Bool.false_eq_true, reduceIte]
exact ihn
theorem clzAuxRec_eq_iff_of_getLsbD_false {x : BitVec w} (h : i, n < i x.getLsbD i = false) :
x.clzAuxRec n = BitVec.ofNat w w j, j n x.getLsbD j = false := by
rcases w with _|w
· simp [of_length_zero]
· have := Nat.lt_pow_self (a := 2) (n := w + 1)
induction n
· case zero =>
simp only [clzAuxRec_zero, Nat.zero_lt_succ, getLsbD_eq_getElem, Nat.add_one_sub_one,
ite_eq_right_iff, Nat.le_zero_eq, forall_eq]
by_cases hx0 : x.getLsbD 0
· simp [hx0, toNat_eq, toNat_ofNat, Nat.mod_eq_of_lt (a := w) (b := 2 ^ (w + 1)) (by omega)]
· simp only [Nat.zero_lt_succ, getLsbD_eq_getElem, Bool.not_eq_true] at hx0
simp [hx0]
· case succ n ihn =>
simp only [clzAuxRec_succ, Nat.add_one_sub_one]
by_cases hxn : x.getLsbD (n + 1)
· simp only [hxn, reduceIte, toNat_eq, toNat_ofNat,
Nat.mod_eq_of_lt (a := w - (n + 1)) (b := 2 ^ (w + 1)) (by omega), Nat.mod_two_pow_self,
show ¬w - (n + 1) = w + 1 by omega, false_iff, Classical.not_forall,
Bool.not_eq_false]
exists n + 1, by omega
· have : (i : Nat), n < i x.getLsbD i = false := by
intro i hi
by_cases hi' : i = n + 1
· simp [hi', hxn]
· apply h; omega
specialize ihn this
simp only [Bool.not_eq_true] at ihn hxn
simp only [hxn, Bool.false_eq_true, reduceIte, ihn]
constructor
<;> intro h' j hj
<;> (by_cases hj' : j = n + 1; simp [hj', hxn]; (apply h'; omega))
theorem clz_le {x : BitVec w} :
clz x w := by
unfold clz
rcases w with _|w
· simp [of_length_zero]
· exact clzAuxRec_le (n := w)
@[simp]
theorem clz_eq_iff_eq_zero {x : BitVec w} :
clz x = w x = 0#w := by
rcases w with _|w
· simp [clz, of_length_zero]
· simp only [clz, Nat.add_one_sub_one, natCast_eq_ofNat, zero_iff_eq_false]
rw [clzAuxRec_eq_iff_of_getLsbD_false (x := x) (n := w) (w := w + 1) (by intros i hi; simp [show w + 1 i by omega])]
constructor
· intro h i
by_cases i w
· apply h; omega
· simp [show w + 1 i by omega]
· intro h j hj
apply h
theorem clzAuxRec_eq_zero_iff {x : BitVec w} (h : i, n < i x.getLsbD i = false) (hw : 0 < w) :
(x.clzAuxRec n).toNat = 0 x[w - 1] = true := by
have := Nat.lt_pow_self (a := 2) (n := w)
induction n
· case zero =>
simp only [clzAuxRec_zero]
by_cases hw1 : w - 1 = 0
· by_cases hx0 : x.getLsbD 0
· simp [hw1, hx0]
· simp [hw1, show ¬ w = 0 by omega, hx0, getLsbD_eq_getElem]
· by_cases hx0 : x.getLsbD 0
· simp only [hx0, reduceIte, toNat_ofNat,
Nat.mod_eq_of_lt (a := w - 1) (b := 2 ^ w) (by omega), show ¬w - 1 = 0 by omega, false_iff,
Bool.not_eq_true]
specialize h (w - 1) (by omega)
exact h
· simp [hx0, show ¬ w = 0 by omega]
specialize h (w - 1) (by omega)
exact h
· case succ n ihn =>
by_cases hxn : x.getLsbD (n + 1)
· simp only [clzAuxRec_succ, hxn, reduceIte, toNat_ofNat]
rw [Nat.mod_eq_of_lt (by omega)]
by_cases hwn : w - 1 - (n + 1) = 0
· have := lt_of_getLsbD hxn
simp only [show w - 1 = n + 1 by omega, Nat.sub_self, true_iff]
exact hxn
· simp only [hwn, false_iff, Bool.not_eq_true]
specialize h (w - 1) (by omega)
exact h
· simp only [clzAuxRec_succ, hxn, Bool.false_eq_true, reduceIte]
apply ihn
intro i hi
by_cases hi : i = n + 1
· simp [hi, hxn]
· apply h; omega
theorem clz_eq_zero_iff {x : BitVec w} (hw : 0 < w) :
(clz x).toNat = 0 2 ^ (w - 1) x.toNat := by
simp only [clz, clzAuxRec_eq_zero_iff (x := x) (n := w - 1) (by intro i hi; simp [show w i by omega]) hw]
by_cases hxw : x[w - 1]
· simp [hxw, two_pow_le_toNat_of_getElem_eq_true (x := x) (i := w - 1) (by omega) hxw]
· simp only [hxw, Bool.false_eq_true, false_iff, Nat.not_le]
simp only [ getLsbD_eq_getElem, msb_eq_getLsbD_last, Bool.not_eq_true] at hxw
exact toNat_lt_of_msb_false hxw
/-- The number of leading zeroes is strictly less than the bitwidth iff the bitvector is nonzero. -/
theorem clz_lt_iff_ne_zero {x : BitVec w} :
clz x < w x 0#w := by
have hle := clz_le (x := x)
have heq := clz_eq_iff_eq_zero (x := x)
constructor
· intro h
simp only [natCast_eq_ofNat, BitVec.ne_of_lt (x := x.clz) (y := BitVec.ofNat w w) h,
false_iff] at heq
simp only [ne_eq, heq, not_false_eq_true]
· intro h
simp only [natCast_eq_ofNat, h, iff_false] at heq
apply BitVec.lt_of_le_ne (x := x.clz) (y := BitVec.ofNat w w) hle heq
theorem getLsbD_false_of_clzAuxRec {x : BitVec w} (h : i, n < i x.getLsbD i = false) :
j, x.getLsbD (w - (x.clzAuxRec n).toNat + j) = false := by
rcases w with _|w
· simp
· have := Nat.lt_pow_self (a := 2) (n := w + 1)
induction n
· case zero =>
intro j
simp only [clzAuxRec_zero, Nat.zero_lt_succ, getLsbD_eq_getElem, Nat.add_one_sub_one]
by_cases hx0 : x[0]
· specialize h (1 + j) (by omega)
simp [h, hx0, Nat.mod_eq_of_lt (a := w) (b := 2 ^ (w + 1)) (by omega)]
· simp only [hx0, Bool.false_eq_true, reduceIte, toNat_ofNat, Nat.mod_two_pow_self,
Nat.sub_self, Nat.zero_add]
by_cases hj0 : j = 0
· simp [hj0, hx0]
· specialize h j (by omega)
exact h
· case succ n ihn =>
intro j
by_cases hxn : x.getLsbD (n + 1)
· have := lt_of_getLsbD hxn
specialize h (n + j + 1 + 1) (by omega)
simp [h, clzAuxRec_succ, hxn, Nat.mod_eq_of_lt (a := w - (n + 1)) (b := 2 ^ (w + 1)) (by omega),
show (w + 1 - (w - (n + 1)) + j) = n + j + 1 + 1 by omega]
· simp only [clzAuxRec_succ, hxn, Bool.false_eq_true, reduceIte]
apply ihn
intro i hi
by_cases hin : i = n + 1
· simp [hin, hxn]
· specialize h i (by omega)
exact h
theorem getLsbD_true_of_eq_clzAuxRec_of_ne_zero {x : BitVec w} (hx : ¬ x = 0#w) (hn : i, n < i x.getLsbD i = false) :
x.getLsbD (w - 1 - (x.clzAuxRec n).toNat) = true := by
rcases w with _|w
· simp [of_length_zero] at hx
· have := Nat.lt_pow_self (a := 2) (n := w + 1)
induction n
· case zero =>
by_cases hx0 : x[0]
· simp only [Nat.add_one_sub_one, clzAuxRec_zero, Nat.zero_lt_succ, getLsbD_eq_getElem, hx0,
reduceIte, toNat_ofNat, Nat.mod_eq_of_lt (a := w) (b := 2 ^(w + 1)) (by omega), show w - w = 0 by omega]
· simp only [zero_iff_eq_false, Classical.not_forall, Bool.not_eq_false] at hx
obtain m,hm := hx
specialize hn m
by_cases hm0 : m = 0
· simp [hm0, hx0] at hm
· simp [show 0 < m by omega, hm] at hn
· case succ n ihn =>
by_cases hxn : x.getLsbD (n + 1)
· have := lt_of_getLsbD hxn
simp [clzAuxRec_succ, hxn, toNat_ofNat, Nat.mod_eq_of_lt (a := w - (n + 1)) (b := 2 ^ (w + 1)) (by omega),
show w - (w - (n + 1)) = n + 1 by omega]
· simp only [Nat.add_one_sub_one, clzAuxRec_succ, hxn, Bool.false_eq_true, reduceIte]
simp only [Nat.add_one_sub_one] at ihn
apply ihn
intro j hj
by_cases hjn : j = n + 1
· simp [hjn, hxn]
· specialize hn j (by omega)
exact hn
theorem getLsbD_true_clz_of_ne_zero {x : BitVec w} (hw : 0 < w) (hx : x 0#w) :
x.getLsbD (w - 1 - (clz x).toNat) = true := by
unfold clz
apply getLsbD_true_of_eq_clzAuxRec_of_ne_zero (x := x) (n := w - 1) (by omega)
intro i hi
simp [show w i by omega]
/-- A nonzero bitvector is lower-bounded by its leading zeroes. -/
theorem two_pow_sub_clz_le_toNat_of_ne_zero {x : BitVec w} (hw : 0 < w) (hx : x 0#w) :
2 ^ (w - 1 - (clz x).toNat) x.toNat := by
by_cases hc0 : x.clz.toNat = 0
· simp [hc0, clz_eq_zero_iff (x := x) hw]
· have hclz := getLsbD_true_clz_of_ne_zero (x := x) hw hx
rw [getLsbD_eq_getElem (by omega)] at hclz
have hge := Nat.ge_two_pow_of_testBit hclz
push_cast at hge
exact hge
/-- A bitvector is upper bounded by the number of leading zeroes. -/
theorem toNat_lt_two_pow_sub_clz {x : BitVec w} :
x.toNat < 2 ^ (w - (clz x).toNat) := by
rcases w with _|w
· simp [of_length_zero]
· unfold clz
have hlt := toNat_lt_iff_getLsbD_eq_false (x := x)
have hzero := clzAuxRec_eq_zero_iff (x := x) (n := w) (by intro i hi; simp [show w + 1 i by omega]) (by omega)
simp only [Nat.add_one_sub_one] at hzero
by_cases hxw : x[w]
· simp only [hxw, iff_true] at hzero
simp only [Nat.add_one_sub_one, hzero, Nat.sub_zero, gt_iff_lt]
omega
· simp only [hxw, Bool.false_eq_true, iff_false] at hzero
rw [hlt]
· intro k
apply getLsbD_false_of_clzAuxRec (x := x) (n := w)
intro i hi
by_cases hiw : i = w
· simp [hiw, hxw]
· simp [show w + 1 i by omega]
· simp; omega
/-! ### Deprecations -/
set_option linter.missingDocs false

View File

@@ -6,10 +6,10 @@ Author: Leonardo de Moura
module
prelude
public import Init.Data.Array.Basic
public import Init.Data.Array.DecidableEq
public import Init.Data.UInt.Basic
public import all Init.Data.UInt.BasicAux
public import Init.Data.UInt.BasicAux
import all Init.Data.UInt.BasicAux
public import Init.Data.Option.Basic
@[expose] public section

View File

@@ -6,7 +6,8 @@ Authors: Leonardo de Moura
module
prelude
public import all Init.Data.Char.Basic
public import Init.Data.Char.Basic
import all Init.Data.Char.Basic
public import Init.Data.UInt.Lemmas
public section

12
src/Init/Data/Dyadic.lean Normal file
View File

@@ -0,0 +1,12 @@
/-
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.Dyadic.Basic
public import Init.Data.Dyadic.Instances
public import Init.Data.Dyadic.Round
public import Init.Data.Dyadic.Inv

View File

@@ -0,0 +1,747 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison, Robin Arnez
-/
module
prelude
public import Init.Data.Rat.Lemmas
import Init.Data.Int.Bitwise.Lemmas
import Init.Data.Int.DivMod.Lemmas
/-!
# The dyadic rationals
Constructs the dyadic rationals as an ordered ring, equipped with a compatible embedding into the rationals.
-/
set_option linter.missingDocs true
@[expose] public section
open Nat
namespace Int
/-- The number of trailing zeros in the binary representation of `i`. -/
def trailingZeros (i : Int) : Nat :=
if h : i = 0 then 0 else aux i.natAbs i h (Nat.le_refl _) 0
where
aux (k : Nat) (i : Int) (hi : i 0) (hk : i.natAbs k) (acc : Nat) : Nat :=
match k, (by omega : k 0) with
| k + 1, _ =>
if h : i % 2 = 0 then aux k (i / 2) (by omega) (by omega) (acc + 1)
else acc
-- TODO: check performance of `trailingZeros` in the kernel and VM.
private theorem trailingZeros_aux_irrel (hi : i 0) (hk : i.natAbs k) (hk' : i.natAbs k') :
trailingZeros.aux k i hi hk acc = trailingZeros.aux k' i hi hk' acc := by
fun_induction trailingZeros.aux k i hi hk acc generalizing k' <;>
fun_cases trailingZeros.aux k' _ _ hk' _
· rename_i ih _ _ _ _ _
exact ih _
· contradiction
· contradiction
· rfl
private theorem trailingZeros_aux_succ :
trailingZeros.aux k i hi hk (acc + 1) = trailingZeros.aux k i hi hk acc + 1 := by
fun_induction trailingZeros.aux k i hi hk acc <;> simp_all [trailingZeros.aux]
theorem trailingZeros_zero : trailingZeros 0 = 0 := rfl
theorem trailingZeros_two_mul_add_one (i : Int) :
Int.trailingZeros (2 * i + 1) = 0 := by
unfold trailingZeros trailingZeros.aux
rw [dif_neg (by omega)]
split <;> simp_all
theorem trailingZeros_eq_zero_of_mod_eq {i : Int} (h : i % 2 = 1) :
Int.trailingZeros i = 0 := by
unfold trailingZeros trailingZeros.aux
rw [dif_neg (by omega)]
split <;> simp_all
theorem trailingZeros_two_mul {i : Int} (h : i 0) :
Int.trailingZeros (2 * i) = Int.trailingZeros i + 1 := by
rw [Int.trailingZeros, dif_neg (Int.mul_ne_zero (by decide) h), Int.trailingZeros.aux.eq_def]
simp only [ne_eq, mul_emod_right, reduceDIte, Int.reduceEq, not_false_eq_true,
mul_ediv_cancel_left, Nat.zero_add]
split
rw [trailingZeros, trailingZeros_aux_succ, dif_neg h]
apply congrArg Nat.succ (trailingZeros_aux_irrel ..) <;> omega
theorem shiftRight_trailingZeros_mod_two {i : Int} (h : i 0) :
(i >>> i.trailingZeros) % 2 = 1 := by
rw (occs := .pos [2]) [ Int.emod_add_mul_ediv i 2]
rcases i.emod_two_eq with h' | h' <;> rw [h']
· rcases Int.dvd_of_emod_eq_zero h' with a, rfl
simp only [ne_eq, Int.mul_eq_zero, Int.reduceEq, false_or] at h
rw [Int.zero_add, mul_ediv_cancel_left _ (by decide), trailingZeros_two_mul h, Nat.add_comm,
shiftRight_add, shiftRight_eq_div_pow _ 1]
simpa using shiftRight_trailingZeros_mod_two h
· rwa [Int.add_comm, trailingZeros_two_mul_add_one, shiftRight_zero]
termination_by i.natAbs
theorem two_pow_trailingZeros_dvd {i : Int} (h : i 0) :
2 ^ i.trailingZeros i := by
rcases i.emod_two_eq with h' | h'
· rcases Int.dvd_of_emod_eq_zero h' with a, rfl
simp only [ne_eq, Int.mul_eq_zero, Int.reduceEq, false_or] at h
rw [trailingZeros_two_mul h, Int.pow_succ']
exact Int.mul_dvd_mul_left _ (two_pow_trailingZeros_dvd h)
· rw (occs := .pos [1]) [ Int.emod_add_mul_ediv i 2, h', Int.add_comm, trailingZeros_two_mul_add_one]
exact Int.one_dvd _
termination_by i.natAbs
theorem trailingZeros_shiftLeft {x : Int} (hx : x 0) (n : Nat) :
trailingZeros (x <<< n) = x.trailingZeros + n := by
have : NeZero x := hx
induction n <;> simp [Int.shiftLeft_succ', trailingZeros_two_mul (NeZero.ne _), *, Nat.add_assoc]
@[simp]
theorem trailingZeros_neg (x : Int) : trailingZeros (-x) = x.trailingZeros := by
by_cases hx : x = 0
· simp [hx]
rcases x.emod_two_eq with h | h
· rcases Int.dvd_of_emod_eq_zero h with a, rfl
simp only [Int.mul_ne_zero_iff, ne_eq, Int.reduceEq, not_false_eq_true, true_and] at hx
rw [ Int.mul_neg, trailingZeros_two_mul hx, trailingZeros_two_mul (Int.neg_ne_zero.mpr hx)]
rw [trailingZeros_neg]
· simp [trailingZeros_eq_zero_of_mod_eq, h]
termination_by x.natAbs
end Int
/--
A dyadic rational is either zero or of the form `n * 2^(-k)` for some (unique) `n k : Int`
where `n` is odd.
-/
inductive Dyadic where
/-- The dyadic number `0`. -/
| zero
/-- The dyadic number `n * 2^(-k)` for some odd `n` and integer `k`. -/
| ofOdd (n : Int) (k : Int) (hn : n % 2 = 1)
deriving DecidableEq
namespace Dyadic
/-- Returns the dyadic number representation of `i * 2 ^ (-exp)`. -/
def ofIntWithPrec (i : Int) (prec : Int) : Dyadic :=
if h : i = 0 then .zero
else .ofOdd (i >>> i.trailingZeros) (prec - i.trailingZeros) (Int.shiftRight_trailingZeros_mod_two h)
/-- Convert an integer to a dyadic number (which will necessarily have non-positive precision). -/
def ofInt (i : Int) : Dyadic :=
Dyadic.ofIntWithPrec i 0
instance (n : Nat) : OfNat Dyadic n where
ofNat := Dyadic.ofInt n
instance : IntCast Dyadic := ofInt
instance : NatCast Dyadic := fun x => ofInt x
/-- Add two dyadic numbers. -/
protected def add (x y : Dyadic) : Dyadic :=
match x, y with
| .zero, y => y
| x, .zero => x
| .ofOdd n₁ k₁ hn₁, .ofOdd n₂ k₂ hn₂ =>
match k₁ - k₂ with
| 0 => .ofIntWithPrec (n₁ + n₂) k₁
-- TODO: these `simp_all` calls where previously factored out into a `where finally` clause,
-- but there is apparently a bad interaction with the module system.
| (d@hd:(d' + 1) : Nat) => .ofOdd (n₁ + (n₂ <<< d)) k₁ (by simp_all [Int.shiftLeft_eq, Int.pow_succ, Int.mul_assoc])
| -(d + 1 : Nat) => .ofOdd (n₁ <<< (d + 1) + n₂) k₂ (by simp_all [Int.shiftLeft_eq, Int.pow_succ, Int.mul_assoc])
instance : Add Dyadic := Dyadic.add
/-- Multiply two dyadic numbers. -/
protected def mul (x y : Dyadic) : Dyadic :=
match x, y with
| .zero, _ => .zero
| _, .zero => .zero
| .ofOdd n₁ k₁ hn₁, .ofOdd n₂ k₂ hn₂ =>
.ofOdd (n₁ * n₂) (k₁ + k₂) (by rw [Int.mul_emod, hn₁, hn₂]; rfl)
instance : Mul Dyadic := Dyadic.mul
/-- Multiply two dyadic numbers. -/
protected def pow (x : Dyadic) (i : Nat) : Dyadic :=
match x with
| .zero => if i = 0 then 1 else 0
| .ofOdd n k hn =>
.ofOdd (n ^ i) (k * i) (by induction i <;> simp [Int.pow_succ, Int.mul_emod, *])
instance : Pow Dyadic Nat := Dyadic.pow
/-- Negate a dyadic number. -/
protected def neg (x : Dyadic) : Dyadic :=
match x with
| .zero => .zero
| .ofOdd n k hn => .ofOdd (-n) k (by rwa [Int.neg_emod_two])
instance : Neg Dyadic := Dyadic.neg
/-- Subtract two dyadic numbers. -/
protected def sub (x y : Dyadic) : Dyadic := x + (- y)
instance : Sub Dyadic := Dyadic.sub
/-- Shift a dyadic number left by `i` bits. -/
protected def shiftLeft (x : Dyadic) (i : Int) : Dyadic :=
match x with
| .zero => .zero
| .ofOdd n k hn => .ofOdd n (k - i) hn
/-- Shift a dyadic number right by `i` bits. -/
protected def shiftRight (x : Dyadic) (i : Int) : Dyadic :=
match x with
| .zero => .zero
| .ofOdd n k hn => .ofOdd n (k + i) hn
instance : HShiftLeft Dyadic Int Dyadic := Dyadic.shiftLeft
instance : HShiftRight Dyadic Int Dyadic := Dyadic.shiftRight
instance : HShiftLeft Dyadic Nat Dyadic := fun x y => x <<< (y : Int)
instance : HShiftRight Dyadic Nat Dyadic := fun x y => x >>> (y : Int)
-- TODO: move this
theorem _root_.Int.natAbs_emod_two (i : Int) : i.natAbs % 2 = (i % 2).natAbs := by omega
/-- Convert a dyadic number to a rational number. -/
def toRat (x : Dyadic) : Rat :=
match x with
| .zero => 0
| .ofOdd n (k : Nat) hn =>
have reduced : n.natAbs.Coprime (2 ^ k) := by
apply Coprime.pow_right
rw [coprime_iff_gcd_eq_one, Nat.gcd_comm, Nat.gcd_def]
simp [hn, Int.natAbs_emod_two]
n, 2 ^ k, Nat.ne_of_gt (Nat.pow_pos (by decide)), reduced
| .ofOdd n (-((k : Nat) + 1)) hn =>
(n * (2 ^ (k + 1) : Nat) : Int)
@[simp] protected theorem zero_eq : Dyadic.zero = 0 := rfl
@[simp] protected theorem add_zero (x : Dyadic) : x + 0 = x := by cases x <;> rfl
@[simp] protected theorem zero_add (x : Dyadic) : 0 + x = x := by cases x <;> rfl
@[simp] protected theorem neg_zero : (-0 : Dyadic) = 0 := rfl
@[simp] protected theorem mul_zero (x : Dyadic) : x * 0 = 0 := by cases x <;> rfl
@[simp] protected theorem zero_mul (x : Dyadic) : 0 * x = 0 := by cases x <;> rfl
@[simp] theorem toRat_zero : toRat 0 = 0 := rfl
theorem _root_.Rat.mkRat_one (x : Int) : mkRat x 1 = x := by
rw [ Rat.mk_den_one, Rat.mk_eq_mkRat]
theorem toRat_ofOdd_eq_mkRat :
toRat (.ofOdd n k hn) = mkRat (n <<< (-k).toNat) (1 <<< k.toNat) := by
cases k
· simp [toRat, Rat.mk_eq_mkRat, Int.shiftLeft_eq, Nat.shiftLeft_eq]
· simp [toRat, Int.neg_negSucc, Rat.mkRat_one, Int.shiftLeft_eq]
theorem toRat_ofIntWithPrec_eq_mkRat :
toRat (.ofIntWithPrec n k) = mkRat (n <<< (-k).toNat) (1 <<< k.toNat) := by
simp only [ofIntWithPrec]
split
· simp_all
rw [toRat_ofOdd_eq_mkRat, Rat.mkRat_eq_iff (NeZero.ne _) (NeZero.ne _)]
simp only [Int.natCast_shiftLeft, Int.cast_ofNat_Int, Int.shiftLeft_mul_shiftLeft, Int.mul_one]
have : (-(k - n.trailingZeros) : Int).toNat + k.toNat =
n.trailingZeros + ((-k).toNat + (k - n.trailingZeros).toNat) := by omega
rw [this, Int.shiftLeft_add, Int.shiftRight_shiftLeft_cancel]
exact Int.two_pow_trailingZeros_dvd _
theorem toRat_ofIntWithPrec_eq_mul_two_pow : toRat (.ofIntWithPrec n k) = n * 2 ^ (-k) := by
rw [toRat_ofIntWithPrec_eq_mkRat, Rat.zpow_neg, Int.shiftLeft_eq, Nat.one_shiftLeft]
rw [Rat.mkRat_eq_div, Rat.div_def]
have : ((2 : Int) : Rat) 0 := by decide
simp only [Rat.intCast_mul, Rat.intCast_pow, Rat.zpow_natCast, Rat.intCast_natCast,
Int.natCast_pow, Int.cast_ofNat_Int, Rat.zpow_neg, Rat.mul_assoc, ne_eq,
Rat.intCast_eq_zero_iff, Int.reduceEq, not_false_eq_true, Rat.zpow_add]
rw [Int.add_neg_eq_sub, Int.neg_sub, Int.toNat_sub_toNat_neg]
rfl
example : ((3 : Dyadic) >>> 2) + ((3 : Dyadic) >>> 2) = ((3 : Dyadic) >>> 1) := rfl -- 3/4 + 3/4 = 3/2
example : ((7 : Dyadic) >>> 3) + ((1 : Dyadic) >>> 3) = 1 := rfl -- 7/8 + 1/8 = 1
example : (12 : Dyadic) + ((3 : Dyadic) >>> 1) = (27 : Dyadic) >>> 1 := rfl -- 12 + 3/2 = 27/2 = (2 * 13 + 1)/2^1
example : ((3 : Dyadic) >>> 1).add 12 = (27 : Dyadic) >>> 1 := rfl -- 3/2 + 12 = 27/2 = (2 * 13 + 1)/2^1
example : (12 : Dyadic).add 12 = 24 := rfl -- 12 + 12 = 24
@[simp]
theorem toRat_add (x y : Dyadic) : toRat (x + y) = toRat x + toRat y := by
match x, y with
| .zero, _ => simp [toRat, Rat.zero_add]
| _, .zero => simp [toRat, Rat.add_zero]
| .ofOdd n₁ k₁ hn₁, .ofOdd n₂ k₂ hn₂ =>
change (Dyadic.add _ _).toRat = _
rw [Dyadic.add, toRat_ofOdd_eq_mkRat, toRat_ofOdd_eq_mkRat]
rw [Rat.mkRat_add_mkRat _ _ (NeZero.ne _) (NeZero.ne _)]
split
· rename_i h
cases Int.sub_eq_zero.mp h
rw [toRat_ofIntWithPrec_eq_mkRat, Rat.mkRat_eq_iff (NeZero.ne _) (NeZero.ne _)]
simp [Int.shiftLeft_mul_shiftLeft, Int.add_shiftLeft, Int.add_mul, Nat.add_assoc]
· rename_i h
cases Int.sub_eq_iff_eq_add.mp h
rw [toRat_ofOdd_eq_mkRat, Rat.mkRat_eq_iff (NeZero.ne _) (NeZero.ne _)]
simp only [succ_eq_add_one, Int.ofNat_eq_coe, Int.add_shiftLeft, Int.shiftLeft_add,
Int.natCast_mul, Int.natCast_shiftLeft, Int.shiftLeft_mul_shiftLeft, Int.add_mul]
congr 2 <;> omega
· rename_i h
cases Int.sub_eq_iff_eq_add.mp h
rw [toRat_ofOdd_eq_mkRat, Rat.mkRat_eq_iff (NeZero.ne _) (NeZero.ne _)]
simp only [Int.add_shiftLeft, Int.shiftLeft_add, Int.natCast_mul, Int.natCast_shiftLeft,
Int.cast_ofNat_Int, Int.shiftLeft_mul_shiftLeft, Int.mul_one, Int.add_mul]
congr 2 <;> omega
@[simp]
theorem toRat_neg (x : Dyadic) : toRat (-x) = - toRat x := by
change x.neg.toRat = _
cases x
· rfl
· simp [Dyadic.neg, Rat.neg_mkRat, Int.neg_shiftLeft, toRat_ofOdd_eq_mkRat]
@[simp]
theorem toRat_sub (x y : Dyadic) : toRat (x - y) = toRat x - toRat y := by
change toRat (x + -y) = _
simp [Rat.sub_eq_add_neg]
@[simp]
theorem toRat_mul (x y : Dyadic) : toRat (x * y) = toRat x * toRat y := by
match x, y with
| .zero, _ => simp
| _, .zero => simp
| .ofOdd n₁ k₁ hn₁, .ofOdd n₂ k₂ hn₂ =>
change (Dyadic.mul _ _).toRat = _
rw [Dyadic.mul, toRat_ofOdd_eq_mkRat, toRat_ofOdd_eq_mkRat, toRat_ofOdd_eq_mkRat,
Rat.mkRat_mul_mkRat, Rat.mkRat_eq_iff (NeZero.ne _) (NeZero.ne _)]
simp only [Int.natCast_mul, Int.natCast_shiftLeft, Int.cast_ofNat_Int,
Int.shiftLeft_mul_shiftLeft, Int.mul_one]
congr 1; omega
@[simp]
protected theorem pow_zero (x : Dyadic) : x ^ 0 = 1 := by
change x.pow 0 = 1
cases x <;> simp [Dyadic.pow] <;> rfl
protected theorem pow_succ (x : Dyadic) (n : Nat) : x ^ (n + 1) = x ^ n * x := by
change x.pow (n + 1) = x.pow n * x
cases x
· simp [Dyadic.pow]
· change _ = Dyadic.mul _ _
simp [Dyadic.pow, Dyadic.mul, Int.pow_succ, Int.mul_add]
@[simp]
theorem toRat_pow (x : Dyadic) (n : Nat) : toRat (x ^ n) = toRat x ^ n := by
induction n with
| zero => simp; rfl
| succ k ih => simp [Dyadic.pow_succ, Rat.pow_succ, ih]
@[simp]
theorem toRat_intCast (x : Int) : (x : Dyadic).toRat = x := by
change (ofInt x).toRat = x
simp [ofInt, toRat_ofIntWithPrec_eq_mul_two_pow]
@[simp]
theorem toRat_natCast (x : Nat) : (x : Dyadic).toRat = x := by
change (ofInt x).toRat = x
simp [ofInt, toRat_ofIntWithPrec_eq_mul_two_pow, Rat.intCast_natCast]
@[simp] theorem of_ne_zero : ofOdd n k hn 0 := Dyadic.noConfusion
@[simp] theorem zero_ne_of : 0 ofOdd n k hn := Dyadic.noConfusion
@[simp]
theorem toRat_eq_zero_iff {x : Dyadic} : x.toRat = 0 x = 0 := by
refine fun h => ?_, fun h => h rfl
cases x
· rfl
· simp only [toRat_ofOdd_eq_mkRat, ne_eq, shiftLeft_eq_zero_iff, succ_ne_self, not_false_eq_true,
Rat.mkRat_eq_zero, Int.shiftLeft_eq_zero_iff] at h
cases h
contradiction
theorem ofOdd_eq_ofIntWithPrec : ofOdd n k hn = ofIntWithPrec n k := by
simp only [ofIntWithPrec, Dyadic.zero_eq, Int.trailingZeros_eq_zero_of_mod_eq hn,
Int.shiftRight_zero, Int.cast_ofNat_Int, Int.sub_zero, right_eq_dite_iff, of_ne_zero, imp_false]
intro rfl; contradiction
theorem toRat_ofOdd_eq_mul_two_pow : toRat (.ofOdd n k hn) = n * 2 ^ (-k) := by
rw [ofOdd_eq_ofIntWithPrec, toRat_ofIntWithPrec_eq_mul_two_pow]
@[simp]
theorem ofIntWithPrec_zero {i : Int} : ofIntWithPrec 0 i = 0 := rfl
@[simp]
theorem neg_ofOdd : -ofOdd n k hn = ofOdd (-n) k (by simpa using hn) := rfl
@[simp]
theorem neg_ofIntWithPrec {i prec : Int} : -ofIntWithPrec i prec = ofIntWithPrec (-i) prec := by
rw [ofIntWithPrec, ofIntWithPrec]
simp only [Dyadic.zero_eq, Int.neg_eq_zero, Int.trailingZeros_neg]
split
· rfl
· obtain a, h := Int.two_pow_trailingZeros_dvd _
rw [Int.mul_comm, Int.shiftLeft_eq] at h
conv => enter [1, 1, 1, 1]; rw [h]
conv => enter [2, 1, 1]; rw [h]
simp only [Int.shiftLeft_shiftRight_cancel, neg_ofOdd, Int.neg_shiftLeft]
theorem ofIntWithPrec_shiftLeft_add {n : Nat} :
ofIntWithPrec ((x : Int) <<< n) (i + n) = ofIntWithPrec x i := by
rw [ofIntWithPrec, ofIntWithPrec]
simp only [Int.shiftLeft_eq_zero_iff]
split
· rfl
· simp [Int.trailingZeros_shiftLeft, *, Int.shiftLeft_shiftRight_eq_shiftRight_of_le,
Int.add_comm x.trailingZeros n, Int.sub_sub]
/-- The "precision" of a dyadic number, i.e. in `n * 2^(-p)` with `n` odd the precision is `p`. -/
-- TODO: If `WithBot` is upstreamed, replace this with `WithBot Int`.
def precision : Dyadic Option Int
| .zero => none
| .ofOdd _ p _ => some p
theorem precision_ofIntWithPrec_le {i : Int} (h : i 0) (prec : Int) :
(ofIntWithPrec i prec).precision some prec := by
simp [ofIntWithPrec, h, precision]
omega
@[simp] theorem precision_zero : (0 : Dyadic).precision = none := rfl
@[simp] theorem precision_neg {x : Dyadic} : (-x).precision = x.precision :=
match x with
| .zero => rfl
| .ofOdd _ _ _ => rfl
end Dyadic
namespace Rat
open Dyadic
/--
Convert a rational number `x` to the greatest dyadic number with precision at most `prec`
which is less than or equal to `x`.
-/
def toDyadic (x : Rat) (prec : Int) : Dyadic :=
match prec with
| (n : Nat) => .ofIntWithPrec ((x.num <<< n) / x.den) prec
| -(n + 1 : Nat) => .ofIntWithPrec (x.num / (x.den <<< (n + 1))) prec
theorem toDyadic_mkRat (a : Int) (b : Nat) (prec : Int) :
Rat.toDyadic (mkRat a b) prec =
.ofIntWithPrec ((a <<< prec.toNat) / (b <<< (-prec).toNat)) prec := by
by_cases hb : b = 0
· cases prec <;> simp [hb, Rat.toDyadic]
rcases h : mkRat a b with n, d, hnz, hr
obtain m, hm, rfl, rfl := Rat.mkRat_num_den hb h
cases prec
· simp only [Rat.toDyadic, Int.ofNat_eq_coe, Int.toNat_natCast, Int.toNat_neg_natCast,
shiftLeft_zero, Int.natCast_mul]
rw [Int.mul_comm d, Int.ediv_ediv (by simp), Int.shiftLeft_mul,
Int.mul_ediv_cancel _ (by simpa using hm)]
· simp only [Rat.toDyadic, Int.natCast_shiftLeft, Int.negSucc_eq, Int.natCast_add_one,
Int.toNat_neg_natCast, Int.shiftLeft_zero, Int.neg_neg, Int.toNat_natCast, Int.natCast_mul]
rw [Int.mul_comm d, Int.mul_shiftLeft, Int.ediv_ediv (by simp),
Int.mul_ediv_cancel _ (by simpa using hm)]
theorem toDyadic_eq_ofIntWithPrec (x : Rat) (prec : Int) :
x.toDyadic prec = .ofIntWithPrec ((x.num <<< prec.toNat) / (x.den <<< (-prec).toNat)) prec := by
conv => lhs; rw [ Rat.mkRat_self x]
rw [Rat.toDyadic_mkRat]
/--
Converting a rational to a dyadic at a given precision and then back to a rational
gives the same result as taking the floor of the rational at precision `2 ^ prec`.
-/
theorem toRat_toDyadic (x : Rat) (prec : Int) :
(x.toDyadic prec).toRat = (x * 2 ^ prec).floor / 2 ^ prec := by
rw [Rat.toDyadic_eq_ofIntWithPrec, toRat_ofIntWithPrec_eq_mul_two_pow, Rat.zpow_neg, Rat.div_def]
congr 2
rw [Rat.floor_def, Int.shiftLeft_eq, Nat.shiftLeft_eq]
match prec with
| .ofNat prec =>
simp only [Int.ofNat_eq_coe, Int.toNat_natCast, Int.toNat_neg_natCast, Nat.pow_zero,
Nat.mul_one]
have : (2 ^ prec : Rat) = ((2 ^ prec : Nat) : Rat) := by simp
rw [Rat.zpow_natCast, this, Rat.mul_def']
simp only [Rat.num_mkRat, Rat.den_mkRat]
simp only [Rat.natCast_pow, Rat.natCast_ofNat, Rat.num_pow, Rat.num_ofNat, Rat.den_pow,
Rat.den_ofNat, Nat.one_pow, Nat.mul_one]
split
· simp_all
· rw [Int.ediv_ediv (Int.ofNat_zero_le _)]
congr 1
rw [Int.natCast_ediv, Int.mul_ediv_cancel']
rw [Int.natCast_dvd_natCast]
apply gcd_dvd_left
| .negSucc prec =>
simp only [Int.toNat_negSucc, Int.pow_zero, Int.mul_one, Int.neg_negSucc, Int.natCast_mul,
Int.natCast_pow, Int.cast_ofNat_Int]
have : (2 ^ ((prec : Int) + 1)) = ((2 ^ (prec + 1) : Nat) : Rat) := by simp; rfl
rw [Int.negSucc_eq, Rat.zpow_neg, this, Rat.mul_def']
simp only [Rat.num_mkRat, Rat.den_mkRat]
simp only [natCast_pow, natCast_ofNat, den_inv, num_pow, num_ofNat, Int.natAbs_pow,
Int.reduceAbs, num_inv, den_pow, den_ofNat, Nat.one_pow, Int.cast_ofNat_Int, Int.mul_one]
have : ¬ (2 ^ (prec + 1) : Int) = 0 := NeZero.out
simp only [if_neg this]
have : (2 ^ (prec + 1) : Int).sign = 1 := by simpa using Int.pow_pos (by decide)
simp only [this]
have : x.den * 2 ^ (prec + 1) = 0 x.den = 0 := by
rw [Nat.mul_eq_zero]
simp_all
simp only [this, Int.mul_one]
split
· simp_all
· rw [Int.ediv_ediv (Int.ofNat_zero_le _)]
congr 1
rw [Int.natCast_ediv, Int.mul_ediv_cancel']
· simp
· rw [Int.natCast_dvd_natCast]
apply gcd_dvd_left
theorem toRat_toDyadic_le {x : Rat} {prec : Int} : (x.toDyadic prec).toRat x := by
rw [toRat_toDyadic]
have : (x * 2 ^ prec).floor x * 2 ^ prec := Rat.floor_le _
apply Rat.le_of_mul_le_mul_right (c := 2 ^ prec)
rw [Rat.div_mul_cancel]
exact this
· apply Rat.ne_of_gt (Rat.zpow_pos (by decide))
· exact Rat.zpow_pos (by decide)
theorem lt_toRat_toDyadic_add {x : Rat} {prec : Int} :
x < (x.toDyadic prec + ofIntWithPrec 1 prec).toRat := by
rw [toRat_add, toRat_toDyadic, toRat_ofIntWithPrec_eq_mul_two_pow]
have := Rat.lt_floor_add_one (x * 2 ^ prec)
rw [Rat.zpow_neg, Rat.div_def, Rat.add_mul]
apply Rat.lt_of_mul_lt_mul_right (c := 2 ^ prec)
rw [Rat.mul_assoc, Rat.inv_mul_cancel, Rat.mul_one]
exact mod_cast this
· apply Rat.ne_of_gt (Rat.zpow_pos (by decide))
· exact Rat.zpow_nonneg (by decide)
-- TODO: `x.toDyadic prec` is the unique dyadic with the given precision satisfying the two inequalities above.
end Rat
namespace Dyadic
/--
Rounds a dyadic rational `x` down to the greatest dyadic number with precision at most `prec`
which is less than or equal to `x`.
-/
def roundDown (x : Dyadic) (prec : Int) : Dyadic :=
match x with
| .zero => .zero
| .ofOdd n k _ =>
match k - prec with
| .ofNat l => .ofIntWithPrec (n >>> l) prec
| .negSucc _ => x
theorem roundDown_eq_self_of_le {x : Dyadic} {prec : Int} (h : x.precision some prec) :
roundDown x prec = x := by
rcases x with _ | n, k, hn
· rfl
· simp only [precision] at h
obtain a, rfl := h.dest
rcases a with _ | a
· simp [roundDown, ofOdd_eq_ofIntWithPrec]
· have : k - (k + (a + 1 : Nat)) = Int.negSucc a := by omega
simp only [roundDown, this]
@[simp]
theorem toDyadic_toRat (x : Dyadic) (prec : Int) :
x.toRat.toDyadic prec = x.roundDown prec := by
rcases x with _ | n, k, hn
· cases prec <;> simp [Rat.toDyadic, roundDown]
· simp only [toRat_ofOdd_eq_mkRat, roundDown]
rw [Rat.toDyadic_mkRat]
simp only [ Int.shiftLeft_add, Int.natCast_shiftLeft, Int.cast_ofNat_Int]
rw [Int.shiftLeft_eq' 1, Int.one_mul, Int.shiftRight_eq_div_pow]
rw [Int.shiftLeft_shiftRight_eq, Int.toNat_sub, Int.toNat_sub, Int.neg_sub]
have : ((k.toNat + (-prec).toNat : Nat) - ((-k).toNat + prec.toNat : Nat) : Int) = k - prec := by
omega
rw [this]
cases h : k - prec
· simp
· simp only [Int.neg_negSucc, Int.natCast_add, Int.cast_ofNat_Int, Int.toNat_natCast_add_one,
Int.toNat_negSucc, Int.shiftRight_zero]
rw [Int.negSucc_eq, Int.eq_neg_comm, Int.neg_sub, eq_comm, Int.sub_eq_iff_eq_add] at h
simp only [h, Int.natCast_add_one, Int.add_comm _ k, ofIntWithPrec_shiftLeft_add,
ofOdd_eq_ofIntWithPrec]
theorem toRat_inj {x y : Dyadic} : x.toRat = y.toRat x = y := by
refine fun h => ?_, fun h => h rfl
cases x <;> cases y
· rfl
· simp [eq_comm (a := (0 : Rat))] at h
· simp at h
· rename_i n₁ k₁ hn₁ n₂ k₂ hn₂
replace h := congrArg (·.toDyadic (max k₁ k₂)) h
simpa [toDyadic_toRat, roundDown_eq_self_of_le, precision, Int.le_max_left, Int.le_max_right]
using h
theorem add_comm (x y : Dyadic) : x + y = y + x := by
rw [ toRat_inj, toRat_add, toRat_add, Rat.add_comm]
theorem add_assoc (x y z : Dyadic) : (x + y) + z = x + (y + z) := by
rw [ toRat_inj, toRat_add, toRat_add, toRat_add, toRat_add, Rat.add_assoc]
theorem mul_comm (x y : Dyadic) : x * y = y * x := by
rw [ toRat_inj, toRat_mul, toRat_mul, Rat.mul_comm]
theorem mul_assoc (x y z : Dyadic) : (x * y) * z = x * (y * z) := by
rw [ toRat_inj, toRat_mul, toRat_mul, toRat_mul, toRat_mul, Rat.mul_assoc]
theorem mul_one (x : Dyadic) : x * 1 = x := by
rw [ toRat_inj, toRat_mul]
exact Rat.mul_one x.toRat
theorem one_mul (x : Dyadic) : 1 * x = x := by
rw [ toRat_inj, toRat_mul]
exact Rat.one_mul x.toRat
theorem add_mul (x y z : Dyadic) : (x + y) * z = x * z + y * z := by
simp [ toRat_inj, Rat.add_mul]
theorem mul_add (x y z : Dyadic) : x * (y + z) = x * y + x * z := by
simp [ toRat_inj, Rat.mul_add]
theorem neg_add_cancel (x : Dyadic) : -x + x = 0 := by
simp [ toRat_inj, Rat.neg_add_cancel]
theorem neg_mul (x y : Dyadic) : -x * y = -(x * y) := by
simp [ toRat_inj, Rat.neg_mul]
/-- Determine if a dyadic rational is strictly less than another. -/
def blt (x y : Dyadic) : Bool :=
match x, y with
| .zero, .zero => false
| .zero, .ofOdd n₂ _ _ => 0 < n₂
| .ofOdd n₁ _ _, .zero => n₁ < 0
| .ofOdd n₁ k₁ _, .ofOdd n₂ k₂ _ =>
match k₂ - k₁ with
| (l : Nat) => (n₁ <<< l) < n₂
| -((l+1 : Nat)) => n₁ < (n₂ <<< (l + 1))
/-- Determine if a dyadic rational is less than or equal to another. -/
def ble (x y : Dyadic) : Bool :=
match x, y with
| .zero, .zero => true
| .zero, .ofOdd n₂ _ _ => 0 n₂
| .ofOdd n₁ _ _, .zero => n₁ 0
| .ofOdd n₁ k₁ _, .ofOdd n₂ k₂ _ =>
match k₂ - k₁ with
| (l : Nat) => (n₁ <<< l) n₂
| -((l+1 : Nat)) => n₁ (n₂ <<< (l + 1))
theorem blt_iff_toRat {x y : Dyadic} : blt x y x.toRat < y.toRat := by
rcases x with _ | n₁, k₁, hn₁ <;> rcases y with _ | n₂, k₂, hn₂
· decide
· simp only [blt, decide_eq_true_eq, Dyadic.zero_eq, toRat_zero, toRat_ofOdd_eq_mul_two_pow,
Rat.mul_pos_iff_of_pos_right (Rat.zpow_pos (by decide : (0 : Rat) < 2)), Rat.intCast_pos]
· simp only [blt, decide_eq_true_eq, Dyadic.zero_eq, toRat_zero, toRat_ofOdd_eq_mul_two_pow,
Rat.mul_neg_iff_of_pos_right (Rat.zpow_pos (by decide : (0 : Rat) < 2)), Rat.intCast_neg_iff]
· simp only [blt, toRat_ofOdd_eq_mul_two_pow,
Rat.div_lt_iff (Rat.zpow_pos (by decide : (0 : Rat) < 2)), Rat.div_def, Rat.zpow_neg,
Int.neg_neg, Rat.mul_assoc, ne_eq, Rat.ofNat_eq_ofNat, reduceCtorEq, not_false_eq_true,
Rat.zpow_add, Int.shiftLeft_eq]
rw [Int.add_comm, Int.add_neg_eq_sub]
split
· simp [decide_eq_true_eq, Rat.intCast_lt_intCast, Rat.zpow_natCast, *]
· simp only [decide_eq_true_eq, Int.negSucc_eq, *]
rw [Rat.zpow_neg, Rat.div_def, Rat.div_lt_iff (Rat.zpow_pos (by decide))]
simp [ Rat.intCast_lt_intCast, Rat.zpow_natCast, *]
theorem blt_eq_false_iff : blt x y = false ble y x = true := by
cases x <;> cases y
· simp [ble, blt]
· simp [ble, blt]
· simp [ble, blt]
· rename_i n₁ k₁ hn₁ n₂ k₂ hn₂
simp only [blt, ble]
rw [ Int.neg_sub]
rcases k₁ - k₂ with (_ | _) | _
· simp
· simp [ Int.negSucc_eq]
· simp only [Int.neg_negSucc, decide_eq_false_iff_not, Int.not_lt,
decide_eq_true_eq]
theorem ble_iff_toRat : ble x y x.toRat y.toRat := by
rw [ blt_eq_false_iff, Bool.eq_false_iff]
simp only [ne_eq, blt_iff_toRat, Rat.not_lt]
instance : LT Dyadic where
lt x y := blt x y
instance : LE Dyadic where
le x y := ble x y
instance : DecidableLT Dyadic := fun _ _ => inferInstanceAs (Decidable (_ = true))
instance : DecidableLE Dyadic := fun _ _ => inferInstanceAs (Decidable (_ = true))
theorem lt_iff_toRat {x y : Dyadic} : x < y x.toRat < y.toRat := blt_iff_toRat
theorem le_iff_toRat {x y : Dyadic} : x y x.toRat y.toRat := ble_iff_toRat
@[simp]
protected theorem not_le {x y : Dyadic} : ¬x < y y x := by
simp only [· ·, · < ·, Bool.not_eq_true, blt_eq_false_iff]
@[simp]
protected theorem not_lt {x y : Dyadic} : ¬x y y < x := by
rw [ Dyadic.not_le, Decidable.not_not]
@[simp]
protected theorem le_refl (x : Dyadic) : x x := by
rw [le_iff_toRat]
exact Rat.le_refl
protected theorem le_trans {x y z : Dyadic} (h : x y) (h' : y z) : x z := by
rw [le_iff_toRat] at h h'
exact Rat.le_trans h h'
protected theorem le_antisymm {x y : Dyadic} (h : x y) (h' : y x) : x = y := by
rw [le_iff_toRat] at h h'
rw [ toRat_inj]
exact Rat.le_antisymm h h'
protected theorem le_total (x y : Dyadic) : x y y x := by
rw [le_iff_toRat, le_iff_toRat]
exact Rat.le_total
instance : Std.LawfulOrderLT Dyadic where
lt_iff a b := by rw [ Dyadic.not_lt, iff_and_self]; exact (Dyadic.le_total _ _).resolve_left
instance : Std.IsPreorder Dyadic where
le_refl := Dyadic.le_refl
le_trans _ _ _ := Dyadic.le_trans
instance : Std.IsPartialOrder Dyadic where
le_antisymm _ _ := Dyadic.le_antisymm
instance : Std.IsLinearPreorder Dyadic where
le_total := Dyadic.le_total
instance : Std.IsLinearOrder Dyadic where
/-- `roundUp x prec` is the least dyadic number with precision at most `prec` which is greater than or equal to `x`. -/
def roundUp (x : Dyadic) (prec : Int) : Dyadic :=
match x with
| .zero => .zero
| .ofOdd n k _ =>
match k - prec with
| .ofNat l => .ofIntWithPrec (-((-n) >>> l)) prec
| .negSucc _ => x
theorem roundUp_eq_neg_roundDown_neg (x : Dyadic) (prec : Int) :
x.roundUp prec = -((-x).roundDown prec) := by
rcases x with _ | n, k, hn
· rfl
· change _ = -(ofOdd ..).roundDown prec
rw [roundDown, roundUp]
split <;> simp
end Dyadic

View File

@@ -0,0 +1,60 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison, Robin Arnez
-/
module
prelude
public import Init.Data.Dyadic.Basic
public import Init.Grind.Ring.Basic
public import Init.Grind.Ordered.Ring
/-! # Internal `grind` algebra instances for `Dyadic`. -/
open Lean.Grind
namespace Dyadic
instance : CommRing Dyadic where
nsmul := (· * ·)
zsmul := (· * ·)
add_zero := Dyadic.add_zero
add_comm := Dyadic.add_comm
add_assoc := Dyadic.add_assoc
mul_assoc := Dyadic.mul_assoc
mul_one := Dyadic.mul_one
one_mul := Dyadic.one_mul
zero_mul := Dyadic.zero_mul
mul_zero := Dyadic.mul_zero
mul_comm := Dyadic.mul_comm
pow_zero := Dyadic.pow_zero
pow_succ := Dyadic.pow_succ
sub_eq_add_neg _ _ := rfl
neg_add_cancel := Dyadic.neg_add_cancel
neg_zsmul i a := by
change ((-i : Int) : Dyadic) * a = -(i * a)
simp [ toRat_inj, Rat.neg_mul]
left_distrib := Dyadic.mul_add
right_distrib := Dyadic.add_mul
intCast_neg _ := by simp [ toRat_inj]
ofNat_succ n := by
change ((n + 1 : Int) : Dyadic) = ((n : Int) : Dyadic) + 1
simp [ toRat_inj, Rat.intCast_add]; rfl
instance : IsCharP Dyadic 0 := IsCharP.mk' _ _
(ofNat_eq_zero_iff := fun x => by change (x : Dyadic) = 0 _; simp [ toRat_inj])
instance : NoNatZeroDivisors Dyadic where
no_nat_zero_divisors k a b h₁ h₂ := by
change k * a = k * b at h₂
simp only [ toRat_inj, toRat_mul, toRat_natCast] at h₂
simpa [ Rat.mul_assoc, Rat.inv_mul_cancel, h₁] using congrArg ((k : Rat)⁻¹ * ·) h₂
instance : OrderedRing Dyadic where
zero_lt_one := by decide
add_le_left_iff _ := by simp [le_iff_toRat, Rat.add_le_add_right]
mul_lt_mul_of_pos_left {_ _ _} := by simpa [lt_iff_toRat] using Rat.mul_lt_mul_of_pos_left
mul_lt_mul_of_pos_right {_ _ _} := by simpa [lt_iff_toRat] using Rat.mul_lt_mul_of_pos_right
end Dyadic

View File

@@ -0,0 +1,80 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
module
prelude
import Init.Data.Dyadic.Basic
import Init.Data.Dyadic.Round
import Init.Grind.Ordered.Ring
/-!
# Inversion for dyadic numbers
-/
namespace Dyadic
/--
Inverts a dyadic number at a given (maximum) precision.
Returns the greatest dyadic number with precision at most `prec` which is less than or equal to `1/x`.
For `x = 0`, returns `0`.
-/
def invAtPrec (x : Dyadic) (prec : Int) : Dyadic :=
match x with
| .zero => .zero
| _ => x.toRat.inv.toDyadic prec
/-- For a positive dyadic `x`, `invAtPrec x prec * x ≤ 1`. -/
theorem invAtPrec_mul_le_one {x : Dyadic} (hx : 0 < x) (prec : Int) :
invAtPrec x prec * x 1 := by
rw [le_iff_toRat]
rw [toRat_mul]
rw [show (1 : Dyadic).toRat = (1 : Rat) from rfl]
unfold invAtPrec
cases x with
| zero =>
exfalso
contradiction
| ofOdd n k hn =>
simp only
have h_le : ((ofOdd n k hn).toRat.inv.toDyadic prec).toRat (ofOdd n k hn).toRat.inv := Rat.toRat_toDyadic_le
have h_pos : 0 (ofOdd n k hn).toRat := by
rw [lt_iff_toRat, toRat_zero] at hx
exact Rat.le_of_lt hx
calc ((ofOdd n k hn).toRat.inv.toDyadic prec).toRat * (ofOdd n k hn).toRat
(ofOdd n k hn).toRat.inv * (ofOdd n k hn).toRat := Rat.mul_le_mul_of_nonneg_right h_le h_pos
_ = 1 := by
apply Rat.inv_mul_cancel
rw [lt_iff_toRat, toRat_zero] at hx
exact Rat.ne_of_gt hx
/-- For a positive dyadic `x`, `1 < (invAtPrec x prec + 2^(-prec)) * x`. -/
theorem one_lt_invAtPrec_add_inc_mul {x : Dyadic} (hx : 0 < x) (prec : Int) :
1 < (invAtPrec x prec + ofIntWithPrec 1 prec) * x := by
rw [lt_iff_toRat]
rw [toRat_mul]
rw [show (1 : Dyadic).toRat = (1 : Rat) from rfl]
unfold invAtPrec
cases x with
| zero =>
exfalso
contradiction
| ofOdd n k hn =>
simp only
have h_le : (ofOdd n k hn).toRat.inv < ((ofOdd n k hn).toRat.inv.toDyadic prec + ofIntWithPrec 1 prec).toRat :=
Rat.lt_toRat_toDyadic_add
have h_pos : 0 < (ofOdd n k hn).toRat := by
rwa [lt_iff_toRat, toRat_zero] at hx
calc
1 = (ofOdd n k hn).toRat.inv * (ofOdd n k hn).toRat := by
symm
apply Rat.inv_mul_cancel
rw [lt_iff_toRat, toRat_zero] at hx
exact Rat.ne_of_gt hx
_ < ((ofOdd n k hn).toRat.inv.toDyadic prec + ofIntWithPrec 1 prec).toRat * (ofOdd n k hn).toRat :=
Rat.mul_lt_mul_of_pos_right h_le h_pos
-- TODO: `invAtPrec` is the unique dyadic with the given precision satisfying the two inequalities above.
end Dyadic

View File

@@ -0,0 +1,77 @@
/-
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.Dyadic.Basic
import all Init.Data.Dyadic.Instances
import Init.Data.Int.Bitwise.Lemmas
import Init.Grind.Ordered.Rat
import Init.Grind.Ordered.Field
namespace Dyadic
/-!
Theorems about `roundUp` and `roundDown`.
-/
public section
theorem roundDown_le {x : Dyadic} {prec : Int} : roundDown x prec x :=
match x with
| .zero => Dyadic.le_refl _
| .ofOdd n k _ => by
unfold roundDown
dsimp
match h : k - prec with
| .ofNat l =>
dsimp
rw [ofOdd_eq_ofIntWithPrec, le_iff_toRat]
replace h : k = Int.ofNat l + prec := by omega
subst h
simp only [toRat_ofIntWithPrec_eq_mul_two_pow]
rw [Int.neg_add, Rat.zpow_add (by decide), Rat.mul_assoc]
refine Lean.Grind.OrderedRing.mul_le_mul_of_nonneg_right ?_ (Rat.zpow_nonneg (by decide))
rw [Int.shiftRight_eq_div_pow]
rw [ Lean.Grind.Field.IsOrdered.mul_le_mul_iff_of_pos_right (c := 2^(Int.ofNat l)) (Rat.zpow_pos (by decide))]
simp only [Int.natCast_pow, Int.cast_ofNat_Int, Int.ofNat_eq_coe]
rw [Rat.mul_assoc, Rat.zpow_add (by decide), Int.add_left_neg, Rat.zpow_zero, Rat.mul_one]
have : (2 : Rat) ^ (l : Int) = (2 ^ l : Int) := by
rw [Rat.zpow_natCast, Rat.intCast_pow, Rat.intCast_ofNat]
rw [this, Rat.intCast_mul, Rat.intCast_le_intCast]
exact Int.ediv_mul_le n (Int.pow_ne_zero (by decide))
| .negSucc _ =>
apply Dyadic.le_refl
theorem precision_roundDown {x : Dyadic} {prec : Int} : (roundDown x prec).precision some prec := by
unfold roundDown
match x with
| zero => simp [precision]
| ofOdd n k hn =>
dsimp
split
· rename_i n' h
by_cases h' : n >>> n' = 0
· simp [h']
· exact precision_ofIntWithPrec_le h' _
· simp [precision]
omega
-- This theorem would characterize `roundDown` in terms of the order and `precision`.
-- theorem le_roundDown {x y : Dyadic} {prec : Int} (h : y.precision ≤ some prec) (h' : y ≤ x) :
-- y ≤ x.roundDown prec := sorry
theorem le_roundUp {x : Dyadic} {prec : Int} : x roundUp x prec := by
rw [roundUp_eq_neg_roundDown_neg, Lean.Grind.OrderedAdd.le_neg_iff]
apply roundDown_le
theorem precision_roundUp {x : Dyadic} {prec : Int} : (roundUp x prec).precision some prec := by
rw [roundUp_eq_neg_roundDown_neg, precision_neg]
exact precision_roundDown
-- This theorem would characterize `roundUp` in terms of the order and `precision`.
-- theorem roundUp_le {x y : Dyadic} {prec : Int} (h : y.precision ≤ some prec) (h' : x ≤ y) :
-- x.roundUp prec ≤ y := sorry

View File

@@ -25,12 +25,12 @@ namespace Fin
@[deprecated ofNat_zero (since := "2025-05-28")] abbrev ofNat'_zero := @ofNat_zero
theorem mod_def (a m : Fin n) : a % m = Fin.mk (a % m) (Nat.lt_of_le_of_lt (Nat.mod_le _ _) a.2) :=
theorem mod_def (a m : Fin n) : a % m = Fin.mk (a.val % m.val) (Nat.lt_of_le_of_lt (Nat.mod_le _ _) a.2) :=
rfl
theorem mul_def (a b : Fin n) : a * b = Fin.mk ((a * b) % n) (Nat.mod_lt _ a.pos) := rfl
theorem mul_def (a b : Fin n) : a * b = Fin.mk ((a.val * b.val) % n) (Nat.mod_lt _ a.pos) := rfl
theorem sub_def (a b : Fin n) : a - b = Fin.mk (((n - b) + a) % n) (Nat.mod_lt _ a.pos) := rfl
theorem sub_def (a b : Fin n) : a - b = Fin.mk (((n - b.val) + a.val) % n) (Nat.mod_lt _ a.pos) := rfl
theorem pos' : [Nonempty (Fin n)], 0 < n | i => i.pos
@@ -81,7 +81,7 @@ theorem mk_val (i : Fin n) : (⟨i, i.isLt⟩ : Fin n) = i := Fin.eta ..
@[deprecated ofNat_self (since := "2025-05-28")] abbrev ofNat'_self := @ofNat_self
@[simp] theorem ofNat_val_eq_self [NeZero n] (x : Fin n) : (Fin.ofNat n x) = x := by
@[simp] theorem ofNat_val_eq_self [NeZero n] (x : Fin n) : (Fin.ofNat n x.val) = x := by
ext
rw [val_ofNat, Nat.mod_eq_of_lt]
exact x.2
@@ -121,8 +121,6 @@ Non-trivial loops lead to undesirable and counterintuitive elaboration behavior.
For example, for `x : Fin k` and `n : Nat`,
it causes `x < n` to be elaborated as `x < ↑n` rather than `↑x < n`,
silently introducing wraparound arithmetic.
Note: as of 2025-06-03, Mathlib has such a coercion for `Fin n` anyway!
-/
@[expose]
def instNatCast (n : Nat) [NeZero n] : NatCast (Fin n) where
@@ -265,7 +263,7 @@ 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, grind =] theorem val_rev (i : Fin n) : (rev i).val = n - (i + 1) := rfl
@[simp] theorem rev_rev (i : Fin n) : rev (rev i) = i := Fin.ext <| by
rw [val_rev, val_rev, Nat.sub_sub, Nat.sub_sub_self (by exact i.2), Nat.add_sub_cancel]
@@ -500,9 +498,11 @@ theorem succ_succ_ne_one (a : Fin n) : Fin.succ (Fin.succ a) ≠ 1 :=
ext
simp
@[simp] theorem cast_trans {k : Nat} (h : n = m) (h' : m = k) {i : Fin n} :
@[simp, grind =] theorem cast_cast {k : Nat} (h : n = m) (h' : m = k) {i : Fin n} :
(i.cast h).cast h' = i.cast (Eq.trans h h') := rfl
@[deprecated cast_cast (since := "2025-09-03")] abbrev cast_trans := @cast_cast
theorem castLE_of_eq {m n : Nat} (h : m = n) {h' : m n} : castLE h' = Fin.cast h := rfl
@[simp] theorem coe_castAdd (m : Nat) (i : Fin n) : (castAdd m i : Nat) = i := rfl
@@ -531,7 +531,7 @@ theorem cast_castAdd_left {n n' m : Nat} (i : Fin n') (h : n' + m = n + m) :
(i.castAdd m').cast h = i.castAdd m := rfl
theorem castAdd_castAdd {m n p : Nat} (i : Fin m) :
(i.castAdd n).castAdd p = (i.castAdd (n + p)).cast (Nat.add_assoc ..).symm := rfl
(i.castAdd n).castAdd p = (i.castAdd (n + p)).cast (Nat.add_assoc ..).symm := rfl
/-- The cast of the successor is the successor of the cast. See `Fin.succ_cast_eq` for rewriting in
the reverse direction. -/

View File

@@ -31,6 +31,7 @@ This file defines the `Int` type as well as
Division and modulus operations are defined in `Init.Data.Int.DivMod.Basic`.
-/
set_option genCtorIdx false in
/--
The integers.
@@ -320,6 +321,8 @@ def natAbs (m : @& Int) : Nat :=
| ofNat m => m
| -[m +1] => m.succ
attribute [gen_constructor_elims] Int
/-! ## sign -/
/--

View File

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

View File

@@ -7,7 +7,8 @@ module
prelude
public import Init.Data.Nat.Bitwise.Lemmas
public import all Init.Data.Int.Bitwise.Basic
public import Init.Data.Int.Bitwise.Basic
import all Init.Data.Int.Bitwise.Basic
public import Init.Data.Int.DivMod.Lemmas
public section
@@ -16,8 +17,8 @@ namespace Int
theorem shiftRight_eq (n : Int) (s : Nat) : n >>> s = Int.shiftRight n s := rfl
@[simp]
theorem natCast_shiftRight (n s : Nat) : (n : Int) >>> s = n >>> s := rfl
@[simp, norm_cast]
theorem natCast_shiftRight (n s : Nat) : n >>> s = (n : Int) >>> s := rfl
@[simp]
theorem negSucc_shiftRight (m n : Nat) :
@@ -37,11 +38,11 @@ theorem shiftRight_eq_div_pow (m : Int) (n : Nat) :
· rw [negSucc_ediv _ (by norm_cast; exact Nat.pow_pos (Nat.zero_lt_two))]
rfl
@[simp]
@[simp, grind =]
theorem zero_shiftRight (n : Nat) : (0 : Int) >>> n = 0 := by
simp [Int.shiftRight_eq_div_pow]
@[simp]
@[simp, grind =]
theorem shiftRight_zero (n : Int) : n >>> 0 = n := by
simp [Int.shiftRight_eq_div_pow]
@@ -67,7 +68,7 @@ theorem shiftRight_le_of_nonneg {n : Int} {s : Nat} (h : 0 ≤ n) : n >>> s ≤
by_cases hm : m = 0
· simp [hm]
· have := Nat.shiftRight_le m s
simp
rw [ofNat_eq_coe]
omega
case _ _ _ m =>
omega
@@ -88,4 +89,94 @@ theorem shiftRight_le_of_nonpos {n : Int} {s : Nat} (h : n ≤ 0) : (n >>> s)
have rl : n / 2 ^ s 0 := Int.ediv_nonpos_of_nonpos_of_neg (by omega) (by norm_cast at *; omega)
norm_cast at *
@[simp, norm_cast]
theorem natCast_shiftLeft (n s : Nat) : n <<< s = (n : Int) <<< s := rfl
@[simp, grind =]
theorem zero_shiftLeft (n : Nat) : (0 : Int) <<< n = 0 := by
change ((0 <<< n : Nat) : Int) = 0
simp
@[simp, grind =]
theorem shiftLeft_zero (n : Int) : n <<< 0 = n := by
change Int.shiftLeft _ _ = _
match n with
| Int.ofNat n
| Int.negSucc n => simp [Int.shiftLeft]
theorem shiftLeft_succ (m : Int) (n : Nat) : m <<< (n + 1) = (m <<< n) * 2 := by
change Int.shiftLeft _ _ = Int.shiftLeft _ _ * 2
match m with
| (m : Nat) =>
dsimp only [Int.shiftLeft, Int.ofNat_eq_coe]
rw [Nat.shiftLeft_succ, Nat.mul_comm, natCast_mul, ofNat_two]
| Int.negSucc m =>
dsimp only [Int.shiftLeft]
rw [Nat.shiftLeft_succ, Nat.mul_comm, Int.negSucc_eq]
have := Nat.le_shiftLeft (a := m + 1) (b := n)
omega
theorem shiftLeft_succ' (m : Int) (n : Nat) : m <<< (n + 1) = 2 * (m <<< n) := by
rw [shiftLeft_succ, Int.mul_comm]
theorem shiftLeft_eq (a : Int) (b : Nat) : a <<< b = a * 2 ^ b := by
induction b with
| zero => simp
| succ b ih =>
rw [shiftLeft_succ, ih, Int.pow_succ, Int.mul_assoc]
theorem shiftLeft_eq' (a : Int) (b : Nat) : a <<< b = a * (2 ^ b : Nat) := by
simp [shiftLeft_eq]
theorem shiftLeft_add (a : Int) (b c : Nat) : a <<< (b + c) = a <<< b <<< c := by
simp [shiftLeft_eq, Int.pow_add, Int.mul_assoc]
@[simp]
theorem shiftLeft_shiftRight_cancel (a : Int) (b : Nat) : a <<< b >>> b = a := by
simp [shiftLeft_eq, shiftRight_eq_div_pow, mul_ediv_cancel _ (NeZero.ne _)]
theorem shiftLeft_shiftRight_eq_shiftLeft_of_le {b c : Nat} (h : c b) (a : Int) :
a <<< b >>> c = a <<< (b - c) := by
obtain b, rfl := h.dest
simp [shiftLeft_eq, Int.pow_add, shiftRight_eq_div_pow, Int.mul_left_comm a,
Int.mul_ediv_cancel_left _ (NeZero.ne _)]
theorem shiftLeft_shiftRight_eq_shiftRight_of_le {b c : Nat} (h : b c) (a : Int) :
a <<< b >>> c = a >>> (c - b) := by
obtain c, rfl := h.dest
simp [shiftRight_add]
theorem shiftLeft_shiftRight_eq (a : Int) (b c : Nat) :
a <<< b >>> c = a <<< (b - c) >>> (c - b) := by
rcases Nat.le_total b c with h | h
· simp [shiftLeft_shiftRight_eq_shiftRight_of_le h, Nat.sub_eq_zero_of_le h]
· simp [shiftLeft_shiftRight_eq_shiftLeft_of_le h, Nat.sub_eq_zero_of_le h]
@[simp]
theorem shiftRight_shiftLeft_cancel {a : Int} {b : Nat} (h : 2 ^ b a) : a >>> b <<< b = a := by
simp [shiftLeft_eq, shiftRight_eq_div_pow, Int.ediv_mul_cancel h]
theorem add_shiftLeft (a b : Int) (n : Nat) : (a + b) <<< n = a <<< n + b <<< n := by
simp [shiftLeft_eq, Int.add_mul]
theorem neg_shiftLeft (a : Int) (n : Nat) : (-a) <<< n = -a <<< n := by
simp [Int.shiftLeft_eq, Int.neg_mul]
theorem shiftLeft_mul (a b : Int) (n : Nat) : a <<< n * b = (a * b) <<< n := by
simp [shiftLeft_eq, Int.mul_right_comm]
theorem mul_shiftLeft (a b : Int) (n : Nat) : a * b <<< n = (a * b) <<< n := by
simp [shiftLeft_eq, Int.mul_assoc]
theorem shiftLeft_mul_shiftLeft (a b : Int) (m n : Nat) :
a <<< m * b <<< n = (a * b) <<< (m + n) := by
simp [shiftLeft_mul, mul_shiftLeft, shiftLeft_add]
@[simp]
theorem shiftLeft_eq_zero_iff {a : Int} {n : Nat} : a <<< n = 0 a = 0 := by
simp [shiftLeft_eq, Int.mul_eq_zero, NeZero.ne]
instance {a : Int} {n : Nat} [NeZero a] : NeZero (a <<< n) :=
mt shiftLeft_eq_zero_iff.mp (NeZero.ne _)
end Int

View File

@@ -6,7 +6,8 @@ Authors: Leonardo de Moura, Jeremy Avigad, Mario Carneiro, Paul Reichert
module
prelude
public import all Init.Data.Ord.Basic
public import Init.Data.Ord.Basic
import all Init.Data.Ord.Basic
public import Init.Data.Int.Order
public section

View File

@@ -97,7 +97,7 @@ theorem ofNat_emod (m n : Nat) : (↑(m % n) : Int) = m % n := natCast_emod m n
/-! ### mod definitions -/
theorem emod_add_ediv : a b : Int, a % b + b * (a / b) = a
theorem emod_add_mul_ediv : a b : Int, a % b + b * (a / b) = a
| ofNat _, ofNat _ => congrArg ofNat <| Nat.mod_add_div ..
| ofNat m, -[n+1] => by
change (m % succ n + -(succ n) * -(m / succ n) : Int) = m
@@ -111,19 +111,35 @@ where
Int.neg_neg (_-_), Int.neg_sub, Int.sub_sub_self, Int.add_right_comm]
exact congrArg (fun x => -(ofNat x + 1)) (Nat.mod_add_div ..)
/-- Variant of `emod_add_ediv` with the multiplication written the other way around. -/
theorem emod_add_ediv' (a b : Int) : a % b + a / b * b = a := by
rw [Int.mul_comm]; exact emod_add_ediv ..
@[deprecated emod_add_mul_ediv (since := "2025-09-01")]
def emod_add_ediv := @emod_add_mul_ediv
theorem ediv_add_emod (a b : Int) : b * (a / b) + a % b = a := by
rw [Int.add_comm]; exact emod_add_ediv ..
theorem emod_add_ediv_mul (a b : Int) : a % b + a / b * b = a := by
rw [Int.mul_comm]; exact emod_add_mul_ediv ..
/-- Variant of `ediv_add_emod` with the multiplication written the other way around. -/
theorem ediv_add_emod' (a b : Int) : a / b * b + a % b = a := by
rw [Int.mul_comm]; exact ediv_add_emod ..
@[deprecated emod_add_ediv_mul (since := "2025-09-01")]
def emod_add_ediv' := @emod_add_ediv_mul
theorem mul_ediv_add_emod (a b : Int) : b * (a / b) + a % b = a := by
rw [Int.add_comm]; exact emod_add_mul_ediv ..
@[deprecated mul_ediv_add_emod (since := "2025-09-01")]
def ediv_add_emod := @mul_ediv_add_emod
theorem ediv_mul_add_emod (a b : Int) : a / b * b + a % b = a := by
rw [Int.mul_comm]; exact mul_ediv_add_emod ..
@[deprecated ediv_mul_add_emod (since := "2025-09-01")]
def ediv_add_emod' := @ediv_mul_add_emod
theorem emod_def (a b : Int) : a % b = a - b * (a / b) := by
rw [ Int.add_sub_cancel (a % b), emod_add_ediv]
rw [ Int.add_sub_cancel (a % b), emod_add_mul_ediv]
theorem mul_ediv_self (a b : Int) : b * (a / b) = a - a % b := by
rw [emod_def, Int.sub_sub_self]
theorem ediv_mul_self (a b : Int) : a / b * b = a - a % b := by
rw [Int.mul_comm, emod_def, Int.sub_sub_self]
/-! ### `/` ediv -/
@@ -226,7 +242,7 @@ theorem add_mul_emod_self {a b c : Int} : (a + b * c) % c = a % c :=
@[simp] theorem emod_add_emod (m n k : Int) : (m % n + k) % n = (m + k) % n := by
have := (add_mul_emod_self_left (m % n + k) n (m / n)).symm
rwa [Int.add_right_comm, emod_add_ediv] at this
rwa [Int.add_right_comm, emod_add_mul_ediv] at this
@[simp] theorem add_emod_emod (m n k : Int) : (m + n % k) % k = (m + n) % k := by
rw [Int.add_comm, emod_add_emod, Int.add_comm]
@@ -252,7 +268,7 @@ theorem emod_add_cancel_right {m n k : Int} (i) : (m + i) % n = (k + i) % n ↔
theorem mul_emod (a b n : Int) : (a * b) % n = (a % n) * (b % n) % n := by
conv => lhs; rw [
emod_add_ediv a n, emod_add_ediv' b n, Int.add_mul, Int.mul_add, Int.mul_add,
emod_add_mul_ediv a n, emod_add_ediv_mul b n, Int.add_mul, Int.mul_add, Int.mul_add,
Int.mul_assoc, Int.mul_assoc, Int.mul_add n _ _, add_mul_emod_self_left,
Int.mul_assoc, add_mul_emod_self_right]
@@ -261,7 +277,7 @@ theorem mul_emod (a b n : Int) : (a * b) % n = (a % n) * (b % n) % n := by
@[simp] theorem emod_emod_of_dvd (n : Int) {m k : Int}
(h : m k) : (n % k) % m = n % m := by
conv => rhs; rw [ emod_add_ediv n k]
conv => rhs; rw [ emod_add_mul_ediv n k]
match k, h with
| _, t, rfl => rw [Int.mul_assoc, add_mul_emod_self_left]
@@ -275,7 +291,7 @@ theorem sub_emod (a b n : Int) : (a - b) % n = (a % n - b % n) % n := by
/-! ### properties of `/` and `%` -/
theorem mul_ediv_cancel_of_emod_eq_zero {a b : Int} (H : a % b = 0) : b * (a / b) = a := by
have := emod_add_ediv a b; rwa [H, Int.zero_add] at this
have := emod_add_mul_ediv a b; rwa [H, Int.zero_add] at this
theorem ediv_mul_cancel_of_emod_eq_zero {a b : Int} (H : a % b = 0) : a / b * b = a := by
rw [Int.mul_comm, mul_ediv_cancel_of_emod_eq_zero H]
@@ -326,11 +342,11 @@ theorem emod_pos_of_not_dvd {a b : Int} (h : ¬ a b) : a = 0 0 < b % a :
theorem mul_ediv_self_le {x k : Int} (h : k 0) : k * (x / k) x :=
calc k * (x / k)
_ k * (x / k) + x % k := Int.le_add_of_nonneg_right (emod_nonneg x h)
_ = x := ediv_add_emod _ _
_ = x := mul_ediv_add_emod _ _
theorem lt_mul_ediv_self_add {x k : Int} (h : 0 < k) : x < k * (x / k) + k :=
calc x
_ = k * (x / k) + x % k := (ediv_add_emod _ _).symm
_ = k * (x / k) + x % k := (mul_ediv_add_emod _ _).symm
_ < k * (x / k) + k := Int.add_lt_add_left (emod_lt_of_pos x h) _
/-! ### bmod -/

View File

@@ -26,6 +26,10 @@ namespace Int
@[simp high] theorem natCast_eq_zero {n : Nat} : (n : Int) = 0 n = 0 := by omega
instance {n : Nat} [NeZero n] : NeZero (n : Int) := mt Int.natCast_eq_zero.mp (NeZero.ne _)
instance {n : Nat} [NeZero n] : NeZero (no_index (OfNat.ofNat n) : Int) :=
mt Int.natCast_eq_zero.mp (NeZero.ne _)
protected theorem exists_add_of_le {a b : Int} (h : a b) : (c : Nat), b = a + c :=
(b - a).toNat, by omega
@@ -330,7 +334,7 @@ theorem fdiv_eq_ediv_of_dvd {a b : Int} (h : b a) : a.fdiv b = a / b := by
/-! ### mod definitions -/
theorem tmod_add_tdiv : a b : Int, tmod a b + b * (a.tdiv b) = a
theorem tmod_add_mul_tdiv : a b : Int, tmod a b + b * (a.tdiv b) = a
| ofNat _, ofNat _ => congrArg ofNat (Nat.mod_add_div ..)
| ofNat m, -[n+1] => by
change (m % succ n + -(succ n) * -(m / succ n) : Int) = m
@@ -347,21 +351,37 @@ theorem tmod_add_tdiv : ∀ a b : Int, tmod a b + b * (a.tdiv b) = a
rw [Int.neg_mul, Int.neg_add]
exact congrArg (-ofNat ·) (Nat.mod_add_div ..)
theorem tdiv_add_tmod (a b : Int) : b * a.tdiv b + tmod a b = a := by
rw [Int.add_comm]; apply tmod_add_tdiv ..
@[deprecated tmod_add_mul_tdiv (since := "2025-09-01")]
def tmod_add_tdiv := @tmod_add_mul_tdiv
/-- Variant of `tmod_add_tdiv` with the multiplication written the other way around. -/
theorem tmod_add_tdiv' (m k : Int) : tmod m k + m.tdiv k * k = m := by
rw [Int.mul_comm]; apply tmod_add_tdiv
theorem mul_tdiv_add_tmod (a b : Int) : b * a.tdiv b + tmod a b = a := by
rw [Int.add_comm]; apply tmod_add_mul_tdiv ..
/-- Variant of `tdiv_add_tmod` with the multiplication written the other way around. -/
theorem tdiv_add_tmod' (m k : Int) : m.tdiv k * k + tmod m k = m := by
rw [Int.mul_comm]; apply tdiv_add_tmod
@[deprecated mul_tdiv_add_tmod (since := "2025-09-01")]
def tdiv_add_tmod := @mul_tdiv_add_tmod
theorem tmod_add_tdiv_mul (m k : Int) : tmod m k + m.tdiv k * k = m := by
rw [Int.mul_comm]; apply tmod_add_mul_tdiv
@[deprecated tmod_add_tdiv_mul (since := "2025-09-01")]
def tmod_add_tdiv' := @tmod_add_mul_tdiv
theorem tdiv_mul_add_tmod (m k : Int) : m.tdiv k * k + tmod m k = m := by
rw [Int.mul_comm]; apply mul_tdiv_add_tmod
@[deprecated tdiv_mul_add_tmod (since := "2025-09-01")]
def tdiv_add_tmod' := @tdiv_mul_add_tmod
theorem tmod_def (a b : Int) : tmod a b = a - b * a.tdiv b := by
rw [ Int.add_sub_cancel (tmod a b), tmod_add_tdiv]
rw [ Int.add_sub_cancel (tmod a b), tmod_add_mul_tdiv]
theorem fmod_add_fdiv : a b : Int, a.fmod b + b * a.fdiv b = a
theorem mul_tdiv_self (a b : Int) : b * (a.tdiv b) = a - a.tmod b := by
rw [tmod_def, Int.sub_sub_self]
theorem tdiv_mul_self (a b : Int) : a.tdiv b * b = a - a.tmod b := by
rw [Int.mul_comm, tmod_def, Int.sub_sub_self]
theorem fmod_add_mul_fdiv : a b : Int, a.fmod b + b * a.fdiv b = a
| 0, ofNat _ | 0, -[_+1] => congrArg ofNat <| by simp
| succ _, ofNat _ => congrArg ofNat <| Nat.mod_add_div ..
| succ m, -[n+1] => by
@@ -378,19 +398,35 @@ theorem fmod_add_fdiv : ∀ a b : Int, a.fmod b + b * a.fdiv b = a
change -((succ m % succ n) : Int) + -(succ n * (succ m / succ n)) = -(succ m)
rw [ Int.neg_add]; exact congrArg (-ofNat ·) <| Nat.mod_add_div ..
/-- Variant of `fmod_add_fdiv` with the multiplication written the other way around. -/
theorem fmod_add_fdiv' (a b : Int) : a.fmod b + (a.fdiv b) * b = a := by
rw [Int.mul_comm]; exact fmod_add_fdiv ..
@[deprecated fmod_add_mul_fdiv (since := "2025-09-01")]
def fmod_add_fdiv := @fmod_add_mul_fdiv
theorem fdiv_add_fmod (a b : Int) : b * a.fdiv b + a.fmod b = a := by
rw [Int.add_comm]; exact fmod_add_fdiv ..
theorem fmod_add_fdiv_mul (a b : Int) : a.fmod b + (a.fdiv b) * b = a := by
rw [Int.mul_comm]; exact fmod_add_mul_fdiv ..
/-- Variant of `fdiv_add_fmod` with the multiplication written the other way around. -/
theorem fdiv_add_fmod' (a b : Int) : (a.fdiv b) * b + a.fmod b = a := by
rw [Int.mul_comm]; exact fdiv_add_fmod ..
@[deprecated fmod_add_fdiv_mul (since := "2025-09-01")]
def fmod_add_fdiv' := @fmod_add_fdiv_mul
theorem mul_fdiv_add_fmod (a b : Int) : b * a.fdiv b + a.fmod b = a := by
rw [Int.add_comm]; exact fmod_add_mul_fdiv ..
@[deprecated mul_fdiv_add_fmod (since := "2025-09-01")]
def fdiv_add_fmod := @mul_fdiv_add_fmod
theorem fdiv_mul_add_fmod (a b : Int) : (a.fdiv b) * b + a.fmod b = a := by
rw [Int.mul_comm]; exact mul_fdiv_add_fmod ..
@[deprecated mul_fdiv_add_fmod (since := "2025-09-01")]
def fdiv_add_fmod' := @mul_fdiv_add_fmod
theorem fmod_def (a b : Int) : a.fmod b = a - b * a.fdiv b := by
rw [ Int.add_sub_cancel (a.fmod b), fmod_add_fdiv]
rw [ Int.add_sub_cancel (a.fmod b), fmod_add_mul_fdiv]
theorem mul_fdiv_self (a b : Int) : b * (a.fdiv b) = a - a.fmod b := by
rw [fmod_def, Int.sub_sub_self]
theorem fdiv_mul_self (a b : Int) : a.fdiv b * b = a - a.fmod b := by
rw [Int.mul_comm, fmod_def, Int.sub_sub_self]
/-! ### mod equivalences -/
@@ -769,7 +805,7 @@ protected theorem ediv_emod_unique {a b r q : Int} (h : 0 < b) :
a / b = q a % b = r r + b * q = a 0 r r < b := by
constructor
· intro rfl, rfl
exact emod_add_ediv a b, emod_nonneg _ (Int.ne_of_gt h), emod_lt_of_pos _ h
exact emod_add_mul_ediv a b, emod_nonneg _ (Int.ne_of_gt h), emod_lt_of_pos _ h
· intro rfl, hz, hb
constructor
· rw [Int.add_mul_ediv_left r q (Int.ne_of_gt h), ediv_eq_zero_of_lt hz hb]
@@ -793,7 +829,7 @@ theorem neg_ediv {a b : Int} : (-a) / b = -(a / b) - if b a then 0 else b.si
if hb : b = 0 then
simp [hb]
else
conv => lhs; rw [ ediv_add_emod a b]
conv => lhs; rw [ mul_ediv_add_emod a b]
rw [Int.neg_add, Int.mul_neg, mul_add_ediv_left _ _ hb, Int.add_comm]
split <;> rename_i h
· rw [emod_eq_zero_of_dvd h]
@@ -1083,6 +1119,10 @@ theorem emod_natAbs_of_neg {x : Int} (h : x < 0) {n : Nat} (w : n ≠ 0) :
protected theorem ediv_mul_le (a : Int) {b : Int} (H : b 0) : a / b * b a :=
Int.le_of_sub_nonneg <| by rw [Int.mul_comm, emod_def]; apply emod_nonneg _ H
protected theorem lt_ediv_mul (a : Int) {b : Int} (H : 0 < b) : a - b < a / b * b := by
rw [ediv_mul_self, Int.sub_lt_sub_left_iff]
exact emod_lt_of_pos a H
theorem le_of_mul_le_mul_left {a b c : Int} (w : a * b a * c) (h : 0 < a) : b c := by
have w := Int.sub_nonneg_of_le w
rw [ Int.mul_sub] at w
@@ -1173,9 +1213,9 @@ theorem ediv_eq_iff_of_pos {k x y : Int} (h : 0 < k) : x / k = y ↔ y * k ≤ x
theorem add_ediv_of_pos {a b c : Int} (h : 0 < c) :
(a + b) / c = a / c + b / c + if c a % c + b % c then 1 else 0 := by
have h' : c 0 := by omega
conv => lhs; rw [ Int.ediv_add_emod a c]
conv => lhs; rw [ Int.mul_ediv_add_emod a c]
rw [Int.add_assoc, Int.mul_add_ediv_left _ _ h']
conv => lhs; rw [ Int.ediv_add_emod b c]
conv => lhs; rw [ Int.mul_ediv_add_emod b c]
rw [Int.add_comm (a % c), Int.add_assoc, Int.mul_add_ediv_left _ _ h',
Int.add_assoc, Int.add_comm (b % c)]
congr
@@ -1206,7 +1246,7 @@ theorem not_dvd_iff_lt_mul_succ (m : Int) (hn : 0 < n) :
¬n m ( k, n * k < m m < n * (k + 1)) := by
refine fun h ?_, ?_
· rw [dvd_iff_emod_eq_zero, Ne] at h
rw [ emod_add_ediv m n]
rw [ emod_add_mul_ediv m n]
refine m / n, Int.lt_add_of_pos_left _ ?_, ?_
· have := emod_nonneg m (Int.ne_of_gt hn)
omega
@@ -1218,6 +1258,26 @@ theorem not_dvd_iff_lt_mul_succ (m : Int) (hn : 0 < n) :
rw [Int.lt_add_one_iff, Int.not_lt] at h2k
exact h2k h1k
private theorem ediv_ediv_of_pos {x y z : Int} (hy : 0 < y) (hz : 0 < z) :
x / y / z = x / (y * z) := by
rw [eq_comm, Int.ediv_eq_iff_of_pos (Int.mul_pos hy hz)]
constructor
· rw [Int.mul_comm y, Int.mul_assoc]
exact Int.le_trans
(Int.mul_le_mul_of_nonneg_right (Int.ediv_mul_le _ (Int.ne_of_gt hz)) (Int.le_of_lt hy))
(Int.ediv_mul_le x (Int.ne_of_gt hy))
· rw [Int.mul_comm y, Int.mul_assoc, Int.add_mul, Int.mul_comm _ z]
exact Int.lt_mul_of_ediv_lt hy (Int.lt_mul_ediv_self_add hz)
theorem ediv_ediv {x y z : Int} (hy : 0 y) : x / y / z = x / (y * z) := by
rcases y with (_ | a) | a
· simp
· rcases z with (_ | b) | b
· simp
· simp [ediv_ediv_of_pos]
· simp [Int.negSucc_eq, Int.mul_neg, ediv_ediv_of_pos]
· simp at hy
/-! ### tdiv -/
-- `tdiv` analogues of `ediv` lemmas from `Bootstrap.lean`
@@ -1461,7 +1521,7 @@ theorem sign_tmod (a b : Int) : sign (tmod a b) = if b a then 0 else sign a
-- Analogues of statements about `ediv` and `emod` from `Bootstrap.lean`
theorem mul_tdiv_cancel_of_tmod_eq_zero {a b : Int} (H : a.tmod b = 0) : b * (a.tdiv b) = a := by
have := tmod_add_tdiv a b; rwa [H, Int.zero_add] at this
have := tmod_add_mul_tdiv a b; rwa [H, Int.zero_add] at this
theorem tdiv_mul_cancel_of_tmod_eq_zero {a b : Int} (H : a.tmod b = 0) : a.tdiv b * b = a := by
rw [Int.mul_comm, mul_tdiv_cancel_of_tmod_eq_zero H]
@@ -2186,7 +2246,7 @@ theorem fmod_add_cancel_right {m n k : Int} (i) : (m + i).fmod n = (k + i).fmod
theorem mul_fmod (a b n : Int) : (a * b).fmod n = (a.fmod n * b.fmod n).fmod n := by
conv => lhs; rw [
fmod_add_fdiv a n, fmod_add_fdiv' b n, Int.add_mul, Int.mul_add, Int.mul_add,
fmod_add_mul_fdiv a n, fmod_add_fdiv_mul b n, Int.add_mul, Int.mul_add, Int.mul_add,
Int.mul_assoc, Int.mul_assoc, Int.mul_add n _ _, add_mul_fmod_self_left,
Int.mul_assoc, add_mul_fmod_self_right]
@@ -2195,7 +2255,7 @@ theorem mul_fmod (a b n : Int) : (a * b).fmod n = (a.fmod n * b.fmod n).fmod n :
@[simp] theorem fmod_fmod_of_dvd (n : Int) {m k : Int}
(h : m k) : (n.fmod k).fmod m = n.fmod m := by
conv => rhs; rw [ fmod_add_fdiv n k]
conv => rhs; rw [ fmod_add_mul_fdiv n k]
match k, h with
| _, t, rfl => rw [Int.mul_assoc, add_mul_fmod_self_left]
@@ -2225,7 +2285,7 @@ theorem fmod_eq_of_lt {a b : Int} (H1 : 0 ≤ a) (H2 : a < b) : a.fmod b = a :=
-- Analogues of properties of `ediv` and `emod` from `Bootstrap.lean`
theorem mul_fdiv_cancel_of_fmod_eq_zero {a b : Int} (H : a.fmod b = 0) : b * (a.fdiv b) = a := by
have := fmod_add_fdiv a b; rwa [H, Int.zero_add] at this
have := fmod_add_mul_fdiv a b; rwa [H, Int.zero_add] at this
theorem fdiv_mul_cancel_of_fmod_eq_zero {a b : Int} (H : a.fmod b = 0) : (a.fdiv b) * b= a := by
rw [Int.mul_comm, mul_fdiv_cancel_of_fmod_eq_zero H]
@@ -2467,9 +2527,9 @@ theorem bdiv_add_bmod (x : Int) (m : Nat) : m * bdiv x m + bmod x m = x := by
ite_self]
· dsimp only
split
· exact ediv_add_emod x m
· exact mul_ediv_add_emod x m
· rw [Int.mul_add, Int.mul_one, Int.add_assoc, Int.add_comm m, Int.sub_add_cancel]
exact ediv_add_emod x m
exact mul_ediv_add_emod x m
theorem bmod_add_bdiv (x : Int) (m : Nat) : bmod x m + m * bdiv x m = x := by
rw [Int.add_comm]; exact bdiv_add_bmod x m
@@ -2726,7 +2786,7 @@ theorem le_bmod {x : Int} {m : Nat} (h : 0 < m) : - (m/2) ≤ Int.bmod x m := by
· exact Int.ne_of_gt (natCast_pos.mpr h)
· simp [Int.not_lt] at w
refine Int.le_trans ?_ (Int.sub_le_sub_right w _)
rw [ ediv_add_emod m 2]
rw [ mul_ediv_add_emod m 2]
generalize (m : Int) / 2 = q
generalize h : (m : Int) % 2 = r at *
rcases v with rfl | rfl
@@ -2887,7 +2947,7 @@ theorem neg_bmod {a : Int} {b : Nat} :
simp only [gt_iff_lt, Nat.zero_lt_succ, Nat.mul_pos_iff_of_pos_left, Int.natCast_mul,
cast_ofNat_Int, Int.not_lt] at *
rw [Int.mul_dvd_mul_iff_left (by omega)]
have := ediv_add_emod a (2 * c)
have := mul_ediv_add_emod a (2 * c)
rw [(by omega : a % (2 * c) = c)] at this
rw [ this]
apply Int.dvd_add _ (by simp)

View File

@@ -40,7 +40,7 @@ theorem ofNat_succ (n : Nat) : (succ n : Int) = n + 1 := rfl
theorem neg_ofNat_zero : -((0 : Nat) : Int) = 0 := rfl
theorem neg_ofNat_succ (n : Nat) : -(succ n : Int) = -[n+1] := rfl
theorem neg_negSucc (n : Nat) : -(-[n+1]) = succ n := rfl
@[simp] theorem neg_negSucc (n : Nat) : -(-[n+1]) = ((n + 1 : Nat) : Int) := rfl
theorem negOfNat_eq : negOfNat n = -ofNat n := rfl
@@ -566,6 +566,9 @@ protected theorem mul_eq_zero {a b : Int} : a * b = 0 ↔ a = 0 b = 0 := by
protected theorem mul_ne_zero {a b : Int} (a0 : a 0) (b0 : b 0) : a * b 0 :=
Or.rec a0 b0 Int.mul_eq_zero.mp
instance {a b : Int} [NeZero a] [NeZero b] : NeZero (a * b) :=
Int.mul_ne_zero (NeZero.ne _) (NeZero.ne _)
@[simp] protected theorem mul_ne_zero_iff {a b : Int} : a * b 0 a 0 b 0 := by
rw [ne_eq, Int.mul_eq_zero, not_or, ne_eq]

View File

@@ -12,9 +12,11 @@ public import Init.Data.Int.Lemmas
public import Init.Data.Int.LemmasAux
public import Init.Data.Int.DivMod.Bootstrap
public import Init.Data.Int.Cooper
public import all Init.Data.Int.Gcd
public import Init.Data.Int.Gcd
import all Init.Data.Int.Gcd
public import Init.Data.RArray
public import all Init.Data.AC
public import Init.Data.AC
import all Init.Data.AC
public section
@@ -37,7 +39,7 @@ inductive Expr where
| neg (a : Expr)
| mulL (k : Int) (a : Expr)
| mulR (a : Expr) (k : Int)
deriving Inhabited, BEq
deriving Inhabited, @[expose] BEq
@[expose]
def Expr.denote (ctx : Context) : Expr Int
@@ -52,7 +54,7 @@ def Expr.denote (ctx : Context) : Expr → Int
inductive Poly where
| num (k : Int)
| add (k : Int) (v : Var) (p : Poly)
deriving BEq
deriving @[expose] BEq
@[expose]
protected noncomputable def Poly.beq' (p₁ : Poly) : Poly Bool :=
@@ -245,7 +247,7 @@ def cmod (a b : Int) : Int :=
theorem cdiv_add_cmod (a b : Int) : b*(cdiv a b) + cmod a b = a := by
unfold cdiv cmod
have := Int.ediv_add_emod (-a) b
have := Int.mul_ediv_add_emod (-a) b
have := congrArg (Neg.neg) this
simp at this
conv => rhs; rw[ this]
@@ -270,7 +272,7 @@ private abbrev div_mul_cancel_of_mod_zero :=
theorem cdiv_eq_div_of_divides {a b : Int} (h : a % b = 0) : a/b = cdiv a b := by
replace h := div_mul_cancel_of_mod_zero h
have hz : a % b = 0 := by
have := Int.ediv_add_emod a b
have := Int.mul_ediv_add_emod a b
conv at this => rhs; rw [ Int.add_zero a]
rw [Int.mul_comm, h] at this
exact Int.add_left_cancel this
@@ -1751,7 +1753,7 @@ theorem cooper_right_split_dvd (ctx : Context) (p₁ p₂ : Poly) (k : Nat) (b :
intros; subst b p'; simp; assumption
private theorem one_emod_eq_one {a : Int} (h : a > 1) : 1 % a = 1 := by
have aux₁ := Int.ediv_add_emod 1 a
have aux₁ := Int.mul_ediv_add_emod 1 a
have : 1 / a = 0 := Int.ediv_eq_zero_of_lt (by decide) h
simp [this] at aux₁
assumption
@@ -1778,7 +1780,7 @@ private theorem ex_of_dvd {α β a b d x : Int}
rw [Int.mul_emod, aux₁, Int.one_mul, Int.emod_emod] at this
assumption
have : x = (x / d)*d + (- α * b) % d := by
conv => lhs; rw [ Int.ediv_add_emod x d]
conv => lhs; rw [ Int.mul_ediv_add_emod x d]
rw [Int.mul_comm, this]
exists x / d
@@ -1861,7 +1863,7 @@ theorem cooper_unsat (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (α β :
exact cooper_unsat' h₁ h₂ h₃ h₄ h₅ h₆
theorem ediv_emod (x y : Int) : -1 * x + y * (x / y) + x % y = 0 := by
rw [Int.add_assoc, Int.ediv_add_emod x y, Int.add_comm]
rw [Int.add_assoc, Int.mul_ediv_add_emod x y, Int.add_comm]
simp
rw [Int.add_neg_eq_sub, Int.sub_self]
@@ -2196,6 +2198,9 @@ 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
theorem pow_eq (a : Int) (b : Nat) (a' b' k : Int) (h₁ : a = a') (h₂ : b = b') (h₃ : k == a'^b'.toNat) : a^b = k := by
simp [ h₁, h₂] at h₃; simp [h₃]
end Int.Linear
theorem Int.not_le_eq (a b : Int) : (¬a b) = (b + 1 a) := by

View File

@@ -701,10 +701,13 @@ theorem toNat_sub_toNat_neg : ∀ n : Int, ↑n.toNat - ↑(-n).toNat = n
| (_+1:Nat) => Nat.add_zero _
| -[_+1] => Nat.zero_add _
@[simp] theorem toNat_neg_nat : n : Nat, (-(n : Int)).toNat = 0
@[simp] theorem toNat_neg_natCast : n : Nat, (-(n : Int)).toNat = 0
| 0 => rfl
| _+1 => rfl
@[deprecated toNat_neg_natCast (since := "2025-08-29")]
theorem toNat_neg_nat : n : Nat, (-(n : Int)).toNat = 0 := toNat_neg_natCast
/-! ### toNat? -/
theorem mem_toNat? : {a : Int} {n : Nat}, toNat? a = some n a = n

View File

@@ -21,6 +21,11 @@ protected theorem pow_succ (b : Int) (e : Nat) : b ^ (e+1) = (b ^ e) * b := rfl
protected theorem pow_succ' (b : Int) (e : Nat) : b ^ (e+1) = b * (b ^ e) := by
rw [Int.mul_comm, Int.pow_succ]
protected theorem pow_add (a : Int) (m n : Nat) : a ^ (m + n) = a ^ m * a ^ n := by
induction n with
| zero => rw [Nat.add_zero, Int.pow_zero, Int.mul_one]
| succ _ ih => rw [Nat.add_succ, Int.pow_succ, Int.pow_succ, ih, Int.mul_assoc]
protected theorem zero_pow {n : Nat} (h : n 0) : (0 : Int) ^ n = 0 := by
match n, h with
| n + 1, _ => simp [Int.pow_succ]
@@ -43,6 +48,8 @@ protected theorem pow_ne_zero {n : Int} {m : Nat} : n ≠ 0 → n ^ m ≠ 0 := b
| zero => simp
| succ m ih => exact fun h => Int.mul_ne_zero (ih h) h
instance {n : Int} {m : Nat} [NeZero n] : NeZero (n ^ m) := Int.pow_ne_zero (NeZero.ne _)
@[deprecated Nat.pow_le_pow_left (since := "2025-02-17")]
abbrev pow_le_pow_of_le_left := @Nat.pow_le_pow_left

View File

@@ -6,8 +6,10 @@ Authors: Paul Reichert
module
prelude
public import all Init.Data.Iterators.Combinators.Attach
public import all Init.Data.Iterators.Combinators.Monadic.Attach
public import Init.Data.Iterators.Combinators.Attach
import all Init.Data.Iterators.Combinators.Attach
public import Init.Data.Iterators.Combinators.Monadic.Attach
import all Init.Data.Iterators.Combinators.Monadic.Attach
public import Init.Data.Iterators.Lemmas.Combinators.Monadic.Attach
public import Init.Data.Iterators.Lemmas.Consumers.Collect
public import Init.Data.Array.Attach

View File

@@ -6,7 +6,8 @@ Authors: Paul Reichert
module
prelude
public import all Init.Data.Iterators.Combinators.Monadic.Attach
public import Init.Data.Iterators.Combinators.Monadic.Attach
import all Init.Data.Iterators.Combinators.Monadic.Attach
public import Init.Data.Iterators.Lemmas.Consumers.Monadic.Collect
public section

View File

@@ -9,7 +9,8 @@ prelude
public import Init.Data.Iterators.Internal.LawfulMonadLiftFunction
public import Init.Data.Iterators.Combinators.Monadic.FilterMap
public import Init.Data.Iterators.Lemmas.Consumers.Monadic
public import all Init.Data.Iterators.Consumers.Monadic.Collect
public import Init.Data.Iterators.Consumers.Monadic.Collect
import all Init.Data.Iterators.Consumers.Monadic.Collect
public section

View File

@@ -6,7 +6,8 @@ Authors: Paul Reichert
module
prelude
public import all Init.Data.Iterators.Combinators.Monadic.ULift
public import Init.Data.Iterators.Combinators.Monadic.ULift
import all Init.Data.Iterators.Combinators.Monadic.ULift
public import Init.Data.Iterators.Lemmas.Consumers.Monadic.Collect
public section

View File

@@ -6,7 +6,8 @@ Authors: Paul Reichert
module
prelude
public import all Init.Data.Iterators.Combinators.ULift
public import Init.Data.Iterators.Combinators.ULift
import all Init.Data.Iterators.Combinators.ULift
public import Init.Data.Iterators.Lemmas.Combinators.Monadic.ULift
public import Init.Data.Iterators.Lemmas.Consumers.Collect

View File

@@ -8,8 +8,10 @@ module
prelude
public import Init.Data.Iterators.Lemmas.Basic
public import Init.Data.Iterators.Lemmas.Consumers.Monadic.Collect
public import all Init.Data.Iterators.Consumers.Access
public import all Init.Data.Iterators.Consumers.Collect
public import Init.Data.Iterators.Consumers.Access
import all Init.Data.Iterators.Consumers.Access
public import Init.Data.Iterators.Consumers.Collect
import all Init.Data.Iterators.Consumers.Collect
public section

View File

@@ -8,9 +8,12 @@ module
prelude
public import Init.Control.Lawful.MonadLift.Instances
public import Init.Data.Iterators.Lemmas.Consumers.Collect
public import all Init.Data.Iterators.Lemmas.Consumers.Monadic.Loop
public import all Init.Data.Iterators.Consumers.Loop
public import all Init.Data.Iterators.Consumers.Monadic.Collect
public import Init.Data.Iterators.Lemmas.Consumers.Monadic.Loop
import all Init.Data.Iterators.Lemmas.Consumers.Monadic.Loop
public import Init.Data.Iterators.Consumers.Loop
import all Init.Data.Iterators.Consumers.Loop
public import Init.Data.Iterators.Consumers.Monadic.Collect
import all Init.Data.Iterators.Consumers.Monadic.Collect
public section

View File

@@ -8,7 +8,8 @@ module
prelude
public import Init.Data.Array.Lemmas
public import Init.Data.Iterators.Lemmas.Monadic.Basic
public import all Init.Data.Iterators.Consumers.Monadic.Collect
public import Init.Data.Iterators.Consumers.Monadic.Collect
import all Init.Data.Iterators.Consumers.Monadic.Collect
public section

View File

@@ -7,7 +7,8 @@ module
prelude
public import Init.Data.Iterators.Lemmas.Consumers.Monadic.Collect
public import all Init.Data.Iterators.Consumers.Monadic.Loop
public import Init.Data.Iterators.Consumers.Monadic.Loop
import all Init.Data.Iterators.Consumers.Monadic.Loop
public section

View File

@@ -6,7 +6,8 @@ Authors: Mario Carneiro
module
prelude
public import all Init.Data.List.Lemmas -- for dsimping with `getElem?_cons_succ`
public import Init.Data.List.Lemmas -- for dsimping with `getElem?_cons_succ`
import all Init.Data.List.Lemmas -- for dsimping with `getElem?_cons_succ`
public import Init.Data.List.Count
public import Init.Data.Subtype.Basic
public import Init.BinderNameHint

View File

@@ -6,7 +6,8 @@ Authors: François G. Dorais
module
prelude
public import all Init.Data.List.OfFn
public import Init.Data.List.OfFn
import all Init.Data.List.OfFn
public import Init.Data.List.Monadic
public section

View File

@@ -11,7 +11,8 @@ public import Init.Data.List.Lemmas
public import Init.Data.List.Sublist
public import Init.Data.List.Range
public import Init.Data.List.Impl
public import all Init.Data.List.Attach
public import Init.Data.List.Attach
import all Init.Data.List.Attach
public import Init.Data.Fin.Lemmas
public section

View File

@@ -9,8 +9,10 @@ module
prelude
public import Init.Data.Bool
public import Init.Data.Option.Lemmas
public import all Init.Data.List.BasicAux
public import all Init.Data.List.Control
public import Init.Data.List.BasicAux
import all Init.Data.List.BasicAux
public import Init.Data.List.Control
import all Init.Data.List.Control
public import Init.Control.Lawful.Basic
public import Init.BinderPredicates

View File

@@ -272,7 +272,7 @@ theorem max?_eq_some_iff_legacy [Max α] [LE α] [anti : Std.Antisymm (· ≤ ·
(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 : LawfulOrderMax α := .of_max_le_iff (fun _ _ _ => max_le_iff _ _ _) max_eq_or
haveI : Refl (α := α) (· ·) := le_refl
haveI : IsLinearOrder α := .of_refl_of_antisymm_of_lawfulOrderMax
apply max?_eq_some_iff

View File

@@ -10,7 +10,8 @@ public import Init.Data.List.TakeDrop
public import Init.Data.List.Attach
public import Init.Data.List.OfFn
public import Init.Data.Array.Bootstrap
public import all Init.Data.List.Control
public import Init.Data.List.Control
import all Init.Data.List.Control
public section

View File

@@ -9,7 +9,8 @@ prelude
public import Init.Data.List.Pairwise
public import Init.Data.List.Erase
public import Init.Data.List.Find
public import all Init.Data.List.Attach
public import Init.Data.List.Attach
import all Init.Data.List.Attach
public section

View File

@@ -6,7 +6,8 @@ Authors: Kim Morrison
module
prelude
public import all Init.Data.List.Sort.Basic
public import Init.Data.List.Sort.Basic
import all Init.Data.List.Sort.Basic
public import Init.Data.List.Sort.Lemmas
public section

View File

@@ -7,7 +7,8 @@ module
prelude
public import Init.Data.List.Perm
public import all Init.Data.List.Sort.Basic
public import Init.Data.List.Sort.Basic
import all Init.Data.List.Sort.Basic
public import Init.Data.List.Nat.Range
public import Init.Data.Bool

View File

@@ -6,7 +6,8 @@ Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, M
module
prelude
public import all Init.Data.List.Basic
public import Init.Data.List.Basic
import all Init.Data.List.Basic
public import Init.Data.List.Lemmas
public section

View File

@@ -6,14 +6,17 @@ Authors: Mario Carneiro
module
prelude
public import all Init.Data.List.Control
public import Init.Data.List.Control
import all Init.Data.List.Control
public import Init.Data.List.Impl
public import Init.Data.List.Nat.Erase
public import Init.Data.List.Monadic
public import Init.Data.List.Nat.InsertIdx
public import Init.Data.Array.Lex.Basic
public import all Init.Data.Array.Basic
public import all Init.Data.Array.Set
public import Init.Data.Array.Basic
import all Init.Data.Array.Basic
public import Init.Data.Array.Set
import all Init.Data.Array.Set
public section

View File

@@ -274,11 +274,9 @@ theorem zip_map {f : αγ} {g : β → δ} :
| _, [] => by simp only [map, zip_nil_right]
| _ :: _, _ :: _ => by simp only [map, zip_cons_cons, zip_map, Prod.map]
@[grind _=_]
theorem zip_map_left {f : α γ} {l₁ : List α} {l₂ : List β} :
zip (l₁.map f) l₂ = (zip l₁ l₂).map (Prod.map f id) := by rw [ zip_map, map_id]
@[grind _=_]
theorem zip_map_right {f : β γ} {l₁ : List α} {l₂ : List β} :
zip l₁ (l₂.map f) = (zip l₁ l₂).map (Prod.map id f) := by rw [ zip_map, map_id]

View File

@@ -9,7 +9,8 @@ module
prelude
public import Init.Data.Bool
public import Init.Data.Int.Pow
public import all Init.Data.Nat.Bitwise.Basic
public import Init.Data.Nat.Bitwise.Basic
import all Init.Data.Nat.Bitwise.Basic
public import Init.Data.Nat.Lemmas
public import Init.Data.Nat.Simproc
public import Init.TacticsExtra
@@ -844,6 +845,12 @@ theorem shiftLeft_add_eq_or_of_lt {b : Nat} (b_lt : b < 2^i) (a : Nat) :
rw [shiftLeft_eq, Nat.mul_comm]
rw [two_pow_add_eq_or_of_lt b_lt]
@[simp]
theorem shiftLeft_eq_zero_iff {a n : Nat} : a <<< n = 0 a = 0 := by
simp [shiftLeft_eq, mul_eq_zero]
instance {a n : Nat} [NeZero a] : NeZero (a <<< n) := mt shiftLeft_eq_zero_iff.mp (NeZero.ne _)
/-! ### le -/
theorem le_of_testBit {n m : Nat} (h : i, n.testBit i = true m.testBit i = true) : n m := by

View File

@@ -6,7 +6,8 @@ Authors: Leonardo de Moura, Jeremy Avigad, Mario Carneiro
module
prelude
public import all Init.Data.Ord.Basic
public import Init.Data.Ord.Basic
import all Init.Data.Ord.Basic
public section

View File

@@ -128,6 +128,12 @@ theorem fold_congr {α : Type u} {n m : Nat} (w : n = m)
subst m
rfl
theorem foldRev_congr {α : Type u} {n m : Nat} (w : n = m)
(f : (i : Nat) i < n α α) (init : α) :
foldRev n f init = foldRev m (fun i h => f i (by omega)) init := by
subst m
rfl
private theorem foldTR_loop_congr {α : Type u} {n m : Nat} (w : n = m)
(f : (i : Nat) i < n α α) (j : Nat) (h : j n) (init : α) :
foldTR.loop n f j h init = foldTR.loop m (fun i h => f i (by omega)) j (by omega) init := by
@@ -270,6 +276,16 @@ def dfoldRev (n : Nat) {α : (i : Nat) → (h : i ≤ n := by omega) → Type u}
| succ n ih =>
simp [ih, List.finRange_succ_last, List.foldl_map]
theorem fold_add
{α n m} (f : (i : Nat) i < n + m α α) (init : α) :
fold (n + m) f init =
fold m (fun i h => f (n + i) (by omega))
(fold n (fun i h => f i (by omega)) init) := by
induction m with
| zero => simp; rfl
| succ m ih =>
simp [fold_congr (Nat.add_assoc n m 1).symm, ih]
/-! ### `foldRev` -/
@[simp] theorem foldRev_zero {α : Type u} (f : (i : Nat) i < 0 α α) (init : α) :
@@ -285,6 +301,17 @@ def dfoldRev (n : Nat) {α : (i : Nat) → (h : i ≤ n := by omega) → Type u}
| zero => simp
| succ n ih => simp [ih, List.finRange_succ_last, List.foldr_map]
theorem foldRev_add
{α n m} (f : (i : Nat) i < n + m α α) (init : α) :
foldRev (n + m) f init =
foldRev n (fun i h => f i (by omega))
(foldRev m (fun i h => f (n + i) (by omega)) init) := by
induction m generalizing init with
| zero => simp; rfl
| succ m ih =>
rw [foldRev_congr (Nat.add_assoc n m 1).symm]
simp [ih]
/-! ### `any` -/
@[simp] theorem any_zero {f : (i : Nat) i < 0 Bool} : any 0 f = false := by simp [any]

View File

@@ -6,9 +6,11 @@ Authors: Leonardo de Moura, Jeremy Avigad, Mario Carneiro, Floris van Doorn
module
prelude
public import all Init.Data.Nat.Bitwise.Basic
public import Init.Data.Nat.Bitwise.Basic
import all Init.Data.Nat.Bitwise.Basic
public import Init.Data.Nat.MinMax
public import all Init.Data.Nat.Log2
public import Init.Data.Nat.Log2
import all Init.Data.Nat.Log2
public import Init.Data.Nat.Power2
public import Init.Data.Nat.Mod
@@ -1313,13 +1315,13 @@ theorem pow_eq_self_iff {a b : Nat} (ha : 1 < a) : a ^ b = a ↔ b = 1 := by
@[simp]
theorem log2_zero : Nat.log2 0 = 0 := by
simp [Nat.log2]
simp [Nat.log2_def]
theorem le_log2 (h : n 0) : k n.log2 2 ^ k n := by
match k with
| 0 => simp [show 1 n from Nat.pos_of_ne_zero h]
| k+1 =>
rw [log2]; split
rw [log2_def]; split
· have n0 : 0 < n / 2 := (Nat.le_div_iff_mul_le (by decide)).2 _
simp only [Nat.add_le_add_iff_right, le_log2 (Nat.ne_of_gt n0),
Nat.pow_succ]
@@ -1331,6 +1333,25 @@ theorem le_log2 (h : n ≠ 0) : k ≤ n.log2 ↔ 2 ^ k ≤ n := by
theorem log2_lt (h : n 0) : n.log2 < k n < 2 ^ k := by
rw [ Nat.not_le, Nat.not_le, le_log2 h]
theorem log2_eq_iff (h : n 0) : n.log2 = k 2 ^ k n n < 2 ^ (k + 1) := by
constructor
· intro w
exact (le_log2 h).mp (Nat.le_of_eq w.symm), (log2_lt h).mp (by subst w; apply lt_succ_self)
· intro w
apply Nat.le_antisymm
· apply Nat.le_of_lt_add_one
exact (log2_lt h).mpr w.2
· exact (le_log2 h).mpr w.1
theorem log2_two_mul (h : n 0) : (2 * n).log2 = n.log2 + 1 := by
obtain h₁, h₂ := (log2_eq_iff h).mp rfl
rw [log2_eq_iff (Nat.mul_ne_zero (by decide) h)]
constructor
· rw [Nat.pow_succ, Nat.mul_comm]
exact mul_le_mul_left 2 h₁
· rw [Nat.pow_succ, Nat.mul_comm]
rwa [Nat.mul_lt_mul_right (by decide)]
@[simp]
theorem log2_two_pow : (2 ^ n).log2 = n := by
apply Nat.eq_of_le_of_lt_succ <;> simp [le_log2, log2_lt, NeZero.ne, Nat.pow_lt_pow_iff_right]

View File

@@ -1,7 +1,7 @@
/-
Copyright (c) 2021 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Gabriel Ebner
Authors: Gabriel Ebner, Robin Arnez
-/
module
@@ -37,11 +37,38 @@ Examples:
-/
@[extern "lean_nat_log2"]
def log2 (n : @& Nat) : Nat :=
if n 2 then log2 (n / 2) + 1 else 0
decreasing_by exact log2_terminates _ _
-- Lean "assembly"
n.rec (fun _ => nat_lit 0) (fun _ ih n =>
((nat_lit 2).ble n).rec (nat_lit 0) ((ih (n.div (nat_lit 2))).succ)) n
private theorem log2_rec_irrel {n k k' : Nat} (hk : n k) (hk' : n k') :
(k.rec (fun _ => 0) (fun _ ih n => ((2).ble n).rec 0 ((ih (n / 2)).succ)) n : Nat) =
k'.rec (fun _ => 0) (fun _ ih n => ((2).ble n).rec 0 ((ih (n / 2)).succ)) n := by
induction k generalizing n k' with
| zero => cases hk; cases k' <;> rfl
| succ k ih =>
cases k'
· cases hk'; rfl
· dsimp only
cases h : ble 2 n
· rfl
· have hn : n / 2 < n := log2_terminates n (Nat.le_of_ble_eq_true h)
exact congrArg Nat.succ (ih (Nat.le_of_lt_add_one (Nat.lt_of_lt_of_le hn hk))
(Nat.le_of_lt_add_one (Nat.lt_of_lt_of_le hn hk')))
theorem log2_def (n : Nat) : n.log2 = if 2 n then (n / 2).log2 + 1 else 0 := by
rw [Nat.log2, Nat.log2]
cases n
· rfl
split
· rename_i n h
simp only [ble_eq_true_of_le h]
exact congrArg Nat.succ
(log2_rec_irrel (Nat.le_of_lt_add_one (log2_terminates _ h)) (Nat.le_refl _))
· simp only [mt le_of_ble_eq_true _]
theorem log2_le_self (n : Nat) : Nat.log2 n n := by
unfold Nat.log2; split
rw [log2_def]; split
next h =>
have := log2_le_self (n / 2)
exact Nat.lt_of_le_of_lt this (Nat.div_lt_self (Nat.le_of_lt h) (by decide))

View File

@@ -25,14 +25,14 @@ public instance : LawfulOrderLT Nat := by
simp [Nat.lt_iff_le_and_ne]
public instance : LawfulOrderMin Nat := by
apply LawfulOrderMin.of_le
apply LawfulOrderMin.of_le_min_iff
· apply Nat.le_min
· intro a b
simp only [Nat.min_def]
split <;> simp
public instance : LawfulOrderMax Nat := by
apply LawfulOrderMax.of_le
apply LawfulOrderMax.of_max_le_iff
· apply Nat.max_le
· intro a b
simp only [Nat.max_def]

View File

@@ -8,7 +8,8 @@ module
prelude
public import Init.Data.Array.Lemmas
public import Init.Data.Option.List
public import all Init.Data.Option.Instances
public import Init.Data.Option.Instances
import all Init.Data.Option.Instances
public section

View File

@@ -6,8 +6,10 @@ Authors: Mario Carneiro
module
prelude
public import all Init.Data.Option.BasicAux
public import all Init.Data.Option.Instances
public import Init.Data.Option.BasicAux
import all Init.Data.Option.BasicAux
public import Init.Data.Option.Instances
import all Init.Data.Option.Instances
public import Init.Data.BEq
public import Init.Classical
public import Init.Ext
@@ -795,7 +797,7 @@ 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 : α β) : Option.elim none x f = x := rfl
@[simp, grind =] theorem elim_some (x : β) (f : α β) (a : α) : (some a).elim x f = f a := rfl

View File

@@ -7,8 +7,10 @@ module
prelude
public import Init.Data.List.Lemmas
public import all Init.Data.List.Control
public import all Init.Data.Option.Instances
public import Init.Data.List.Control
import all Init.Data.List.Control
public import Init.Data.Option.Instances
import all Init.Data.Option.Instances
public section

View File

@@ -7,7 +7,8 @@ module
prelude
public import all Init.Data.Option.Instances
public import Init.Data.Option.Instances
import all Init.Data.Option.Instances
public import Init.Data.Option.Attach
public import Init.Control.Lawful.Basic

View File

@@ -9,7 +9,8 @@ prelude
public import Init.Data.String.Basic
public import Init.Data.Array.Basic
public import Init.Data.SInt.Basic
public import all Init.Data.Vector.Basic
public import Init.Data.Vector.Basic
import all Init.Data.Vector.Basic
public section
@@ -96,7 +97,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
/--
@@ -530,7 +531,7 @@ instance [Ord α] : Ord (Option α) where
| some x, some y => compare x y
instance : Ord Ordering where
compare := compareOn (·.toCtorIdx)
compare := compareOn (·.ctorIdx)
namespace List

View File

@@ -17,6 +17,26 @@ This module provides utilities for the creation of order-related typeclass insta
section OfLE
/--
Creates a `Min α` instance from `LE α` and `DecidableLE α` so that `min a b` is either `a` or `b`,
preferring `a` over `b` when in doubt.
Has a `LawfulOrderLeftLeaningMin α` instance.
-/
@[inline]
public def _root_.Min.leftLeaningOfLE (α : Type u) [LE α] [DecidableLE α] : Min α where
min a b := if a b then a else b
/--
Creates a `Max α` instance from `LE α` and `DecidableLE α` so that `max a b` is either `a` or `b`,
preferring `a` over `b` when in doubt.
Has a `LawfulOrderLeftLeaningMax α` instance.
-/
@[inline]
public def _root_.Max.leftLeaningOfLE (α : Type u) [LE α] [DecidableLE α] : Max α where
max a b := if b a then a else b
/--
This instance is only publicly defined in `Init.Data.Order.Lemmas`.
-/
@@ -95,12 +115,34 @@ 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
public theorem LawfulOrderMin.of_le_min_iff {α : Type u} [Min α] [LE α]
(le_min_iff : a b c : α, a min b c a b a c := by exact LawfulOrderInf.le_min_iff)
(min_eq_or : a b : α, min a b = a min a b = b := by exact MinEqOr.min_eq_or) :
LawfulOrderMin α where
toLawfulOrderInf := .of_le le_min_iff
toMinEqOr := min_eq_or
/--
Returns a `LawfulOrderMin α` instance given that `max a b` returns either `a` or `b` and that
it is less than or equal to both of them. The `≤` relation needs to be transitive.
The produced instance entails `LawfulOrderInf α` and `MinEqOr α`.
-/
public theorem LawfulOrderMin.of_min_le {α : Type u} [Min α] [LE α]
[i : Trans (α := α) (· ·) (· ·) (· ·)]
(min_le_left : a b : α, min a b a) (min_le_right : a b : α, min a b b)
(min_eq_or : a b : α, min a b = a min a b = b := by exact MinEqOr.min_eq_or) :
LawfulOrderMin α := by
apply LawfulOrderMin.of_le_min_iff
· simp [autoParam]
intro a b c
constructor
· intro h
exact i.trans h (min_le_left b c), i.trans h (min_le_right b c)
· intro h
cases min_eq_or b c <;> simp [*]
· exact min_eq_or
/--
This lemma characterizes in terms of `LE α` when a `Max α` instance "behaves like a supremum
operator".
@@ -117,12 +159,34 @@ 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
public def LawfulOrderMax.of_max_le_iff {α : Type u} [Max α] [LE α]
(max_le_iff : a b c : α, max a b c a c b c := by exact LawfulOrderInf.le_min_iff)
(max_eq_or : a b : α, max a b = a max a b = b := by exact MaxEqOr.max_eq_or) :
LawfulOrderMax α where
toLawfulOrderSup := .of_le max_le_iff
toMaxEqOr := max_eq_or
/--
Returns a `LawfulOrderMax α` instance given that `max a b` returns either `a` or `b` and that
it is larger than or equal to both of them. The `≤` relation needs to be transitive.
The produced instance entails `LawfulOrderSup α` and `MaxEqOr α`.
-/
public theorem LawfulOrderMax.of_le_max {α : Type u} [Max α] [LE α]
[i : Trans (α := α) (· ·) (· ·) (· ·)]
(left_le_max : a b : α, a max a b) (right_le_max : a b : α, b max a b)
(max_eq_or : a b : α, max a b = a max a b = b := by exact MaxEqOr.max_eq_or) :
LawfulOrderMax α := by
apply LawfulOrderMax.of_max_le_iff
· simp [autoParam]
intro a b c
constructor
· intro h
exact i.trans (left_le_max a b) h, i.trans (right_le_max a b) h
· intro h
cases max_eq_or a b <;> simp [*]
· exact max_eq_or
end OfLE
section OfLT
@@ -132,7 +196,8 @@ 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
@[inline]
public def _root_.LE.ofLT (α : Type u) [LT α] : LE α where
le a b := ¬ b < a
/--

View File

@@ -8,6 +8,7 @@ module
prelude
public import Init.Data.Order.Classes
public import Init.Data.Order.Factories
import all Init.Data.Order.Factories
import Init.SimpLemmas
public import Init.Classical
public import Init.Data.BEq
@@ -95,6 +96,11 @@ public theorem not_gt_of_lt {α : Type u} [LT α] [i : Std.Asymm (α := α) (·
(h : a < b) : ¬ b < a :=
i.asymm a b h
public theorem le_of_lt {α : Type u} [LT α] [LE α] [LawfulOrderLT α] {a b : α} (h : a < b) :
a b := by
simp only [LawfulOrderLT.lt_iff] at h
exact h.1
public instance {α : Type u} [LT α] [LE α] [LawfulOrderLT α] :
Std.Asymm (α := α) (· < ·) where
asymm a b := by
@@ -141,6 +147,15 @@ public theorem lt_of_le_of_lt {α : Type u} [LE α] [LT α]
· intro hca
exact hbc.2.elim (le_trans hca hab)
public theorem lt_of_lt_of_le {α : Type u} [LE α] [LT α]
[Trans (α := α) (· ·) (· ·) (· ·)] [LawfulOrderLT α] {a b c : α} (hab : a < b)
(hbc : b c) : a < c := by
simp only [lt_iff_le_and_not_ge] at hab
apply And.intro
· exact le_trans hab.1 hbc
· intro hca
exact hab.2.elim (le_trans hbc hca)
public theorem lt_of_le_of_ne {α : Type u} [LE α] [LT α]
[Std.Antisymm (α := α) (· ·)] [LawfulOrderLT α] {a b : α}
(hle : a b) (hne : a b) : a < b := by
@@ -306,8 +321,50 @@ public instance {α : Type u} [LE α] [Min α] [IsLinearOrder α] [LawfulOrderIn
· apply min_le_right
· simp [le_min_iff, le_refl, le_of_not_ge hab]
end Min
public theorem LawfulOrderLeftLeaningMin.of_eq {α : Type u} [LE α] [Min α] [DecidableLE α]
(min_eq : a b : α, min a b = if a b then a else b) : LawfulOrderLeftLeaningMin α where
min_eq_left a b := by simp +contextual [min_eq]
min_eq_right a b := by simp +contextual [min_eq]
attribute [local instance] Min.leftLeaningOfLE
public instance [LE α] [DecidableLE α] : LawfulOrderLeftLeaningMin α :=
.of_eq (fun a b => by simp [min])
public instance {α : Type u} [LE α] [Min α] [LawfulOrderLeftLeaningMin α] :
MinEqOr α where
min_eq_or a b := by
open scoped Classical in
suffices min_eq : min a b = if a b then a else b by
rw [min_eq]
split <;> simp
split <;> simp [*, LawfulOrderLeftLeaningMin.min_eq_left, LawfulOrderLeftLeaningMin.min_eq_right]
public instance {α : Type u} [LE α] [Min α] [IsLinearPreorder α] [LawfulOrderLeftLeaningMin α] :
LawfulOrderMin α where
toMinEqOr := inferInstance
le_min_iff a b c := by
open scoped Classical in
suffices min_eq : min b c = if b c then b else c by
rw [min_eq]
split <;> rename_i hbc
· simp only [iff_self_and]
exact fun hab => le_trans hab hbc
· simp only [iff_and_self]
exact fun hac => le_trans hac (by simpa [hbc] using Std.le_total (a := b) (b := c))
split <;> simp [*, LawfulOrderLeftLeaningMin.min_eq_left, LawfulOrderLeftLeaningMin.min_eq_right]
end Min
end Std
namespace Classical.Order
open Std
public noncomputable scoped instance instMin {α : Type u} [LE α] : Min α :=
.leftLeaningOfLE α
end Classical.Order
namespace Std
section Max
public theorem max_self {α : Type u} [Max α] [Std.IdempotentOp (max : α α α)] {a : α} :
@@ -401,6 +458,45 @@ public instance {α : Type u} [LE α] [Max α] [IsLinearOrder α] [LawfulOrderSu
· simp [max_le_iff, le_refl, le_of_not_ge hab]
· apply right_le_max
end Max
public theorem LawfulOrderLeftLeaningMax.of_eq {α : Type u} [LE α] [Max α] [DecidableLE α]
(min_eq : a b : α, max a b = if b a then a else b) : LawfulOrderLeftLeaningMax α where
max_eq_left a b := by simp +contextual [min_eq]
max_eq_right a b := by simp +contextual [min_eq]
attribute [local instance] Max.leftLeaningOfLE
public instance [LE α] [DecidableLE α] : LawfulOrderLeftLeaningMax α :=
.of_eq (fun a b => by simp [max])
public instance {α : Type u} [LE α] [Max α] [LawfulOrderLeftLeaningMax α] :
MaxEqOr α where
max_eq_or a b := by
open scoped Classical in
suffices min_eq : max a b = if b a then a else b by
rw [min_eq]
split <;> simp
split <;> simp [*, LawfulOrderLeftLeaningMax.max_eq_left, LawfulOrderLeftLeaningMax.max_eq_right]
public instance {α : Type u} [LE α] [Max α] [IsLinearPreorder α] [LawfulOrderLeftLeaningMax α] :
LawfulOrderMax α where
toMaxEqOr := inferInstance
max_le_iff a b c := by
open scoped Classical in
suffices max_eq : max a b = if b a then a else b by
rw [max_eq]
split <;> rename_i hba
· simp only [iff_self_and]
exact fun hac => le_trans hba hac
· simp only [iff_and_self]
exact fun hbc => le_trans (by simpa [hba] using Std.le_total (a := b) (b := a)) hbc
split <;> simp [*, LawfulOrderLeftLeaningMax.max_eq_left, LawfulOrderLeftLeaningMax.max_eq_right]
end Max
end Std
namespace Classical.Order
open Std
public noncomputable scoped instance instMax {α : Type u} [LE α] : Max α :=
.leftLeaningOfLE α
end Classical.Order

View File

@@ -37,6 +37,10 @@ namespace ReflCmp
theorem cmp_eq_of_eq {α : Type u} {cmp : α α Ordering} [Std.ReflCmp cmp] {a b : α} : a = b cmp a b = .eq := by
intro h; subst a; apply compare_self
theorem ne_of_cmp_ne_eq {α : Type u} {cmp : α α Ordering} [Std.ReflCmp cmp] {a b : α} :
cmp a b .eq a b :=
mt cmp_eq_of_eq
end ReflCmp
/-- A typeclasses for ordered types for which `compare a a = .eq` for all `a`. -/

View File

@@ -67,20 +67,20 @@ public structure Packages.PreorderOfLEArgs (α : Type u) where
extract_lets
first
| infer_instance
| exact Classical.Order.instLT
| exact _root_.Classical.Order.instLT
beq :
let := le; let := decidableLE
BEq α := by
extract_lets
first
| infer_instance
| exact FactoryInstances.beqOfDecidableLE
| exact _root_.Std.FactoryInstances.beqOfDecidableLE
lt_iff :
let := le; let := lt
a b : α, a < b a b ¬ b a := by
extract_lets
first
| exact LawfulOrderLT.lt_iff
| exact _root_.Std.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`."
@@ -89,10 +89,10 @@ public structure Packages.PreorderOfLEArgs (α : Type u) where
have := lt_iff
DecidableLT α := by
extract_lets
haveI := @LawfulOrderLT.mk (lt_iff := by assumption) ..
haveI := @_root_.Std.LawfulOrderLT.mk (lt_iff := by assumption) ..
first
| infer_instance
| exact FactoryInstances.decidableLTOfLE
| exact _root_.Std.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`."
@@ -101,7 +101,7 @@ public structure Packages.PreorderOfLEArgs (α : Type u) where
a b : α, a == b a b b a := by
extract_lets
first
| exact LawfulOrderBEq.beq_iff_le_and_ge
| exact _root_.Std.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`."
@@ -110,7 +110,7 @@ public structure Packages.PreorderOfLEArgs (α : Type u) where
a : α, a a := by
extract_lets
first
| exact Std.Refl.refl (r := (· ·))
| exact _root_.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`."
@@ -119,7 +119,7 @@ public structure Packages.PreorderOfLEArgs (α : Type u) where
a b c : α, a b b c a c := by
extract_lets
first
| exact fun _ _ _ hab hbc => Trans.trans (r := (· ·)) (s := (· ·)) (t := (· ·)) hab hbc
| exact fun _ _ _ hab hbc => _root_.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`."
@@ -202,7 +202,7 @@ public structure Packages.PartialOrderOfLEArgs (α : Type u) extends Packages.Pr
a b : α, a b b a a = b := by
extract_lets
first
| exact Antisymm.antisymm
| exact _root_.Std.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`."
@@ -310,11 +310,11 @@ public structure Packages.LinearPreorderOfLEArgs (α : Type u) extends
extract_lets
first
| infer_instance
| exact FactoryInstances.instOrdOfDecidableLE
| exact _root_.Std.FactoryInstances.instOrdOfDecidableLE
le_total :
a b : α, a b b a := by
first
| exact Total.total
| exact _root_.Std.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`."
@@ -324,7 +324,7 @@ public structure Packages.LinearPreorderOfLEArgs (α : Type u) extends
a b : α, (compare a b).isLE a b := by
extract_lets
first
| exact LawfulOrderOrd.isLE_compare
| exact _root_.Std.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`."
@@ -333,7 +333,7 @@ public structure Packages.LinearPreorderOfLEArgs (α : Type u) extends
a b : α, (compare a b).isGE b a := by
extract_lets
first
| exact LawfulOrderOrd.isGE_compare
| exact _root_.Std.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`."
@@ -390,26 +390,6 @@ public def LinearPreorderPackage.ofLE (α : Type u)
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 `α`.
@@ -431,20 +411,20 @@ public structure Packages.LinearOrderOfLEArgs (α : Type u) extends
extract_lets
first
| infer_instance
| exact FactoryInstances.instMinOfDecidableLE
| exact _root_.Min.leftLeaningOfLE _
max :
let := le; let := decidableLE
Max α := by
extract_lets
first
| infer_instance
| exact FactoryInstances.instMaxOfDecidableLE
| exact _root_.Max.leftLeaningOfLE _
min_eq :
let := le; let := decidableLE; let := min
a b : α, Min.min a b = if a b then a else b := by
extract_lets
first
| exact fun a b => Std.min_eq_if (a := a) (b := b)
| exact fun a b => _root_.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`."
@@ -453,41 +433,11 @@ public structure Packages.LinearOrderOfLEArgs (α : Type u) extends
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)
| exact fun a b => _root_.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.
@@ -540,14 +490,16 @@ public def LinearOrderPackage.ofLE (α : Type u)
le_antisymm := (PartialOrderPackage.ofLE α args.toPartialOrderOfLEArgs).le_antisymm
toMin := args.min
toMax := args.max
toLawfulOrderMin :=
toLawfulOrderMin := by
letI := LinearPreorderPackage.ofLE α args.toLinearPreorderOfLEArgs
letI := args.decidableLE; letI := args.min
IsLinearPreorder.lawfulOrderMin_of_min_eq args.min_eq
toLawfulOrderMax :=
haveI : LawfulOrderLeftLeaningMin α := .of_eq args.min_eq
infer_instance
toLawfulOrderMax := by
letI := LinearPreorderPackage.ofLE α args.toLinearPreorderOfLEArgs
letI := args.decidableLE; letI := args.max
IsLinearPreorder.lawfulOrderMax_of_max_eq args.max_eq
haveI : LawfulOrderLeftLeaningMax α := .of_eq args.max_eq
infer_instance
section LinearPreorder
@@ -586,8 +538,8 @@ public structure Packages.LinearPreorderOfOrdArgs (α : Type u) where
extract_lets
first
| infer_instance
| exact LE.ofOrd _
lawful_le :
| exact _root_.LE.ofOrd _
lawfulOrderOrd :
let := ord; let := transOrd; let := le
LawfulOrderOrd α := by
extract_lets
@@ -595,14 +547,14 @@ public structure Packages.LinearPreorderOfOrdArgs (α : Type u) where
| 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`."
manually provide the field `lawfulOrderOrd`."
decidableLE :
let := ord; let := le; have := lawful_le
let := ord; let := le; have := lawfulOrderOrd
DecidableLE α := by
extract_lets
first
| infer_instance
| exact DecidableLE.ofOrd _
| exact _root_.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`."
@@ -614,21 +566,21 @@ public structure Packages.LinearPreorderOfOrdArgs (α : Type u) where
| infer_instance
| exact LT.ofOrd _
lt_iff :
let := ord; let := le; have := lawful_le; let := lt
let := ord; let := le; have := lawfulOrderOrd; let := lt
a b : α, a < b compare a b = .lt := by
extract_lets
first
| exact fun _ _ => Std.compare_eq_lt.symm
| exact fun _ _ => _root_.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
let := ord; let := lt; let := le; have := lawfulOrderOrd; have := lt_iff
DecidableLT α := by
extract_lets
first
| infer_instance
| exact DecidableLT.ofOrd _
| exact _root_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`."
@@ -637,9 +589,9 @@ public structure Packages.LinearPreorderOfOrdArgs (α : Type u) where
extract_lets
first
| infer_instance
| exact BEq.ofOrd _
| exact _root_.BEq.ofOrd _
beq_iff :
let := ord; let := le; have := lawful_le; let := beq
let := ord; let := le; have := lawfulOrderOrd; let := beq
a b : α, a == b compare a b = .eq := by
extract_lets
first
@@ -697,15 +649,15 @@ public def LinearPreorderPackage.ofOrd (α : Type u)
letI := args.ord
haveI := args.transOrd
letI := args.le
haveI := args.lawful_le
haveI := args.lawfulOrderOrd
{ toOrd := args.ord
toLE := args.le
toLT := args.lt
toBEq := args.beq
toLawfulOrderOrd := args.lawful_le
toLawfulOrderOrd := args.lawfulOrderOrd
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 _,
all_goals simp [h, args.lawfulOrderOrd.isLE_compare a _, args.lawfulOrderOrd.isGE_compare a _,
args.lt_iff]
beq_iff_le_and_ge a b := by
simp [args.beq_iff, Ordering.eq_eq_iff_isLE_and_isGE, isLE_compare,
@@ -756,7 +708,7 @@ public structure Packages.LinearOrderOfOrdArgs (α : Type u) extends
a b : α, compare a b = .eq a = b := by
extract_lets
first
| exact LawfulEqOrd.eq_of_compare
| exact fun _ _ => _root_.Std.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`."
@@ -766,29 +718,29 @@ public structure Packages.LinearOrderOfOrdArgs (α : Type u) extends
extract_lets
first
| infer_instance
| exact FactoryInstances.instMinOfOrd
| exact _root_.Std.FactoryInstances.instMinOfOrd
max :
let := ord
Max α := by
extract_lets
first
| infer_instance
| exact FactoryInstances.instMaxOfOrd
| exact _root_.Std.FactoryInstances.instMaxOfOrd
min_eq :
let := ord; let := le; let := min; have := lawful_le
let := ord; let := le; let := min; have := lawfulOrderOrd
a b : α, Min.min a b = if (compare a b).isLE then a else b := by
extract_lets
first
| exact fun a b => Std.min_eq_if_isLE_compare (a := a) (b := b)
| exact fun a b => _root_.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`."
manually provide the field `min_eq`."
max_eq :
let := ord; let := le; let := max; have := lawful_le
let := ord; let := le; let := max; have := lawfulOrderOrd
a b : α, Max.max a b = if (compare a b).isGE then a else b := by
extract_lets
first
| exact fun a b => Std.max_eq_if_isGE_compare (a := a) (b := b)
| exact fun a b => _root_.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`."
@@ -847,10 +799,12 @@ public def LinearOrderPackage.ofOrd (α : Type u)
{ 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) }
toLawfulOrderMin := by
haveI : LawfulOrderLeftLeaningMin α := .of_eq (by simpa [isLE_compare] using args.min_eq)
infer_instance
toLawfulOrderMax := by
haveI : LawfulOrderLeftLeaningMax α := .of_eq (by simpa [isGE_compare] using args.max_eq)
infer_instance }
end LinearOrder

View File

@@ -6,7 +6,8 @@ Authors: Kim Morrison
module
prelude
public import all Init.Data.Range.Basic
public import Init.Data.Range.Basic
import all Init.Data.Range.Basic
public import Init.Data.List.Range
public import Init.Data.List.Monadic
public import Init.Data.Nat.Div.Lemmas

View File

@@ -11,6 +11,7 @@ public import Init.Data.Range.Polymorphic.Iterators
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.Int
public import Init.Data.Range.Polymorphic.NatLemmas
public import Init.Data.Range.Polymorphic.GetElemTactic

View File

@@ -26,10 +26,9 @@ class RangeSize (shape : BoundShape) (α : Type u) where
This typeclass ensures that a `RangeSize` instance returns the correct size for all ranges.
-/
class LawfulRangeSize (su : BoundShape) (α : Type u) [UpwardEnumerable α]
[SupportsUpperBound su α] [RangeSize su α]
[LawfulUpwardEnumerable α] [HasFiniteRanges su α] where
[SupportsUpperBound su α] [RangeSize su α] where
/-- If the smallest value in the range is beyond the upper bound, the size is zero. -/
size_eq_zero_of_not_satisfied (upperBound : Bound su α) (init : α)
size_eq_zero_of_not_isSatisfied (upperBound : Bound su α) (init : α)
(h : ¬ SupportsUpperBound.IsSatisfied upperBound init) :
RangeSize.size upperBound init = 0
/--
@@ -43,11 +42,38 @@ class LawfulRangeSize (su : BoundShape) (α : Type u) [UpwardEnumerable α]
/--
If the smallest value in the range satisfies the upper bound and has a successor, the size is
one larger than the size of the range starting at the successor. -/
size_eq_succ_of_succ?_eq_some (upperBound : Bound su α) (init : α)
size_eq_succ_of_succ?_eq_some (upperBound : Bound su α) (init a : α)
(h : SupportsUpperBound.IsSatisfied upperBound init)
(h' : UpwardEnumerable.succ? init = some a) :
RangeSize.size upperBound init = RangeSize.size upperBound a + 1
theorem LawfulRangeSize.size_eq_zero_iff_not_isSatisfied {su : BoundShape} {α : Type u}
[UpwardEnumerable α] [SupportsUpperBound su α] [RangeSize su α] [LawfulRangeSize su α]
{bound : Bound su α} {init : α} :
RangeSize.size bound init = 0 ¬ SupportsUpperBound.IsSatisfied bound init := by
match h : decide (SupportsUpperBound.IsSatisfied bound init) with
| true =>
simp only [decide_eq_true_eq] at h
match hs : UpwardEnumerable.succ? init with
| none => simp [h, LawfulRangeSize.size_eq_one_of_succ?_eq_none (h := h) (h' := hs)]
| some b => simp [h, LawfulRangeSize.size_eq_succ_of_succ?_eq_some (h := h) (h' := hs)]
| false =>
simp only [decide_eq_false_iff_not] at h
simp [h, LawfulRangeSize.size_eq_zero_of_not_isSatisfied]
theorem LawfulRangeSize.size_pos_iff_isSatisfied {su : BoundShape} {α : Type u}
[UpwardEnumerable α] [SupportsUpperBound su α] [RangeSize su α] [LawfulRangeSize su α]
{bound : Bound su α} {init : α} :
0 < RangeSize.size bound init SupportsUpperBound.IsSatisfied bound init := by
have := size_eq_zero_iff_not_isSatisfied (bound := bound) (init := init)
match h : decide (SupportsUpperBound.IsSatisfied bound init) with
| true =>
simp only [decide_eq_true_eq] at h
simp_all [Nat.pos_iff_ne_zero]
| false =>
simp only [decide_eq_false_iff_not] at h
simp_all
/--
Checks whether the range contains any value.

View File

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

View File

@@ -0,0 +1,56 @@
/-
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.Instances
public import Init.Data.Order.Classes
import Init.Omega
public section
namespace Std.PRange
instance : UpwardEnumerable Int where
succ? x := some (x + 1)
succMany? n x := some (x + n)
instance : LawfulUpwardEnumerable Int where
ne_of_lt := by
simp only [UpwardEnumerable.LT, UpwardEnumerable.succMany?, Option.some.injEq]
omega
succMany?_zero := by simp [UpwardEnumerable.succMany?]
succMany?_succ := by
simp only [UpwardEnumerable.succMany?, UpwardEnumerable.succ?,
Option.bind_some, Option.some.injEq]
omega
instance : InfinitelyUpwardEnumerable Int where
isSome_succ? x := by simp [UpwardEnumerable.succ?]
instance : LawfulUpwardEnumerableLE Int where
le_iff x y := by
simp [UpwardEnumerable.LE, UpwardEnumerable.succMany?, Int.le_def, Int.nonneg_def,
Int.sub_eq_iff_eq_add', eq_comm (a := y)]
instance : RangeSize .closed Int where
size bound a := (bound + 1 - a).toNat
instance : RangeSize .open Int := RangeSize.openOfClosed
instance : LawfulRangeSize .closed Int where
size_eq_zero_of_not_isSatisfied bound x := by
simp only [SupportsUpperBound.IsSatisfied, RangeSize.size]
omega
size_eq_one_of_succ?_eq_none bound x := by
simp [SupportsUpperBound.IsSatisfied, RangeSize.size, UpwardEnumerable.succ?]
size_eq_succ_of_succ?_eq_some bound init x := by
simp only [SupportsUpperBound.IsSatisfied, UpwardEnumerable.succ?, RangeSize.size,
Option.some.injEq]
omega
end Std.PRange

View File

@@ -7,11 +7,15 @@ module
prelude
public import Init.Data.Iterators
public import Init.Data.Iterators.Lemmas.Consumers.Collect
public import all Init.Data.Range.Polymorphic.Basic
public import all Init.Data.Range.Polymorphic.RangeIterator
public import all Init.Data.Range.Polymorphic.Iterators
public import all Init.Data.Iterators.Consumers.Loop
import Init.Data.Iterators.Lemmas.Consumers.Collect
public import Init.Data.Range.Polymorphic.Basic
import all Init.Data.Range.Polymorphic.Basic
public import Init.Data.Range.Polymorphic.RangeIterator
import all Init.Data.Range.Polymorphic.RangeIterator
public import Init.Data.Range.Polymorphic.Iterators
import all Init.Data.Range.Polymorphic.Iterators
public import Init.Data.Iterators.Consumers.Loop
import all Init.Data.Iterators.Consumers.Loop
public section
@@ -26,13 +30,13 @@ open Std.Iterators
variable {shape : RangeShape} {α : Type u}
private theorem Internal.iter_open_eq_iter_closed_of_isSome_succ? {su} [UpwardEnumerable α]
private theorem Internal.iter_Rox_eq_iter_Rcx_of_isSome_succ? {su} [UpwardEnumerable α]
[SupportsUpperBound su α] [HasFiniteRanges su α]
[LawfulUpwardEnumerable α]
{lo : Bound .open α} {hi} (h : (UpwardEnumerable.succ? lo).isSome) :
Internal.iter (PRange.mk (shape := .open, su) lo hi) =
Internal.iter (PRange.mk (shape := .closed, su) (UpwardEnumerable.succ? lo |>.get h) hi) := by
simp [Internal.iter, BoundedUpwardEnumerable.init?]
simp [Internal.iter, init?]
private theorem Internal.toList_eq_toList_iter {sl su} [UpwardEnumerable α]
[BoundedUpwardEnumerable sl α] [SupportsUpperBound su α] [HasFiniteRanges su α]
@@ -40,7 +44,7 @@ private theorem Internal.toList_eq_toList_iter {sl su} [UpwardEnumerable α]
r.toList = (Internal.iter r).toList := by
rfl
theorem RangeIterator.toList_eq_match {su} [UpwardEnumerable α]
public theorem RangeIterator.toList_eq_match {su} [UpwardEnumerable α]
[SupportsUpperBound su α] [HasFiniteRanges su α]
[LawfulUpwardEnumerable α]
{it : Iter (α := RangeIterator su α) α} :
@@ -57,11 +61,11 @@ theorem RangeIterator.toList_eq_match {su} [UpwardEnumerable α]
· simp [*]
· split <;> rename_i heq' <;> simp [*]
theorem toList_eq_match {sl su} [UpwardEnumerable α] [BoundedUpwardEnumerable sl α]
public theorem toList_eq_match {sl su} [UpwardEnumerable α] [BoundedUpwardEnumerable sl α]
[SupportsUpperBound su α] [HasFiniteRanges su α]
[LawfulUpwardEnumerable α]
{r : PRange sl, su α} :
r.toList = match BoundedUpwardEnumerable.init? r.lower with
r.toList = match init? r.lower with
| none => []
| some a => if SupportsUpperBound.IsSatisfied r.upper a then
a :: (PRange.mk (shape := .open, su) a r.upper).toList
@@ -69,26 +73,35 @@ theorem toList_eq_match {sl su} [UpwardEnumerable α] [BoundedUpwardEnumerable s
[] := by
rw [Internal.toList_eq_toList_iter, RangeIterator.toList_eq_match]; rfl
theorem toList_open_eq_toList_closed_of_isSome_succ? {su} [UpwardEnumerable α]
public theorem toList_Rox_eq_toList_Rcx_of_isSome_succ? {su} [UpwardEnumerable α]
[SupportsUpperBound su α] [HasFiniteRanges su α]
[LawfulUpwardEnumerable α]
{lo : Bound .open α} {hi} (h : (UpwardEnumerable.succ? lo).isSome) :
(PRange.mk (shape := .open, su) lo hi).toList =
(PRange.mk (shape := .closed, su) (UpwardEnumerable.succ? lo |>.get h) hi).toList := by
simp [Internal.toList_eq_toList_iter, Internal.iter_open_eq_iter_closed_of_isSome_succ?, h]
simp [Internal.toList_eq_toList_iter, Internal.iter_Rox_eq_iter_Rcx_of_isSome_succ?, h]
theorem toList_eq_nil_iff {sl su} [UpwardEnumerable α]
@[deprecated toList_Rox_eq_toList_Rcx_of_isSome_succ? (since := "2025-08-22")]
public theorem toList_open_eq_toList_closed_of_isSome_succ? {su} [UpwardEnumerable α]
[SupportsUpperBound su α] [HasFiniteRanges su α]
[LawfulUpwardEnumerable α]
{lo : Bound .open α} {hi} (h : (UpwardEnumerable.succ? lo).isSome) :
(PRange.mk (shape := .open, su) lo hi).toList =
(PRange.mk (shape := .closed, su) (UpwardEnumerable.succ? lo |>.get h) hi).toList :=
toList_Rox_eq_toList_Rcx_of_isSome_succ? h
public theorem toList_eq_nil_iff {sl su} [UpwardEnumerable α]
[SupportsUpperBound su α] [HasFiniteRanges su α] [BoundedUpwardEnumerable sl α]
[LawfulUpwardEnumerable α]
{r : PRange sl, su α} :
r.toList = []
¬ ( a, BoundedUpwardEnumerable.init? r.lower = some a SupportsUpperBound.IsSatisfied r.upper a) := by
¬ ( a, init? r.lower = some a SupportsUpperBound.IsSatisfied r.upper a) := by
rw [Internal.toList_eq_toList_iter]
rw [RangeIterator.toList_eq_match, Internal.iter]
simp only
split <;> rename_i heq <;> simp [heq]
theorem mem_toList_iff_mem {sl su} [UpwardEnumerable α]
public theorem mem_toList_iff_mem {sl su} [UpwardEnumerable α]
[SupportsUpperBound su α] [SupportsLowerBound sl α] [HasFiniteRanges su α]
[BoundedUpwardEnumerable sl α] [LawfulUpwardEnumerable α]
[LawfulUpwardEnumerableLowerBound sl α] [LawfulUpwardEnumerableUpperBound su α]
@@ -97,17 +110,24 @@ theorem mem_toList_iff_mem {sl su} [UpwardEnumerable α]
rw [Internal.toList_eq_toList_iter, Iter.mem_toList_iff_isPlausibleIndirectOutput,
Internal.isPlausibleIndirectOutput_iter_iff]
theorem BoundedUpwardEnumerable.Closed.init?_succ [UpwardEnumerable α]
public theorem BoundedUpwardEnumerable.init?_succ?_closed [UpwardEnumerable α]
[LawfulUpwardEnumerable α] {lower lower' : Bound .closed α}
(h : UpwardEnumerable.succ? lower = some lower') :
BoundedUpwardEnumerable.init? lower' = (BoundedUpwardEnumerable.init? lower).bind UpwardEnumerable.succ? := by
init? lower' = (init? lower).bind UpwardEnumerable.succ? := by
cases h : init? lower <;> rename_i ilower <;> cases h' : init? lower' <;> rename_i ilower'
· simp
· simp [init?] at h
· simp [init?] at h'
· simp_all [init?]
theorem pairwise_toList_upwardEnumerableLt {sl su} [UpwardEnumerable α]
@[deprecated BoundedUpwardEnumerable.init?_succ?_closed (since := "2025-08-22")]
public theorem BoundedUpwardEnumerable.Closed.init?_succ [UpwardEnumerable α]
[LawfulUpwardEnumerable α] {lower lower' : Bound .closed α}
(h : UpwardEnumerable.succ? lower = some lower') :
init? lower' = (init? lower).bind UpwardEnumerable.succ? :=
init?_succ?_closed h
public theorem pairwise_toList_upwardEnumerableLt {sl su} [UpwardEnumerable α]
[SupportsUpperBound su α] [SupportsLowerBound sl α] [HasFiniteRanges su α]
[BoundedUpwardEnumerable sl α] [LawfulUpwardEnumerable α]
[LawfulUpwardEnumerableLowerBound sl α] [LawfulUpwardEnumerableUpperBound su α]
@@ -126,14 +146,14 @@ theorem pairwise_toList_upwardEnumerableLt {sl su} [UpwardEnumerable α]
simp only at ha
have : UpwardEnumerable.LT a ha.choose := by
refine 0, ?_
simp only [UpwardEnumerable.succMany?_succ, UpwardEnumerable.succMany?_zero,
simp only [succMany?_succ?, succMany?_zero,
Option.bind_some]
exact ha.choose_spec.1
exact UpwardEnumerable.lt_of_lt_of_le this ha.choose_spec.2
· apply ihy (out := a)
simp_all [RangeIterator.isPlausibleStep_iff, RangeIterator.step]
theorem pairwise_toList_ne {sl su} [UpwardEnumerable α]
public theorem pairwise_toList_ne {sl su} [UpwardEnumerable α]
[SupportsUpperBound su α] [SupportsLowerBound sl α] [HasFiniteRanges su α]
[BoundedUpwardEnumerable sl α] [LawfulUpwardEnumerable α]
[LawfulUpwardEnumerableLowerBound sl α] [LawfulUpwardEnumerableUpperBound su α]
@@ -141,7 +161,7 @@ theorem pairwise_toList_ne {sl su} [UpwardEnumerable α]
r.toList.Pairwise (fun a b => a b) :=
List.Pairwise.imp (fun hlt => UpwardEnumerable.ne_of_lt hlt) pairwise_toList_upwardEnumerableLt
theorem pairwise_toList_lt {sl su} [LT α] [UpwardEnumerable α]
public theorem pairwise_toList_lt {sl su} [LT α] [UpwardEnumerable α]
[SupportsUpperBound su α] [SupportsLowerBound sl α] [HasFiniteRanges su α]
[BoundedUpwardEnumerable sl α] [LawfulUpwardEnumerable α] [LawfulUpwardEnumerableLT α]
[LawfulUpwardEnumerableLowerBound sl α] [LawfulUpwardEnumerableUpperBound su α]
@@ -150,7 +170,7 @@ theorem pairwise_toList_lt {sl su} [LT α] [UpwardEnumerable α]
List.Pairwise.imp
(fun hlt => (LawfulUpwardEnumerableLT.lt_iff ..).mpr hlt) pairwise_toList_upwardEnumerableLt
theorem pairwise_toList_le {sl su} [LE α] [UpwardEnumerable α]
public theorem pairwise_toList_le {sl su} [LE α] [UpwardEnumerable α]
[SupportsUpperBound su α] [SupportsLowerBound sl α] [HasFiniteRanges su α]
[BoundedUpwardEnumerable sl α] [LawfulUpwardEnumerable α] [LawfulUpwardEnumerableLE α]
[LawfulUpwardEnumerableLowerBound sl α] [LawfulUpwardEnumerableUpperBound su α]
@@ -158,38 +178,44 @@ theorem pairwise_toList_le {sl su} [LE α] [UpwardEnumerable α]
r.toList.Pairwise (fun a b => a b) :=
pairwise_toList_upwardEnumerableLt
|> List.Pairwise.imp UpwardEnumerable.le_of_lt
|> List.Pairwise.imp (fun hle => (LawfulUpwardEnumerableLE.le_iff ..).mpr hle)
|> List.Pairwise.imp (fun hle => (UpwardEnumerable.le_iff ..).mpr hle)
theorem ClosedOpen.mem_succ_iff [UpwardEnumerable α]
public theorem mem_Rco_succ_succ_iff [UpwardEnumerable α]
[LinearlyUpwardEnumerable α] [InfinitelyUpwardEnumerable α] [SupportsUpperBound .open α]
[SupportsLowerBound .closed α] [LawfulUpwardEnumerableLowerBound .closed α]
[HasFiniteRanges .open α] [LawfulUpwardEnumerable α] [LawfulOpenUpperBound α]
{lower : Bound .closed α} {upper : Bound .open α} {a : α} :
a PRange.mk (shape := .closed, .open) (UpwardEnumerable.succ lower) (UpwardEnumerable.succ upper)
a', a = UpwardEnumerable.succ a' a' PRange.mk (shape := .closed, .open) lower upper := by
(a (succ lower)...(succ upper)) a', a = succ a' a' lower...upper := by
simp [Membership.mem, LawfulUpwardEnumerableLowerBound.isSatisfied_iff,
BoundedUpwardEnumerable.init?, LawfulOpenUpperBound.isSatisfied_iff_le]
init?, LawfulOpenUpperBound.isSatisfied_iff_le]
rw [ Option.some_get (InfinitelyUpwardEnumerable.isSome_succ? _)]
simp only [Option.some.injEq, UpwardEnumerable.succ.eq_def]
simp
constructor
· rintro n, hn, h
rw [UpwardEnumerable.succMany?_eq_some_iff_succMany, UpwardEnumerable.succMany_one,
UpwardEnumerable.succMany_add, Nat.add_comm, UpwardEnumerable.succMany_add,
UpwardEnumerable.succMany_one] at hn
rw [succMany?_eq_some_iff_succMany, succMany_one, succMany_add, Nat.add_comm, succMany_add,
succMany_one] at hn
rw [ hn]
refine UpwardEnumerable.succMany n lower, rfl, ?_, ?_
· exact n, by simp [UpwardEnumerable.succMany_eq_get]
refine succMany n lower, rfl, ?_, ?_
· exact n, by simp [succMany_eq_get]
· obtain m, hm := h
refine m, ?_
rw [UpwardEnumerable.succMany?_eq_some_iff_succMany] at hm
rwa [ hn, UpwardEnumerable.succMany_one, UpwardEnumerable.succMany_add, Nat.add_comm,
UpwardEnumerable.succMany_add, UpwardEnumerable.succMany_one,
UpwardEnumerable.succ_eq_succ_iff] at hm
rw [succMany?_eq_some_iff_succMany] at hm
rwa [ hn, succMany_one, succMany_add, Nat.add_comm, succMany_add, succMany_one,
succ_eq_succ_iff] at hm
· rintro a', rfl, hl, hu
simp [UpwardEnumerable.succ_le_succ_iff, UpwardEnumerable.succ_lt_succ_iff]
exact hl, hu
@[deprecated mem_Rco_succ_succ_iff (since := "2025-08-22")]
public theorem ClosedOpen.mem_succ_iff [UpwardEnumerable α]
[LinearlyUpwardEnumerable α] [InfinitelyUpwardEnumerable α] [SupportsUpperBound .open α]
[SupportsLowerBound .closed α] [LawfulUpwardEnumerableLowerBound .closed α]
[HasFiniteRanges .open α] [LawfulUpwardEnumerable α] [LawfulOpenUpperBound α]
{lower : Bound .closed α} {upper : Bound .open α} {a : α} :
(a (succ lower)...(succ upper)) a', a = succ a' a' lower...upper :=
mem_Rco_succ_succ_iff
private theorem eq_of_pairwise_lt_of_mem_iff_mem {lt : α α Prop} [asymm : Asymm lt]
{l l' : List α} (hl : l.Pairwise lt) (hl' : l'.Pairwise lt)
(h : a, a l a l') : l = l' := by
@@ -242,13 +268,13 @@ private theorem eq_of_pairwise_lt_of_mem_iff_mem {lt : αα → Prop} [asym
have hgt := hl.1 y _
cases Asymm.asymm _ _ hlt hgt
theorem ClosedOpen.toList_succ_succ_eq_map [UpwardEnumerable α] [SupportsLowerBound .closed α]
public theorem toList_Rco_succ_succ_eq_map [UpwardEnumerable α] [SupportsLowerBound .closed α]
[LinearlyUpwardEnumerable α] [InfinitelyUpwardEnumerable α] [SupportsUpperBound .open α]
[HasFiniteRanges .open α] [LawfulUpwardEnumerable α] [LawfulOpenUpperBound α]
[LawfulUpwardEnumerableLowerBound .closed α] [LawfulUpwardEnumerableUpperBound .open α]
{lower : Bound .closed α} {upper : Bound .open α} :
(PRange.mk (shape := .closed, .open) (UpwardEnumerable.succ lower) (UpwardEnumerable.succ upper)).toList =
(PRange.mk (shape := .closed, .open) lower upper).toList.map UpwardEnumerable.succ := by
((succ lower)...(succ upper)).toList =
(lower...upper).toList.map succ := by
apply eq_of_pairwise_lt_of_mem_iff_mem (lt := UpwardEnumerable.LT) (asymm := ?_)
· apply pairwise_toList_upwardEnumerableLt
· apply List.Pairwise.map (R := UpwardEnumerable.LT) (S := UpwardEnumerable.LT)
@@ -257,7 +283,7 @@ theorem ClosedOpen.toList_succ_succ_eq_map [UpwardEnumerable α] [SupportsLowerB
· apply pairwise_toList_upwardEnumerableLt
· simp only [List.mem_map, mem_toList_iff_mem]
intro a
rw [mem_succ_iff]
rw [mem_Rco_succ_succ_iff]
constructor
· rintro a, rfl, h
exact a, h, rfl
@@ -265,6 +291,16 @@ theorem ClosedOpen.toList_succ_succ_eq_map [UpwardEnumerable α] [SupportsLowerB
exact _, h'.symm, h
· exact fun _ _ => UpwardEnumerable.not_gt_of_lt
@[deprecated toList_Rco_succ_succ_eq_map (since := "2025-08-22")]
public theorem ClosedOpen.toList_succ_succ_eq_map [UpwardEnumerable α] [SupportsLowerBound .closed α]
[LinearlyUpwardEnumerable α] [InfinitelyUpwardEnumerable α] [SupportsUpperBound .open α]
[HasFiniteRanges .open α] [LawfulUpwardEnumerable α] [LawfulOpenUpperBound α]
[LawfulUpwardEnumerableLowerBound .closed α] [LawfulUpwardEnumerableUpperBound .open α]
{lower : Bound .closed α} {upper : Bound .open α} :
((succ lower)...(succ upper)).toList =
(lower...upper).toList.map succ :=
toList_Rco_succ_succ_eq_map
private theorem Internal.forIn'_eq_forIn'_iter [UpwardEnumerable α]
[SupportsUpperBound su α] [SupportsLowerBound sl α] [HasFiniteRanges su α]
[BoundedUpwardEnumerable sl α] [LawfulUpwardEnumerable α]
@@ -276,7 +312,7 @@ private theorem Internal.forIn'_eq_forIn'_iter [UpwardEnumerable α]
ForIn'.forIn' (Internal.iter r) init (fun a ha acc => f a (Internal.isPlausibleIndirectOutput_iter_iff.mp ha) acc) := by
rfl
theorem forIn'_eq_forIn'_toList [UpwardEnumerable α]
public theorem forIn'_eq_forIn'_toList [UpwardEnumerable α]
[SupportsUpperBound su α] [SupportsLowerBound sl α] [HasFiniteRanges su α]
[BoundedUpwardEnumerable sl α] [LawfulUpwardEnumerable α]
[LawfulUpwardEnumerableLowerBound sl α] [LawfulUpwardEnumerableUpperBound su α]
@@ -288,7 +324,7 @@ theorem forIn'_eq_forIn'_toList [UpwardEnumerable α]
simp [Internal.forIn'_eq_forIn'_iter, Internal.toList_eq_toList_iter,
Iter.forIn'_eq_forIn'_toList]
theorem forIn'_toList_eq_forIn' [UpwardEnumerable α]
public theorem forIn'_toList_eq_forIn' [UpwardEnumerable α]
[SupportsUpperBound su α] [SupportsLowerBound sl α] [HasFiniteRanges su α]
[BoundedUpwardEnumerable sl α] [LawfulUpwardEnumerable α]
[LawfulUpwardEnumerableLowerBound sl α] [LawfulUpwardEnumerableUpperBound su α]
@@ -299,7 +335,7 @@ theorem forIn'_toList_eq_forIn' [UpwardEnumerable α]
ForIn'.forIn' r init (fun a ha acc => f a (mem_toList_iff_mem.mpr ha) acc) := by
simp [forIn'_eq_forIn'_toList]
theorem mem_of_mem_open [UpwardEnumerable α]
public theorem mem_of_mem_open [UpwardEnumerable α]
[SupportsUpperBound su α] [SupportsLowerBound sl α] [HasFiniteRanges su α]
[BoundedUpwardEnumerable sl α] [LawfulUpwardEnumerable α]
[LawfulUpwardEnumerableLowerBound sl α] [LawfulUpwardEnumerableUpperBound su α]
@@ -310,22 +346,21 @@ theorem mem_of_mem_open [UpwardEnumerable α]
a r := by
refine ?_, hmem.2
have := hmem.1
simp only [LawfulUpwardEnumerableLowerBound.isSatisfied_iff,
BoundedUpwardEnumerable.init?] at this hrb
simp only [LawfulUpwardEnumerableLowerBound.isSatisfied_iff, init?] at this hrb
obtain init, hi := hrb
obtain b', hb' := this
refine init, hi.1, UpwardEnumerable.le_trans hi.2 (UpwardEnumerable.le_trans ?_ hb'.2)
exact UpwardEnumerable.le_of_succ?_eq hb'.1
theorem SupportsLowerBound.isSatisfied_init? {sl} [UpwardEnumerable α]
public theorem SupportsLowerBound.isSatisfied_init? {sl} [UpwardEnumerable α]
[SupportsLowerBound sl α] [BoundedUpwardEnumerable sl α] [LawfulUpwardEnumerable α]
[LawfulUpwardEnumerableLowerBound sl α]
{bound : Bound sl α} {a : α} (h : BoundedUpwardEnumerable.init? bound = some a) :
{bound : Bound sl α} {a : α} (h : init? bound = some a) :
SupportsLowerBound.IsSatisfied bound a := by
simp only [LawfulUpwardEnumerableLowerBound.isSatisfied_iff]
exact a, h, UpwardEnumerable.le_refl _
theorem forIn'_eq_match {sl su} [UpwardEnumerable α]
public theorem forIn'_eq_match {sl su} [UpwardEnumerable α]
[SupportsUpperBound su α] [SupportsLowerBound sl α] [HasFiniteRanges su α]
[BoundedUpwardEnumerable sl α] [LawfulUpwardEnumerable α]
[LawfulUpwardEnumerableLowerBound sl α] [LawfulUpwardEnumerableUpperBound su α]
@@ -333,7 +368,7 @@ theorem forIn'_eq_match {sl su} [UpwardEnumerable α]
{r : PRange sl, su α}
{γ : Type u} {init : γ} {m : Type u Type w} [Monad m] [LawfulMonad m]
{f : (a : α) _ γ m (ForInStep γ)} :
ForIn'.forIn' r init f = match hi : BoundedUpwardEnumerable.init? r.lower with
ForIn'.forIn' r init f = match hi : init? r.lower with
| none => pure init
| some a => if hu : SupportsUpperBound.IsSatisfied r.upper a then do
match f a SupportsLowerBound.isSatisfied_init? hi, hu init with
@@ -358,7 +393,7 @@ theorem forIn'_eq_match {sl su} [UpwardEnumerable α]
· simp
· simp
instance {su} [UpwardEnumerable α] [SupportsUpperBound su α] [RangeSize su α]
public instance {su} [UpwardEnumerable α] [SupportsUpperBound su α] [RangeSize su α]
[LawfulUpwardEnumerable α] [HasFiniteRanges su α] [LawfulRangeSize su α] :
LawfulIteratorSize (RangeIterator su α) where
size_eq_size_toArray {it} := by
@@ -376,7 +411,7 @@ instance {su} [UpwardEnumerable α] [SupportsUpperBound su α] [RangeSize su α]
cases hn : UpwardEnumerable.succ? next
· have := LawfulRangeSize.size_eq_one_of_succ?_eq_none _ _ h hn
simp [*] at this
· have := LawfulRangeSize.size_eq_succ_of_succ?_eq_some _ _ h hn
· have := LawfulRangeSize.size_eq_succ_of_succ?_eq_some _ _ _ h hn
simp [*] at this
· simp [h]
· rename_i ih
@@ -389,14 +424,14 @@ instance {su} [UpwardEnumerable α] [SupportsUpperBound su α] [RangeSize su α]
simp only [RangeIterator.step, Array.size_empty]
simp_all [LawfulRangeSize.size_eq_one_of_succ?_eq_none _ _ h' hn]
· rename_i next'
have := LawfulRangeSize.size_eq_succ_of_succ?_eq_some _ _ h' hn
have := LawfulRangeSize.size_eq_succ_of_succ?_eq_some _ _ _ h' hn
simp only [this, Nat.add_right_cancel_iff] at h
specialize ih (it := some next', it.internalState.upperBound) next' rfl h
rw [ih, Nat.add_comm]
· have := LawfulRangeSize.size_eq_zero_of_not_satisfied _ _ h'
· have := LawfulRangeSize.size_eq_zero_of_not_isSatisfied _ _ h'
simp [*] at this
theorem isEmpty_iff_forall_not_mem {sl su} [UpwardEnumerable α] [LawfulUpwardEnumerable α]
public theorem isEmpty_iff_forall_not_mem {sl su} [UpwardEnumerable α] [LawfulUpwardEnumerable α]
[BoundedUpwardEnumerable sl α] [SupportsLowerBound sl α] [SupportsUpperBound su α]
[LawfulUpwardEnumerableLowerBound sl α] [LawfulUpwardEnumerableUpperBound su α]
{r : PRange sl, su α} :
@@ -418,6 +453,6 @@ theorem isEmpty_iff_forall_not_mem {sl su} [UpwardEnumerable α] [LawfulUpwardEn
intro hu
have hl := SupportsLowerBound.isSatisfied_init? (bound := r.lower)
(Option.some_get hi).symm
exact h ((BoundedUpwardEnumerable.init? r.lower).get hi) hl, hu
exact h ((init? r.lower).get hi) hl, hu
end Std.PRange

View File

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

View File

@@ -13,13 +13,16 @@ public section
namespace Std.PRange.Nat
theorem succ_eq {n : Nat} : UpwardEnumerable.succ n = n + 1 :=
theorem succ_eq {n : Nat} : succ n = n + 1 :=
rfl
theorem ClosedOpen.toList_succ_succ {m n : Nat} :
((m+1)...(n+1)).toList =
(m...n).toList.map (· + 1) := by
theorem toList_Rco_succ_succ {m n : Nat} :
((m+1)...(n+1)).toList = (m...n).toList.map (· + 1) := by
simp only [ succ_eq]
rw [Std.PRange.ClosedOpen.toList_succ_succ_eq_map]
rw [Std.PRange.toList_Rco_succ_succ_eq_map]
@[deprecated toList_Rco_succ_succ (since := "2025-08-22")]
theorem ClosedOpen.toList_succ_succ {m n : Nat} :
((m+1)...(n+1)).toList = (m...n).toList.map (· + 1) := toList_Rco_succ_succ
end Std.PRange.Nat

View File

@@ -53,14 +53,16 @@ A range of elements of some type `α`. It is characterized by its upper and lowe
may be inclusive, exclusive or absent.
* `a...=b` is the range of elements greater than or equal to `a` and less than or equal to `b`.
* `a<...=b` is the range of elements greater than `a` and less than or equal to `b`.
* `a...b` or `a...<b` is the range of elements greater than or equal to `a` and less than `b`.
* `a...*` is the range of elements greater than or equal to `a`.
* `a<...=b` is the range of elements greater than `a` and less than or equal to `b`.
* `a<...b` or `a<...<b` is the range of elements greater than `a` and less than `b`.
* `a<...*` is the range of elements greater than `a`.
* `*...=b` is the range of elements less than or equal to `b`.
* `*...b` or `*...<b` is the range of elements less than `b`.
* `a...*` is the range of elements greater than or equal to `a`.
* `a<...*` is the range of elements greater than `a`.
* `*...*` contains all elements of `α`.
The recommended spelling for these ranges can be found in the `PRange.mk` constructor's docstring.
-/
structure _root_.Std.PRange (shape : RangeShape) (α : Type u) where
/-- The lower bound of the range. -/
@@ -68,6 +70,14 @@ structure _root_.Std.PRange (shape : RangeShape) (α : Type u) where
/-- The upper bound of the range. -/
upper : Bound shape.upper α
/--
Creates a new range. For more information about ranges, see `Std.PRange`.
The implicit `shape` parameter specifies the shape of the explicitly given
lower and upper bounds.
-/
add_decl_doc _root_.Std.PRange.mk
/-- `a...*` is the range of elements greater than or equal to `a`. See also `Std.PRange`. -/
syntax:max (term "...*") : term
/-- `*...*` is the range that is unbounded in both directions. See also `Std.PRange`. -/
@@ -125,6 +135,27 @@ macro_rules
| `($a<...<$b) => ``(PRange.mk (shape := RangeShape.mk BoundShape.open BoundShape.open) $a $b)
| `($a<...$b) => ``(PRange.mk (shape := RangeShape.mk BoundShape.open BoundShape.open) $a $b)
recommended_spelling "Rcc" for "a...=b" in [PRange.mk, «term_...=_»]
recommended_spelling "Rco" for "a...b" in [PRange.mk, «term_..._», «term_...<_»]
recommended_spelling "Rco" for "a...<b" in [«term_...<_»]
recommended_spelling "Rci" for "a...*" in [PRange.mk, «term_...*»]
recommended_spelling "Roc" for "a<...=b" in [PRange.mk, «term_<...=_»]
recommended_spelling "Roo" for "a<...b" in [PRange.mk, «term_<..._», «term_<...<_»]
recommended_spelling "Roo" for "a<...<b" in [«term_<...<_»]
recommended_spelling "Roi" for "a<...*" in [PRange.mk, «term_<...*»]
recommended_spelling "Ric" for "*...=b" in [PRange.mk, «term*...=_»]
recommended_spelling "Rio" for "*...b" in [PRange.mk, «term*..._», «term*...<_»]
recommended_spelling "Rio" for "*...<b" in [«term*...<_»]
recommended_spelling "Rii" for "*...*" in [PRange.mk, «term*...*»]
recommended_spelling "Rcx" for "PRange.mk .closed ub" in [PRange.mk]
recommended_spelling "Rox" for "PRange.mk .open ub" in [PRange.mk]
recommended_spelling "Rix" for "PRange.mk .unbounded ub" in [PRange.mk]
recommended_spelling "Rxc" for "PRange.mk lb .closed" in [PRange.mk]
recommended_spelling "Rxo" for "PRange.mk lb .open" in [PRange.mk]
recommended_spelling "Rxi" for "PRange.mk lb .unbounded" in [PRange.mk]
recommended_spelling "Rxx" for "PRange.mk lb ub" in [PRange.mk]
/--
This typeclass provides decidable lower bound checks of the given shape.
@@ -138,6 +169,8 @@ class SupportsLowerBound (shape : BoundShape) (α : Type u) where
IsSatisfied : Bound shape α α Prop
decidableSatisfiesLowerBound : DecidableRel IsSatisfied := by infer_instance
attribute [simp] SupportsLowerBound.IsSatisfied
instance : SupportsLowerBound .unbounded α where
IsSatisfied _ _ := True
@@ -154,6 +187,8 @@ class SupportsUpperBound (shape : BoundShape) (α : Type u) where
IsSatisfied : Bound shape α α Prop
decidableSatisfiesUpperBound : DecidableRel IsSatisfied := by infer_instance
attribute [simp] SupportsUpperBound.IsSatisfied
instance {α} : SupportsUpperBound .unbounded α where
IsSatisfied _ _ := True
@@ -175,9 +210,9 @@ instance {sl su α a} [SupportsLowerBound sl α] [SupportsUpperBound su α] (r :
This typeclass ensures that ranges with the given shape of upper bounds are always finite.
This is a prerequisite for many functions and instances, such as `PRange.toList` or `ForIn'`.
-/
class HasFiniteRanges (shape α) [SupportsUpperBound shape α] : Prop where
mem_of_satisfiesUpperBound (u : Bound shape α) :
enumeration : List α, (a : α) SupportsUpperBound.IsSatisfied u a a enumeration
class HasFiniteRanges (shape α) [UpwardEnumerable α] [SupportsUpperBound shape α] : Prop where
finite (init : α) (u : Bound shape α) :
n, (UpwardEnumerable.succMany? n init).elim True (¬ SupportsUpperBound.IsSatisfied u ·)
/--
This typeclass will usually be used together with `UpwardEnumerable α`. It provides the starting
@@ -192,6 +227,9 @@ Instances are automatically generated in the following cases:
class BoundedUpwardEnumerable (lowerBoundShape : BoundShape) (α : Type u) where
init? : Bound lowerBoundShape α Option α
attribute [simp] BoundedUpwardEnumerable.init?
export BoundedUpwardEnumerable (init?)
/--
This typeclass ensures that the lower bound predicate from `SupportsLowerBound sl α`
can be characterized in terms of `UpwardEnumerable α` and `BoundedUpwardEnumerable sl α`.
@@ -200,11 +238,10 @@ class LawfulUpwardEnumerableLowerBound (sl α) [UpwardEnumerable α]
[SupportsLowerBound sl α] [BoundedUpwardEnumerable sl α] where
/--
An element `a` satisfies the lower bound `l` if and only if it is
`BoundedUpwardEnumerable.init? l` or one of its transitive successors.
`init? l` or one of its transitive successors.
-/
isSatisfied_iff (a : α) (l : Bound sl α) :
SupportsLowerBound.IsSatisfied l a
init, BoundedUpwardEnumerable.init? l = some init UpwardEnumerable.LE init a
SupportsLowerBound.IsSatisfied l a init, init? l = some init UpwardEnumerable.LE init a
/--
This typeclass ensures that if `b` is a transitive successor of `a` and `b` satisfies an upper bound

View File

@@ -232,36 +232,49 @@ private def List.length_filter_strict_mono {l : List α} {P Q : α → Bool} {a
private def RangeIterator.instFinitenessRelation [UpwardEnumerable α] [SupportsUpperBound su α]
[LawfulUpwardEnumerable α] [HasFiniteRanges su α] :
FinitenessRelation (RangeIterator su α) Id where
rel :=
open Classical in
InvImage WellFoundedRelation.rel
(fun it => (HasFiniteRanges.mem_of_satisfiesUpperBound it.internalState.upperBound).choose
|>.filter ( a, it.internalState.next = some a UpwardEnumerable.LE a ·)
|>.length)
wf := InvImage.wf _ WellFoundedRelation.wf
subrelation {it it'} h := by
simp_wf
rw [Monadic.isPlausibleSuccessorOf_iff] at h
obtain a, hn, hu, hn', hu' := h
rw [hu']
apply List.length_filter_strict_mono (a := a)
· intro u h
simp only [decide_eq_true_eq] at h
obtain a', ha', hle := h
refine a, hn, UpwardEnumerable.le_trans 1, ?_ hle
rw [ha'] at hn'
rw [UpwardEnumerable.succMany?_succ, LawfulUpwardEnumerable.succMany?_zero,
Option.bind_some, hn']
· exact (HasFiniteRanges.mem_of_satisfiesUpperBound _).choose_spec _ hu
· intro h
simp only [decide_eq_true_eq] at h
obtain x, hx, h := h
rw [hx] at hn'
have hlt : UpwardEnumerable.LT a x :=
0, by simp [UpwardEnumerable.succMany?_succ, UpwardEnumerable.succMany?_zero, hn']
exact UpwardEnumerable.not_gt_of_le h hlt
· simp only [decide_eq_true_eq]
exact a, hn, UpwardEnumerable.le_refl _
rel it' it := it'.IsPlausibleSuccessorOf it
wf := by
constructor
intro it
have hnone : bound, Acc (fun it' it : IterM (α := RangeIterator su α) Id α => it'.IsPlausibleSuccessorOf it)
none, bound := by
intro bound
constructor
intro it' step, hs₁, hs₂
simp only [IterM.IsPlausibleStep, Iterator.IsPlausibleStep, Monadic.step] at hs₂
simp [hs₂, IterStep.successor] at hs₁
simp only [IterM.IsPlausibleSuccessorOf, IterM.IsPlausibleStep, Iterator.IsPlausibleStep,
Monadic.step, exists_eq_right] at hnone
match it with
| none, _ => apply hnone
| some init, bound =>
obtain n, hn := HasFiniteRanges.finite init bound
induction n generalizing init with
| zero =>
simp only [succMany?_zero, Option.elim_some] at hn
constructor
simp [hn, IterStep.successor]
| succ n ih =>
constructor
rintro it'
simp only [succMany?_succ?_eq_succ?_bind_succMany?] at hn
match hs : succ? init with
| none =>
simp only [hs]
intro h
split at h
· cases h
apply hnone
· cases h
| some a =>
intro h
simp only [hs] at h hn
specialize ih _ hn
split at h
· cases h
exact ih
· cases h
subrelation := id
@[no_expose]
instance RangeIterator.instFinite {su} [UpwardEnumerable α] [SupportsUpperBound su α]
@@ -303,7 +316,7 @@ instance RangeIterator.instIteratorAccess {su} [UpwardEnumerable α] [SupportsUp
· split <;> rename_i heq
· apply IterM.IsPlausibleNthOutputStep.done
simp only [Monadic.isPlausibleStep_iff, Monadic.step]
simp only [Option.bind_eq_none_iff, UpwardEnumerable.succMany?_zero, reduceCtorEq,
simp only [Option.bind_eq_none_iff, succMany?_zero, reduceCtorEq,
imp_false] at heq
cases heq' : it.internalState.next
· simp
@@ -312,7 +325,7 @@ instance RangeIterator.instIteratorAccess {su} [UpwardEnumerable α] [SupportsUp
exact heq _ rfl
· cases heq' : it.internalState.next
· simp [heq'] at heq
simp only [heq', Option.bind_some, UpwardEnumerable.succMany?_zero, Option.some.injEq] at heq
simp only [heq', Option.bind_some, succMany?_zero, Option.some.injEq] at heq
cases heq
split <;> rename_i heq''
· apply IterM.IsPlausibleNthOutputStep.zero_yield
@@ -325,7 +338,7 @@ instance RangeIterator.instIteratorAccess {su} [UpwardEnumerable α] [SupportsUp
· apply IterM.IsPlausibleNthOutputStep.done
simp only [Monadic.isPlausibleStep_iff, Monadic.step, heq']
· rename_i out
simp only [heq', Option.bind_some, LawfulUpwardEnumerable.succMany?_succ_eq_succ?_bind_succMany?] at heq
simp only [heq', Option.bind_some, succMany?_succ?_eq_succ?_bind_succMany?] at heq
specialize ih UpwardEnumerable.succ? out, it.internalState.upperBound
simp only [heq] at ih
by_cases heq'' : SupportsUpperBound.IsSatisfied it.internalState.upperBound out
@@ -341,7 +354,7 @@ instance RangeIterator.instIteratorAccess {su} [UpwardEnumerable α] [SupportsUp
rename_i out
simp only [heq', Option.bind_some] at heq
have hle : UpwardEnumerable.LE out _ := n + 1, heq
simp only [LawfulUpwardEnumerable.succMany?_succ_eq_succ?_bind_succMany?] at heq
simp only [succMany?_succ?_eq_succ?_bind_succMany?] at heq
specialize ih UpwardEnumerable.succ? out, it.internalState.upperBound
simp only [heq] at ih
by_cases hout : SupportsUpperBound.IsSatisfied it.internalState.upperBound out
@@ -365,7 +378,7 @@ theorem RangeIterator.Monadic.isPlausibleIndirectOutput_iff {su α}
[LawfulUpwardEnumerable α] [LawfulUpwardEnumerableUpperBound su α]
{it : IterM (α := RangeIterator su α) Id α} {out : α} :
it.IsPlausibleIndirectOutput out
n, it.internalState.next.bind (UpwardEnumerable.succMany? n ·) = some out
n, it.internalState.next.bind (succMany? n ·) = some out
SupportsUpperBound.IsSatisfied it.internalState.upperBound out := by
constructor
· intro h
@@ -378,7 +391,7 @@ theorem RangeIterator.Monadic.isPlausibleIndirectOutput_iff {su α}
obtain n, hn := ih
obtain a, ha, h₁, h₂, h₃ := h
refine n + 1, ?_
simp [ha, h₃, hn.2, LawfulUpwardEnumerable.succMany?_succ_eq_succ?_bind_succMany?, h₂, hn]
simp [ha, h₃, hn.2, succMany?_succ?_eq_succ?_bind_succMany?, h₂, hn]
· rintro n, hn, hu
induction n generalizing it
case zero =>
@@ -391,8 +404,8 @@ theorem RangeIterator.Monadic.isPlausibleIndirectOutput_iff {su α}
rename_i a
simp only [hn', Option.bind_some] at hn
have hle : UpwardEnumerable.LE a out := _, hn
rw [LawfulUpwardEnumerable.succMany?_succ_eq_succ?_bind_succMany?] at hn
cases hn' : UpwardEnumerable.succ? a
rw [succMany?_succ?_eq_succ?_bind_succMany?] at hn
cases hn' : succ? a
· simp only [hn', Option.bind_none, reduceCtorEq] at hn
rename_i a'
simp only [hn', Option.bind_some] at hn
@@ -409,7 +422,7 @@ theorem RangeIterator.isPlausibleIndirectOutput_iff {su α}
[LawfulUpwardEnumerable α] [LawfulUpwardEnumerableUpperBound su α]
{it : Iter (α := RangeIterator su α) α} {out : α} :
it.IsPlausibleIndirectOutput out
n, it.internalState.next.bind (UpwardEnumerable.succMany? n ·) = some out
n, it.internalState.next.bind (succMany? n ·) = some out
SupportsUpperBound.IsSatisfied it.internalState.upperBound out := by
simp only [Iter.isPlausibleIndirectOutput_iff_isPlausibleIndirectOutput_toIterM,
Monadic.isPlausibleIndirectOutput_iff, Iter.toIterM]
@@ -463,7 +476,7 @@ instance RangeIterator.instIteratorLoop {su} [UpwardEnumerable α] [SupportsUppe
exact UpwardEnumerable.le_refl _
case hle' =>
refine UpwardEnumerable.le_trans hl 1, ?_
simp [UpwardEnumerable.succMany?_one, hs]
simp [succMany?_one, hs]
partial instance RepeatIterator.instIteratorLoopPartial {su} [UpwardEnumerable α]
[SupportsUpperBound su α] [LawfulUpwardEnumerable α] [LawfulUpwardEnumerableUpperBound su α]
@@ -483,7 +496,7 @@ partial instance RepeatIterator.instIteratorLoopPartial {su} [UpwardEnumerable
(next : α) (hl : UpwardEnumerable.LE least next) (hu : SupportsUpperBound.IsSatisfied upperBound next) : n γ := do
match f next hl hu acc with
| .yield acc' =>
match hs : UpwardEnumerable.succ? next with
match hs : succ? next with
| some next' =>
if hu : SupportsUpperBound.IsSatisfied upperBound next' then
loop γ upperBound least acc' f next' ?hle' hu
@@ -500,10 +513,10 @@ partial instance RepeatIterator.instIteratorLoopPartial {su} [UpwardEnumerable
exact UpwardEnumerable.le_refl _
case hle' =>
refine UpwardEnumerable.le_trans hl 1, ?_
simp [UpwardEnumerable.succMany?_one, hs]
simp [succMany?_one, hs]
theorem RangeIterator.instIteratorLoop.loop_eq {su} [UpwardEnumerable α] [SupportsUpperBound su α]
[LawfulUpwardEnumerable α] [LawfulUpwardEnumerableUpperBound su α]
theorem RangeIterator.instIteratorLoop.loop_eq {su} [UpwardEnumerable α]
[SupportsUpperBound su α] [LawfulUpwardEnumerable α] [LawfulUpwardEnumerableUpperBound su α]
{n : Type u Type w} [Monad n] [LawfulMonad n] {γ : Type u}
{lift} [Internal.LawfulMonadLiftBindFunction lift]
{PlausibleForInStep} {upperBound} {next} {hl} {hu} {f} {acc} {wf} :
@@ -511,16 +524,18 @@ theorem RangeIterator.instIteratorLoop.loop_eq {su} [UpwardEnumerable α] [Suppo
(do
match f next hl hu acc with
| .yield c, _ =>
letI it' : IterM (α := RangeIterator su α) Id α := UpwardEnumerable.succ? next, upperBound
letI it' : IterM (α := RangeIterator su α) Id α := succ? next, upperBound
IterM.DefaultConsumers.forIn' (m := Id) lift γ
PlausibleForInStep wf it' c it'.IsPlausibleIndirectOutput (fun _ => id)
(fun b h c => f b
(by
refine UpwardEnumerable.le_trans hl ?_
simp only [RangeIterator.Monadic.isPlausibleIndirectOutput_iff, it',
LawfulUpwardEnumerable.succMany?_succ_eq_succ?_bind_succMany?] at h
succMany?_succ?_eq_succ?_bind_succMany?] at h
exact h.choose + 1, h.choose_spec.1)
(by simp only [RangeIterator.Monadic.isPlausibleIndirectOutput_iff, it'] at h; exact h.choose_spec.2) c)
(by
simp only [RangeIterator.Monadic.isPlausibleIndirectOutput_iff, it'] at h
exact h.choose_spec.2) c)
| .done c, _ => return c) := by
rw [loop]
apply bind_congr

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