Compare commits

..

58 Commits

Author SHA1 Message Date
Leonardo de Moura
dc95aadd6c chore: remove workaround 2025-04-17 18:57:05 -07:00
Leonardo de Moura
6f4c33a31c WIP 2025-04-17 18:55:08 -07:00
Lean stage0 autoupdater
3cbffee94b chore: update stage0 2025-04-18 01:52:46 +00:00
Leonardo de Moura
807182d63e chore: allow RArray to be universe polymorphic (#8013)
This PR ensures that `RArray` can be made universe polymorphic. We need
an update-stage0 before finalizing this modification.
2025-04-18 01:10:44 +00:00
Lean stage0 autoupdater
a21377b9ec chore: update stage0 2025-04-18 00:52:57 +00:00
Leonardo de Moura
96fd2f195c feat: add debug.terminalTacticsAsSorry (#8012)
This PR adds the option `debug.terminalTacticsAsSorry`. When enabled,
terminal tactics such as `grind` and `omega` are replaced with `sorry`.
Useful for debugging and fixing bootstrapping issues.
2025-04-18 00:10:59 +00:00
Leonardo de Moura
5823d03283 feat: add IsCharP support to multivariate polynomial library (#8011)
This PR adds `IsCharP` support to the multivariate‑polynomial library in
`CommRing`.
2025-04-17 23:55:21 +00:00
Cameron Zwarich
d981fa0faf fix: make implemented_by of casesOn work correctly with hash consing (#8010)
This PR fixes caseOn expressions with an implemented_by to work
correctly with hash consing, even when the elaborator produces terms
that reconstruct the discriminant rather than just reusing a variable.
2025-04-17 23:32:59 +00:00
Cameron Zwarich
7b292090ce fix: restrict lifting outside of cases expressions on Decidable (#8009)
This PR restricts lifting outside of cases expressions on values of a
Decidable type, since we can't correctly represent the dependency on the
erased proposition in the later stages of the compiler.
2025-04-17 23:01:56 +00:00
Cameron Zwarich
f0033cd15e fix: consider params to be ground variables in specialization (#8008)
This PR changes specialization in the new code generator to consider
callee params to be ground variables, which improves the specialization
of polymorphic functions.
2025-04-17 22:34:16 +00:00
Cameron Zwarich
7bbcfdf712 fix: modify eager lambda lifting heuristics to match the old compiler (#8007)
This PR changes eager lambda lifting heuristics in the new compiler to
match the old compiler, which ensures that inlining/specializing monadic
code does not accidentally create mutual tail recursion that the code
generator can't handle.
2025-04-17 21:46:51 +00:00
Cameron Zwarich
130e2d93a5 fix: change inlining heuristics to match old code generator (#8006)
This PR changes the inlining heuristics of the new code generator to
match the old one, which ensures that monadic folds get sufficiently
inlined for their tail recursion to be exposed to the code generator.
2025-04-17 20:47:40 +00:00
Mac Malone
5b16ea98f5 fix: lake: extern_lib linking (#7987)
This PR fixes a bug in #7967 that broke external library linking.

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

---------

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

View File

@@ -108,6 +108,7 @@ ExternalProject_add(stage2
INSTALL_COMMAND ""
DEPENDS stage1
EXCLUDE_FROM_ALL ON
STEP_TARGETS configure
)
ExternalProject_add(stage3
SOURCE_DIR "${LEAN_SOURCE_DIR}"

View File

@@ -780,12 +780,11 @@ add_custom_target(clean-olean
DEPENDS clean-stdlib)
install(DIRECTORY "${CMAKE_BINARY_DIR}/lib/" DESTINATION lib
PATTERN temp
PATTERN "*.export"
PATTERN "*.hash"
PATTERN "*.trace"
PATTERN "*.rsp"
EXCLUDE)
PATTERN temp EXCLUDE
PATTERN "*.export" EXCLUDE
PATTERN "*.hash" EXCLUDE
PATTERN "*.trace" EXCLUDE
PATTERN "*.rsp" EXCLUDE)
# symlink source into expected installation location for go-to-definition, if file system allows it
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/src)

View File

@@ -738,6 +738,20 @@ Unlike `x ≠ y` (which is notation for `Ne x y`), this is `Bool` valued instead
recommended_spelling "bne" for "!=" in [bne, «term_!=_»]
/-- `ReflBEq α` says that the `BEq` implementation is reflexive. -/
class ReflBEq (α) [BEq α] : Prop where
/-- `==` is reflexive, that is, `(a == a) = true`. -/
protected rfl {a : α} : a == a
@[simp] theorem BEq.rfl [BEq α] [ReflBEq α] {a : α} : a == a := ReflBEq.rfl
theorem BEq.refl [BEq α] [ReflBEq α] (a : α) : a == a := BEq.rfl
theorem beq_of_eq [BEq α] [ReflBEq α] {a b : α} : a = b a == b
| rfl => BEq.rfl
theorem not_eq_of_beq_eq_false [BEq α] [ReflBEq α] {a b : α} (h : (a == b) = false) : ¬a = b := by
intro h'; subst h'; have : true = false := BEq.rfl.symm.trans h; contradiction
/--
A Boolean equality test coincides with propositional equality.
@@ -745,11 +759,9 @@ In other words:
* `a == b` implies `a = b`.
* `a == a` is true.
-/
class LawfulBEq (α : Type u) [BEq α] : Prop where
class LawfulBEq (α : Type u) [BEq α] : Prop extends ReflBEq α where
/-- If `a == b` evaluates to `true`, then `a` and `b` are equal in the logic. -/
eq_of_beq : {a b : α} a == b a = b
/-- `==` is reflexive, that is, `(a == a) = true`. -/
protected rfl : {a : α} a == a
export LawfulBEq (eq_of_beq)
@@ -761,6 +773,15 @@ instance [DecidableEq α] : LawfulBEq α where
eq_of_beq := of_decide_eq_true
rfl := of_decide_eq_self_eq_true _
/--
Non-instance for `DecidableEq` from `LawfulBEq`.
To use this, add `attribute [local instance 5] instDecidableEqOfLawfulBEq` at the top of a file.
-/
def instDecidableEqOfLawfulBEq [BEq α] [LawfulBEq α] : DecidableEq α := fun x y =>
match h : x == y with
| false => .isFalse (not_eq_of_beq_eq_false h)
| true => .isTrue (eq_of_beq h)
instance : LawfulBEq Char := inferInstance
instance : LawfulBEq String := inferInstance
@@ -855,8 +876,8 @@ theorem Bool.of_not_eq_false : {b : Bool} → ¬ (b = false) → b = true
| true, _ => rfl
| false, h => absurd rfl h
theorem ne_of_beq_false [BEq α] [LawfulBEq α] {a b : α} (h : (a == b) = false) : a b := by
intro h'; subst h'; have : true = false := Eq.trans LawfulBEq.rfl.symm h; contradiction
theorem ne_of_beq_false [BEq α] [ReflBEq α] {a b : α} (h : (a == b) = false) : a b :=
not_eq_of_beq_eq_false h
theorem beq_false_of_ne [BEq α] [LawfulBEq α] {a b : α} (h : a b) : (a == b) = false :=
have : ¬ (a == b) = true := by
@@ -1296,6 +1317,15 @@ theorem eta (a : {x // p x}) (h : p (val a)) : mk (val a) h = a := by
cases a
exact rfl
instance {α : Type u} {p : α Prop} [BEq α] : BEq {x : α // p x} :=
fun x y => x.1 == y.1
instance {α : Type u} {p : α Prop} [BEq α] [ReflBEq α] : ReflBEq {x : α // p x} where
rfl {x} := BEq.refl x.1
instance {α : Type u} {p : α Prop} [BEq α] [LawfulBEq α] : LawfulBEq {x : α // p x} where
eq_of_beq h := Subtype.eq (eq_of_beq h)
instance {α : Type u} {p : α Prop} [DecidableEq α] : DecidableEq {x : α // p x} :=
fun a, h₁ b, h₂ =>
if h : a = b then isTrue (by subst h; exact rfl)
@@ -1564,7 +1594,7 @@ theorem Nat.succ.injEq (u v : Nat) : (u.succ = v.succ) = (u = v) :=
Eq.propIntro Nat.succ.inj (congrArg Nat.succ)
@[simp] theorem beq_iff_eq [BEq α] [LawfulBEq α] {a b : α} : a == b a = b :=
eq_of_beq, by intro h; subst h; exact LawfulBEq.rfl
eq_of_beq, beq_of_eq
/-! # Prop lemmas -/

View File

@@ -525,7 +525,7 @@ theorem countP_attachWith {p : α → Prop} {q : α → Bool} {xs : Array α} {H
simp
@[simp]
theorem count_attach [DecidableEq α] {xs : Array α} {a : {x // x xs}} :
theorem count_attach [BEq α] {xs : Array α} {a : {x // x xs}} :
xs.attach.count a = xs.count a := by
rcases xs with xs
simp only [List.attach_toArray, List.attachWith_mem_toArray, List.count_toArray]
@@ -534,7 +534,7 @@ theorem count_attach [DecidableEq α] {xs : Array α} {a : {x // x ∈ xs}} :
rw [List.countP_pmap, List.countP_attach (p := (fun x => x == a.1)), List.count]
@[simp]
theorem count_attachWith [DecidableEq α] {p : α Prop} {xs : Array α} (H : a xs, p a) {a : {x // p x}} :
theorem count_attachWith [BEq α] {p : α Prop} {xs : Array α} (H : a xs, p a) {a : {x // p x}} :
(xs.attachWith p H).count a = xs.count a := by
cases xs
simp

View File

@@ -257,8 +257,8 @@ theorem filter_beq {xs : Array α} (a : α) : xs.filter (· == a) = replicate (c
rcases xs with xs
simp [List.filter_beq]
theorem filter_eq {α} [DecidableEq α] {xs : Array α} (a : α) : xs.filter (· = a) = replicate (count a xs) a :=
filter_beq a
theorem filter_eq {α} [BEq α] [LawfulBEq α] [DecidableEq α] {xs : Array α} (a : α) : xs.filter (· = a) = replicate (count a xs) a :=
funext (Bool.beq_eq_decide_eq · a) filter_beq a
theorem replicate_count_eq_of_count_eq_size {xs : Array α} (h : count a xs = xs.size) :
replicate (count a xs) a = xs := by
@@ -273,7 +273,7 @@ abbrev mkArray_count_eq_of_count_eq_size := @replicate_count_eq_of_count_eq_size
rcases xs with xs
simp [List.count_filter, h]
theorem count_le_count_map [DecidableEq β] {xs : Array α} {f : α β} {x : α} :
theorem count_le_count_map [BEq β] [LawfulBEq β] {xs : Array α} {f : α β} {x : α} :
count x xs count (f x) (map f xs) := by
rcases xs with xs
simp [List.count_le_count_map, countP_map]
@@ -299,15 +299,14 @@ theorem count_replace {a b c : α} {xs : Array α} :
if xs.contains a then xs.count c + (if b == c then 1 else 0) - (if a == c then 1 else 0) else xs.count c := by
simp [count_eq_countP, countP_replace]
-- FIXME these theorems can be restored once `List.erase` and `Array.erase` have been related.
theorem count_erase (a b : α) (xs : Array α) : count a (xs.erase b) = count a xs - if b == a then 1 else 0 := by
rcases xs with l
simp [List.count_erase]
-- theorem count_erase (a b : α) (l : Array α) : count a (l.erase b) = count a l - if b == a then 1 else 0 := by
-- sorry
@[simp] theorem count_erase_self (a : α) (xs : Array α) :
count a (xs.erase a) = count a xs - 1 := by rw [count_erase, if_pos (by simp)]
-- @[simp] theorem count_erase_self (a : α) (l : Array α) :
-- count a (l.erase a) = count a l - 1 := by rw [count_erase, if_pos (by simp)]
-- @[simp] theorem count_erase_of_ne (ab : a ≠ b) (l : Array α) : count a (l.erase b) = count a l := by
-- rw [count_erase, if_neg (by simpa using ab.symm), Nat.sub_zero]
@[simp] theorem count_erase_of_ne (ab : a b) (xs : Array α) : count a (xs.erase b) = count a xs := by
rw [count_erase, if_neg (by simpa using ab.symm), Nat.sub_zero]
end count

View File

@@ -114,8 +114,10 @@ end List
namespace Array
instance [BEq α] [LawfulBEq α] : LawfulBEq (Array α) where
instance [BEq α] [ReflBEq α] : ReflBEq (Array α) where
rfl := by simp [BEq.beq, isEqv_self_beq]
instance [BEq α] [LawfulBEq α] : LawfulBEq (Array α) where
eq_of_beq := by
rintro _ _ h
simpa using h

View File

@@ -59,7 +59,7 @@ theorem exists_or_eq_self_of_eraseP (p) (xs : Array α) :
@[simp] theorem size_eraseP_of_mem {xs : Array α} (al : a xs) (pa : p a) :
(xs.eraseP p).size = xs.size - 1 := by
let _, ys, zs, _, _, e₁, e₂ := exists_of_eraseP al pa
rw [e₂]; simp [size_append, e₁]; omega
rw [e₂]; simp [size_append, e₁]
theorem size_eraseP {xs : Array α} : (xs.eraseP p).size = if xs.any p then xs.size - 1 else xs.size := by
split <;> rename_i h

View File

@@ -135,6 +135,9 @@ theorem getElem?_eq_none {xs : Array α} (h : xs.size ≤ i) : xs[i]? = none :=
theorem getElem?_eq_some_iff {xs : Array α} : xs[i]? = some b h : i < xs.size, xs[i] = b := by
simp [getElem?_def]
theorem getElem_of_getElem? {xs : Array α} : xs[i]? = some a h : i < xs.size, xs[i] = a :=
getElem?_eq_some_iff.mp
theorem some_eq_getElem?_iff {xs : Array α} : some b = xs[i]? h : i < xs.size, xs[i] = b := by
rw [eq_comm, getElem?_eq_some_iff]
@@ -826,9 +829,6 @@ theorem contains_eq_true_of_mem [BEq α] [LawfulBEq α] {a : α} {as : Array α}
@[deprecated contains_eq_true_of_mem (since := "2024-12-12")]
abbrev elem_eq_true_of_mem := @contains_eq_true_of_mem
instance [BEq α] [LawfulBEq α] (a : α) (as : Array α) : Decidable (a as) :=
decidable_of_decidable_of_iff (Iff.intro mem_of_contains_eq_true contains_eq_true_of_mem)
@[simp] theorem elem_eq_contains [BEq α] {a : α} {xs : Array α} :
elem a xs = xs.contains a := by
simp [elem]
@@ -839,6 +839,9 @@ theorem elem_iff [BEq α] [LawfulBEq α] {a : α} {xs : Array α} :
theorem contains_iff [BEq α] [LawfulBEq α] {a : α} {xs : Array α} :
xs.contains a = true a xs := mem_of_contains_eq_true, contains_eq_true_of_mem
instance [BEq α] [LawfulBEq α] (a : α) (as : Array α) : Decidable (a as) :=
decidable_of_decidable_of_iff elem_iff
theorem elem_eq_mem [BEq α] [LawfulBEq α] {a : α} {xs : Array α} :
elem a xs = decide (a xs) := by rw [Bool.eq_iff_iff, elem_iff, decide_eq_true_iff]
@@ -1091,33 +1094,23 @@ private theorem beq_of_beq_singleton [BEq α] {a b : α} : #[a] == #[b] → a ==
apply beq_of_beq_singleton
simp
· intro h
constructor
apply Array.isEqv_self_beq
infer_instance
@[simp] theorem lawfulBEq_iff [BEq α] : LawfulBEq (Array α) LawfulBEq α := by
constructor
· intro h
have : ReflBEq α := reflBEq_iff.mp inferInstance
constructor
· intro a b h
apply singleton_inj.1
apply eq_of_beq
simpa [instBEq, isEqv, isEqvAux]
· intro a
apply beq_of_beq_singleton
simp
intro a b h
apply singleton_inj.1
apply eq_of_beq
simpa [instBEq, isEqv, isEqvAux]
· intro h
constructor
· intro xs ys h
obtain hs, hi := isEqv_iff_rel.mp h
ext i h₁ h₂
· exact hs
· simpa using hi _ h₁
· intro xs
apply Array.isEqv_self_beq
infer_instance
/-! ### isEqv -/
@[simp] theorem isEqv_eq [DecidableEq α] {xs ys : Array α} : xs.isEqv ys (· == ·) = (xs = ys) := by
@[simp] theorem isEqv_eq [BEq α] [LawfulBEq α] {xs ys : Array α} : xs.isEqv ys (· == ·) = (xs = ys) := by
cases xs
cases ys
simp
@@ -3035,7 +3028,7 @@ theorem foldrM_start_stop {m} [Monad m] {xs : Array α} {f : α → β → m β}
simp
| succ i ih =>
unfold foldrM.fold
simp only [beq_iff_eq, Nat.add_right_eq_self, Nat.add_one_ne_zero, reduceIte, Nat.add_eq,
simp only [beq_iff_eq, Nat.add_eq_left, Nat.add_one_ne_zero, reduceIte, Nat.add_eq,
getElem_extract]
congr
funext b
@@ -3777,14 +3770,8 @@ variable [LawfulBEq α]
theorem getElem?_replace {xs : Array α} {i : Nat} :
(xs.replace a b)[i]? = if xs[i]? == some a then if a xs.take i then some a else some b else xs[i]? := by
rcases xs with xs
simp only [List.replace_toArray, List.getElem?_toArray, List.getElem?_replace, beq_iff_eq,
take_eq_extract, List.extract_toArray, List.extract_eq_drop_take, Nat.sub_zero, List.drop_zero,
mem_toArray]
split <;> rename_i h
· rw (occs := [2]) [if_pos]
simpa using h
· rw [if_neg]
simpa using h
simp only [List.replace_toArray, List.getElem?_toArray, List.getElem?_replace, take_eq_extract,
List.extract_toArray, List.extract_eq_drop_take, Nat.sub_zero, List.drop_zero, mem_toArray]
theorem getElem?_replace_of_ne {xs : Array α} {i : Nat} (h : xs[i]? some a) :
(xs.replace a b)[i]? = xs[i]? := by
@@ -4276,11 +4263,9 @@ theorem getElem?_push_eq {xs : Array α} {x : α} : (xs.push x)[xs.size]? = some
/-! ### contains -/
theorem contains_def [DecidableEq α] {a : α} {xs : Array α} : xs.contains a a xs := by
rw [mem_def, contains, any_toList, List.any_eq_true]; simp [and_comm]
instance [DecidableEq α] (a : α) (xs : Array α) : Decidable (a xs) :=
decidable_of_iff _ contains_def
@[deprecated contains_iff (since := "2025-04-07")]
abbrev contains_def [DecidableEq α] {a : α} {xs : Array α} : xs.contains a a xs :=
contains_iff
/-! ### isPrefixOf -/

View File

@@ -150,13 +150,13 @@ instance [DecidableEq α] [LT α] [DecidableLT α]
Std.Total (· · : Array α Array α Prop) where
total := Array.le_total
@[simp] theorem lex_eq_true_iff_lt [DecidableEq α] [LT α] [DecidableLT α]
@[simp] theorem lex_eq_true_iff_lt [BEq α] [LawfulBEq α] [LT α] [DecidableLT α]
{xs ys : Array α} : lex xs ys = true xs < ys := by
cases xs
cases ys
simp
@[simp] theorem lex_eq_false_iff_ge [DecidableEq α] [LT α] [DecidableLT α]
@[simp] theorem lex_eq_false_iff_ge [BEq α] [LawfulBEq α] [LT α] [DecidableLT α]
{xs ys : Array α} : lex xs ys = false ys xs := by
cases xs
cases ys

View File

@@ -34,7 +34,7 @@ theorem perm_iff_toList_perm {as bs : Array α} : as ~ bs ↔ as.toList ~ bs.toL
cases xs
simp
protected theorem Perm.rfl {xs : List α} : xs ~ xs := .refl _
protected theorem Perm.rfl {xs : Array α} : xs ~ xs := .refl _
theorem Perm.of_eq {xs ys : Array α} (h : xs = ys) : xs ~ ys := h .rfl
@@ -53,6 +53,17 @@ instance : Trans (Perm (α := α)) (Perm (α := α)) (Perm (α := α)) where
theorem perm_comm {xs ys : Array α} : xs ~ ys ys ~ xs := Perm.symm, Perm.symm
theorem Perm.length_eq {xs ys : Array α} (p : xs ~ ys) : xs.size = ys.size := by
cases xs; cases ys
simp only [perm_toArray] at p
simpa using p.length_eq
theorem Perm.mem_iff {a : α} {xs ys : Array α} (p : xs ~ ys) : a xs a ys := by
rcases xs with xs
rcases ys with ys
simp at p
simpa using p.mem_iff
theorem Perm.push (x y : α) {xs ys : Array α} (p : xs ~ ys) :
(xs.push x).push y ~ (ys.push y).push x := by
cases xs; cases ys
@@ -70,7 +81,7 @@ namespace Perm
set_option linter.indexVariables false in
theorem extract {xs ys : Array α} (h : xs ~ ys) {lo hi : Nat}
(wlo : i, i < lo xs[i]? = ys[i]?) (whi : i, hi i xs[i]? = ys[i]?) :
(xs.extract lo (hi + 1)) ~ (ys.extract lo (hi + 1)) := by
(xs.extract lo hi) ~ (ys.extract lo hi) := by
rcases xs with xs
rcases ys with ys
simp_all only [perm_toArray, List.getElem?_toArray, List.extract_toArray,

View File

@@ -19,21 +19,9 @@ class PartialEquivBEq (α) [BEq α] : Prop where
/-- Transitivity for `BEq`. If `a == b` and `b == c` then `a == c`. -/
trans : (a : α) == b b == c a == c
/-- `ReflBEq α` says that the `BEq` implementation is reflexive. -/
class ReflBEq (α) [BEq α] : Prop where
/-- Reflexivity for `BEq`. -/
refl : (a : α) == a
/-- `EquivBEq` says that the `BEq` implementation is an equivalence relation. -/
class EquivBEq (α) [BEq α] : Prop extends PartialEquivBEq α, ReflBEq α
@[simp]
theorem BEq.refl [BEq α] [ReflBEq α] {a : α} : a == a :=
ReflBEq.refl
theorem beq_of_eq [BEq α] [ReflBEq α] {a b : α} : a = b a == b
| rfl => BEq.refl
theorem BEq.symm [BEq α] [PartialEquivBEq α] {a b : α} : a == b b == a :=
PartialEquivBEq.symm
@@ -66,6 +54,5 @@ theorem BEq.neq_of_beq_of_neq [BEq α] [PartialEquivBEq α] {a b c : α} :
fun h₁ h₂ => Bool.eq_false_iff.2 fun h₃ => Bool.eq_false_iff.1 h₂ (BEq.trans (BEq.symm h₁) h₃)
instance (priority := low) [BEq α] [LawfulBEq α] : EquivBEq α where
refl := LawfulBEq.rfl
symm h := beq_iff_eq.2 <| Eq.symm <| beq_iff_eq.1 h
trans hab hbc := beq_iff_eq.2 <| (beq_iff_eq.1 hab).trans <| beq_iff_eq.1 hbc

View File

@@ -566,7 +566,7 @@ theorem ult_eq_msb_of_msb_neq {x y : BitVec w} (h : x.msb ≠ y.msb) :
theorem slt_eq_not_ult_of_msb_neq {x y : BitVec w} (h : x.msb y.msb) :
x.slt y = !x.ult y := by
simp only [BitVec.slt, toInt_eq_msb_cond, Bool.eq_not_of_ne h, ult_eq_msb_of_msb_neq h]
cases y.msb <;> (simp; omega)
cases y.msb <;> (simp [-Int.natCast_pow]; omega)
theorem slt_eq_ult {x y : BitVec w} :
x.slt y = (x.msb != y.msb).xor (x.ult y) := by
@@ -1332,7 +1332,7 @@ theorem saddOverflow_eq {w : Nat} (x y : BitVec w) :
simp only [ decide_or, msb_eq_toInt, decide_beq_decide, toInt_add, decide_not, decide_and,
decide_eq_decide]
rw_mod_cast [Int.bmod_neg_iff (by omega) (by omega)]
simp
simp only [Nat.add_one_sub_one, ge_iff_le]
omega
theorem usubOverflow_eq {w : Nat} (x y : BitVec w) :
@@ -1456,7 +1456,6 @@ theorem udiv_intMin_of_msb_false {x : BitVec w} (h : x.msb = false) :
have wpos : 0 < w := by omega
have := Nat.two_pow_pos (w-1)
simp [toNat_eq, wpos]
rw [Nat.div_eq_zero_iff_lt (by omega)]
exact toNat_lt_of_msb_false h
theorem sdiv_intMin {x : BitVec w} :
@@ -1571,7 +1570,8 @@ theorem intMin_udiv_eq_intMin_iff (x : BitVec w) :
· intro h
rw [ toInt_inj, toInt_eq_msb_cond] at h
have : (intMin w / x).msb = false := by simp [msb_udiv, msb_intMin, wpos, hx]
simp [this, wpos, toInt_intMin] at h
simp only [this, false_eq_true, reduceIte, toNat_udiv, toNat_intMin, wpos,
Nat.two_pow_pred_mod_two_pow, Int.natCast_ediv, toInt_intMin] at h
omega
· intro h
subst h
@@ -1612,11 +1612,11 @@ theorem toInt_sdiv_of_ne_or_ne (a b : BitVec w) (h : a ≠ intMin w b ≠ -1
Nat.two_pow_pred_mod_two_pow, Int.neg_tdiv, Int.neg_neg]
rw [Int.tdiv_self (by omega)]
· by_cases ha_nonneg : 0 a.toInt
· simp [Int.tdiv_eq_zero_of_lt ha_nonneg (by norm_cast at *), ha_intMin]
· simp [Int.tdiv_eq_zero_of_lt ha_nonneg (by norm_cast at *), ha_intMin, -Int.natCast_pow]
· simp only [ne_eq, toInt_inj, toInt_intMin, wpos, Nat.two_pow_pred_mod_two_pow] at h
rw [ Int.neg_tdiv, Int.tdiv_eq_zero_of_lt (by omega)]
· simp [ha_intMin]
· simp [wpos, toInt_ne, toInt_intMin] at ha_intMin
· simp [wpos, toInt_ne, toInt_intMin, -Int.natCast_pow] at ha_intMin
omega
· by_cases ha : a.msb <;> by_cases hb : b.msb

View File

@@ -68,6 +68,9 @@ theorem getElem?_eq_some_iff {l : BitVec w} : l[n]? = some a ↔ ∃ h : n < w,
· simp_all
· simp; omega
theorem getElem_of_getElem? {l : BitVec w} : l[n]? = some a h : n < w, l[n] = a :=
getElem?_eq_some_iff.mp
set_option linter.missingDocs false in
@[deprecated getElem?_eq_some_iff (since := "2025-02-17")]
abbrev getElem?_eq_some := @getElem?_eq_some_iff
@@ -449,7 +452,7 @@ theorem getLsbD_ofNat (n : Nat) (x : Nat) (i : Nat) :
@[simp] theorem sub_add_bmod_cancel {x y : BitVec w} :
((((2 ^ w : Nat) - y.toNat) : Int) + x.toNat).bmod (2 ^ w) =
((x.toNat : Int) - y.toNat).bmod (2 ^ w) := by
rw [Int.sub_eq_add_neg, Int.add_assoc, Int.add_comm, Int.bmod_add_cancel, Int.add_comm,
rw [Int.sub_eq_add_neg, Int.add_assoc, Int.add_comm, Int.add_bmod_right, Int.add_comm,
Int.sub_eq_add_neg]
private theorem lt_two_pow_of_le {x m n : Nat} (lt : x < 2 ^ m) (le : m n) : x < 2 ^ n :=
@@ -655,20 +658,19 @@ theorem toInt_ne {x y : BitVec n} : x.toInt ≠ y.toInt ↔ x ≠ y := by
unfold BitVec.ofInt
simp
theorem toInt_ofNat {n : Nat} (x : Nat) :
(BitVec.ofNat n x).toInt = (x : Int).bmod (2^n) := by
simp [toInt_eq_toNat_bmod]
theorem toInt_ofNat {n : Nat} (x : Nat) : (BitVec.ofNat n x).toInt = (x : Int).bmod (2^n) := by
simp [toInt_eq_toNat_bmod, -Int.natCast_pow]
@[simp] theorem toInt_ofInt {n : Nat} (i : Int) :
(BitVec.ofInt n i).toInt = i.bmod (2^n) := by
have _ := Nat.two_pow_pos n
have p : 0 i % (2^n : Nat) := by omega
simp [toInt_eq_toNat_bmod, Int.toNat_of_nonneg p]
simp [toInt_eq_toNat_bmod, Int.toNat_of_nonneg p, -Int.natCast_pow]
theorem toInt_ofInt_eq_self {w : Nat} (hw : 0 < w) {n : Int}
(h : -2 ^ (w - 1) n) (h' : n < 2 ^ (w - 1)) : (BitVec.ofInt w n).toInt = n := by
have hw : w = (w - 1) + 1 := by omega
rw [toInt_ofInt, Int.bmod_eq_self_of_le] <;> (rw [hw]; simp [Int.natCast_pow]; omega)
rw [toInt_ofInt, Int.bmod_eq_of_le] <;> (rw [hw]; simp [Int.natCast_pow]; omega)
@[simp] theorem ofInt_natCast (w n : Nat) :
BitVec.ofInt w (n : Int) = BitVec.ofNat w n := rfl
@@ -676,16 +678,13 @@ theorem toInt_ofInt_eq_self {w : Nat} (hw : 0 < w) {n : Int}
@[simp] theorem ofInt_ofNat (w n : Nat) :
BitVec.ofInt w (no_index (OfNat.ofNat n)) = BitVec.ofNat w (OfNat.ofNat n) := rfl
@[simp] theorem ofInt_toInt {x : BitVec w} : BitVec.ofInt w x.toInt = x := by
by_cases h : 2 * x.toNat < 2^w <;> ext <;> simp [getLsbD_eq_getElem, getLsbD, h, BitVec.toInt]
theorem toInt_neg_iff {w : Nat} {x : BitVec w} :
BitVec.toInt x < 0 2 ^ w 2 * x.toNat := by
simp [toInt_eq_toNat_cond]; omega
simp only [toInt_eq_toNat_cond]; omega
theorem toInt_pos_iff {w : Nat} {x : BitVec w} :
0 BitVec.toInt x 2 * x.toNat < 2 ^ w := by
simp [toInt_eq_toNat_cond]; omega
simp only [toInt_eq_toNat_cond]; omega
theorem eq_zero_or_eq_one (a : BitVec 1) : a = 0#1 a = 1#1 := by
obtain a, ha := a
@@ -777,6 +776,12 @@ theorem le_toInt {w : Nat} (x : BitVec w) : -2 ^ (w - 1) ≤ x.toInt := by
rw [(show w = w - 1 + 1 by omega), Int.pow_succ] at this
omega
@[simp] theorem ofInt_toInt {x : BitVec w} : BitVec.ofInt w x.toInt = x := by
apply eq_of_toInt_eq
rw [toInt_ofInt, Int.bmod_eq_of_le_mul_two]
· simpa [Int.mul_comm _ 2] using le_two_mul_toInt
· simpa [Int.mul_comm _ 2] using two_mul_toInt_lt
/-! ### sle/slt -/
/--
@@ -795,7 +800,7 @@ theorem slt_zero_iff_msb_cond {x : BitVec w} : x.slt 0#w ↔ x.msb = true := by
omega /- Can't have `x.toInt` which is equal to `x.toNat` be strictly less than zero -/
· intros h
simp only [h, reduceIte] at this
simp [BitVec.slt, this]
simp only [BitVec.slt, this, toInt_zero, decide_eq_true_eq]
omega
theorem slt_zero_eq_msb {w : Nat} {x : BitVec w} : x.slt 0#w = x.msb := by
@@ -866,7 +871,7 @@ theorem zeroExtend_eq_setWidth {v : Nat} {x : BitVec w} :
@[simp] theorem toInt_setWidth (x : BitVec w) :
(x.setWidth v).toInt = Int.bmod x.toNat (2^v) := by
simp [toInt_eq_toNat_bmod, toNat_setWidth, Int.emod_bmod]
simp [toInt_eq_toNat_bmod, toNat_setWidth, Int.emod_bmod, -Int.natCast_pow]
@[simp] theorem toFin_setWidth {x : BitVec w} :
(x.setWidth v).toFin = Fin.ofNat' (2^v) x.toNat := by
@@ -1059,7 +1064,7 @@ and the second `setWidth` is a non-trivial extension.
theorem toInt_setWidth' {m n : Nat} (p : m n) {x : BitVec m} :
(setWidth' p x).toInt = if m = n then x.toInt else x.toNat := by
split
case isTrue h => simp [h, toInt_eq_toNat_bmod]
case isTrue h => simp [h, toInt_eq_toNat_bmod, -Int.natCast_pow]
case isFalse h => rw [toInt_setWidth'_of_lt (by omega)]
@[simp] theorem toFin_setWidth' {m n : Nat} (p : m n) (x : BitVec m) :
@@ -1267,7 +1272,7 @@ theorem extractLsb'_eq_zero {x : BitVec w} {start : Nat} :
· subst h
simp
· have : 1 < 2 ^ w := by simp [h]
simp [BitVec.toInt]
simp [BitVec.toInt, -Int.natCast_pow]
omega
@[simp] theorem toFin_allOnes : (allOnes w).toFin = Fin.ofNat' (2^w) (2^w - 1) := by
@@ -1627,7 +1632,8 @@ theorem not_def {x : BitVec v} : ~~~x = allOnes v ^^^ x := rfl
@[simp] theorem toInt_not {x : BitVec w} :
(~~~x).toInt = Int.bmod (2^w - 1 - x.toNat) (2^w) := by
rw_mod_cast [BitVec.toInt, BitVec.toNat_not, Int.bmod_def]
simp [show ((2^w : Nat) : Int) - 1 - x.toNat = ((2^w - 1 - x.toNat) : Nat) by omega]
simp [show ((2^w : Nat) : Int) - 1 - x.toNat = ((2^w - 1 - x.toNat) : Nat) by omega,
-Int.natCast_pow]
rw_mod_cast [Nat.mod_eq_of_lt (by omega)]
omega
@@ -1803,7 +1809,7 @@ theorem not_xor_right {x y : BitVec w} : ~~~ (x ^^^ y) = x ^^^ ~~~ y := by
@[simp] theorem toInt_shiftLeft {x : BitVec w} :
(x <<< n).toInt = (x.toNat <<< n : Int).bmod (2^w) := by
rw [toInt_eq_toNat_bmod, toNat_shiftLeft, Nat.shiftLeft_eq]
simp
simp [-Int.natCast_pow]
@[simp] theorem toFin_shiftLeft {n : Nat} (x : BitVec w) :
(x <<< n).toFin = Fin.ofNat' (2^w) (x.toNat <<< n) := rfl
@@ -2032,7 +2038,6 @@ theorem toInt_ushiftRight_of_lt {x : BitVec w} {n : Nat} (hn : 0 < n) :
have : 2^w 2^n := Nat.pow_le_pow_of_le (by decide) (by omega)
omega
simp [this] at h
omega
/--
Unsigned shift right by at least one bit makes the interpretations of the bitvector as an `Int` or `Nat` agree,
@@ -2137,8 +2142,8 @@ theorem sshiftRight_eq_of_msb_true {x : BitVec w} {s : Nat} (h : x.msb = true) :
simp only [hxbound, reduceIte, toNat_ofInt, toNat_not, toNat_ushiftRight]
rw [ Int.subNatNat_eq_coe, Int.subNatNat_of_lt (by omega),
Nat.pred_eq_sub_one, Int.negSucc_shiftRight,
Int.emod_negSucc, Int.natAbs_ofNat, Nat.succ_eq_add_one,
Int.subNatNat_of_le (by omega), Int.toNat_ofNat, Nat.mod_eq_of_lt,
Int.emod_negSucc, Int.natAbs_natCast, Nat.succ_eq_add_one,
Int.subNatNat_of_le (by omega), Int.toNat_natCast, Nat.mod_eq_of_lt,
Nat.sub_right_comm]
omega
· rw [Nat.shiftRight_eq_div_pow]
@@ -2334,7 +2339,7 @@ theorem toInt_sshiftRight {x : BitVec w} {n : Nat} :
have := @toInt_shiftRight_lt w x n
have := @le_toInt_shiftRight w x n
norm_cast at *
exact Int.bmod_eq_self_of_le (by omega) (by omega)
exact Int.bmod_eq_of_le (by omega) (by omega)
/-! ### sshiftRight reductions from BitVec to Nat -/
@@ -2427,9 +2432,9 @@ theorem signExtend_eq_not_setWidth_not_of_msb_true {x : BitVec w} {v : Nat} (hms
toNat_setWidth, hmsb, reduceIte]
norm_cast
rw [Int.ofNat_sub_ofNat_of_lt, Int.negSucc_emod]
simp only [Int.natAbs_ofNat, Nat.succ_eq_add_one]
simp only [Int.natAbs_natCast, Nat.succ_eq_add_one]
rw [Int.subNatNat_of_le]
· rw [Int.toNat_ofNat, Nat.add_comm, Nat.sub_add_eq]
· rw [Int.toNat_natCast, Nat.add_comm, Nat.sub_add_eq]
· apply Nat.le_trans
· apply Nat.succ_le_of_lt
apply Nat.mod_lt
@@ -2539,7 +2544,7 @@ where
simp only [this, toNat_setWidth, Int.natCast_add, Int.ofNat_emod, Int.natCast_mul]
by_cases h : x.msb
<;> norm_cast
<;> simp [h, Nat.mod_eq_of_lt (Nat.lt_of_lt_of_le x.isLt H)]
<;> simp [h, Nat.mod_eq_of_lt (Nat.lt_of_lt_of_le x.isLt H), -Int.natCast_pow]
omega
/--
@@ -3314,7 +3319,7 @@ theorem setWidth_add (x y : BitVec w) (h : i ≤ w) :
@[simp, bitvec_to_nat] theorem toInt_add (x y : BitVec w) :
(x + y).toInt = (x.toInt + y.toInt).bmod (2^w) := by
simp [toInt_eq_toNat_bmod]
simp [toInt_eq_toNat_bmod, -Int.natCast_pow]
theorem ofInt_add {n} (x y : Int) : BitVec.ofInt n (x + y) =
BitVec.ofInt n x + BitVec.ofInt n y := by
@@ -3344,7 +3349,7 @@ theorem sub_def {n} (x y : BitVec n) : x - y = .ofNat n ((2^n - y.toNat) + x.toN
@[simp, bitvec_to_nat] theorem toInt_sub {x y : BitVec w} :
(x - y).toInt = (x.toInt - y.toInt).bmod (2 ^ w) := by
simp [toInt_eq_toNat_bmod, @Int.ofNat_sub y.toNat (2 ^ w) (by omega)]
simp [toInt_eq_toNat_bmod, @Int.ofNat_sub y.toNat (2 ^ w) (by omega), -Int.natCast_pow]
theorem toInt_sub_toInt_lt_twoPow_iff {x y : BitVec w} :
(x.toInt - y.toInt < - 2 ^ (w - 1))
@@ -3356,7 +3361,7 @@ theorem toInt_sub_toInt_lt_twoPow_iff {x y : BitVec w} :
simp only [Nat.add_one_sub_one]
constructor
· intros h
rw_mod_cast [ Int.bmod_add_cancel, Int.bmod_eq_self_of_le]
rw_mod_cast [ Int.add_bmod_right, Int.bmod_eq_of_le]
<;> omega
· have := Int.bmod_neg_iff (x := x.toInt - y.toInt) (m := 2 ^ (w + 1))
push_cast at this
@@ -3373,7 +3378,7 @@ theorem twoPow_le_toInt_sub_toInt_iff {x y : BitVec w} :
constructor
· intros h
simp only [show 0 x.toInt by omega, show y.toInt < 0 by omega, _root_.true_and]
rw_mod_cast [ Int.bmod_sub_cancel, Int.bmod_eq_self_of_le (by omega) (by omega)]
rw_mod_cast [ Int.sub_bmod_right, Int.bmod_eq_of_le (by omega) (by omega)]
omega
· have := Int.bmod_neg_iff (x := x.toInt - y.toInt) (m := 2 ^ (w + 1))
push_cast at this
@@ -3707,7 +3712,7 @@ theorem two_mul {x : BitVec w} : 2#w * x = x + x := by rw [BitVec.mul_comm, mul_
@[simp, bitvec_to_nat] theorem toInt_mul (x y : BitVec w) :
(x * y).toInt = (x.toInt * y.toInt).bmod (2^w) := by
simp [toInt_eq_toNat_bmod]
simp [toInt_eq_toNat_bmod, -Int.natCast_pow]
theorem ofInt_mul {n} (x y : Int) : BitVec.ofInt n (x * y) =
BitVec.ofInt n x * BitVec.ofInt n y := by
@@ -3981,7 +3986,6 @@ then `x / y` is nonnegative, thus `toInt` and `toNat` coincide.
theorem toInt_udiv_of_msb {x : BitVec w} (h : x.msb = false) (y : BitVec w) :
(x / y).toInt = x.toNat / y.toNat := by
simp [toInt_eq_msb_cond, msb_udiv_eq_false_of h]
norm_cast
/-! ### umod -/
@@ -4914,7 +4918,6 @@ theorem intMin_eq_zero_iff {w : Nat} : intMin w = 0#w ↔ w = 0 := by
· constructor
· have := Nat.two_pow_pos (w - 1)
simp [toNat_eq, show 0 < w by omega]
omega
· simp [h]
/--
@@ -5152,7 +5155,7 @@ theorem two_pow_le_toInt_mul_toInt_iff {x y : BitVec w} :
toInt_intMax, Nat.add_one_sub_one]
push_cast
rw [ Nat.two_pow_pred_add_two_pow_pred (by omega),
Int.bmod_eq_self_of_le_mul_two (by rw [ Nat.mul_two]; push_cast; omega)
Int.bmod_eq_of_le_mul_two (by rw [ Nat.mul_two]; push_cast; omega)
(by rw [ Nat.mul_two]; push_cast; omega)]
omega
@@ -5169,14 +5172,14 @@ theorem toInt_mul_toInt_lt_neg_two_pow_iff {x y : BitVec w} :
simp only [toInt_twoPow, show ¬w + 1 w by omega, reduceIte]
push_cast
rw [ Nat.two_pow_pred_add_two_pow_pred (by omega),
Int.bmod_eq_self_of_le_mul_two (by rw [ Nat.mul_two]; push_cast; omega)
Int.bmod_eq_of_le_mul_two (by rw [ Nat.mul_two]; push_cast; omega)
(by rw [ Nat.mul_two]; push_cast; omega)]
/-! ### neg -/
theorem msb_eq_toInt {x : BitVec w}:
x.msb = decide (x.toInt < 0) := by
by_cases h : x.msb <;> simp [h, toInt_eq_msb_cond] <;> omega
by_cases h : x.msb <;> simp [h, toInt_eq_msb_cond, -Int.natCast_pow] <;> omega
theorem msb_eq_toNat {x : BitVec w}:
x.msb = decide (x.toNat 2 ^ (w - 1)) := by

View File

@@ -690,9 +690,5 @@ def boolRelToRel : Coe (αα → Bool) (αα → Prop) where
/-! ### subtypes -/
@[simp] theorem Subtype.beq_iff {α : Type u} [DecidableEq α] {p : α Prop} {x y : {a : α // p a}} :
(x == y) = (x.1 == y.1) := by
cases x
cases y
rw [Bool.eq_iff_iff]
simp [beq_iff_eq]
@[simp] theorem Subtype.beq_iff {α : Type u} [BEq α] {p : α Prop} {x y : {a : α // p a}} :
(x == y) = (x.1 == y.1) := rfl

View File

@@ -6,6 +6,7 @@ Authors: Leonardo de Moura
prelude
import Init.Data.Int.Basic
import Init.Data.Int.Bitwise
import Init.Data.Int.Compare
import Init.Data.Int.DivMod
import Init.Data.Int.Gcd
import Init.Data.Int.Lemmas

View File

@@ -28,7 +28,7 @@ theorem shiftRight_eq_div_pow (m : Int) (n : Nat) :
m >>> n = m / ((2 ^ n) : Nat) := by
simp only [shiftRight_eq, Int.shiftRight, Nat.shiftRight_eq_div_pow]
split
· simp; norm_cast
· simp
· rw [negSucc_ediv _ (by norm_cast; exact Nat.pow_pos (Nat.zero_lt_two))]
rfl

View File

@@ -18,9 +18,6 @@ namespace Int
protected theorem lt_or_eq_of_le {n m : Int} (h : n m) : n < m n = m := by
omega
protected theorem le_iff_lt_or_eq {n m : Int} : n m n < m n = m :=
Int.lt_or_eq_of_le, fun | .inl h => Int.le_of_lt h | .inr rfl => Int.le_refl _
theorem compare_eq_ite_lt (a b : Int) :
compare a b = if a < b then .lt else if b < a then .gt else .eq := by
simp only [compare, compareOfLessAndEq]

View File

@@ -45,9 +45,9 @@ protected theorem dvd_trans : ∀ {a b c : Int}, a b → b c → a c
Iff.intro (fun k, e => by rw [e, Int.zero_mul])
(fun h => h.symm Int.dvd_refl _)
protected theorem dvd_mul_right (a b : Int) : a a * b := _, rfl
@[simp] protected theorem dvd_mul_right (a b : Int) : a a * b := _, rfl
protected theorem dvd_mul_left (a b : Int) : b a * b := _, Int.mul_comm ..
@[simp] protected theorem dvd_mul_left (a b : Int) : b a * b := _, Int.mul_comm ..
@[simp] protected theorem neg_dvd {a b : Int} : -a b a b := by
constructor <;> exact fun k, e =>
@@ -59,13 +59,13 @@ protected theorem dvd_mul_left (a b : Int) : b a * b := ⟨_, Int.mul_comm .
@[simp] theorem natAbs_dvd_natAbs {a b : Int} : natAbs a natAbs b a b := by
refine fun k, hk => ?_, fun k, hk => natAbs k, hk.symm natAbs_mul a k
rw [ natAbs_ofNat k, natAbs_mul, natAbs_eq_natAbs_iff] at hk
rw [ natAbs_natCast k, natAbs_mul, natAbs_eq_natAbs_iff] at hk
cases hk <;> subst b
· apply Int.dvd_mul_right
· rw [ Int.mul_neg]; apply Int.dvd_mul_right
theorem ofNat_dvd_left {n : Nat} {z : Int} : (n : Int) z n z.natAbs := by
rw [ natAbs_dvd_natAbs, natAbs_ofNat]
rw [ natAbs_dvd_natAbs, natAbs_natCast]
/-! ### ediv zero -/
@@ -156,7 +156,7 @@ theorem add_mul_ediv_right (a b : Int) {c : Int} (H : c ≠ 0) : (a + b * c) / c
show ediv ((n * succ k) + -((m : Int) + 1)) (succ k) = n + -((m / succ k) + 1 : Int)
rw [H h, H ((Nat.le_div_iff_mul_le k.succ_pos).2 h)]
apply congrArg negSucc
rw [Nat.mul_comm, Nat.sub_mul_div]; rwa [Nat.mul_comm]
rw [Nat.mul_comm, Nat.sub_mul_div_of_le]; rwa [Nat.mul_comm]
theorem add_mul_ediv_left (a : Int) {b : Int}
(c : Int) (H : b 0) : (a + b * c) / b = a / b + c :=
@@ -198,7 +198,7 @@ theorem emod_lt_of_pos (a : Int) {b : Int} (H : 0 < b) : a % b < b :=
| ofNat _, _, _, rfl => ofNat_lt.2 (Nat.mod_lt _ (Nat.succ_pos _))
| -[_+1], _, _, rfl => Int.sub_lt_self _ (ofNat_lt.2 <| Nat.succ_pos _)
@[simp] theorem add_mul_emod_self {a b c : Int} : (a + b * c) % c = a % c :=
@[simp] theorem add_mul_emod_self_right (a b c : Int) : (a + b * c) % c = a % c :=
if cz : c = 0 then by
rw [cz, Int.mul_zero, Int.add_zero]
else by
@@ -206,7 +206,17 @@ theorem emod_lt_of_pos (a : Int) {b : Int} (H : 0 < b) : a % b < b :=
Int.mul_add, Int.mul_comm, Int.sub_sub, Int.add_sub_cancel]
@[simp] theorem add_mul_emod_self_left (a b c : Int) : (a + b * c) % b = a % b := by
rw [Int.mul_comm, Int.add_mul_emod_self]
rw [Int.mul_comm, add_mul_emod_self_right]
@[simp] theorem mul_add_emod_self_right (a b c : Int) : (a * b + c) % b = c % b := by
rw [Int.add_comm, add_mul_emod_self_right]
@[simp] theorem mul_add_emod_self_left (a b c : Int) : (a * b + c) % a = c % a := by
rw [Int.add_comm, add_mul_emod_self_left]
@[deprecated add_mul_emod_self_right (since := "2025-04-11")]
theorem add_mul_emod_self {a b c : Int} : (a + b * c) % c = a % c :=
add_mul_emod_self_right ..
@[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
@@ -229,7 +239,7 @@ theorem emod_add_cancel_right {m n k : Int} (i) : (m + i) % n = (k + i) % n ↔
add_emod_eq_add_emod_right _
@[simp] theorem mul_emod_left (a b : Int) : (a * b) % b = 0 := by
rw [ Int.zero_add (a * b), Int.add_mul_emod_self, Int.zero_emod]
rw [ Int.zero_add (a * b), add_mul_emod_self_right, Int.zero_emod]
@[simp] theorem mul_emod_right (a b : Int) : (a * b) % a = 0 := by
rw [Int.mul_comm, mul_emod_left]
@@ -238,7 +248,7 @@ 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,
Int.mul_assoc, Int.mul_assoc, Int.mul_add n _ _, add_mul_emod_self_left,
Int.mul_assoc, add_mul_emod_self]
Int.mul_assoc, add_mul_emod_self_right]
@[simp] theorem emod_self {a : Int} : a % a = 0 := by
have := mul_emod_left 1 a; rwa [Int.one_mul] at this
@@ -324,10 +334,10 @@ theorem lt_mul_ediv_self_add {x k : Int} (h : 0 < k) : x < k * (x / k) + k :=
split <;> simp [Int.sub_emod]
theorem bmod_def (x : Int) (m : Nat) : bmod x m =
if (x % m) < (m + 1) / 2 then
x % m
else
(x % m) - m :=
if (x % m) < (m + 1) / 2 then
x % m
else
(x % m) - m :=
rfl
end Int

File diff suppressed because it is too large Load Diff

View File

@@ -135,7 +135,7 @@ theorem natAbs_div_gcd_pos_of_ne_zero_right (a : Int) (h : b ≠ 0) : 0 < b.natA
Nat.div_gcd_pos_of_pos_right _ (natAbs_pos.2 h)
theorem ediv_gcd_ne_zero_of_ne_zero_left (b : Int) (h : a 0) : a / gcd a b 0 := by
rw [ natAbs_pos, natAbs_ediv_of_dvd (gcd_dvd_left _ _), natAbs_ofNat]
rw [ natAbs_pos, natAbs_ediv_of_dvd (gcd_dvd_left _ _), natAbs_natCast]
exact natAbs_div_gcd_pos_of_ne_zero_left _ h
theorem ediv_gcd_ne_zero_if_ne_zero_right (a : Int) (h : b 0) : b / gcd a b 0 := by
@@ -393,12 +393,12 @@ theorem pow_gcd_pow_of_gcd_eq_one {n m : Int} {k l : Nat} (h : gcd n m = 1) : gc
theorem gcd_ediv_gcd_ediv_gcd_of_ne_zero_left {n m : Int} (h : n 0) :
gcd (n / gcd n m) (m / gcd n m) = 1 := by
rw [gcd_ediv (gcd_dvd_left _ _) (gcd_dvd_right _ _), natAbs_ofNat,
rw [gcd_ediv (gcd_dvd_left _ _) (gcd_dvd_right _ _), natAbs_natCast,
Nat.div_self (gcd_pos_of_ne_zero_left _ h)]
theorem gcd_ediv_gcd_ediv_gcd_of_ne_zero_right {n m : Int} (h : m 0) :
gcd (n / gcd n m) (m / gcd n m) = 1 := by
rw [gcd_ediv (gcd_dvd_left _ _) (gcd_dvd_right _ _), natAbs_ofNat,
rw [gcd_ediv (gcd_dvd_left _ _) (gcd_dvd_right _ _), natAbs_natCast,
Nat.div_self (gcd_pos_of_ne_zero_right _ h)]
theorem gcd_ediv_gcd_ediv_gcd {i j : Int} (h : 0 < gcd i j) : gcd (i / gcd i j) (j / gcd i j) = 1 :=

View File

@@ -566,7 +566,11 @@ theorem eq_one_of_mul_eq_self_left {a b : Int} (Hpos : a ≠ 0) (H : b * a = a)
theorem eq_one_of_mul_eq_self_right {a b : Int} (Hpos : b 0) (H : b * a = b) : a = 1 :=
Int.eq_of_mul_eq_mul_left Hpos <| by rw [Int.mul_one, H]
/-! NatCast lemmas -/
protected theorem two_mul (n : Int) : 2 * n = n + n := calc
2 * n = (1 + 1) * n := rfl
_ = n + n := by simp only [Int.add_mul, Int.one_mul]
/-! ## NatCast lemmas -/
/-!
The following lemmas are later subsumed by e.g. `Nat.cast_add` and `Nat.cast_mul` in Mathlib
@@ -577,10 +581,8 @@ protected theorem natCast_zero : ((0 : Nat) : Int) = (0 : Int) := rfl
protected theorem natCast_one : ((1 : Nat) : Int) = (1 : Int) := rfl
@[simp] protected theorem natCast_add (a b : Nat) : ((a + b : Nat) : Int) = (a : Int) + (b : Int) := by
-- Note this only works because of local simp attributes in this file,
-- so it still makes sense to tag the lemmas with `@[simp]`.
simp
@[simp, norm_cast] protected theorem natCast_add (a b : Nat) : ((a + b : Nat) : Int) = (a : Int) + (b : Int) := by
rfl
protected theorem natCast_succ (n : Nat) : ((n + 1 : Nat) : Int) = (n : Int) + 1 := rfl

View File

@@ -5,6 +5,7 @@ Authors: Kim Morrison
-/
prelude
import Init.Data.Int.Order
import Init.Data.Int.Pow
import Init.Data.Int.DivMod.Lemmas
import Init.Omega
@@ -39,6 +40,39 @@ namespace Int
theorem neg_lt_self_iff {n : Int} : -n < n 0 < n := by
omega
protected theorem ofNat_add_out (m n : Nat) : m + n = ((m + n) : Int) := rfl
protected theorem ofNat_mul_out (m n : Nat) : m * n = ((m * n) : Int) := rfl
protected theorem ofNat_add_one_out (n : Nat) : n + (1 : Int) = (Nat.succ n) := rfl
@[simp] theorem ofNat_eq_natCast (n : Nat) : Int.ofNat n = n := rfl
@[norm_cast] theorem natCast_inj {m n : Nat} : (m : Int) = (n : Int) m = n := ofNat_inj
@[simp, norm_cast] theorem natAbs_cast (n : Nat) : natAbs n = n := rfl
@[norm_cast]
protected theorem natCast_sub {n m : Nat} : n m ((m - n) : Int) = m - n := ofNat_sub
@[simp high] theorem natCast_eq_zero {n : Nat} : (n : Int) = 0 n = 0 := by omega
theorem natCast_ne_zero {n : Nat} : (n : Int) 0 n 0 := by omega
theorem natCast_ne_zero_iff_pos {n : Nat} : (n : Int) 0 0 < n := by omega
@[simp high] theorem natCast_pos {n : Nat} : (0 : Int) < n 0 < n := by omega
theorem natCast_succ_pos (n : Nat) : 0 < (n.succ : Int) := natCast_pos.2 n.succ_pos
@[simp high] theorem natCast_nonpos_iff {n : Nat} : (n : Int) 0 n = 0 := by omega
theorem natCast_nonneg (n : Nat) : 0 (n : Int) := ofNat_le.2 (Nat.zero_le _)
@[simp] theorem sign_natCast_add_one (n : Nat) : sign (n + 1) = 1 := rfl
@[simp, norm_cast] theorem cast_id {n : Int} : Int.cast n = n := rfl
/-! ### toNat -/
@[simp] theorem toNat_sub' (a : Int) (b : Nat) : (a - b).toNat = a.toNat - b := by
@@ -69,12 +103,24 @@ theorem neg_lt_self_iff {n : Int} : -n < n ↔ 0 < n := by
@[simp] theorem toNat_le {m : Int} {n : Nat} : m.toNat n m n := by omega
@[simp] theorem toNat_lt' {m : Int} {n : Nat} (hn : 0 < n) : m.toNat < n m < n := by omega
@[simp] theorem lt_toNat {m : Nat} {n : Int} : m < toNat n m < n := by omega
theorem lt_of_toNat_lt {a b : Int} (h : toNat a < toNat b) : a < b := by omega
theorem toNat_sub_of_le {a b : Int} (h : b a) : (toNat (a - b) : Int) = a - b := by omega
theorem pos_iff_toNat_pos {n : Int} : 0 < n 0 < n.toNat := by
omega
theorem ofNat_toNat_eq_self {a : Int} : a.toNat = a 0 a := by omega
theorem eq_ofNat_toNat {a : Int} : a = a.toNat 0 a := by omega
theorem natCast_toNat_eq_self {a : Int} : a.toNat = a 0 a := by omega
@[deprecated natCast_toNat_eq_self (since := "2025-04-16")]
theorem ofNat_toNat_eq_self {a : Int} : a.toNat = a 0 a := natCast_toNat_eq_self
theorem eq_natCast_toNat {a : Int} : a = a.toNat 0 a := by omega
@[deprecated eq_natCast_toNat (since := "2025-04-16")]
theorem eq_ofNat_toNat {a : Int} : a = a.toNat 0 a := eq_natCast_toNat
theorem toNat_le_toNat {n m : Int} (h : n m) : n.toNat m.toNat := by omega
theorem toNat_lt_toNat {n m : Int} (hn : 0 < m) : n.toNat < m.toNat n < m := by omega
@@ -116,6 +162,8 @@ protected theorem sub_min_sub_left (a b c : Int) : min (a - b) (a - c) = a - max
protected theorem sub_max_sub_left (a b c : Int) : max (a - b) (a - c) = a - min b c := by omega
/-! ## mul -/
theorem mul_le_mul_of_natAbs_le {x y : Int} {s t : Nat} (hx : x.natAbs s) (hy : y.natAbs t) :
x * y s * t := by
by_cases 0 < s 0 < t
@@ -167,4 +215,9 @@ theorem neg_mul_le_mul {x y : Int} {s t : Nat} (lbx : -s ≤ x) (ubx : x < s) (l
norm_cast
omega
/-! ## pow -/
theorem natAbs_pow_two (a : Int) : (natAbs a : Int) ^ 2 = a ^ 2 := by
simp [Int.pow_succ]
end Int

View File

@@ -191,9 +191,9 @@ theorem cmod_nonpos (a : Int) {b : Int} (h : b ≠ 0) : cmod a b ≤ 0 := by
theorem cmod_eq_zero_iff_emod_eq_zero (a b : Int) : cmod a b = 0 a%b = 0 := by
unfold cmod
have := @Int.emod_eq_emod_iff_emod_sub_eq_zero b b a
simp at this
simp [Int.neg_emod_eq_sub_emod, this, Eq.comm]
have := @Int.emod_eq_emod_iff_emod_sub_eq_zero b b a
simp only [emod_self, sub_emod_left] at this
rw [Int.neg_eq_zero, this, Eq.comm]
private abbrev div_mul_cancel_of_mod_zero :=
@Int.ediv_mul_cancel_of_emod_eq_zero
@@ -435,7 +435,7 @@ def norm_eq_coeff_cert (lhs rhs : Expr) (p : Poly) (k : Int) : Bool :=
theorem norm_eq_coeff (ctx : Context) (lhs rhs : Expr) (p : Poly) (k : Int)
: norm_eq_coeff_cert lhs rhs p k (lhs.denote ctx = rhs.denote ctx) = (p.denote' ctx = 0) := by
simp [norm_eq_coeff_cert]
rw [norm_eq ctx lhs rhs (lhs.sub rhs).norm BEq.refl, Poly.denote'_eq_denote]
rw [norm_eq ctx lhs rhs (lhs.sub rhs).norm BEq.rfl, Poly.denote'_eq_denote]
apply norm_eq_coeff'
private theorem mul_le_zero_iff (a k : Int) (h₁ : k > 0) : k * a 0 a 0 := by
@@ -454,7 +454,7 @@ private theorem norm_le_coeff' (ctx : Context) (p p' : Poly) (k : Int) : p = p'.
theorem norm_le_coeff (ctx : Context) (lhs rhs : Expr) (p : Poly) (k : Int)
: norm_eq_coeff_cert lhs rhs p k (lhs.denote ctx rhs.denote ctx) = (p.denote' ctx 0) := by
simp [norm_eq_coeff_cert]
rw [norm_le ctx lhs rhs (lhs.sub rhs).norm BEq.refl, Poly.denote'_eq_denote]
rw [norm_le ctx lhs rhs (lhs.sub rhs).norm BEq.rfl, Poly.denote'_eq_denote]
apply norm_le_coeff'
private theorem mul_add_cmod_le_iff {a k b : Int} (h : k > 0) : a*k + cmod b k 0 a 0 := by
@@ -499,7 +499,7 @@ def norm_le_coeff_tight_cert (lhs rhs : Expr) (p : Poly) (k : Int) : Bool :=
theorem norm_le_coeff_tight (ctx : Context) (lhs rhs : Expr) (p : Poly) (k : Int)
: norm_le_coeff_tight_cert lhs rhs p k (lhs.denote ctx rhs.denote ctx) = (p.denote' ctx 0) := by
simp [norm_le_coeff_tight_cert]
rw [norm_le ctx lhs rhs (lhs.sub rhs).norm BEq.refl, Poly.denote'_eq_denote]
rw [norm_le ctx lhs rhs (lhs.sub rhs).norm BEq.rfl, Poly.denote'_eq_denote]
apply eq_of_norm_eq_of_divCoeffs
def Poly.isUnsatEq (p : Poly) : Bool :=
@@ -665,7 +665,7 @@ theorem norm_dvd (ctx : Context) (k : Int) (e : Expr) (p : Poly) : e.norm == p
simp; intro h; simp [ h]
theorem dvd_eq_false (ctx : Context) (k : Int) (e : Expr) (h : e.norm.isUnsatDvd k) : (k e.denote ctx) = False := by
rw [norm_dvd ctx k e e.norm BEq.refl]
rw [norm_dvd ctx k e e.norm BEq.rfl]
apply dvd_eq_false' ctx k e.norm h
def dvd_coeff_cert (k₁ : Int) (p₁ : Poly) (k₂ : Int) (p₂ : Poly) (k : Int) : Bool :=

View File

@@ -84,6 +84,11 @@ theorem ofNat_succ_pos (n : Nat) : 0 < (succ n : Int) := ofNat_lt.2 <| Nat.succ_
@[simp] protected theorem le_refl (a : Int) : a a :=
le.intro _ (Int.add_zero a)
protected theorem le_rfl {a : Int} : a a := a.le_refl
protected theorem le_of_eq {a b : Int} (hab : a = b) : a b := by rw [hab]; exact Int.le_rfl
protected theorem ge_of_eq {a b : Int} (hab : a = b) : b a := Int.le_of_eq hab.symm
protected theorem le_trans {a b c : Int} (h₁ : a b) (h₂ : b c) : a c :=
let n, hn := le.dest h₁; let m, hm := le.dest h₂
le.intro (n + m) <| by rw [ hm, hn, Int.add_assoc, ofNat_add]
@@ -94,6 +99,9 @@ protected theorem le_antisymm {a b : Int} (h₁ : a ≤ b) (h₂ : b ≤ a) : a
have := Int.ofNat.inj <| Int.add_left_cancel <| this.trans (Int.add_zero _).symm
rw [ hn, Nat.eq_zero_of_add_eq_zero_left this, ofNat_zero, Int.add_zero a]
protected theorem le_antisymm_iff {a b : Int} : a = b a b b a :=
fun h Int.le_of_eq h, Int.ge_of_eq h, fun h Int.le_antisymm h.1 h.2
@[simp] protected theorem lt_irrefl (a : Int) : ¬a < a := fun H =>
let n, hn := lt.dest H
have : (a+Nat.succ n) = a+0 := by
@@ -119,6 +127,12 @@ protected theorem lt_succ (a : Int) : a < a + 1 := Int.le_refl _
protected theorem zero_lt_one : (0 : Int) < 1 := _
protected theorem one_pos : 0 < (1 : Int) := Int.zero_lt_one
protected theorem one_ne_zero : (1 : Int) 0 := by decide
protected theorem one_nonneg : 0 (1 : Int) := Int.le_of_lt Int.zero_lt_one
protected theorem lt_iff_le_not_le {a b : Int} : a < b a b ¬b a := by
rw [Int.lt_iff_le_and_ne]
constructor <;> refine fun h, h' => h, h'.imp fun h' => ?_
@@ -137,6 +151,10 @@ protected theorem not_le_of_gt {a b : Int} (h : b < a) : ¬a ≤ b :=
@[simp] protected theorem not_lt {a b : Int} : ¬a < b b a :=
by rw [ Int.not_le, Decidable.not_not]
protected theorem lt_asymm {a b : Int} : a < b ¬ b < a := by rw [Int.not_lt]; exact Int.le_of_lt
protected theorem lt_or_le (a b : Int) : a < b b a := by rw [ Int.not_lt]; exact Decidable.em _
protected theorem le_of_not_gt {a b : Int} (h : ¬ a > b) : a b :=
Int.not_lt.mp h
@@ -161,12 +179,23 @@ protected theorem ne_iff_lt_or_gt {a b : Int} : a ≠ b ↔ a < b b < a := b
protected theorem lt_or_gt_of_ne {a b : Int} : a b a < b b < a:= Int.ne_iff_lt_or_gt.mp
protected theorem lt_or_lt_of_ne {a b : Int} : a b a < b b < a := Int.lt_or_gt_of_ne
protected theorem eq_iff_le_and_ge {x y : Int} : x = y x y y x := by
constructor
· simp_all
· intro h₁, h₂
exact Int.le_antisymm h₁ h₂
protected theorem le_iff_eq_or_lt {a b : Int} : a b a = b a < b :=
match Int.lt_trichotomy a b with
| Or.inl h => by simp [h, Int.le_of_lt]
| Or.inr (Or.inl h) => by simp [h]
| Or.inr (Or.inr h) => by simp [h, Int.not_le_of_gt, Int.ne_of_gt, Int.le_of_lt]
protected theorem le_iff_lt_or_eq {a b : Int} : a b a < b a = b := by
rw [Int.le_iff_eq_or_lt, or_comm]
protected theorem lt_of_le_of_lt {a b c : Int} (h₁ : a b) (h₂ : b < c) : a < c :=
Int.not_le.1 fun h => Int.not_le.2 h₂ (Int.le_trans h h₁)
@@ -283,9 +312,8 @@ protected theorem neg_lt_neg {a b : Int} (h : a < b) : -b < -a := by
@[simp] protected theorem zero_lt_neg_iff {a : Int} : 0 < -a a < 0 := by
rw [ Int.neg_zero, Int.neg_lt_neg_iff, Int.neg_zero]
protected theorem neg_neg_of_pos {a : Int} (h : 0 < a) : -a < 0 := by
have : -a < -0 := Int.neg_lt_neg h
rwa [Int.neg_zero] at this
protected theorem neg_neg_of_pos {a : Int} (h : 0 < a) : -a < 0 :=
Int.neg_lt_zero_iff.2 h
protected theorem neg_pos_of_neg {a : Int} (h : a < 0) : 0 < -a := by
have : -0 < -a := Int.neg_lt_neg h
@@ -507,7 +535,17 @@ protected theorem mul_le_mul_of_nonpos_left {a b c : Int}
/- ## natAbs -/
@[simp, norm_cast] theorem natAbs_ofNat (n : Nat) : natAbs n = n := rfl
@[simp, norm_cast] theorem natAbs_natCast (n : Nat) : natAbs n = n := rfl
@[deprecated natAbs_natCast (since := "2025-04-16")]
theorem natAbs_ofNat (n : Nat) : natAbs n = n := natAbs_natCast n
/-
TODO: rename `natAbs_ofNat'` to `natAbs_ofNat` once the current deprecated alias
`natAbs_ofNat := natAbs_natCast` is removed
-/
@[simp] theorem natAbs_ofNat' (n : Nat) : natAbs (ofNat n) = n := rfl
@[simp] theorem natAbs_negSucc (n : Nat) : natAbs -[n+1] = n.succ := rfl
@[simp] theorem natAbs_zero : natAbs (0 : Int) = (0 : Nat) := rfl
@[simp] theorem natAbs_one : natAbs (1 : Int) = (1 : Nat) := rfl
@@ -518,7 +556,8 @@ protected theorem mul_le_mul_of_nonpos_left {a b c : Int}
| -[_+1] => absurd H (succ_ne_zero _),
fun e => e rfl
theorem natAbs_pos : 0 < natAbs a a 0 := by rw [Nat.pos_iff_ne_zero, Ne, natAbs_eq_zero]
@[simp] theorem natAbs_pos : 0 < natAbs a a 0 := by
rw [Nat.pos_iff_ne_zero, Ne, natAbs_eq_zero]
@[simp] theorem natAbs_neg : (a : Int), natAbs (-a) = natAbs a
| 0 => rfl
@@ -585,12 +624,18 @@ theorem toNat_eq_max : ∀ a : Int, (toNat a : Int) = max a 0
theorem toNat_of_nonneg {a : Int} (h : 0 a) : (toNat a : Int) = a := by
rw [toNat_eq_max, Int.max_eq_left h]
@[simp] theorem toNat_ofNat (n : Nat) : toNat n = n := rfl
@[simp] theorem toNat_natCast (n : Nat) : toNat n = n := rfl
@[deprecated toNat_natCast (since := "2025-04-16")]
theorem toNat_ofNat (n : Nat) : toNat n = n := toNat_natCast n
@[simp] theorem toNat_negSucc (n : Nat) : (Int.negSucc n).toNat = 0 := by
simp [toNat]
@[simp] theorem toNat_ofNat_add_one {n : Nat} : ((n : Int) + 1).toNat = n + 1 := rfl
@[simp] theorem toNat_natCast_add_one {n : Nat} : ((n : Int) + 1).toNat = n + 1 := rfl
@[deprecated toNat_natCast_add_one (since := "2025-04-16")]
theorem toNat_ofNat_add_one {n : Nat} : ((n : Int) + 1).toNat = n + 1 := toNat_natCast_add_one
@[simp] theorem ofNat_toNat (a : Int) : (a.toNat : Int) = max a 0 := by
match a with
@@ -768,6 +813,16 @@ protected theorem neg_lt_of_neg_lt {a b : Int} (h : -a < b) : -b < a := by
have h := Int.neg_lt_neg h
rwa [Int.neg_neg] at h
@[simp high]
protected theorem neg_pos : 0 < -a a < 0 := Int.neg_of_neg_pos, Int.neg_pos_of_neg
@[simp high]
protected theorem neg_nonneg : 0 -a a 0 :=
Int.nonpos_of_neg_nonneg, Int.neg_nonneg_of_nonpos
@[simp high]
protected theorem neg_neg_iff_pos : -a < 0 0 < a := Int.pos_of_neg_neg, Int.neg_neg_of_pos
protected theorem sub_nonpos_of_le {a b : Int} (h : a b) : a - b 0 := by
have h := Int.add_le_add_right h (-b)
rwa [Int.add_right_neg] at h
@@ -780,6 +835,14 @@ protected theorem sub_neg_of_lt {a b : Int} (h : a < b) : a - b < 0 := by
have h := Int.add_lt_add_right h (-b)
rwa [Int.add_right_neg] at h
@[simp high]
protected theorem sub_pos {a b : Int} : 0 < a - b b < a :=
Int.lt_of_sub_pos, Int.sub_pos_of_lt
@[simp high]
protected theorem sub_nonneg {a b : Int} : 0 a - b b a :=
Int.le_of_sub_nonneg, Int.sub_nonneg_of_le
protected theorem lt_of_sub_neg {a b : Int} (h : a - b < 0) : a < b := by
have h := Int.add_lt_add_right h b
rwa [Int.sub_add_cancel, Int.zero_add] at h
@@ -1020,6 +1083,33 @@ theorem le_sub_one_of_lt {a b : Int} (H : a < b) : a ≤ b - 1 := Int.le_sub_rig
theorem lt_of_le_sub_one {a b : Int} (H : a b - 1) : a < b := Int.add_le_of_le_sub_right H
theorem le_add_one_iff {m n : Int} : m n + 1 m n m = n + 1 := by
rw [Int.le_iff_lt_or_eq, Int.le_iff_lt_add_one]
theorem sub_one_lt_iff {m n : Int} : m - 1 < n m n :=
le_of_sub_one_lt, sub_one_lt_of_le
theorem le_sub_one_iff {m n : Int} : m n - 1 m < n :=
lt_of_le_sub_one, le_sub_one_of_lt
protected theorem add_le_iff_le_sub {a b c : Int} : a + b c a c - b :=
Int.le_sub_right_of_add_le, Int.add_le_of_le_sub_right
protected theorem le_add_iff_sub_le {a b c : Int} : a b + c a - c b :=
Int.sub_right_le_of_le_add, Int.le_add_of_sub_right_le
protected theorem add_le_zero_iff_le_neg {a b : Int} : a + b 0 a -b := by
rw [Int.add_le_iff_le_sub, Int.zero_sub]
protected theorem add_le_zero_iff_le_neg' {a b : Int} : a + b 0 b -a := by
rw [Int.add_comm, Int.add_le_zero_iff_le_neg]
protected theorem add_nonnneg_iff_neg_le {a b : Int} : 0 a + b -b a := by
rw [Int.le_add_iff_sub_le, Int.zero_sub]
protected theorem add_nonnneg_iff_neg_le' {a b : Int} : 0 a + b -a b := by
rw [Int.add_comm, Int.add_nonnneg_iff_neg_le]
/- ### Order properties and multiplication -/
protected theorem mul_lt_mul {a b c d : Int}
@@ -1095,13 +1185,21 @@ theorem sign_neg_one : sign (-1) = -1 := rfl
theorem natAbs_sign (z : Int) : z.sign.natAbs = if z = 0 then 0 else 1 :=
match z with | 0 | succ _ | -[_+1] => rfl
theorem natAbs_sign_of_nonzero {z : Int} (hz : z 0) : z.sign.natAbs = 1 := by
theorem natAbs_sign_of_ne_zero {z : Int} (hz : z 0) : z.sign.natAbs = 1 := by
rw [Int.natAbs_sign, if_neg hz]
theorem sign_ofNat_of_nonzero {n : Nat} (hn : n 0) : Int.sign n = 1 :=
@[deprecated natAbs_sign_of_ne_zero (since := "2025-04-16")]
theorem natAbs_sign_of_nonzero {z : Int} (hz : z 0) : z.sign.natAbs = 1 :=
natAbs_sign_of_ne_zero hz
theorem sign_natCast_of_ne_zero {n : Nat} (hn : n 0) : Int.sign n = 1 :=
match n, Nat.exists_eq_succ_of_ne_zero hn with
| _, n, rfl => Int.sign_of_add_one n
@[deprecated sign_natCast_of_ne_zero (since := "2025-04-16")]
theorem sign_ofNat_of_nonzero {n : Nat} (hn : n 0) : Int.sign n = 1 :=
sign_natCast_of_ne_zero hn
@[simp] theorem sign_neg (z : Int) : Int.sign (-z) = -Int.sign z := by
match z with | 0 | succ _ | -[_+1] => rfl
@@ -1183,7 +1281,7 @@ theorem neg_of_sign_eq_neg_one : ∀ {a : Int}, sign a = -1 → a < 0
@[deprecated mul_sign_self (since := "2025-02-24")] abbrev mul_sign := @mul_sign_self
@[simp] theorem sign_mul_self : sign i * i = natAbs i := by
@[simp] theorem sign_mul_self (i : Int) : sign i * i = natAbs i := by
rw [Int.mul_comm, mul_sign_self]
theorem sign_trichotomy (a : Int) : sign a = 1 sign a = 0 sign a = -1 := by
@@ -1209,15 +1307,15 @@ theorem natAbs_mul_natAbs_eq {a b : Int} {c : Nat}
rw [ Int.ofNat_mul, natAbs_mul_self]
theorem natAbs_eq_iff {a : Int} {n : Nat} : a.natAbs = n a = n a = -n := by
rw [ Int.natAbs_eq_natAbs_iff, Int.natAbs_ofNat]
rw [ Int.natAbs_eq_natAbs_iff, Int.natAbs_natCast]
theorem natAbs_add_le (a b : Int) : natAbs (a + b) natAbs a + natAbs b := by
suffices a b : Nat, natAbs (subNatNat a b.succ) (a + b).succ by
match a, b with
| (a:Nat), (b:Nat) => rw [ ofNat_add, natAbs_ofNat]; apply Nat.le_refl
| (a:Nat), -[b+1] => rw [natAbs_ofNat, natAbs_negSucc]; apply this
| (a:Nat), (b:Nat) => rw [ ofNat_add, natAbs_natCast]; apply Nat.le_refl
| (a:Nat), -[b+1] => rw [natAbs_natCast, natAbs_negSucc]; apply this
| -[a+1], (b:Nat) =>
rw [natAbs_negSucc, natAbs_ofNat, Nat.succ_add, Nat.add_comm a b]; apply this
rw [natAbs_negSucc, natAbs_natCast, Nat.succ_add, Nat.add_comm a b]; apply this
| -[a+1], -[b+1] => rw [natAbs_negSucc, succ_add]; apply Nat.le_refl
refine fun a b => subNatNat_elim a b.succ
(fun m n i => n = b.succ natAbs i (m + b).succ) ?_
@@ -1233,6 +1331,15 @@ theorem natAbs_add_le (a b : Int) : natAbs (a + b) ≤ natAbs a + natAbs b := by
theorem natAbs_sub_le (a b : Int) : natAbs (a - b) natAbs a + natAbs b := by
rw [ Int.natAbs_neg b]; apply natAbs_add_le
theorem natAbs_add_of_nonneg : {a b : Int}, 0 a 0 b natAbs (a + b) = natAbs a + natAbs b
| ofNat _, ofNat _, _, _ => rfl
theorem natAbs_add_of_nonpos {a b : Int} (ha : a 0) (hb : b 0) :
natAbs (a + b) = natAbs a + natAbs b := by
rw [ Int.neg_neg a, Int.neg_neg b, Int.neg_add, natAbs_neg,
natAbs_add_of_nonneg (Int.neg_nonneg_of_nonpos ha) (Int.neg_nonneg_of_nonpos hb),
natAbs_neg (-a), natAbs_neg (-b)]
@[deprecated negSucc_eq (since := "2025-03-11")]
theorem negSucc_eq' (m : Nat) : -[m+1] = -m - 1 := by simp only [negSucc_eq, Int.neg_add]; rfl

View File

@@ -36,7 +36,7 @@ abbrev pow_le_pow_of_le_right := @Nat.pow_le_pow_right
@[deprecated Nat.pow_pos (since := "2025-02-17")]
abbrev pos_pow_of_pos := @Nat.pow_pos
@[norm_cast]
@[simp, norm_cast]
protected theorem natCast_pow (b n : Nat) : ((b^n : Nat) : Int) = (b : Int) ^ n := by
match n with
| 0 => rfl
@@ -54,7 +54,7 @@ protected theorem two_pow_pred_sub_two_pow' {w : Nat} (h : 0 < w) :
(2 : Int) ^ (w - 1) - (2 : Int) ^ w = - (2 : Int) ^ (w - 1) := by
norm_cast
rw [ Nat.two_pow_pred_add_two_pow_pred h]
simp [h]
simp [h, -Int.natCast_pow]
theorem pow_lt_pow_of_lt {a : Int} {b c : Nat} (ha : 1 < a) (hbc : b < c):
a ^ b < a ^ c := by
@@ -63,7 +63,7 @@ theorem pow_lt_pow_of_lt {a : Int} {b c : Nat} (ha : 1 < a) (hbc : b < c):
simp only [Int.ofNat_lt]
omega
theorem natAbs_pow (n : Int) : (k : Nat) (n ^ k).natAbs = n.natAbs ^ k
@[simp] theorem natAbs_pow (n : Int) : (k : Nat) (n ^ k).natAbs = n.natAbs ^ k
| 0 => rfl
| k + 1 => by rw [Int.pow_succ, natAbs_mul, natAbs_pow, Nat.pow_succ]

View File

@@ -640,12 +640,12 @@ theorem countP_attachWith {p : α → Prop} {q : α → Bool} {l : List α} (H :
simp only [ Function.comp_apply (g := Subtype.val), countP_map, attachWith_map_subtype_val]
@[simp]
theorem count_attach [DecidableEq α] {l : List α} {a : {x // x l}} :
theorem count_attach [BEq α] {l : List α} {a : {x // x l}} :
l.attach.count a = l.count a :=
Eq.trans (countP_congr fun _ _ => by simp [Subtype.ext_iff]) <| countP_attach
@[simp]
theorem count_attachWith [DecidableEq α] {p : α Prop} {l : List α} (H : a l, p a) {a : {x // p x}} :
theorem count_attachWith [BEq α] {p : α Prop} {l : List α} (H : a l, p a) {a : {x // p x}} :
(l.attachWith p H).count a = l.count a :=
Eq.trans (countP_congr fun _ _ => by simp [Subtype.ext_iff]) <| countP_attachWith _

View File

@@ -131,6 +131,12 @@ theorem beq_cons₂ [BEq α] {a b : α} {as bs : List α} : List.beq (a::as) (b:
instance [BEq α] : BEq (List α) := List.beq
instance [BEq α] [ReflBEq α] : ReflBEq (List α) where
rfl {as} := by
induction as with
| nil => rfl
| cons a as ih => simp [BEq.beq, List.beq]; exact ih
instance [BEq α] [LawfulBEq α] : LawfulBEq (List α) where
eq_of_beq {as bs} := by
induction as generalizing bs with
@@ -142,10 +148,6 @@ instance [BEq α] [LawfulBEq α] : LawfulBEq (List α) where
simp [show (a::as == b::bs) = (a == b && as == bs) from rfl, -and_imp]
intro h₁, h₂
exact h₁, ih h₂
rfl {as} := by
induction as with
| nil => rfl
| cons a as ih => simp [BEq.beq, List.beq, LawfulBEq.rfl]; exact ih
/--
Returns `true` if `as` and `bs` have the same length and they are pairwise related by `eqv`.
@@ -900,10 +902,10 @@ theorem mem_of_elem_eq_true [BEq α] [LawfulBEq α] {a : α} {as : List α} : el
next h => intros; simp [BEq.beq] at h; subst h; apply Mem.head
next _ => intro h; exact Mem.tail _ (mem_of_elem_eq_true h)
theorem elem_eq_true_of_mem [BEq α] [LawfulBEq α] {a : α} {as : List α} (h : a as) : elem a as = true := by
theorem elem_eq_true_of_mem [BEq α] [ReflBEq α] {a : α} {as : List α} (h : a as) : elem a as = true := by
induction h with
| head _ => simp [elem]
| tail _ _ ih => simp [elem]; split; rfl; assumption
| tail _ _ ih => simp only [elem]; split; rfl; assumption
instance [BEq α] [LawfulBEq α] (a : α) (as : List α) : Decidable (a as) :=
decidable_of_decidable_of_iff (Iff.intro mem_of_elem_eq_true elem_eq_true_of_mem)

View File

@@ -298,8 +298,8 @@ theorem filter_beq {l : List α} (a : α) : l.filter (· == a) = replicate (coun
simp only [count, countP_eq_length_filter, eq_replicate_iff, mem_filter, beq_iff_eq]
exact trivial, fun _ h => h.2
theorem filter_eq {α} [DecidableEq α] {l : List α} (a : α) : l.filter (· = a) = replicate (count a l) a :=
filter_beq a
theorem filter_eq [DecidableEq α] {l : List α} (a : α) : l.filter (· = a) = replicate (count a l) a :=
funext (Bool.beq_eq_decide_eq · a) filter_beq a
theorem le_count_iff_replicate_sublist {l : List α} : n count a l replicate n a <+ l := by
refine fun h => ?_, fun h => ?_
@@ -314,7 +314,7 @@ theorem replicate_count_eq_of_count_eq_length {l : List α} (h : count a l = len
rw [count, countP_filter]; congr; funext b
simp; rintro rfl; exact h
theorem count_le_count_map [DecidableEq β] {l : List α} {f : α β} {x : α} :
theorem count_le_count_map {β} [BEq β] [LawfulBEq β] {l : List α} {f : α β} {x : α} :
count x l count (f x) (map f l) := by
rw [count, count, countP_map]
apply countP_mono_left; simp +contextual

View File

@@ -88,7 +88,7 @@ theorem exists_or_eq_self_of_eraseP (p) (l : List α) :
@[simp] theorem length_eraseP_of_mem (al : a l) (pa : p a) :
length (l.eraseP p) = length l - 1 := by
let _, l₁, l₂, _, _, e₁, e₂ := exists_of_eraseP al pa
rw [e₂]; simp [length_append, e₁]; rfl
rw [e₂]; simp [length_append, e₁]
theorem length_eraseP {l : List α} : (l.eraseP p).length = if l.any p then l.length - 1 else l.length := by
split <;> rename_i h
@@ -542,7 +542,7 @@ theorem eraseIdx_eq_take_drop_succ :
match l, i with
| [], _
| a::l, 0
| a::l, i + 1 => simp [Nat.succ_inj']
| a::l, i + 1 => simp [Nat.succ_inj]
@[deprecated eraseIdx_eq_nil_iff (since := "2025-01-30")]
abbrev eraseIdx_eq_nil := @eraseIdx_eq_nil_iff
@@ -551,7 +551,7 @@ theorem eraseIdx_ne_nil_iff {l : List α} {i : Nat} : eraseIdx l i ≠ [] ↔ 2
match l with
| []
| [a]
| a::b::l => simp [Nat.succ_inj']
| a::b::l => simp [Nat.succ_inj]
@[deprecated eraseIdx_ne_nil_iff (since := "2025-01-30")]
abbrev eraseIdx_ne_nil := @eraseIdx_ne_nil_iff

View File

@@ -792,7 +792,7 @@ theorem of_findIdx?_eq_some {xs : List α} {p : α → Bool} (w : xs.findIdx? p
| nil => simp_all
| cons x xs ih =>
simp_all only [findIdx?_cons, Nat.zero_add]
split at w <;> cases i <;> simp_all [succ_inj']
split at w <;> cases i <;> simp_all [succ_inj]
@[deprecated of_findIdx?_eq_some (since := "2025-02-02")]
abbrev findIdx?_of_eq_some := @of_findIdx?_eq_some

View File

@@ -259,6 +259,9 @@ theorem getElem?_eq_some_iff {l : List α} : l[i]? = some a ↔ ∃ h : i < l.le
· match i, h with
| i + 1, h => simp [getElem?_eq_some_iff, Nat.succ_lt_succ_iff]
theorem getElem_of_getElem? {l : List α} : l[i]? = some a h : i < l.length, l[i] = a :=
getElem?_eq_some_iff.mp
theorem some_eq_getElem?_iff {l : List α} : some a = l[i]? h : i < l.length, l[i] = a := by
rw [eq_comm, getElem?_eq_some_iff]
@@ -701,7 +704,7 @@ theorem set_comm (a b : α) : ∀ {i j : Nat} {l : List α}, i ≠ j →
| _+1, 0, _ :: _, _ => by simp [set]
| 0, _+1, _ :: _, _ => by simp [set]
| _+1, _+1, _ :: t, h =>
congrArg _ <| set_comm a b fun h' => h <| Nat.succ_inj'.mpr h'
congrArg _ <| set_comm a b fun h' => h <| Nat.succ_inj.mpr h'
@[simp]
theorem set_set (a : α) {b : α} : {l : List α} {i : Nat}, (l.set i a).set i b = l.set i b
@@ -711,8 +714,8 @@ theorem set_set (a : α) {b : α} : ∀ {l : List α} {i : Nat}, (l.set i a).set
theorem mem_set {l : List α} {i : Nat} (h : i < l.length) (a : α) :
a l.set i a := by
simp [mem_iff_getElem]
exact i, (by simpa using h), by simp
simp only [mem_iff_getElem]
exact i, by simpa using h, by simp
theorem mem_or_eq_of_mem_set : {l : List α} {i : Nat} {a b : α}, a l.set i b a l a = b
| _ :: _, 0, _, _, h => ((mem_cons ..).1 h).symm.imp_left (.tail _)
@@ -774,37 +777,24 @@ theorem length_eq_of_beq [BEq α] {l₁ l₂ : List α} (h : l₁ == l₂) : l
simpa only [List.instBEq, List.beq, Bool.and_true]
simp
· intro h
constructor
intro l
induction l with
| nil => simp only [List.instBEq, List.beq]
| cons _ _ ih =>
simp [List.instBEq, List.beq]
exact ih
infer_instance
@[simp] theorem lawfulBEq_iff [BEq α] : LawfulBEq (List α) LawfulBEq α := by
constructor
· intro h
have : ReflBEq α := reflBEq_iff.mp inferInstance
constructor
· intro a b h
apply singleton_inj.1
apply eq_of_beq
simp only [List.instBEq, List.beq]
simpa
· intro a
suffices ([a] == [a]) = true by
simpa only [List.instBEq, List.beq, Bool.and_true]
simp
intro a b h
apply singleton_inj.1
apply eq_of_beq
simp only [List.instBEq, List.beq]
simpa
· intro h
constructor
· intro _ _ h
simpa using h
· intro _
simp
infer_instance
/-! ### isEqv -/
@[simp] theorem isEqv_eq [DecidableEq α] {l₁ l₂ : List α} : l₁.isEqv l₂ (· == ·) = (l₁ = l₂) := by
@[simp] theorem isEqv_eq [BEq α] [LawfulBEq α] {l₁ l₂ : List α} : l₁.isEqv l₂ (· == ·) = (l₁ = l₂) := by
induction l₁ generalizing l₂ with
| nil => cases l₂ <;> simp
| cons a l₁ ih =>
@@ -827,9 +817,15 @@ theorem getElem_length_sub_one_eq_getLast {l : List α} (h : l.length - 1 < l.le
l[l.length - 1] = getLast l (by cases l; simp at h; simp) := by
rw [ getLast_eq_getElem]
@[simp] theorem getLast_cons_cons {a : α} {l : List α} :
getLast (a :: b :: l) (by simp) = getLast (b :: l) (by simp) := by
rfl
theorem getLast_cons {a : α} {l : List α} : (h : l nil),
getLast (a :: l) (cons_ne_nil a l) = getLast l h := by
induction l <;> intros; {contradiction}; rfl
induction l <;> intros
· contradiction
· rfl
theorem getLast_eq_getLastD {a l} (h) : @getLast α (a::l) h = getLastD l a := by
cases l <;> rfl
@@ -1296,7 +1292,7 @@ abbrev filter_length_eq_length := @length_filter_eq_length_iff
@[simp] theorem mem_filter : x filter p as x as p x := by
induction as with
| nil => simp [filter]
| nil => simp
| cons a as ih =>
by_cases h : p a
· simp_all [or_and_left]
@@ -1362,12 +1358,9 @@ theorem filter_eq_cons_iff {l} {a} {as} :
split at h <;> rename_i w
· simp only [cons.injEq] at h
obtain rfl, rfl := h
refine [], l, ?_
simp [w]
· specialize ih h
obtain l₁, l₂, rfl, w₁, w₂, w₃ := ih
refine x :: l₁, l₂, ?_
simp_all
exact [], l, by simp [w]
· obtain l₁, l₂, rfl, w₁, w₂, w₃ := ih h
exact x :: l₁, l₂, by simp_all
· rintro l₁, l₂, rfl, h₁, h, h₂
simp [h₂, filter_cons, filter_eq_nil_iff.mpr h₁, h]
@@ -2046,7 +2039,7 @@ theorem eq_iff_flatten_eq : ∀ {L L' : List (List α)},
| _, [] => by simp_all
| [], _ :: _ => by simp_all
| _ :: _, _ :: _ => by
simp
simp only [cons.injEq, flatten_cons, map_cons]
rw [eq_iff_flatten_eq]
constructor
· rintro rfl, h₁, h₂
@@ -2154,7 +2147,7 @@ theorem replicate_succ' : replicate (n + 1) a = replicate n a ++ [a] := by
| 0 => by simp
| n+1 => by simp [replicate_succ, mem_replicate, Nat.succ_ne_zero]
@[deprecated mem_replicate (since := "2024-09-05")]
@[simp]
theorem contains_replicate [BEq α] {n : Nat} {a b : α} :
(replicate n b).contains a = (a == b && !n == 0) := by
induction n with
@@ -2165,9 +2158,9 @@ theorem contains_replicate [BEq α] {n : Nat} {a b : α} :
@[deprecated mem_replicate (since := "2024-09-05")]
theorem decide_mem_replicate [BEq α] [LawfulBEq α] {a b : α} :
{n}, decide (b replicate n a) = ((¬ n == 0) && b == a)
| 0 => by simp
| n+1 => by simp [replicate_succ, decide_mem_replicate, Nat.succ_ne_zero]
{n}, decide (b replicate n a) = ((¬ n == 0) && b == a) := by
have : DecidableEq α := instDecidableEqOfLawfulBEq
simp [Bool.beq_eq_decide_eq]
theorem eq_of_mem_replicate {a b : α} {n} (h : b replicate n a) : b = a := (mem_replicate.1 h).2
@@ -2709,12 +2702,12 @@ theorem foldr_assoc {op : ααα} [ha : Std.Associative op] :
-- The argument `f : α₁ → α₂` is intentionally explicit, as it is sometimes not found by unification.
theorem foldl_hom (f : α₁ α₂) {g₁ : α₁ β α₁} {g₂ : α₂ β α₂} {l : List β} {init : α₁}
(H : x y, g₂ (f x) y = f (g₁ x y)) : l.foldl g₂ (f init) = f (l.foldl g₁ init) := by
induction l generalizing init <;> simp [*, H]
induction l generalizing init <;> simp [*]
-- The argument `f : β₁ → β₂` is intentionally explicit, as it is sometimes not found by unification.
theorem foldr_hom (f : β₁ β₂) {g₁ : α β₁ β₁} {g₂ : α β₂ β₂} {l : List α} {init : β₁}
(H : x y, g₂ x (f y) = f (g₁ x y)) : l.foldr g₂ (f init) = f (l.foldr g₁ init) := by
induction l <;> simp [*, H]
induction l <;> simp [*]
/--
A reasoning principle for proving propositions about the result of `List.foldl` by establishing an
@@ -3260,6 +3253,10 @@ theorem eq_or_mem_of_mem_insert {l : List α} (h : a ∈ l.insert b) : a = b
@[simp] theorem length_insert_of_not_mem {l : List α} (h : a l) :
length (l.insert a) = length l + 1 := by rw [insert_of_not_mem h]; rfl
theorem length_insert {l : List α} :
(l.insert a).length = l.length + if a l then 0 else 1 := by
split <;> simp_all
theorem length_le_length_insert {l : List α} {a : α} : l.length (l.insert a).length := by
by_cases h : a l
· rw [length_insert_of_mem h]

View File

@@ -281,7 +281,7 @@ protected theorem le_iff_lt_or_eq [DecidableEq α] [LT α] [DecidableLT α]
· exact List.le_of_lt h
· exact List.le_refl l₁
theorem lex_eq_decide_lex [DecidableEq α] (lt : α α Bool) :
theorem lex_eq_decide_lex [BEq α] [LawfulBEq α] [DecidableEq α] (lt : α α Bool) :
lex l₁ l₂ lt = decide (Lex (fun x y => lt x y) l₁ l₂) := by
induction l₁ generalizing l₂ with
| nil =>
@@ -295,21 +295,22 @@ theorem lex_eq_decide_lex [DecidableEq α] (lt : αα → Bool) :
simp [lex, ih, cons_lex_cons_iff, Bool.beq_eq_decide_eq]
/-- Variant of `lex_eq_true_iff` using an arbitrary comparator. -/
@[simp] theorem lex_eq_true_iff_lex [DecidableEq α] (lt : α α Bool) :
@[simp] theorem lex_eq_true_iff_lex [BEq α] [LawfulBEq α] (lt : α α Bool) :
lex l₁ l₂ lt = true Lex (fun x y => lt x y) l₁ l₂ := by
have : DecidableEq α := instDecidableEqOfLawfulBEq
simp [lex_eq_decide_lex]
/-- Variant of `lex_eq_false_iff` using an arbitrary comparator. -/
@[simp] theorem lex_eq_false_iff_not_lex [DecidableEq α] (lt : α α Bool) :
@[simp] theorem lex_eq_false_iff_not_lex [BEq α] [LawfulBEq α] (lt : α α Bool) :
lex l₁ l₂ lt = false ¬ Lex (fun x y => lt x y) l₁ l₂ := by
simp [Bool.eq_false_iff, lex_eq_true_iff_lex]
@[simp] theorem lex_eq_true_iff_lt [DecidableEq α] [LT α] [DecidableLT α]
@[simp] theorem lex_eq_true_iff_lt [BEq α] [LawfulBEq α] [LT α] [DecidableLT α]
{l₁ l₂ : List α} : lex l₁ l₂ = true l₁ < l₂ := by
simp only [lex_eq_true_iff_lex, decide_eq_true_eq]
exact Iff.rfl
@[simp] theorem lex_eq_false_iff_ge [DecidableEq α] [LT α] [DecidableLT α]
@[simp] theorem lex_eq_false_iff_ge [BEq α] [LawfulBEq α] [LT α] [DecidableLT α]
{l₁ l₂ : List α} : lex l₁ l₂ = false l₂ l₁ := by
simp only [lex_eq_false_iff_not_lex, decide_eq_true_eq]
exact Iff.rfl

View File

@@ -118,7 +118,7 @@ theorem mem_eraseIdx_iff_getElem {x : α} :
| a::l, 0 => by simp [mem_iff_getElem, Nat.succ_lt_succ_iff]
| a::l, k+1 => by
rw [ Nat.or_exists_add_one]
simp [mem_eraseIdx_iff_getElem, @eq_comm _ a, succ_inj', Nat.succ_lt_succ_iff]
simp [mem_eraseIdx_iff_getElem, @eq_comm _ a, succ_inj, Nat.succ_lt_succ_iff]
theorem mem_eraseIdx_iff_getElem? {x : α} {l} {k} : x eraseIdx l k i k, l[i]? = some x := by
simp only [mem_eraseIdx_iff_getElem, getElem_eq_iff, exists_and_left]

View File

@@ -40,7 +40,6 @@ theorem getLast?_range' {n : Nat} : (range' s n).getLast? = if n = 0 then none e
simp [h]
· rw [if_neg h]
simp
omega
@[simp] theorem getLast_range' {n : Nat} (h) : (range' s n).getLast h = s + n - 1 := by
cases n with
@@ -358,7 +357,7 @@ theorem zipIdx_singleton {x : α} {k : Nat} : zipIdx [x] k = [(x, k)] :=
@[simp] theorem getLast?_zipIdx {l : List α} {k : Nat} :
(zipIdx l k).getLast? = l.getLast?.map fun a => (a, k + l.length - 1) := by
simp [getLast?_eq_getElem?]
cases l <;> simp; omega
cases l <;> simp
theorem mk_add_mem_zipIdx_iff_getElem? {k i : Nat} {x : α} {l : List α} :
(x, k + i) zipIdx l k l[i]? = some x := by
@@ -497,7 +496,7 @@ theorem head?_enumFrom (n : Nat) (l : List α) :
theorem getLast?_enumFrom (n : Nat) (l : List α) :
(enumFrom n l).getLast? = l.getLast?.map fun a => (n + l.length - 1, a) := by
simp [getLast?_eq_getElem?]
cases l <;> simp; omega
cases l <;> simp
@[deprecated mk_add_mem_zipIdx_iff_getElem? (since := "2025-01-21")]
theorem mk_add_mem_enumFrom_iff_getElem? {n i : Nat} {x : α} {l : List α} :

View File

@@ -532,7 +532,7 @@ theorem dropWhile_eq_drop_findIdx_not {xs : List α} {p : α → Bool} :
| zero => simp
| succ n =>
suffices 1 < m m - (m - (n + 1) % m) + min (m - (n + 1) % m) m = m by
simpa [rotateRight]
simp [rotateRight]
intro h
have : (n + 1) % m < m := Nat.mod_lt _ (by omega)
rw [Nat.min_eq_left (by omega)]

View File

@@ -6,6 +6,7 @@ Authors: Leonardo de Moura, Jeremy Avigad, Mario Carneiro
prelude
import Init.Data.List.Pairwise
import Init.Data.List.Erase
import Init.Data.List.Find
/-!
# List Permutations
@@ -178,7 +179,7 @@ theorem Perm.singleton_eq (h : [a] ~ l) : [a] = l := singleton_perm.mp h
theorem singleton_perm_singleton {a b : α} : [a] ~ [b] a = b := by simp
theorem perm_cons_erase [DecidableEq α] {a : α} {l : List α} (h : a l) : l ~ a :: l.erase a :=
theorem perm_cons_erase [BEq α] [LawfulBEq α] {a : α} {l : List α} (h : a l) : l ~ a :: l.erase a :=
let _, _, _, e₁, e₂ := exists_erase_eq h
e₂ e₁ perm_middle
@@ -201,6 +202,9 @@ theorem Perm.pmap {p : α → Prop} (f : ∀ a, p a → β) {l₁ l₂ : List α
| swap x y => simp [swap]
| trans _p₁ p₂ IH₁ IH₂ => exact IH₁.trans (IH₂ (H₁ := fun a m => H₂ a (p₂.subset m)))
theorem Perm.unattach {α : Type u} {p : α Prop} {l₁ l₂ : List { x // p x }} (h : l₁ ~ l₂) :
l₁.unattach.Perm l₂.unattach := h.map _
theorem Perm.filter (p : α Bool) {l₁ l₂ : List α} (s : l₁ ~ l₂) :
filter p l₁ ~ filter p l₂ := by rw [ filterMap_eq_filter]; apply s.filterMap
@@ -268,7 +272,7 @@ theorem countP_eq_countP_filter_add (l : List α) (p q : α → Bool) :
l.countP p = (l.filter q).countP p + (l.filter fun a => !q a).countP p :=
countP_append .. Perm.countP_eq _ (filter_append_perm _ _).symm
theorem Perm.count_eq [DecidableEq α] {l₁ l₂ : List α} (p : l₁ ~ l₂) (a) :
theorem Perm.count_eq [BEq α] {l₁ l₂ : List α} (p : l₁ ~ l₂) (a) :
count a l₁ = count a l₂ := p.countP_eq _
/-
@@ -369,9 +373,9 @@ theorem perm_append_right_iff {l₁ l₂ : List α} (l) : l₁ ++ l ~ l₂ ++ l
refine fun p => ?_, .append_right _
exact (perm_append_left_iff _).1 <| perm_append_comm.trans <| p.trans perm_append_comm
section DecidableEq
section LawfulBEq
variable [DecidableEq α]
variable [BEq α] [LawfulBEq α]
theorem Perm.erase (a : α) {l₁ l₂ : List α} (p : l₁ ~ l₂) : l₁.erase a ~ l₂.erase a :=
if h₁ : a l₁ then
@@ -401,14 +405,14 @@ theorem perm_iff_count {l₁ l₂ : List α} : l₁ ~ l₂ ↔ ∀ a, count a l
refine ((IH fun b => ?_).cons a).trans (perm_cons_erase this).symm
specialize H b
rw [(perm_cons_erase this).count_eq] at H
by_cases h : b = a <;> simpa [h, count_cons, Nat.succ_inj'] using H
by_cases h : b = a <;> simpa [h, count_cons, Nat.succ_inj] using H
theorem isPerm_iff : {l₁ l₂ : List α}, l₁.isPerm l₂ l₁ ~ l₂
| [], [] => by simp [isPerm, isEmpty]
| [], _ :: _ => by simp [isPerm, isEmpty, Perm.nil_eq]
| a :: l₁, l₂ => by simp [isPerm, isPerm_iff, cons_perm_iff_perm_erase]
instance decidablePerm (l₁ l₂ : List α) : Decidable (l₁ ~ l₂) := decidable_of_iff _ isPerm_iff
instance decidablePerm {α} [DecidableEq α] (l₁ l₂ : List α) : Decidable (l₁ ~ l₂) := decidable_of_iff _ isPerm_iff
protected theorem Perm.insert (a : α) {l₁ l₂ : List α} (p : l₁ ~ l₂) :
l₁.insert a ~ l₂.insert a := by
@@ -425,7 +429,7 @@ theorem perm_insert_swap (x y : α) (l : List α) :
simp [List.insert, xl, yl, xy, Ne.symm xy]
constructor
end DecidableEq
end LawfulBEq
theorem Perm.pairwise_iff {R : α α Prop} (S : {x y}, R x y R y x) :
{l₁ l₂ : List α} (_p : l₁ ~ l₂), Pairwise R l₁ Pairwise R l₂ :=

View File

@@ -69,7 +69,7 @@ theorem mem_range' : ∀ {n}, m ∈ range' s n step ↔ ∃ i < n, m = s + step
| 0 => by simp [range', Nat.not_lt_zero]
| n + 1 => by
have h (i) : i n i = 0 j, i = succ j j < n := by
cases i <;> simp [Nat.succ_le, Nat.succ_inj']
cases i <;> simp [Nat.succ_le, Nat.succ_inj]
simp [range', mem_range', Nat.lt_succ, h]; simp only [ exists_and_right, and_assoc]
rw [exists_comm]; simp [Nat.mul_succ, Nat.add_assoc, Nat.add_comm]

View File

@@ -220,7 +220,7 @@ theorem zipWith_eq_append_iff {f : α → β → γ} {l₁ : List α} {l₂ : Li
· simp_all
· obtain (rfl, rfl | _, rfl, rfl) := h₃
· simp_all
· simp_all [zipWith_append, Nat.succ_inj']
· simp_all [zipWith_append, Nat.succ_inj]
/-- See also `List.zipWith_replicate` in `Init.Data.List.TakeDrop` for a generalization with different lengths. -/
@[simp] theorem zipWith_replicate' {a : α} {b : β} {n : Nat} :

View File

@@ -546,6 +546,10 @@ protected theorem le_of_add_le_add_right {a b c : Nat} : a + b ≤ c + b → a
/-! ### le/lt -/
attribute [simp] not_lt_zero
example : (default : Nat) = 0 := rfl
protected theorem lt_asymm {a b : Nat} (h : a < b) : ¬ b < a := Nat.not_lt.2 (Nat.le_of_lt h)
/-- Alias for `Nat.lt_asymm`. -/
protected abbrev not_lt_of_gt := @Nat.lt_asymm
@@ -618,7 +622,7 @@ protected theorem eq_zero_of_not_pos (h : ¬0 < n) : n = 0 :=
attribute [simp] zero_lt_succ
theorem succ_ne_self (n) : succ n n := Nat.ne_of_gt (lt_succ_self n)
@[simp] theorem succ_ne_self (n) : succ n n := Nat.ne_of_gt (lt_succ_self n)
theorem add_one_ne_self (n) : n + 1 n := Nat.ne_of_gt (lt_succ_self n)
@@ -641,13 +645,20 @@ theorem eq_zero_or_eq_succ_pred : ∀ n, n = 0 n = succ (pred n)
| 0 => .inl rfl
| _+1 => .inr rfl
theorem succ_inj' : succ a = succ b a = b := (Nat.succ.injEq a b).to_iff
theorem succ_inj : succ a = succ b a = b := (Nat.succ.injEq a b).to_iff
@[deprecated succ_inj (since := "2025-04-14")]
theorem succ_inj' : succ a = succ b a = b := succ_inj
theorem succ_le_succ_iff : succ a succ b a b := le_of_succ_le_succ, succ_le_succ
theorem succ_lt_succ_iff : succ a < succ b a < b := lt_of_succ_lt_succ, succ_lt_succ
theorem add_one_inj : a + 1 = b + 1 a = b := succ_inj'
theorem succ_ne_succ_iff : succ a succ b a b := by simp [Nat.succ.injEq]
theorem succ_succ_ne_one (a : Nat) : succ (succ a) 1 := nofun
theorem add_one_inj : a + 1 = b + 1 a = b := succ_inj
theorem ne_add_one (n : Nat) : n n + 1 := fun h => by cases h
@@ -657,6 +668,10 @@ theorem add_one_le_add_one_iff : a + 1 ≤ b + 1 ↔ a ≤ b := succ_le_succ_iff
theorem add_one_lt_add_one_iff : a + 1 < b + 1 a < b := succ_lt_succ_iff
theorem add_one_ne_add_one_iff : a + 1 b + 1 a b := succ_ne_succ_iff
theorem add_one_add_one_ne_one : a + 1 + 1 1 := nofun
theorem pred_inj : {a b}, 0 < a 0 < b pred a = pred b a = b
| _+1, _+1, _, _ => congrArg _
@@ -714,16 +729,18 @@ theorem exists_eq_add_one_of_ne_zero : ∀ {n}, n ≠ 0 → Exists fun k => n =
theorem ctor_eq_zero : Nat.zero = 0 :=
rfl
protected theorem one_ne_zero : 1 (0 : Nat) :=
@[simp] protected theorem one_ne_zero : 1 (0 : Nat) :=
fun h => Nat.noConfusion h
protected theorem zero_ne_one : 0 (1 : Nat) :=
@[simp] protected theorem zero_ne_one : 0 (1 : Nat) :=
fun h => Nat.noConfusion h
theorem succ_ne_zero (n : Nat) : succ n 0 := by simp
@[simp] theorem succ_ne_zero (n : Nat) : succ n 0 := by simp
instance instNeZeroSucc {n : Nat} : NeZero (n + 1) := succ_ne_zero n
@[simp] theorem default_eq_zero : default = 0 := rfl
/-! # mul + order -/
theorem mul_le_mul_left {n m : Nat} (k : Nat) (h : n m) : k * n k * m :=
@@ -1003,7 +1020,7 @@ protected theorem add_sub_add_left (k n m : Nat) : (k + n) - (k + m) = n - m :=
suffices n + m - (0 + m) = n by rw [Nat.zero_add] at this; assumption
by rw [Nat.add_sub_add_right, Nat.sub_zero]
protected theorem add_sub_cancel_left (n m : Nat) : n + m - n = m :=
@[simp] protected theorem add_sub_cancel_left (n m : Nat) : n + m - n = m :=
show n + m - (n + 0) = m from
by rw [Nat.add_sub_add_left, Nat.sub_zero]
@@ -1054,7 +1071,7 @@ protected theorem sub_self_add (n m : Nat) : n - (n + m) = 0 := by
show (n + 0) - (n + m) = 0
rw [Nat.add_sub_add_left, Nat.zero_sub]
protected theorem sub_eq_zero_of_le {n m : Nat} (h : n m) : n - m = 0 := by
@[simp] protected theorem sub_eq_zero_of_le {n m : Nat} (h : n m) : n - m = 0 := by
match le.dest h with
| k, hk => rw [ hk, Nat.sub_self_add]

View File

@@ -473,7 +473,8 @@ Nat.le_antisymm
(le_of_lt_succ ((Nat.div_lt_iff_lt_mul npos).2 hi))
((Nat.le_div_iff_mul_le npos).2 lo)
theorem sub_mul_div (x n p : Nat) (h₁ : n*p x) : (x - n*p) / n = x / n - p := by
/-- See also `sub_mul_div` for a strictly more general version. -/
theorem sub_mul_div_of_le (x n p : Nat) (h₁ : n*p x) : (x - n*p) / n = x / n - p := by
match eq_zero_or_pos n with
| .inl h₀ => rw [h₀, Nat.div_zero, Nat.div_zero, Nat.zero_sub]
| .inr h₀ => induction p with
@@ -551,7 +552,7 @@ protected theorem div_le_of_le_mul {m n : Nat} : ∀ {k}, m ≤ k * n → m / k
@[simp] theorem mul_div_left (m : Nat) {n : Nat} (H : 0 < n) : m * n / n = m := by
rw [Nat.mul_comm, mul_div_right _ H]
protected theorem div_self (H : 0 < n) : n / n = 1 := by
@[simp] protected theorem div_self (H : 0 < n) : n / n = 1 := by
let t := add_div_right 0 H
rwa [Nat.zero_add, Nat.zero_div] at t

View File

@@ -84,6 +84,10 @@ theorem div_le_div_left (hcb : c ≤ b) (hc : 0 < c) : a / b ≤ a / c :=
(Nat.le_div_iff_mul_le hc).2 <|
Nat.le_trans (Nat.mul_le_mul_left _ hcb) (Nat.div_mul_le_self a b)
protected theorem div_le_div {a b c d : Nat} (h1 : a b) (h2 : d c) (h3 : d 0) : a / c b / d :=
calc a / c b / c := Nat.div_le_div_right h1
_ b / d := Nat.div_le_div_left h2 (Nat.pos_of_ne_zero h3)
theorem div_add_le_right {z : Nat} (h : 0 < z) (x y : Nat) :
x / (y + z) x / z :=
div_le_div_left (Nat.le_add_left z y) h
@@ -104,7 +108,7 @@ theorem succ_div_of_dvd {a b : Nat} (h : b a + 1) :
Nat.add_right_cancel_iff] at h
subst h
rw [Nat.add_sub_cancel, Nat.add_one_mul, mul_div_right _ (zero_lt_succ _), Nat.add_comm,
Nat.add_mul_div_left _ _ (zero_lt_succ _), Nat.self_eq_add_left, div_eq_of_lt le.refl]
Nat.add_mul_div_left _ _ (zero_lt_succ _), Nat.right_eq_add, div_eq_of_lt le.refl]
· simp only [Nat.not_le] at h'
replace h' : a + 1 < b + 1 := Nat.add_lt_add_right h' 1
rw [Nat.mod_eq_of_lt h'] at h

View File

@@ -1,21 +1,22 @@
/-
Copyright (c) 2016 Microsoft Corporation. All rights reserved.
Copyright (c) 2014 Floris van Doorn (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura, Jeremy Avigad, Mario Carneiro
Authors: Leonardo de Moura, Jeremy Avigad, Mario Carneiro, Floris van Doorn
-/
prelude
import Init.Data.Nat.MinMax
import Init.Data.Nat.Log2
import Init.Data.Nat.Power2
import Init.Data.Nat.Mod
import Init.Omega
/-! # Basic lemmas about natural numbers
/-! # Basic theorems about natural numbers
The primary purpose of the lemmas in this file is to assist with reasoning
The primary purpose of the theorems in this file is to assist with reasoning
about sizes of objects, array indices and such.
This file was upstreamed from Std,
and later these lemmas should be organised into other files more systematically.
The content of this file was upstreamed from Batteries and mathlib,
and later these theorems should be organised into other files more systematically.
-/
namespace Nat
@@ -100,13 +101,94 @@ theorem exists_lt_succ_left {p : Nat → Prop} :
( m, m < n + 1 p m) p 0 ( m, m < n p (m + 1)) := by
simpa using exists_lt_succ_left' (p := fun m _ => p m)
/-! ## succ/pred -/
protected theorem sub_one (n) : n - 1 = pred n := rfl
theorem one_add (n) : 1 + n = succ n := Nat.add_comm ..
theorem succ_ne_succ : succ m succ n m n :=
mt (congrArg Nat.succ ·), mt succ.inj
theorem one_lt_succ_succ (n : Nat) : 1 < n.succ.succ := succ_lt_succ <| succ_pos _
theorem not_succ_lt_self : ¬ succ n < n := Nat.not_lt_of_ge n.le_succ
theorem succ_le_iff : succ m n m < n := lt_of_succ_le, succ_le_of_lt
theorem le_succ_iff {m n : Nat} : m n.succ m n m = n.succ := by
refine fun hmn (Nat.lt_or_eq_of_le hmn).imp_left le_of_lt_succ, ?_
rintro (hmn | rfl)
· exact le_succ_of_le hmn
· exact Nat.le_refl _
theorem lt_iff_le_pred : {n}, 0 < n (m < n m n - 1) | _ + 1, _ => Nat.lt_succ_iff
-- TODO: state LHS using `- 1` instead?
theorem le_of_pred_lt : {m}, pred m < n m n
| 0 => Nat.le_of_lt
| _ + 1 => id
theorem lt_iff_add_one_le : m < n m + 1 n := by rw [succ_le_iff]
theorem lt_one_add_iff : m < 1 + n m n := by simp only [Nat.add_comm, Nat.lt_succ_iff]
theorem one_add_le_iff : 1 + m n m < n := by simp only [Nat.add_comm, add_one_le_iff]
theorem one_le_iff_ne_zero : 1 n n 0 := Nat.pos_iff_ne_zero
theorem one_lt_iff_ne_zero_and_ne_one : {n : Nat}, 1 < n n 0 n 1
| 0 => by decide
| 1 => by decide
| n + 2 => by omega
theorem le_one_iff_eq_zero_or_eq_one : {n : Nat}, n 1 n = 0 n = 1 := by simp [le_succ_iff]
theorem one_le_of_lt (h : a < b) : 1 b := Nat.lt_of_le_of_lt (Nat.zero_le _) h
theorem pred_one_add (n : Nat) : pred (1 + n) = n := by rw [Nat.add_comm, add_one, Nat.pred_succ]
theorem pred_eq_self_iff : n.pred = n n = 0 := by cases n <;> simp [(Nat.succ_ne_self _).symm]
theorem pred_eq_of_eq_succ {m n : Nat} (H : m = n.succ) : m.pred = n := by simp [H]
@[simp] theorem pred_eq_succ_iff : n - 1 = m + 1 n = m + 2 := by
cases n <;> constructor <;> rintro <;> rfl
@[simp] theorem add_succ_sub_one (m n : Nat) : m + succ n - 1 = m + n := rfl
@[simp]
theorem succ_add_sub_one (n m : Nat) : succ m + n - 1 = m + n := by rw [succ_add, Nat.add_one_sub_one]
theorem pred_sub (n m : Nat) : pred n - m = pred (n - m) := by
rw [ Nat.sub_one, Nat.sub_sub, one_add, sub_succ]
theorem self_add_sub_one : n, n + (n - 1) = 2 * n - 1
| 0 => rfl
| n + 1 => by rw [Nat.two_mul]; exact (add_succ_sub_one (Nat.succ _) _).symm
theorem sub_one_add_self (n : Nat) : (n - 1) + n = 2 * n - 1 := Nat.add_comm _ n self_add_sub_one n
theorem self_add_pred (n : Nat) : n + pred n = (2 * n).pred := self_add_sub_one n
theorem pred_add_self (n : Nat) : pred n + n = (2 * n).pred := sub_one_add_self n
theorem pred_le_iff : pred m n m succ n :=
le_succ_of_pred_le, by
cases m
· exact fun _ zero_le n
· exact le_of_succ_le_succ
theorem lt_of_lt_pred (h : m < n - 1) : m < n := by omega
theorem le_add_pred_of_pos (a : Nat) (hb : b 0) : a b + (a - 1) := by omega
theorem lt_pred_iff : a < pred b succ a < b := by simp; omega
/-! ## add -/
protected theorem add_add_add_comm (a b c d : Nat) : (a + b) + (c + d) = (a + c) + (b + d) := by
rw [Nat.add_assoc, Nat.add_assoc, Nat.add_left_comm b]
theorem one_add (n) : 1 + n = succ n := Nat.add_comm ..
theorem succ_eq_one_add (n) : succ n = 1 + n := (one_add _).symm
theorem succ_add_eq_add_succ (a b) : succ a + b = a + succ b := Nat.succ_add ..
@@ -114,19 +196,31 @@ theorem succ_add_eq_add_succ (a b) : succ a + b = a + succ b := Nat.succ_add ..
protected theorem eq_zero_of_add_eq_zero_right (h : n + m = 0) : n = 0 :=
(Nat.eq_zero_of_add_eq_zero h).1
@[simp] protected theorem add_eq_zero_iff : n + m = 0 n = 0 m = 0 :=
protected theorem add_eq_zero_iff : n + m = 0 n = 0 m = 0 :=
Nat.eq_zero_of_add_eq_zero, fun h₁, h₂ => h₂.symm h₁
@[simp] protected theorem add_left_cancel_iff {n : Nat} : n + m = n + k m = k :=
@[simp high] protected theorem add_left_cancel_iff {n : Nat} : n + m = n + k m = k :=
Nat.add_left_cancel, fun | rfl => rfl
@[simp] protected theorem add_right_cancel_iff {n : Nat} : m + n = k + n m = k :=
@[simp high] protected theorem add_right_cancel_iff {n : Nat} : m + n = k + n m = k :=
Nat.add_right_cancel, fun | rfl => rfl
@[simp] protected theorem add_left_eq_self {a b : Nat} : a + b = b a = 0 := by omega
@[simp] protected theorem add_right_eq_self {a b : Nat} : a + b = a b = 0 := by omega
@[simp] protected theorem self_eq_add_right {a b : Nat} : a = a + b b = 0 := by omega
@[simp] protected theorem self_eq_add_left {a b : Nat} : a = b + a b = 0 := by omega
protected theorem add_left_inj {n : Nat} : m + n = k + n m = k := Nat.add_right_cancel_iff
protected theorem add_right_inj {n : Nat} : n + m = n + k m = k := Nat.add_left_cancel_iff
@[simp high] protected theorem add_eq_left {a b : Nat} : a + b = a b = 0 := by omega
@[simp high] protected theorem add_eq_right {a b : Nat} : a + b = b a = 0 := by omega
@[simp high] protected theorem left_eq_add {a b : Nat} : a = a + b b = 0 := by omega
@[simp high] protected theorem right_eq_add {a b : Nat} : b = a + b a = 0 := by omega
@[deprecated Nat.add_eq_right (since := "2025-04-15")]
protected theorem add_left_eq_self {a b : Nat} : a + b = b a = 0 := Nat.add_eq_right
@[deprecated Nat.add_eq_left (since := "2025-04-15")]
protected theorem add_right_eq_self {a b : Nat} : a + b = a b = 0 := Nat.add_eq_left
@[deprecated Nat.left_eq_add (since := "2025-04-15")]
protected theorem self_eq_add_right {a b : Nat} : a = a + b b = 0 := Nat.left_eq_add
@[deprecated Nat.right_eq_add (since := "2025-04-15")]
protected theorem self_eq_add_left {a b : Nat} : a = b + a b = 0 := Nat.right_eq_add
protected theorem lt_of_add_lt_add_right : {n : Nat}, k + n < m + n k < m
| 0, h => h
@@ -173,9 +267,28 @@ protected theorem add_self_ne_one : ∀ n, n + n ≠ 1
theorem le_iff_lt_add_one : x y x < y + 1 := by
omega
/-! ## sub -/
@[simp high] protected theorem add_eq_zero : m + n = 0 m = 0 n = 0 := by omega
protected theorem sub_one (n) : n - 1 = pred n := rfl
theorem add_pos_iff_pos_or_pos : 0 < m + n 0 < m 0 < n := by omega
theorem add_eq_one_iff : m + n = 1 m = 0 n = 1 m = 1 n = 0 := by omega
theorem add_eq_two_iff : m + n = 2 m = 0 n = 2 m = 1 n = 1 m = 2 n = 0 := by
omega
theorem add_eq_three_iff :
m + n = 3 m = 0 n = 3 m = 1 n = 2 m = 2 n = 1 m = 3 n = 0 := by
omega
theorem le_add_one_iff : m n + 1 m n m = n + 1 := by omega
theorem le_and_le_add_one_iff : n m m n + 1 m = n m = n + 1 := by omega
theorem add_succ_lt_add (hab : a < b) (hcd : c < d) : a + c + 1 < b + d := by omega
theorem le_or_le_of_add_eq_add_pred (h : a + c = b + d - 1) : b a d c := by omega
/-! ## sub -/
protected theorem one_sub : n, 1 - n = if n = 0 then 1 else 0
| 0 => rfl
@@ -213,7 +326,7 @@ protected theorem sub_eq_zero_iff_le : n - m = 0 ↔ n ≤ m :=
protected theorem sub_pos_iff_lt : 0 < n - m m < n :=
Nat.lt_of_sub_pos, Nat.sub_pos_of_lt
protected theorem sub_le_iff_le_add {a b c : Nat} : a - b c a c + b :=
@[simp] protected theorem sub_le_iff_le_add {a b c : Nat} : a - b c a c + b :=
Nat.le_add_of_sub_le, sub_le_of_le_add
protected theorem sub_le_iff_le_add' {a b c : Nat} : a - b c a b + c := by
@@ -274,6 +387,25 @@ protected theorem exists_eq_add_of_le' (h : m ≤ n) : ∃ k : Nat, n = k + m :=
protected theorem exists_eq_add_of_lt (h : m < n) : k : Nat, n = m + k + 1 :=
n - (m + 1), by rw [Nat.add_right_comm, add_sub_of_le h]
/-- A version of `Nat.sub_succ` in the form `_ - 1` instead of `Nat.pred _`. -/
theorem sub_succ' (m n : Nat) : m - n.succ = m - n - 1 := rfl
protected theorem sub_eq_of_eq_add' {a b c : Nat} (h : a = b + c) : a - b = c := by omega
protected theorem eq_sub_of_add_eq {a b c : Nat} (h : c + b = a) : c = a - b := by omega
protected theorem eq_sub_of_add_eq' {a b c : Nat} (h : b + c = a) : c = a - b := by omega
protected theorem lt_sub_iff_add_lt {a b c : Nat} : a < c - b a + b < c := add_lt_of_lt_sub, lt_sub_of_add_lt
protected theorem lt_sub_iff_add_lt' {a b c : Nat} : a < c - b b + a < c := by omega
protected theorem sub_lt_iff_lt_add {a b c : Nat} (hba : b a) : a - b < c a < c + b := by omega
protected theorem sub_lt_iff_lt_add' {a b c : Nat} (hba : b a) : a - b < c a < b + c := by omega
-- TODO: variants
protected theorem sub_sub_sub_cancel_right {a b c : Nat} (h : c b) : a - c - (b - c) = a - b := by omega
protected theorem add_sub_sub_cancel {a b c : Nat} (h : c a) : a + b - (a - c) = b + c := by omega
protected theorem sub_add_sub_cancel {a b c : Nat} (hab : b a) (hcb : c b) : a - b + (b - c) = a - c := by omega
protected theorem sub_lt_sub_iff_right {a b c : Nat} (h : c a) : a - c < b - c a < b := by omega
/-! ### min/max -/
theorem succ_min_succ (x y) : min (succ x) (succ y) = succ (min x y) := by
@@ -417,6 +549,24 @@ protected theorem sub_min_sub_left (a b c : Nat) : min (a - b) (a - c) = a - max
protected theorem sub_max_sub_left (a b c : Nat) : max (a - b) (a - c) = a - min b c := by
omega
protected theorem min_left_comm (a b c : Nat) : min a (min b c) = min b (min a c) := by
rw [ Nat.min_assoc, Nat.min_assoc, b.min_comm]
protected theorem max_left_comm (a b c : Nat) : max a (max b c) = max b (max a c) := by
rw [ Nat.max_assoc, Nat.max_assoc, b.max_comm]
protected theorem min_right_comm (a b c : Nat) : min (min a b) c = min (min a c) b := by
rw [Nat.min_assoc, Nat.min_assoc, b.min_comm]
protected theorem max_right_comm (a b c : Nat) : max (max a b) c = max (max a c) b := by
rw [Nat.max_assoc, Nat.max_assoc, b.max_comm]
@[simp] theorem min_eq_zero_iff : min m n = 0 m = 0 n = 0 := by omega
@[simp] theorem max_eq_zero_iff : max m n = 0 m = 0 n = 0 := by omega
theorem add_eq_max_iff : m + n = max m n m = 0 n = 0 := by omega
theorem add_eq_min_iff : m + n = min m n m = 0 n = 0 := by omega
/-! ### mul -/
protected theorem mul_right_comm (n m k : Nat) : n * m * k = n * k * m := by
@@ -538,6 +688,101 @@ protected theorem mul_dvd_mul_iff_left {a b c : Nat} (h : 0 < a) : a * b a *
protected theorem mul_dvd_mul_iff_right {a b c : Nat} (h : 0 < c) : a * c b * c a b := by
rw [Nat.mul_comm _ c, Nat.mul_comm _ c, Nat.mul_dvd_mul_iff_left h]
protected theorem zero_eq_mul : 0 = m * n m = 0 n = 0 := by rw [eq_comm, Nat.mul_eq_zero]
-- TODO: Replace `Nat.mul_right_cancel_iff` with `Nat.mul_left_inj`
protected theorem mul_left_inj (ha : a 0) : b * a = c * a b = c :=
Nat.mul_right_cancel_iff (Nat.pos_iff_ne_zero.2 ha)
-- TODO: Replace `Nat.mul_left_cancel_iff` with `Nat.mul_right_inj`
protected theorem mul_right_inj (ha : a 0) : a * b = a * c b = c :=
Nat.mul_left_cancel_iff (Nat.pos_iff_ne_zero.2 ha)
protected theorem mul_ne_mul_left (ha : a 0) : b * a c * a b c :=
not_congr (Nat.mul_left_inj ha)
protected theorem mul_ne_mul_right (ha : a 0) : a * b a * c b c :=
not_congr (Nat.mul_right_inj ha)
theorem mul_eq_left (ha : a 0) : a * b = a b = 1 := by simpa using Nat.mul_right_inj ha (c := 1)
theorem mul_eq_right (hb : b 0) : a * b = b a = 1 := by simpa using Nat.mul_left_inj hb (c := 1)
/-- The product of two natural numbers is greater than 1 if and only if
at least one of them is greater than 1 and both are positive. -/
theorem one_lt_mul_iff : 1 < m * n 0 < m 0 < n (1 < m 1 < n) := by
constructor <;> intro h
· refine Decidable.by_contra (fun h' => ?_)
simp only [Nat.le_zero, Decidable.not_and_iff_not_or_not, not_or, Nat.not_lt] at h'
obtain rfl | rfl | h' := h'
· simp at h
· simp at h
· exact Nat.not_lt_of_le (Nat.mul_le_mul h'.1 h'.2) h
· obtain hm | hn := h.2.2
· exact Nat.mul_lt_mul_of_lt_of_le' hm h.2.1 Nat.zero_lt_one
· exact Nat.mul_lt_mul_of_le_of_lt h.1 hn h.1
theorem eq_one_of_mul_eq_one_right (H : m * n = 1) : m = 1 := eq_one_of_dvd_one n, H.symm
theorem eq_one_of_mul_eq_one_left (H : m * n = 1) : n = 1 :=
eq_one_of_mul_eq_one_right (n := m) (by rwa [Nat.mul_comm])
@[simp] protected theorem lt_mul_iff_one_lt_left (hb : 0 < b) : b < a * b 1 < a := by
simpa using Nat.mul_lt_mul_right (b := 1) hb
@[simp] protected theorem lt_mul_iff_one_lt_right (ha : 0 < a) : a < a * b 1 < b := by
simpa using Nat.mul_lt_mul_left (b := 1) ha
theorem eq_zero_of_two_mul_le (h : 2 * n n) : n = 0 := by omega
theorem eq_zero_of_mul_le (hb : 2 n) (h : n * m m) : m = 0 :=
eq_zero_of_two_mul_le <| Nat.le_trans (Nat.mul_le_mul_right _ hb) h
theorem succ_mul_pos (m : Nat) (hn : 0 < n) : 0 < succ m * n := Nat.mul_pos m.succ_pos hn
theorem mul_self_le_mul_self {m n : Nat} (h : m n) : m * m n * n := Nat.mul_le_mul h h
-- This name is consistent with mathlib's top level definitions for groups with zero, where there
-- are `mul_lt_mul`, `mul_lt_mul'` and `mul_lt_mul''`, all with different sets of hypotheses.
theorem mul_lt_mul'' {a b c d : Nat} (hac : a < c) (hbd : b < d) : a * b < c * d :=
Nat.mul_lt_mul_of_lt_of_le hac (Nat.le_of_lt hbd) <| by omega
protected theorem lt_iff_lt_of_mul_eq_mul (ha : a 0) (hbd : a = b * d) (hce : a = c * e) :
c < b d < e where
mp hcb := Nat.lt_of_not_le fun hed Nat.not_lt_of_le (Nat.le_of_eq <| hbd.symm.trans hce) <|
Nat.mul_lt_mul_of_lt_of_le hcb hed <| by simp [hbd, Nat.mul_eq_zero] at ha; omega
mpr hde := Nat.lt_of_not_le fun hbc Nat.not_lt_of_le (Nat.le_of_eq <| hce.symm.trans hbd) <|
Nat.mul_lt_mul_of_le_of_lt hbc hde <| by simp [hce, Nat.mul_eq_zero] at ha; omega
theorem mul_self_lt_mul_self {m n : Nat} (h : m < n) : m * m < n * n := mul_lt_mul'' h h
theorem mul_self_le_mul_self_iff {m n : Nat} : m * m n * n m n :=
fun h => Nat.le_of_not_lt fun h' => Nat.not_le_of_gt (mul_self_lt_mul_self h') h,
mul_self_le_mul_self
theorem mul_self_lt_mul_self_iff {m n : Nat} : m * m < n * n m < n := by
simp only [ Nat.not_le, mul_self_le_mul_self_iff]
theorem le_mul_self : n : Nat, n n * n
| 0 => Nat.le_refl _
| n + 1 => by simp [Nat.mul_add]
theorem mul_self_inj {m n : Nat} : m * m = n * n m = n := by
simp [Nat.le_antisymm_iff, mul_self_le_mul_self_iff]
@[simp] theorem lt_mul_self_iff : {n : Nat}, n < n * n 1 < n
| 0 => by simp
| n + 1 => Nat.lt_mul_iff_one_lt_left n.succ_pos
theorem add_sub_one_le_mul (ha : a 0) (hb : b 0) : a + b - 1 a * b := by
cases a
· cases ha rfl
· rw [succ_add, Nat.add_one_sub_one, succ_mul]
exact Nat.add_le_add_right (Nat.le_mul_of_pos_right _ <| Nat.pos_iff_ne_zero.2 hb) _
protected theorem add_le_mul {a : Nat} (ha : 2 a) : {b : Nat} (_ : 2 b), a + b a * b
| 2, _ => by omega
| b + 3, _ => by have := Nat.add_le_mul ha (Nat.le_add_left _ b); rw [mul_succ]; omega
/-! ### div/mod -/
theorem mod_two_eq_zero_or_one (n : Nat) : n % 2 = 0 n % 2 = 1 :=
@@ -629,6 +874,203 @@ theorem div_eq_self {m n : Nat} : m / n = m ↔ m = 0 n = 1 := by
· exact hn
· exact False.elim (absurd h (Nat.ne_of_lt (Nat.div_lt_self hm hn)))
@[simp] protected theorem div_eq_zero_iff : a / b = 0 b = 0 a < b where
mp h := by
rw [ mod_add_div a b, h, Nat.mul_zero, Nat.add_zero, Decidable.or_iff_not_imp_left]
exact mod_lt _ Nat.pos_iff_ne_zero.2
mpr := by
obtain rfl | hb := Decidable.em (b = 0)
· simp
simp only [hb, false_or]
rw [ Nat.mul_right_inj hb, Nat.add_left_cancel_iff, mod_add_div]
simp +contextual [mod_eq_of_lt]
protected theorem div_ne_zero_iff : a / b 0 b 0 b a := by simp
@[simp] protected theorem div_pos_iff : 0 < a / b 0 < b b a := by
simp [Nat.pos_iff_ne_zero]
theorem lt_mul_of_div_lt (h : a / c < b) (hc : 0 < c) : a < b * c :=
Nat.lt_of_not_ge <| Nat.not_le_of_gt h (Nat.le_div_iff_mul_le hc).2
theorem mul_div_le_mul_div_assoc (a b c : Nat) : a * (b / c) a * b / c :=
if hc0 : c = 0 then by simp [hc0] else
(Nat.le_div_iff_mul_le (Nat.pos_of_ne_zero hc0)).2
(by rw [Nat.mul_assoc]; exact Nat.mul_le_mul_left _ (Nat.div_mul_le_self _ _))
protected theorem eq_mul_of_div_eq_right {a b c : Nat} (H1 : b a) (H2 : a / b = c) :
a = b * c := by
rw [ H2, Nat.mul_div_cancel' H1]
protected theorem eq_mul_of_div_eq_left {a b c : Nat} (H1 : b a) (H2 : a / b = c) : a = c * b := by
rw [Nat.mul_comm, Nat.eq_mul_of_div_eq_right H1 H2]
protected theorem mul_div_cancel_left' {a b : Nat} (Hd : a b) : a * (b / a) = b := by
rw [Nat.mul_comm, Nat.div_mul_cancel Hd]
theorem lt_div_mul_add {a b : Nat} (hb : 0 < b) : a < a / b * b + b := by
rw [ Nat.succ_mul, Nat.div_lt_iff_lt_mul hb]; exact Nat.lt_succ_self _
@[simp]
protected theorem div_left_inj {a b d : Nat} (hda : d a) (hdb : d b) :
a / d = b / d a = b := by
refine fun h ?_, congrArg fun b b / d
rw [ Nat.mul_div_cancel' hda, Nat.mul_div_cancel' hdb, h]
theorem div_le_iff_le_mul_add_pred (hb : 0 < b) : a / b c a b * c + (b - 1) := by
rw [ Nat.lt_succ_iff, div_lt_iff_lt_mul hb, succ_mul, Nat.mul_comm]
cases hb <;> exact Nat.lt_succ_iff
theorem one_le_div_iff {a b : Nat} (hb : 0 < b) : 1 a / b b a := by
rw [le_div_iff_mul_le hb, Nat.one_mul]
theorem div_lt_one_iff (hb : 0 < b) : a / b < 1 a < b := by
simp only [ Nat.not_le, one_le_div_iff hb]
protected theorem div_le_div_right {a b c : Nat} (h : a b) : a / c b / c :=
(c.eq_zero_or_pos.elim fun hc by simp [hc]) fun hc
(le_div_iff_mul_le hc).2 <| Nat.le_trans (Nat.div_mul_le_self _ _) h
theorem lt_of_div_lt_div {a b c : Nat} (h : a / c < b / c) : a < b :=
Nat.lt_of_not_le fun hab Nat.not_le_of_lt h <| Nat.div_le_div_right hab
theorem sub_mul_div (a b c : Nat) : (a - b * c) / b = a / b - c := by
obtain h | h := Nat.le_total (b * c) a
· rw [Nat.sub_mul_div_of_le _ _ _ h]
· rw [Nat.sub_eq_zero_of_le h, Nat.zero_div]
by_cases hn : b = 0
· simp only [hn, Nat.div_zero, zero_le, Nat.sub_eq_zero_of_le]
· have h2 : a / b (b * c) / b := Nat.div_le_div_right h
rw [Nat.mul_div_cancel_left _ (zero_lt_of_ne_zero hn)] at h2
rw [Nat.sub_eq_zero_of_le h2]
theorem mul_sub_div_of_dvd {b c : Nat} (hc : c 0) (hcb : c b) (a : Nat) : (c * a - b) / c = a - b / c := by
obtain _, hx := hcb
simp only [hx, Nat.mul_sub_left_distrib, Nat.mul_div_right, zero_lt_of_ne_zero hc]
theorem mul_add_mul_div_of_dvd (hb : b 0) (hd : d 0) (hba : b a) (hdc : d c) :
(a * d + b * c) / (b * d) = a / b + c / d := by
obtain n, hn := hba
obtain _, hm := hdc
rw [hn, hm, Nat.mul_assoc b n d, Nat.mul_comm n d, Nat.mul_assoc, Nat.mul_assoc,
Nat.mul_add,
Nat.mul_div_right _ (zero_lt_of_ne_zero hb),
Nat.mul_div_right _ (zero_lt_of_ne_zero hd),
Nat.mul_div_right _ (zero_lt_of_ne_zero <| Nat.mul_ne_zero hb hd)]
theorem mul_sub_mul_div_of_dvd (hb : b 0) (hd : d 0) (hba : b a) (hdc : d c) :
(a * d - b * c) / (b * d) = a / b - c / d := by
obtain n, hn := hba
obtain m, hm := hdc
rw [hn, hm]
rw [Nat.mul_assoc,Nat.mul_comm n d, Nat.mul_assoc, Nat.mul_assoc, Nat.mul_sub_left_distrib,
Nat.mul_div_right _ (zero_lt_of_ne_zero hb), Nat.mul_div_right _ (zero_lt_of_ne_zero hd),
Nat.mul_div_right _ (zero_lt_of_ne_zero <| Nat.mul_ne_zero hb hd)]
protected theorem div_mul_right_comm {a b : Nat} (hba : b a) (c : Nat) : a / b * c = a * c / b := by
rw [Nat.mul_comm, Nat.mul_div_assoc _ hba, Nat.mul_comm]
protected theorem mul_div_right_comm {a b : Nat} (hba : b a) (c : Nat) : a * c / b = a / b * c :=
(Nat.div_mul_right_comm hba _).symm
protected theorem div_eq_iff_eq_mul_right {a b c : Nat} (H : 0 < b) (H' : b a) :
a / b = c a = b * c :=
Nat.eq_mul_of_div_eq_right H', Nat.div_eq_of_eq_mul_right H
protected theorem div_eq_iff_eq_mul_left {a b c : Nat} (H : 0 < b) (H' : b a) :
a / b = c a = c * b := by
rw [Nat.mul_comm]; exact Nat.div_eq_iff_eq_mul_right H H'
theorem eq_div_iff_mul_eq_left (hc : c 0) (hcb : c b) : a = b / c b = a * c := by
rw [eq_comm, Nat.div_eq_iff_eq_mul_left (zero_lt_of_ne_zero hc) hcb]
theorem div_mul_div_comm {a b c d : Nat} : b a d c (a / b) * (c / d) = (a * c) / (b * d) := by
rintro x, rfl y, rfl
obtain rfl | hb := b.eq_zero_or_pos
· simp
obtain rfl | hd := d.eq_zero_or_pos
· simp
rw [Nat.mul_div_cancel_left _ hb, Nat.mul_div_cancel_left _ hd, Nat.mul_assoc b,
Nat.mul_left_comm x, Nat.mul_assoc b, Nat.mul_div_cancel_left _ (Nat.mul_pos hb hd)]
protected theorem mul_div_mul_comm {a b c d : Nat} (hba : b a) (hdc : d c) : a * c / (b * d) = a / b * (c / d) :=
(div_mul_div_comm hba hdc).symm
theorem eq_zero_of_le_div (hn : 2 n) (h : m m / n) : m = 0 :=
eq_zero_of_mul_le hn <| by
rw [Nat.mul_comm]; exact (Nat.le_div_iff_mul_le (Nat.lt_of_lt_of_le (by decide) hn)).1 h
theorem div_mul_div_le_div (a b c : Nat) : a / c * b / a b / c := by
obtain rfl | ha := Nat.eq_zero_or_pos a
· simp
· calc
a / c * b / a b * a / c / a :=
Nat.div_le_div_right (by rw [Nat.mul_comm]; exact mul_div_le_mul_div_assoc _ _ _)
_ = b / c := by rw [Nat.div_div_eq_div_mul, Nat.mul_comm b, Nat.mul_comm c,
Nat.mul_div_mul_left _ _ ha]
theorem eq_zero_of_le_div_two (h : n n / 2) : n = 0 := eq_zero_of_le_div (Nat.le_refl _) h
theorem le_div_two_of_div_two_lt_sub (h : a / 2 < a - b) : b a / 2 := by
omega
theorem div_two_le_of_sub_le_div_two (h : a - b a / 2) : a / 2 b := by
omega
protected theorem div_le_div_of_mul_le_mul (hd : d 0) (hdc : d c) (h : a * d c * b) :
a / b c / d :=
Nat.div_le_of_le_mul <| by
rwa [ Nat.mul_div_assoc _ hdc, Nat.le_div_iff_mul_le (Nat.pos_iff_ne_zero.2 hd), b.mul_comm]
theorem div_eq_sub_mod_div {m n : Nat} : m / n = (m - m % n) / n := by
obtain rfl | hn := n.eq_zero_or_pos
· rw [Nat.div_zero, Nat.div_zero]
· have : m - m % n = n * (m / n) := by
rw [Nat.sub_eq_iff_eq_add (Nat.mod_le _ _), Nat.add_comm, mod_add_div]
rw [this, mul_div_right _ hn]
protected theorem eq_div_of_mul_eq_left (hc : c 0) (h : a * c = b) : a = b / c := by
rw [ h, Nat.mul_div_cancel _ (Nat.pos_iff_ne_zero.2 hc)]
protected theorem eq_div_of_mul_eq_right (hc : c 0) (h : c * a = b) : a = b / c := by
rw [ h, Nat.mul_div_cancel_left _ (Nat.pos_iff_ne_zero.2 hc)]
protected theorem mul_le_of_le_div (k x y : Nat) (h : x y / k) : x * k y := by
if hk : k = 0 then
rw [hk, Nat.mul_zero]; exact zero_le _
else
rwa [ le_div_iff_mul_le (Nat.pos_iff_ne_zero.2 hk)]
theorem div_le_iff_le_mul_of_dvd (hb : b 0) (hba : b a) : a / b c a c * b := by
obtain _, hx := hba
simp only [hx]
rw [Nat.mul_div_right _ (zero_lt_of_ne_zero hb), Nat.mul_comm]
exact mul_le_mul_right b, fun h Nat.le_of_mul_le_mul_right h (zero_lt_of_ne_zero hb)
protected theorem div_lt_div_right (ha : a 0) : a b a c (b / a < c / a b < c) := by
rintro d, rfl e, rfl; simp [Nat.mul_div_cancel, Nat.pos_iff_ne_zero.2 ha]
protected theorem div_lt_div_left (ha : a 0) (hba : b a) (hca : c a) :
a / b < a / c c < b := by
obtain d, hd := hba
obtain e, he := hca
rw [Nat.div_eq_of_eq_mul_right _ hd, Nat.div_eq_of_eq_mul_right _ he,
Nat.lt_iff_lt_of_mul_eq_mul ha hd he] <;>
rw [Nat.pos_iff_ne_zero] <;> rintro rfl <;> simp at * <;> contradiction
theorem lt_div_iff_mul_lt_of_dvd (hc : c 0) (hcb : c b) : a < b / c a * c < b := by
simp [ Nat.div_lt_div_right _ _ hcb, hc, Nat.pos_iff_ne_zero, Nat.dvd_mul_left]
protected theorem div_mul_div_le (a b c d : Nat) :
(a / b) * (c / d) (a * c) / (b * d) := by
if hb : b = 0 then simp [hb] else
if hd : d = 0 then simp [hd] else
have hbd : b * d 0 := Nat.mul_ne_zero hb hd
rw [le_div_iff_mul_le (Nat.pos_of_ne_zero hbd)]
refine Nat.le_trans (m := ((a / b) * b) * ((c / d) * d)) ?_ ?_
· apply Nat.le_of_eq; simp only [Nat.mul_assoc, Nat.mul_left_comm]
· apply Nat.mul_le_mul <;> apply div_mul_le_self
/-! ### pow -/
theorem pow_succ' {m n : Nat} : m ^ n.succ = m * m ^ n := by
@@ -802,6 +1244,74 @@ theorem le_pow {a b : Nat} (h : 0 < b) : a ≤ a ^ b := by
rw [(show b = b - 1 + 1 by omega), Nat.pow_succ]
exact Nat.le_mul_of_pos_left _ (Nat.pow_pos ha)
protected theorem pow_lt_pow_right (ha : 1 < a) (h : m < n) : a ^ m < a ^ n :=
(Nat.pow_lt_pow_iff_right ha).2 h
protected theorem pow_le_pow_iff_left {a b n : Nat} (hn : n 0) : a ^ n b ^ n a b where
mp := by simpa only [ Nat.not_le, Decidable.not_imp_not] using (Nat.pow_lt_pow_left · hn)
mpr h := Nat.pow_le_pow_left h _
protected theorem pow_lt_pow_iff_left {a b n : Nat} (hn : n 0) : a ^ n < b ^ n a < b := by
simp only [ Nat.not_le, Nat.pow_le_pow_iff_left hn]
@[simp high] protected theorem pow_eq_zero {a : Nat} : {n : Nat}, a ^ n = 0 a = 0 n 0
| 0 => by simp
| n + 1 => by rw [Nat.pow_succ, mul_eq_zero, Nat.pow_eq_zero]; omega
theorem le_self_pow (hn : n 0) : a : Nat, a a ^ n
| 0 => zero_le _
| a + 1 => by simpa using Nat.pow_le_pow_right a.succ_pos (Nat.one_le_iff_ne_zero.2 hn)
theorem one_le_pow (n m : Nat) (h : 0 < m) : 1 m ^ n := by simpa using Nat.pow_le_pow_left h n
theorem one_lt_pow (hn : n 0) (ha : 1 < a) : 1 < a ^ n := by simpa using Nat.pow_lt_pow_left ha hn
theorem two_pow_succ (n : Nat) : 2 ^ (n + 1) = 2 ^ n + 2 ^ n := by simp [Nat.pow_succ, Nat.mul_two]
theorem one_lt_pow' (n m : Nat) : 1 < (m + 2) ^ (n + 1) :=
one_lt_pow n.succ_ne_zero (Nat.lt_of_sub_eq_succ rfl)
@[simp] theorem one_lt_pow_iff {n : Nat} (hn : n 0) : {a}, 1 < a ^ n 1 < a
| 0 => by simp [Nat.zero_pow (Nat.pos_of_ne_zero hn)]
| 1 => by simp
| a + 2 => by simp [one_lt_pow hn]
theorem one_lt_two_pow' (n : Nat) : 1 < 2 ^ (n + 1) := one_lt_pow n.succ_ne_zero (by decide)
theorem mul_lt_mul_pow_succ (ha : 0 < a) (hb : 1 < b) : n * b < a * b ^ (n + 1) := by
rw [Nat.pow_succ, Nat.mul_assoc, Nat.mul_lt_mul_right (Nat.lt_trans Nat.zero_lt_one hb)]
exact Nat.lt_of_le_of_lt (Nat.le_mul_of_pos_left _ ha)
((Nat.mul_lt_mul_left ha).2 <| Nat.lt_pow_self hb)
theorem pow_two_sub_pow_two (a b : Nat) : a ^ 2 - b ^ 2 = (a + b) * (a - b) := by
simpa [Nat.pow_succ] using Nat.mul_self_sub_mul_self_eq a b
protected theorem pow_pos_iff : 0 < a ^ n 0 < a n = 0 := by
simp [Nat.pos_iff_ne_zero, Decidable.imp_iff_not_or]
theorem pow_self_pos : 0 < n ^ n := by simp [Nat.pow_pos_iff, n.eq_zero_or_pos.symm]
theorem pow_self_mul_pow_self_le : m ^ m * n ^ n (m + n) ^ (m + n) := by
rw [Nat.pow_add]
exact Nat.mul_le_mul (Nat.pow_le_pow_left (le_add_right ..) _)
(Nat.pow_le_pow_left (le_add_left ..) _)
protected theorem pow_right_inj (ha : 1 < a) : a ^ m = a ^ n m = n := by
simp [Nat.le_antisymm_iff, Nat.pow_le_pow_iff_right ha]
@[simp] protected theorem pow_eq_one : a ^ n = 1 a = 1 n = 0 := by
obtain rfl | hn := Decidable.em (n = 0)
· simp
· simpa [hn] using Nat.pow_left_inj hn (b := 1)
/-- For `a > 1`, `a ^ b = a` iff `b = 1`. -/
theorem pow_eq_self_iff {a b : Nat} (ha : 1 < a) : a ^ b = a b = 1 := by
rw [ Nat.pow_right_inj (m := b) ha, Nat.pow_one]
@[simp] protected theorem pow_le_one_iff (hn : n 0) : a ^ n 1 a 1 := by
rw [ Nat.not_lt, one_lt_pow_iff hn, Nat.not_lt]
/-! ### log2 -/
@[simp]
@@ -835,19 +1345,7 @@ theorem lt_log2_self : n < 2 ^ (n.log2 + 1) :=
| 0 => by simp
| n+1 => (log2_lt n.succ_ne_zero).1 (Nat.le_refl _)
/-! ### dvd -/
protected theorem eq_mul_of_div_eq_right {a b c : Nat} (H1 : b a) (H2 : a / b = c) :
a = b * c := by
rw [ H2, Nat.mul_div_cancel' H1]
protected theorem div_eq_iff_eq_mul_right {a b c : Nat} (H : 0 < b) (H' : b a) :
a / b = c a = b * c :=
Nat.eq_mul_of_div_eq_right H', Nat.div_eq_of_eq_mul_right H
protected theorem div_eq_iff_eq_mul_left {a b c : Nat} (H : 0 < b) (H' : b a) :
a / b = c a = c * b := by
rw [Nat.mul_comm]; exact Nat.div_eq_iff_eq_mul_right H H'
/-! ### mod, dvd -/
theorem pow_dvd_pow_iff_pow_le_pow {k l : Nat} :
{x : Nat}, 0 < x (x ^ k x ^ l x ^ k x ^ l)
@@ -903,48 +1401,151 @@ protected theorem div_pow {a b c : Nat} (h : a b) : (b / a) ^ c = b ^ c / a
rw [Nat.pow_succ (b / a), Nat.pow_succ a, Nat.mul_comm _ a, Nat.mul_assoc, Nat.mul_assoc _ a,
Nat.div_mul_cancel h, Nat.mul_comm b, Nat.mul_assoc, ih, Nat.pow_succ]
/-! ### shiftLeft and shiftRight -/
@[simp] theorem mod_two_not_eq_one : ¬n % 2 = 1 n % 2 = 0 := by
cases mod_two_eq_zero_or_one n <;> simp [*]
@[simp] theorem shiftLeft_zero : n <<< 0 = n := rfl
@[simp] theorem mod_two_not_eq_zero : ¬n % 2 = 0 n % 2 = 1 := by
cases mod_two_eq_zero_or_one n <;> simp [*]
/-- Shiftleft on successor with multiple moved inside. -/
theorem shiftLeft_succ_inside (m n : Nat) : m <<< (n+1) = (2*m) <<< n := rfl
theorem mod_two_ne_one : n % 2 1 n % 2 = 0 := mod_two_not_eq_one
theorem mod_two_ne_zero : n % 2 0 n % 2 = 1 := mod_two_not_eq_zero
/-- Shiftleft on successor with multiple moved to outside. -/
theorem shiftLeft_succ : (m n), m <<< (n + 1) = 2 * (m <<< n)
| _, 0 => rfl
| _, k + 1 => by
rw [shiftLeft_succ_inside _ (k+1)]
rw [shiftLeft_succ _ k, shiftLeft_succ_inside]
/-- Variant of `Nat.lt_div_iff_mul_lt` that assumes `d n`. -/
protected theorem lt_div_iff_mul_lt' (hdn : d n) (a : Nat) : a < n / d d * a < n := by
obtain rfl | hd := d.eq_zero_or_pos
· simp [Nat.zero_dvd.1 hdn]
· rw [ Nat.mul_lt_mul_left hd, Nat.eq_mul_of_div_eq_right hdn rfl]
/-- Shiftright on successor with division moved inside. -/
theorem shiftRight_succ_inside : m n, m >>> (n+1) = (m/2) >>> n
| _, 0 => rfl
| _, k + 1 => by
rw [shiftRight_succ _ (k+1)]
rw [shiftRight_succ_inside _ k, shiftRight_succ]
theorem mul_div_eq_iff_dvd {n d : Nat} : d * (n / d) = n d n :=
calc
d * (n / d) = n d * (n / d) = d * (n / d) + (n % d) := by rw [div_add_mod]
_ d n := by rw [eq_comm, Nat.add_eq_left, dvd_iff_mod_eq_zero]
@[simp] theorem zero_shiftLeft : n, 0 <<< n = 0
| 0 => by simp [shiftLeft]
| n + 1 => by simp [shiftLeft, zero_shiftLeft n, shiftLeft_succ]
theorem mul_div_lt_iff_not_dvd {n d : Nat} : d * (n / d) < n ¬ d n := by
simp [Nat.lt_iff_le_and_ne, mul_div_eq_iff_dvd, mul_div_le]
@[simp] theorem zero_shiftRight : n, 0 >>> n = 0
| 0 => by simp [shiftRight]
| n + 1 => by simp [shiftRight, zero_shiftRight n, shiftRight_succ]
theorem div_eq_iff_eq_of_dvd_dvd (hn : n 0) (ha : a n) (hb : b n) : n / a = n / b a = b := by
constructor <;> intro h
· rw [ Nat.mul_right_inj hn]
apply Nat.eq_mul_of_div_eq_left (Nat.dvd_trans hb (Nat.dvd_mul_right _ _))
rw [eq_comm, Nat.mul_comm, Nat.mul_div_assoc _ hb]
exact Nat.eq_mul_of_div_eq_right ha h
· rw [h]
theorem shiftLeft_add (m n : Nat) : k, m <<< (n + k) = (m <<< n) <<< k
| 0 => rfl
| k + 1 => by simp [ Nat.add_assoc, shiftLeft_add _ _ k, shiftLeft_succ]
theorem le_iff_ne_zero_of_dvd (ha : a 0) (hab : a b) : a b b 0 where
mp := by rw [ Nat.pos_iff_ne_zero] at ha ; exact Nat.lt_of_lt_of_le ha
mpr hb := Nat.le_of_dvd (Nat.pos_iff_ne_zero.2 hb) hab
@[simp] theorem shiftLeft_shiftRight (x n : Nat) : x <<< n >>> n = x := by
rw [Nat.shiftLeft_eq, Nat.shiftRight_eq_div_pow, Nat.mul_div_cancel _ (Nat.two_pow_pos _)]
theorem div_ne_zero_iff_of_dvd (hba : b a) : a / b 0 a 0 b 0 := by
obtain rfl | hb := Decidable.em (b = 0) <;>
simp [Nat.div_ne_zero_iff, Nat.le_iff_ne_zero_of_dvd, *]
theorem pow_mod (a b n : Nat) : a ^ b % n = (a % n) ^ b % n := by
induction b with
| zero => rfl
| succ b ih => simp [Nat.pow_succ, Nat.mul_mod, ih]
theorem lt_of_pow_dvd_right (hb : b 0) (ha : 2 a) (h : a ^ n b) : n < b := by
rw [ Nat.pow_lt_pow_iff_right (succ_le_iff.1 ha)]
exact Nat.lt_of_le_of_lt (le_of_dvd (Nat.pos_iff_ne_zero.2 hb) h) (Nat.lt_pow_self ha)
theorem div_dvd_of_dvd {n m : Nat} (h : n m) : m / n m := n, (Nat.div_mul_cancel h).symm
protected theorem div_div_self (h : n m) (hm : m 0) : m / (m / n) = n := by
rcases h with _, rfl
rw [Nat.mul_ne_zero_iff] at hm
rw [mul_div_right _ (Nat.pos_of_ne_zero hm.1), mul_div_left _ (Nat.pos_of_ne_zero hm.2)]
theorem not_dvd_of_pos_of_lt (h1 : 0 < n) (h2 : n < m) : ¬m n := by
rintro k, rfl
rcases Nat.eq_zero_or_pos k with (rfl | hk)
· exact Nat.lt_irrefl 0 h1
· exact Nat.not_lt.2 (Nat.le_mul_of_pos_right _ hk) h2
theorem eq_of_dvd_of_lt_two_mul (ha : a 0) (hdvd : b a) (hlt : a < 2 * b) : a = b := by
obtain _ | _ | c, rfl := hdvd
· simp at ha
· exact Nat.mul_one _
· rw [Nat.mul_comm] at hlt
cases Nat.not_le_of_lt hlt (Nat.mul_le_mul_right _ (by omega))
theorem mod_eq_iff_lt (hn : n 0) : m % n = m m < n :=
fun h by rw [ h]; exact mod_lt _ <| Nat.pos_iff_ne_zero.2 hn, mod_eq_of_lt
@[simp]
theorem mod_succ_eq_iff_lt {m n : Nat} : m % n.succ = m m < n.succ :=
mod_eq_iff_lt (succ_ne_zero _)
@[simp] theorem mod_succ (n : Nat) : n % n.succ = n := mod_eq_of_lt n.lt_succ_self
theorem mod_add_div' (a b : Nat) : a % b + a / b * b = a := by rw [Nat.mul_comm]; exact mod_add_div _ _
theorem div_add_mod' (a b : Nat) : a / b * b + a % b = a := by rw [Nat.mul_comm]; exact div_add_mod _ _
/-- See also `Nat.divModEquiv` for a similar statement as an `Equiv`. -/
protected theorem div_mod_unique (h : 0 < b) :
a / b = d a % b = c c + b * d = a c < b :=
fun e₁, e₂ e₁ e₂ mod_add_div _ _, mod_lt _ h, fun h₁, h₂ h₁ by
rw [add_mul_div_left _ _ h, add_mul_mod_self_left]; simp [div_eq_of_lt, mod_eq_of_lt, h₂]
/-- If `m` and `n` are equal mod `k`, `m - n` is zero mod `k`. -/
theorem sub_mod_eq_zero_of_mod_eq (h : m % k = n % k) : (m - n) % k = 0 := by
rw [ Nat.mod_add_div m k, Nat.mod_add_div n k, h, Nat.sub_sub,
Nat.add_sub_cancel_left, k.mul_sub, Nat.mul_mod_right]
@[simp] theorem one_mod (n : Nat) : 1 % (n + 2) = 1 :=
Nat.mod_eq_of_lt (Nat.add_lt_add_right n.succ_pos 1)
theorem one_mod_eq_one : {n : Nat}, 1 % n = 1 n 1
| 0 | 1 | n + 2 => by simp; try omega
theorem dvd_sub_mod (k : Nat) : n k - k % n :=
k / n, Nat.sub_eq_of_eq_add (Nat.div_add_mod k n).symm
theorem add_mod_eq_ite {m n : Nat} :
(m + n) % k = if k m % k + n % k then m % k + n % k - k else m % k + n % k := by
cases k with
| zero => simp
| succ k =>
rw [Nat.add_mod]
by_cases h : k + 1 m % (k + 1) + n % (k + 1)
· rw [if_pos h, Nat.mod_eq_sub_mod h, Nat.mod_eq_of_lt]
exact (Nat.sub_lt_iff_lt_add h).mpr (Nat.add_lt_add (m.mod_lt (zero_lt_succ _))
(n.mod_lt (zero_lt_succ _)))
· rw [if_neg h]
exact Nat.mod_eq_of_lt (Nat.lt_of_not_ge h)
-- TODO: Replace `Nat.dvd_add_iff_left`
protected theorem dvd_add_left {a b c : Nat} (h : a c) : a b + c a b := (Nat.dvd_add_iff_left h).symm
protected theorem dvd_add_right {a b c : Nat} (h : a b) : a b + c a c := (Nat.dvd_add_iff_right h).symm
theorem add_mod_eq_add_mod_right (c : Nat) (H : a % d = b % d) : (a + c) % d = (b + c) % d := by
rw [ mod_add_mod, mod_add_mod b, H]
theorem add_mod_eq_add_mod_left (c : Nat) (H : a % d = b % d) : (c + a) % d = (c + b) % d := by
rw [Nat.add_comm, add_mod_eq_add_mod_right _ H, Nat.add_comm]
theorem mul_dvd_of_dvd_div {a b c : Nat} (hcb : c b) (h : a b / c) : c * a b :=
have d, hd := h
d, by simpa [Nat.mul_comm, Nat.mul_left_comm] using Nat.eq_mul_of_div_eq_left hcb hd
theorem eq_of_dvd_of_div_eq_one (hab : a b) (h : b / a = 1) : a = b := by
rw [ Nat.div_mul_cancel hab, h, Nat.one_mul]
theorem eq_zero_of_dvd_of_div_eq_zero (hab : a b) (h : b / a = 0) : b = 0 := by
rw [ Nat.div_mul_cancel hab, h, Nat.zero_mul]
theorem lt_mul_div_succ (a : Nat) (hb : 0 < b) : a < b * (a / b + 1) := by
rw [Nat.mul_comm, Nat.div_lt_iff_lt_mul hb]
exact lt_succ_self _
theorem mul_add_div {m : Nat} (m_pos : m > 0) (x y : Nat) : (m * x + y) / m = x + y / m := by
match x with
| 0 => simp
| x + 1 =>
rw [Nat.mul_succ, Nat.add_assoc _ m, mul_add_div m_pos x (m+y), div_eq]
simp +arith [m_pos]; rw [Nat.add_comm, Nat.add_sub_cancel]
simp +arith [m_pos]
theorem mul_add_mod (m x y : Nat) : (m * x + y) % m = y % m := by
match x with
@@ -952,6 +1553,13 @@ theorem mul_add_mod (m x y : Nat) : (m * x + y) % m = y % m := by
| x + 1 =>
simp [Nat.mul_succ, Nat.add_assoc _ m, mul_add_mod _ x]
-- TODO: make naming and statements consistent across all variations of `mod`, `gcd` etc. across
-- `Nat` and `Int`.
theorem mul_add_mod' (a b c : Nat) : (a * b + c) % b = c % b := by rw [Nat.mul_comm, Nat.mul_add_mod]
theorem mul_add_mod_of_lt {a b c : Nat} (h : c < b) : (a * b + c) % b = c := by
rw [Nat.mul_add_mod', Nat.mod_eq_of_lt h]
@[simp] theorem mod_div_self (m n : Nat) : m % n / n = 0 := by
cases n
· exact (m % 0).div_zero
@@ -1009,6 +1617,113 @@ theorem succ_mod_succ_eq_zero_iff {a b : Nat} :
subst h
simp [Nat.add_mul]
theorem dvd_iff_div_mul_eq (n d : Nat) : d n n / d * d = n :=
fun h => Nat.div_mul_cancel h, fun h => by rw [ h]; exact Nat.dvd_mul_left _ _
theorem dvd_iff_le_div_mul (n d : Nat) : d n n n / d * d :=
((dvd_iff_div_mul_eq _ _).trans Nat.le_antisymm_iff).trans (and_iff_right (div_mul_le_self n d))
theorem dvd_iff_dvd_dvd (n d : Nat) : d n k : Nat, k d k n :=
fun h _ hkd => Nat.dvd_trans hkd h, fun h => h _ (Nat.dvd_refl _)
theorem dvd_div_of_mul_dvd {a b c : Nat} (h : a * b c) : b c / a :=
if ha : a = 0 then by simp [ha]
else
have ha : 0 < a := Nat.pos_of_ne_zero ha
have h1 : d, c = a * b * d := h
let d, hd := h1
have h2 : c / a = b * d := Nat.div_eq_of_eq_mul_right ha (by simpa [Nat.mul_assoc] using hd)
show d, c / a = b * d from d, h2
@[simp] theorem dvd_div_iff_mul_dvd {a b c : Nat} (hbc : c b) : a b / c c * a b :=
fun h => mul_dvd_of_dvd_div hbc h, fun h => dvd_div_of_mul_dvd h
theorem dvd_mul_of_div_dvd {a b c : Nat} (h : b a) (hdiv : a / b c) : a b * c := by
obtain e, rfl := hdiv
rw [ Nat.mul_assoc, Nat.mul_comm _ (a / b), Nat.div_mul_cancel h]
exact Nat.dvd_mul_right a e
@[simp] theorem div_div_div_eq_div {a b c : Nat} (dvd : b a) (dvd2 : a c) : c / (a / b) / b = c / a :=
match a, b, c with
| 0, _, _ => by simp
| a + 1, 0, _ => by simp at dvd
| a + 1, c + 1, _ => by
have a_split : a + 1 0 := succ_ne_zero a
have c_split : c + 1 0 := succ_ne_zero c
rcases dvd2 with k, rfl
rcases dvd with k2, pr
have k2_nonzero : k2 0 := fun k2_zero => by simp [k2_zero] at pr
rw [Nat.mul_div_cancel_left k (Nat.pos_of_ne_zero a_split), pr,
Nat.mul_div_cancel_left k2 (Nat.pos_of_ne_zero c_split), Nat.mul_comm ((c + 1) * k2) k,
Nat.mul_assoc k (c + 1) k2, Nat.mul_div_cancel _ (Nat.pos_of_ne_zero k2_nonzero),
Nat.mul_div_cancel _ (Nat.pos_of_ne_zero c_split)]
/-- If a small natural number is divisible by a larger natural number,
the small number is zero. -/
theorem eq_zero_of_dvd_of_lt (w : a b) (h : b < a) : b = 0 :=
Nat.eq_zero_of_dvd_of_div_eq_zero w (by simp [h])
theorem le_of_lt_add_of_dvd {a b : Nat} (h : a < b + n) : n a n b a b := by
rintro a, rfl b, rfl
rw [ mul_succ] at h
exact Nat.mul_le_mul_left _ (Nat.lt_succ_iff.1 <| Nat.lt_of_mul_lt_mul_left h)
/-- `m` is not divisible by `n` if it is between `n * k` and `n * (k + 1)` for some `k`. -/
theorem not_dvd_of_lt_of_lt_mul_succ (h1 : n * k < m) (h2 : m < n * (k + 1)) : ¬n m := by
rintro d, rfl
have := Nat.lt_of_mul_lt_mul_left h1
have := Nat.lt_of_mul_lt_mul_left h2
omega
/-- `n` is not divisible by `a` iff it is between `a * k` and `a * (k + 1)` for some `k`. -/
theorem not_dvd_iff_lt_mul_succ (n : Nat) {a : Nat} (ha : 0 < a) :
¬a n ( k : Nat, a * k < n n < a * (k + 1)) := by
refine Iff.symm
fun k, hk1, hk2 => not_dvd_of_lt_of_lt_mul_succ hk1 hk2, fun han =>
n / a, Nat.lt_of_le_of_ne (mul_div_le n a) ?_, lt_mul_div_succ _ ha
exact mt (n / a, Eq.symm ·) han
theorem div_lt_div_of_lt_of_dvd {a b d : Nat} (hdb : d b) (h : a < b) : a / d < b / d := by
rw [Nat.lt_div_iff_mul_lt' hdb]
exact Nat.lt_of_le_of_lt (mul_div_le a d) h
/-! ### shiftLeft and shiftRight -/
@[simp] theorem shiftLeft_zero : n <<< 0 = n := rfl
/-- Shiftleft on successor with multiple moved inside. -/
theorem shiftLeft_succ_inside (m n : Nat) : m <<< (n+1) = (2*m) <<< n := rfl
/-- Shiftleft on successor with multiple moved to outside. -/
theorem shiftLeft_succ : (m n), m <<< (n + 1) = 2 * (m <<< n)
| _, 0 => rfl
| _, k + 1 => by
rw [shiftLeft_succ_inside _ (k+1)]
rw [shiftLeft_succ _ k, shiftLeft_succ_inside]
/-- Shiftright on successor with division moved inside. -/
theorem shiftRight_succ_inside : m n, m >>> (n+1) = (m/2) >>> n
| _, 0 => rfl
| _, k + 1 => by
rw [shiftRight_succ _ (k+1)]
rw [shiftRight_succ_inside _ k, shiftRight_succ]
@[simp] theorem zero_shiftLeft : n, 0 <<< n = 0
| 0 => by simp [shiftLeft]
| n + 1 => by simp [shiftLeft, zero_shiftLeft n, shiftLeft_succ]
@[simp] theorem zero_shiftRight : n, 0 >>> n = 0
| 0 => by simp [shiftRight]
| n + 1 => by simp [shiftRight, zero_shiftRight n, shiftRight_succ]
theorem shiftLeft_add (m n : Nat) : k, m <<< (n + k) = (m <<< n) <<< k
| 0 => rfl
| k + 1 => by simp [ Nat.add_assoc, shiftLeft_add _ _ k, shiftLeft_succ]
@[simp] theorem shiftLeft_shiftRight (x n : Nat) : x <<< n >>> n = x := by
rw [Nat.shiftLeft_eq, Nat.shiftRight_eq_div_pow, Nat.mul_div_cancel _ (Nat.two_pow_pos _)]
/-! ### Decidability of predicates -/
instance decidableBallLT :

View File

@@ -146,7 +146,7 @@ instance : LawfulBEq PolyCnstr where
rfl {a} := by
cases a; rename_i eq lhs rhs
show (eq == eq && (lhs == lhs && rhs == rhs)) = true
simp [LawfulBEq.rfl]
simp
structure ExprCnstr where
eq : Bool

View File

@@ -52,8 +52,8 @@ protected theorem min_le_right (a b : Nat) : min a b ≤ b := by
protected theorem min_le_left (a b : Nat) : min a b a :=
Nat.min_comm .. Nat.min_le_right ..
protected theorem min_eq_left {a b : Nat} (h : a b) : min a b = a := if_pos h
protected theorem min_eq_right {a b : Nat} (h : b a) : min a b = b :=
@[simp] protected theorem min_eq_left {a b : Nat} (h : a b) : min a b = a := if_pos h
@[simp] protected theorem min_eq_right {a b : Nat} (h : b a) : min a b = b :=
Nat.min_comm .. Nat.min_eq_left h
protected theorem le_min_of_le_of_le {a b c : Nat} : a b a c a min b c := by
@@ -111,9 +111,9 @@ protected theorem le_max_left (a b : Nat) : a ≤ max a b := by
protected theorem le_max_right (a b : Nat) : b max a b :=
Nat.max_comm .. Nat.le_max_left ..
protected theorem max_eq_right {a b : Nat} (h : a b) : max a b = b := if_pos h
@[simp] protected theorem max_eq_right {a b : Nat} (h : a b) : max a b = b := if_pos h
protected theorem max_eq_left {a b : Nat} (h : b a) : max a b = a :=
@[simp] protected theorem max_eq_left {a b : Nat} (h : b a) : max a b = a :=
Nat.max_comm .. Nat.max_eq_right h
protected theorem max_le_of_le_of_le {a b c : Nat} : a c b c max a b c := by

View File

@@ -262,7 +262,7 @@ and simplifies these to the function directly taking the value.
@[simp] theorem bind_subtype {p : α Prop} {o : Option { x // p x }}
{f : { x // p x } Option β} {g : α Option β} (hf : x h, f x, h = g x) :
(o.bind f) = o.unattach.bind g := by
o.bind f = o.unattach.bind g := by
cases o <;> simp [hf]
@[simp] theorem unattach_filter {p : α Prop} {o : Option { x // p x }}

View File

@@ -386,11 +386,13 @@ This is the monadic analogue of `Option.getD`.
| some a => pure a
| none => y
instance (α) [BEq α] [LawfulBEq α] : LawfulBEq (Option α) where
instance (α) [BEq α] [ReflBEq α] : ReflBEq (Option α) where
rfl {x} :=
match x with
| some _ => LawfulBEq.rfl (α := α)
| some _ => BEq.rfl (α := α)
| none => rfl
instance (α) [BEq α] [LawfulBEq α] : LawfulBEq (Option α) where
eq_of_beq {x y h} := by
match x, y with
| some x, some y => rw [LawfulBEq.eq_of_beq (α := α) h]

View File

@@ -694,27 +694,19 @@ variable [BEq α]
simpa only [some_beq_some]
simp
· intro h
constructor
· rintro (_ | a) <;> simp
infer_instance
@[simp] theorem lawfulBEq_iff : LawfulBEq (Option α) LawfulBEq α := by
constructor
· intro h
have : ReflBEq α := reflBEq_iff.mp inferInstance
constructor
· intro a b h
apply Option.some.inj
apply eq_of_beq
simpa
· intro a
suffices (some a == some a) = true by
simpa only [some_beq_some]
simp
intro a b h
apply Option.some.inj
apply eq_of_beq
simpa
· intro h
constructor
· intro a b h
simpa using h
· intro a
simp
infer_instance
end beq

View File

@@ -9,11 +9,13 @@ import Init.NotationExtra
namespace Prod
instance [BEq α] [BEq β] [ReflBEq α] [ReflBEq β] : ReflBEq (α × β) where
rfl {a} := by cases a; simp [BEq.beq]
instance [BEq α] [BEq β] [LawfulBEq α] [LawfulBEq β] : LawfulBEq (α × β) where
eq_of_beq {a b} (h : a.1 == b.1 && a.2 == b.2) := by
cases a; cases b
refine congr (congrArg _ (eq_of_beq ?_)) (eq_of_beq ?_) <;> simp_all
rfl {a} := by cases a; simp [BEq.beq, LawfulBEq.rfl]
@[simp]
protected theorem «forall» {p : α × β Prop} : ( x, p x) a b, p (a, b) :=

View File

@@ -26,11 +26,11 @@ See `RArray.ofFn` and `RArray.ofArray` in module `Lean.Data.RArray` for function
It is not universe-polymorphic. ; smaller proof objects and no complication with the `ToExpr` type
class.
-/
inductive RArray (α : Type) : Type where
inductive RArray (α : Type u) : Type u where
| leaf : α RArray α
| branch : Nat RArray α RArray α RArray α
variable {α : Type}
variable {α : Type u}
/-- The crucial operation, written with very little abstractional overhead -/
noncomputable def RArray.get (a : RArray α) (n : Nat) : α :=

View File

@@ -183,18 +183,18 @@ theorem ISize.neg_ofNat {n : Nat} : -ofNat n = ofInt (-n) := by
rw [ neg_ofInt, ofInt_eq_ofNat]
theorem Int8.toNatClampNeg_ofNat_of_lt {n : Nat} (h : n < 2 ^ 7) : toNatClampNeg (ofNat n) = n := by
rw [toNatClampNeg, ofInt_eq_ofNat, toInt_ofInt_of_le (by omega) (by omega), Int.toNat_ofNat]
rw [toNatClampNeg, ofInt_eq_ofNat, toInt_ofInt_of_le (by omega) (by omega), Int.toNat_natCast]
theorem Int16.toNatClampNeg_ofNat_of_lt {n : Nat} (h : n < 2 ^ 15) : toNatClampNeg (ofNat n) = n := by
rw [toNatClampNeg, ofInt_eq_ofNat, toInt_ofInt_of_le (by omega) (by omega), Int.toNat_ofNat]
rw [toNatClampNeg, ofInt_eq_ofNat, toInt_ofInt_of_le (by omega) (by omega), Int.toNat_natCast]
theorem Int32.toNatClampNeg_ofNat_of_lt {n : Nat} (h : n < 2 ^ 31) : toNatClampNeg (ofNat n) = n := by
rw [toNatClampNeg, ofInt_eq_ofNat, toInt_ofInt_of_le (by omega) (by omega), Int.toNat_ofNat]
rw [toNatClampNeg, ofInt_eq_ofNat, toInt_ofInt_of_le (by omega) (by omega), Int.toNat_natCast]
theorem Int64.toNatClampNeg_ofNat_of_lt {n : Nat} (h : n < 2 ^ 63) : toNatClampNeg (ofNat n) = n := by
rw [toNatClampNeg, ofInt_eq_ofNat, toInt_ofInt_of_le (by omega) (by omega), Int.toNat_ofNat]
rw [toNatClampNeg, ofInt_eq_ofNat, toInt_ofInt_of_le (by omega) (by omega), Int.toNat_natCast]
theorem ISize.toNatClampNeg_ofNat_of_lt {n : Nat} (h : n < 2 ^ 31) : toNatClampNeg (ofNat n) = n := by
rw [toNatClampNeg, ofInt_eq_ofNat, toInt_ofInt_of_le (by omega) (by omega), Int.toNat_ofNat]
rw [toNatClampNeg, ofInt_eq_ofNat, toInt_ofInt_of_le (by omega) (by omega), Int.toNat_natCast]
theorem ISize.toNatClampNeg_ofNat_of_lt_two_pow_numBits {n : Nat} (h : n < 2 ^ (System.Platform.numBits - 1)) :
toNatClampNeg (ofNat n) = n := by
rw [toNatClampNeg, ofInt_eq_ofNat, toInt_ofInt_of_two_pow_numBits_le, Int.toNat_ofNat]
rw [toNatClampNeg, ofInt_eq_ofNat, toInt_ofInt_of_two_pow_numBits_le, Int.toNat_natCast]
· cases System.Platform.numBits_eq <;> simp_all <;> omega
· cases System.Platform.numBits_eq <;> simp_all <;> omega
@@ -537,41 +537,41 @@ theorem ISize.toFin_toBitVec (x : ISize) : x.toBitVec.toFin = x.toUSize.toFin :=
@[simp] theorem Int64.toBitVec_ofIntLE (x : Int) (h₁ h₂) : (Int64.ofIntLE x h₁ h₂).toBitVec = BitVec.ofInt 64 x := rfl
@[simp] theorem ISize.toBitVec_ofIntLE (x : Int) (h₁ h₂) : (ISize.ofIntLE x h₁ h₂).toBitVec = BitVec.ofInt System.Platform.numBits x := rfl
@[simp] theorem Int8.toInt_bmod (x : Int8) : x.toInt.bmod 256 = x.toInt := Int.bmod_eq_self_of_le x.le_toInt x.toInt_lt
@[simp] theorem Int16.toInt_bmod (x : Int16) : x.toInt.bmod 65536 = x.toInt := Int.bmod_eq_self_of_le x.le_toInt x.toInt_lt
@[simp] theorem Int32.toInt_bmod (x : Int32) : x.toInt.bmod 4294967296 = x.toInt := Int.bmod_eq_self_of_le x.le_toInt x.toInt_lt
@[simp] theorem Int64.toInt_bmod (x : Int64) : x.toInt.bmod 18446744073709551616 = x.toInt := Int.bmod_eq_self_of_le x.le_toInt x.toInt_lt
@[simp] theorem Int8.toInt_bmod (x : Int8) : x.toInt.bmod 256 = x.toInt := Int.bmod_eq_of_le x.le_toInt x.toInt_lt
@[simp] theorem Int16.toInt_bmod (x : Int16) : x.toInt.bmod 65536 = x.toInt := Int.bmod_eq_of_le x.le_toInt x.toInt_lt
@[simp] theorem Int32.toInt_bmod (x : Int32) : x.toInt.bmod 4294967296 = x.toInt := Int.bmod_eq_of_le x.le_toInt x.toInt_lt
@[simp] theorem Int64.toInt_bmod (x : Int64) : x.toInt.bmod 18446744073709551616 = x.toInt := Int.bmod_eq_of_le x.le_toInt x.toInt_lt
@[simp] theorem ISize.toInt_bmod_two_pow_numBits (x : ISize) : x.toInt.bmod (2 ^ System.Platform.numBits) = x.toInt := by
refine Int.bmod_eq_self_of_le ?_ ?_
refine Int.bmod_eq_of_le ?_ ?_
· have := x.two_pow_numBits_le_toInt
cases System.Platform.numBits_eq <;> simp_all
· have := x.toInt_lt_two_pow_numBits
cases System.Platform.numBits_eq <;> simp_all
@[simp] theorem Int8.toInt_bmod_65536 (x : Int8) : x.toInt.bmod 65536 = x.toInt :=
Int.bmod_eq_self_of_le (Int.le_trans (by decide) x.le_toInt) (Int.lt_of_lt_of_le x.toInt_lt (by decide))
Int.bmod_eq_of_le (Int.le_trans (by decide) x.le_toInt) (Int.lt_of_lt_of_le x.toInt_lt (by decide))
@[simp] theorem Int8.toInt_bmod_4294967296 (x : Int8) : x.toInt.bmod 4294967296 = x.toInt :=
Int.bmod_eq_self_of_le (Int.le_trans (by decide) x.le_toInt) (Int.lt_of_lt_of_le x.toInt_lt (by decide))
Int.bmod_eq_of_le (Int.le_trans (by decide) x.le_toInt) (Int.lt_of_lt_of_le x.toInt_lt (by decide))
@[simp] theorem Int16.toInt_bmod_4294967296 (x : Int16) : x.toInt.bmod 4294967296 = x.toInt :=
Int.bmod_eq_self_of_le (Int.le_trans (by decide) x.le_toInt) (Int.lt_of_lt_of_le x.toInt_lt (by decide))
Int.bmod_eq_of_le (Int.le_trans (by decide) x.le_toInt) (Int.lt_of_lt_of_le x.toInt_lt (by decide))
@[simp] theorem Int8.toInt_bmod_18446744073709551616 (x : Int8) : x.toInt.bmod 18446744073709551616 = x.toInt :=
Int.bmod_eq_self_of_le (Int.le_trans (by decide) x.le_toInt) (Int.lt_of_lt_of_le x.toInt_lt (by decide))
Int.bmod_eq_of_le (Int.le_trans (by decide) x.le_toInt) (Int.lt_of_lt_of_le x.toInt_lt (by decide))
@[simp] theorem Int16.toInt_bmod_18446744073709551616 (x : Int16) : x.toInt.bmod 18446744073709551616 = x.toInt :=
Int.bmod_eq_self_of_le (Int.le_trans (by decide) x.le_toInt) (Int.lt_of_lt_of_le x.toInt_lt (by decide))
Int.bmod_eq_of_le (Int.le_trans (by decide) x.le_toInt) (Int.lt_of_lt_of_le x.toInt_lt (by decide))
@[simp] theorem Int32.toInt_bmod_18446744073709551616 (x : Int32) : x.toInt.bmod 18446744073709551616 = x.toInt :=
Int.bmod_eq_self_of_le (Int.le_trans (by decide) x.le_toInt) (Int.lt_of_lt_of_le x.toInt_lt (by decide))
Int.bmod_eq_of_le (Int.le_trans (by decide) x.le_toInt) (Int.lt_of_lt_of_le x.toInt_lt (by decide))
@[simp] theorem ISize.toInt_bmod_18446744073709551616 (x : ISize) : x.toInt.bmod 18446744073709551616 = x.toInt :=
Int.bmod_eq_self_of_le x.le_toInt x.toInt_lt
Int.bmod_eq_of_le x.le_toInt x.toInt_lt
@[simp] theorem Int8.toInt_bmod_two_pow_numBits (x : Int8) : x.toInt.bmod (2 ^ System.Platform.numBits) = x.toInt := by
refine Int.bmod_eq_self_of_le (Int.le_trans ?_ x.iSizeMinValue_le_toInt)
refine Int.bmod_eq_of_le (Int.le_trans ?_ x.iSizeMinValue_le_toInt)
(Int.lt_of_le_sub_one (Int.le_trans x.toInt_le_iSizeMaxValue ?_))
all_goals cases System.Platform.numBits_eq <;> simp_all [ISize.toInt_minValue, ISize.toInt_maxValue]
@[simp] theorem Int16.toInt_bmod_two_pow_numBits (x : Int16) : x.toInt.bmod (2 ^ System.Platform.numBits) = x.toInt := by
refine Int.bmod_eq_self_of_le (Int.le_trans ?_ x.iSizeMinValue_le_toInt)
refine Int.bmod_eq_of_le (Int.le_trans ?_ x.iSizeMinValue_le_toInt)
(Int.lt_of_le_sub_one (Int.le_trans x.toInt_le_iSizeMaxValue ?_))
all_goals cases System.Platform.numBits_eq <;> simp_all [ISize.toInt_minValue, ISize.toInt_maxValue]
@[simp] theorem Int32.toInt_bmod_two_pow_numBits (x : Int32) : x.toInt.bmod (2 ^ System.Platform.numBits) = x.toInt := by
refine Int.bmod_eq_self_of_le (Int.le_trans ?_ x.iSizeMinValue_le_toInt)
refine Int.bmod_eq_of_le (Int.le_trans ?_ x.iSizeMinValue_le_toInt)
(Int.lt_of_le_sub_one (Int.le_trans x.toInt_le_iSizeMaxValue ?_))
all_goals cases System.Platform.numBits_eq <;> simp_all [ISize.toInt_minValue, ISize.toInt_maxValue]
@@ -2066,7 +2066,7 @@ theorem ISize.toInt_maxValue_add_one : maxValue.toInt + 1 = 2 ^ (System.Platform
theorem Int8.ofInt_tdiv {a b : Int} (ha₁ : minValue.toInt a) (ha₂ : a maxValue.toInt)
(hb₁ : minValue.toInt b) (hb₂ : b maxValue.toInt) : Int8.ofInt (a.tdiv b) = Int8.ofInt a / Int8.ofInt b := by
rw [Int8.ofInt_eq_iff_bmod_eq_toInt, toInt_div, toInt_ofInt, toInt_ofInt,
Int.bmod_eq_self_of_le (n := a), Int.bmod_eq_self_of_le (n := b)]
Int.bmod_eq_of_le (n := a), Int.bmod_eq_of_le (n := b)]
· exact hb₁
· exact Int.lt_of_le_sub_one hb₂
· exact ha₁
@@ -2074,7 +2074,7 @@ theorem Int8.ofInt_tdiv {a b : Int} (ha₁ : minValue.toInt ≤ a) (ha₂ : a
theorem Int16.ofInt_tdiv {a b : Int} (ha₁ : minValue.toInt a) (ha₂ : a maxValue.toInt)
(hb₁ : minValue.toInt b) (hb₂ : b maxValue.toInt) : Int16.ofInt (a.tdiv b) = Int16.ofInt a / Int16.ofInt b := by
rw [Int16.ofInt_eq_iff_bmod_eq_toInt, toInt_div, toInt_ofInt, toInt_ofInt,
Int.bmod_eq_self_of_le (n := a), Int.bmod_eq_self_of_le (n := b)]
Int.bmod_eq_of_le (n := a), Int.bmod_eq_of_le (n := b)]
· exact hb₁
· exact Int.lt_of_le_sub_one hb₂
· exact ha₁
@@ -2082,7 +2082,7 @@ theorem Int16.ofInt_tdiv {a b : Int} (ha₁ : minValue.toInt ≤ a) (ha₂ : a
theorem Int32.ofInt_tdiv {a b : Int} (ha₁ : minValue.toInt a) (ha₂ : a maxValue.toInt)
(hb₁ : minValue.toInt b) (hb₂ : b maxValue.toInt) : Int32.ofInt (a.tdiv b) = Int32.ofInt a / Int32.ofInt b := by
rw [Int32.ofInt_eq_iff_bmod_eq_toInt, toInt_div, toInt_ofInt, toInt_ofInt,
Int.bmod_eq_self_of_le (n := a), Int.bmod_eq_self_of_le (n := b)]
Int.bmod_eq_of_le (n := a), Int.bmod_eq_of_le (n := b)]
· exact hb₁
· exact Int.lt_of_le_sub_one hb₂
· exact ha₁
@@ -2090,7 +2090,7 @@ theorem Int32.ofInt_tdiv {a b : Int} (ha₁ : minValue.toInt ≤ a) (ha₂ : a
theorem Int64.ofInt_tdiv {a b : Int} (ha₁ : minValue.toInt a) (ha₂ : a maxValue.toInt)
(hb₁ : minValue.toInt b) (hb₂ : b maxValue.toInt) : Int64.ofInt (a.tdiv b) = Int64.ofInt a / Int64.ofInt b := by
rw [Int64.ofInt_eq_iff_bmod_eq_toInt, toInt_div, toInt_ofInt, toInt_ofInt,
Int.bmod_eq_self_of_le (n := a), Int.bmod_eq_self_of_le (n := b)]
Int.bmod_eq_of_le (n := a), Int.bmod_eq_of_le (n := b)]
· exact hb₁
· exact Int.lt_of_le_sub_one hb₂
· exact ha₁
@@ -2098,7 +2098,7 @@ theorem Int64.ofInt_tdiv {a b : Int} (ha₁ : minValue.toInt ≤ a) (ha₂ : a
theorem ISize.ofInt_tdiv {a b : Int} (ha₁ : minValue.toInt a) (ha₂ : a maxValue.toInt)
(hb₁ : minValue.toInt b) (hb₂ : b maxValue.toInt) : ISize.ofInt (a.tdiv b) = ISize.ofInt a / ISize.ofInt b := by
rw [ISize.ofInt_eq_iff_bmod_eq_toInt, toInt_div, toInt_ofInt, toInt_ofInt,
Int.bmod_eq_self_of_le (n := a), Int.bmod_eq_self_of_le (n := b)]
Int.bmod_eq_of_le (n := a), Int.bmod_eq_of_le (n := b)]
· exact le_of_eq_of_le (by cases System.Platform.numBits_eq <;> simp_all [size, toInt_ofInt, toInt_neg]) hb₁
· refine Int.lt_of_le_sub_one (le_of_le_of_eq hb₂ ?_)
cases System.Platform.numBits_eq <;> simp_all [size, toInt_ofInt]
@@ -2921,7 +2921,7 @@ protected theorem ISize.div_self {a : ISize} : a / a = if a = 0 then 0 else 1 :=
simp [ ISize.toInt_inj]
split
· simp_all
· rw [Int.tdiv_self, Int.bmod_eq_self_of_le]
· rw [Int.tdiv_self, Int.bmod_eq_of_le]
· simp
· cases System.Platform.numBits_eq <;> simp_all
· cases System.Platform.numBits_eq <;> simp_all
@@ -3076,15 +3076,15 @@ protected theorem Int64.lt_or_eq_of_le {a b : Int64} : a ≤ b → a < b a =
protected theorem ISize.lt_or_eq_of_le {a b : ISize} : a b a < b a = b := ISize.le_iff_lt_or_eq.mp
theorem Int8.toInt_eq_toNatClampNeg {a : Int8} (ha : 0 a) : a.toInt = a.toNatClampNeg := by
simpa only [ toNat_toInt, Int.eq_ofNat_toNat, le_iff_toInt_le] using ha
simpa only [ toNat_toInt, Int.eq_natCast_toNat, le_iff_toInt_le] using ha
theorem Int16.toInt_eq_toNatClampNeg {a : Int16} (ha : 0 a) : a.toInt = a.toNatClampNeg := by
simpa only [ toNat_toInt, Int.eq_ofNat_toNat, le_iff_toInt_le] using ha
simpa only [ toNat_toInt, Int.eq_natCast_toNat, le_iff_toInt_le] using ha
theorem Int32.toInt_eq_toNatClampNeg {a : Int32} (ha : 0 a) : a.toInt = a.toNatClampNeg := by
simpa only [ toNat_toInt, Int.eq_ofNat_toNat, le_iff_toInt_le] using ha
simpa only [ toNat_toInt, Int.eq_natCast_toNat, le_iff_toInt_le] using ha
theorem Int64.toInt_eq_toNatClampNeg {a : Int64} (ha : 0 a) : a.toInt = a.toNatClampNeg := by
simpa only [ toNat_toInt, Int.eq_ofNat_toNat, le_iff_toInt_le] using ha
simpa only [ toNat_toInt, Int.eq_natCast_toNat, le_iff_toInt_le] using ha
theorem ISize.toInt_eq_toNatClampNeg {a : ISize} (ha : 0 a) : a.toInt = a.toNatClampNeg := by
simpa only [ toNat_toInt, Int.eq_ofNat_toNat, le_iff_toInt_le, toInt_zero] using ha
simpa only [ toNat_toInt, Int.eq_natCast_toNat, le_iff_toInt_le, toInt_zero] using ha
@[simp] theorem UInt8.toInt8_add (a b : UInt8) : (a + b).toInt8 = a.toInt8 + b.toInt8 := rfl
@[simp] theorem UInt16.toInt16_add (a b : UInt16) : (a + b).toInt16 = a.toInt16 + b.toInt16 := rfl
@@ -3322,7 +3322,7 @@ protected theorem ISize.ne_of_lt {a b : ISize} : a < b → a ≠ b := by
theorem Int8.ofInt_tmod {a b : Int} (ha₁ : minValue.toInt a) (ha₂ : a maxValue.toInt)
(hb₁ : minValue.toInt b) (hb₂ : b maxValue.toInt) : Int8.ofInt (a.tmod b) = Int8.ofInt a % Int8.ofInt b := by
rw [Int8.ofInt_eq_iff_bmod_eq_toInt, toInt_bmod_size, toInt_mod, toInt_ofInt, toInt_ofInt,
Int.bmod_eq_self_of_le (n := a), Int.bmod_eq_self_of_le (n := b)]
Int.bmod_eq_of_le (n := a), Int.bmod_eq_of_le (n := b)]
· exact hb₁
· exact Int.lt_of_le_sub_one hb₂
· exact ha₁
@@ -3330,7 +3330,7 @@ theorem Int8.ofInt_tmod {a b : Int} (ha₁ : minValue.toInt ≤ a) (ha₂ : a
theorem Int16.ofInt_tmod {a b : Int} (ha₁ : minValue.toInt a) (ha₂ : a maxValue.toInt)
(hb₁ : minValue.toInt b) (hb₂ : b maxValue.toInt) : Int16.ofInt (a.tmod b) = Int16.ofInt a % Int16.ofInt b := by
rw [Int16.ofInt_eq_iff_bmod_eq_toInt, toInt_bmod_size, toInt_mod, toInt_ofInt, toInt_ofInt,
Int.bmod_eq_self_of_le (n := a), Int.bmod_eq_self_of_le (n := b)]
Int.bmod_eq_of_le (n := a), Int.bmod_eq_of_le (n := b)]
· exact hb₁
· exact Int.lt_of_le_sub_one hb₂
· exact ha₁
@@ -3338,7 +3338,7 @@ theorem Int16.ofInt_tmod {a b : Int} (ha₁ : minValue.toInt ≤ a) (ha₂ : a
theorem Int32.ofInt_tmod {a b : Int} (ha₁ : minValue.toInt a) (ha₂ : a maxValue.toInt)
(hb₁ : minValue.toInt b) (hb₂ : b maxValue.toInt) : Int32.ofInt (a.tmod b) = Int32.ofInt a % Int32.ofInt b := by
rw [Int32.ofInt_eq_iff_bmod_eq_toInt, toInt_bmod_size, toInt_mod, toInt_ofInt, toInt_ofInt,
Int.bmod_eq_self_of_le (n := a), Int.bmod_eq_self_of_le (n := b)]
Int.bmod_eq_of_le (n := a), Int.bmod_eq_of_le (n := b)]
· exact hb₁
· exact Int.lt_of_le_sub_one hb₂
· exact ha₁
@@ -3346,7 +3346,7 @@ theorem Int32.ofInt_tmod {a b : Int} (ha₁ : minValue.toInt ≤ a) (ha₂ : a
theorem Int64.ofInt_tmod {a b : Int} (ha₁ : minValue.toInt a) (ha₂ : a maxValue.toInt)
(hb₁ : minValue.toInt b) (hb₂ : b maxValue.toInt) : Int64.ofInt (a.tmod b) = Int64.ofInt a % Int64.ofInt b := by
rw [Int64.ofInt_eq_iff_bmod_eq_toInt, toInt_bmod_size, toInt_mod, toInt_ofInt, toInt_ofInt,
Int.bmod_eq_self_of_le (n := a), Int.bmod_eq_self_of_le (n := b)]
Int.bmod_eq_of_le (n := a), Int.bmod_eq_of_le (n := b)]
· exact hb₁
· exact Int.lt_of_le_sub_one hb₂
· exact ha₁
@@ -3354,7 +3354,7 @@ theorem Int64.ofInt_tmod {a b : Int} (ha₁ : minValue.toInt ≤ a) (ha₂ : a
theorem ISize.ofInt_tmod {a b : Int} (ha₁ : minValue.toInt a) (ha₂ : a maxValue.toInt)
(hb₁ : minValue.toInt b) (hb₂ : b maxValue.toInt) : ISize.ofInt (a.tmod b) = ISize.ofInt a % ISize.ofInt b := by
rw [ISize.ofInt_eq_iff_bmod_eq_toInt, toInt_bmod_size, toInt_mod, toInt_ofInt, toInt_ofInt,
Int.bmod_eq_self_of_le (n := a), Int.bmod_eq_self_of_le (n := b)]
Int.bmod_eq_of_le (n := a), Int.bmod_eq_of_le (n := b)]
· exact le_of_eq_of_le (by cases System.Platform.numBits_eq <;> simp_all [size, toInt_ofInt, toInt_neg]) hb₁
· refine Int.lt_of_le_sub_one (le_of_le_of_eq hb₂ ?_)
cases System.Platform.numBits_eq <;> simp_all [size, toInt_ofInt]

View File

@@ -1666,7 +1666,7 @@ theorem USize.toUInt32_eq (a b : USize) : a.toUInt32 = b.toUInt32 ↔ a % 429496
simp [ UInt32.toNat_inj, USize.toNat_inj, USize.toNat_ofNat]
have := Nat.mod_eq_of_lt a.toNat_lt_two_pow_numBits
have := Nat.mod_eq_of_lt b.toNat_lt_two_pow_numBits
cases System.Platform.numBits_eq <;> simp_all
cases System.Platform.numBits_eq <;> simp_all [Nat.mod_eq_of_lt]
theorem UInt8.toUInt16_eq_mod_256_iff (a : UInt8) (b : UInt16) : a.toUInt16 = b % 256 a = b.toUInt8 := by
simp [ UInt8.toNat_inj, UInt16.toNat_inj]
@@ -1689,7 +1689,7 @@ theorem UInt32.toUInt64_eq_mod_4294967296_iff (a : UInt32) (b : UInt64) : a.toUI
theorem UInt32.toUSize_eq_mod_4294967296_iff (a : UInt32) (b : USize) : a.toUSize = b % 4294967296 a = b.toUInt32 := by
simp [ UInt32.toNat_inj, USize.toNat_inj, USize.toNat_ofNat]
have := Nat.mod_eq_of_lt b.toNat_lt_two_pow_numBits
cases System.Platform.numBits_eq <;> simp_all
cases System.Platform.numBits_eq <;> simp_all [Nat.mod_eq_of_lt]
theorem USize.toUInt64_eq_mod_usizeSize_iff (a : USize) (b : UInt64) : a.toUInt64 = b % UInt64.ofNat USize.size a = b.toUSize := by
simp [ USize.toNat_inj, UInt64.toNat_inj, USize.size_eq_two_pow]

View File

@@ -18,3 +18,4 @@ import Init.Data.Vector.Monadic
import Init.Data.Vector.InsertIdx
import Init.Data.Vector.FinRange
import Init.Data.Vector.Extract
import Init.Data.Vector.Perm

View File

@@ -432,13 +432,13 @@ theorem countP_attachWith {p : α → Prop} {q : α → Bool} {xs : Vector α n}
simp
@[simp]
theorem count_attach [DecidableEq α] {xs : Vector α n} {a : {x // x xs}} :
theorem count_attach [BEq α] {xs : Vector α n} {a : {x // x xs}} :
xs.attach.count a = xs.count a := by
rcases xs with xs, rfl
simp
@[simp]
theorem count_attachWith [DecidableEq α] {p : α Prop} {xs : Vector α n} (H : a xs, p a) {a : {x // p x}} :
theorem count_attachWith [BEq α] {p : α Prop} {xs : Vector α n} (H : a xs, p a) {a : {x // p x}} :
(xs.attachWith p H).count a = xs.count a := by
cases xs
simp

View File

@@ -229,7 +229,7 @@ theorem count_replicate {a b : α} {n : Nat} : count a (replicate n b) = if b ==
@[deprecated count_replicate (since := "2025-03-18")]
abbrev count_mkVector := @count_replicate
theorem count_le_count_map [DecidableEq β] {xs : Vector α n} {f : α β} {x : α} :
theorem count_le_count_map [BEq β] [LawfulBEq β] {xs : Vector α n} {f : α β} {x : α} :
count x xs count (f x) (map f xs) := by
rcases xs with xs, rfl
simp [Array.count_le_count_map]

View File

@@ -62,8 +62,10 @@ theorem beq_eq_decide [BEq α] (xs ys : Vector α n) :
@[simp] theorem beq_toList [BEq α] (xs ys : Vector α n) : (xs.toList == ys.toList) = (xs == ys) := by
simp [beq_eq_decide, List.beq_eq_decide]
instance [BEq α] [LawfulBEq α] : LawfulBEq (Vector α n) where
instance [BEq α] [ReflBEq α] : ReflBEq (Vector α n) where
rfl := by simp [BEq.beq, isEqv_self_beq]
instance [BEq α] [LawfulBEq α] : LawfulBEq (Vector α n) where
eq_of_beq := by
rintro xs, rfl ys, h h'
simpa using h'

View File

@@ -824,6 +824,9 @@ theorem getElem?_eq_none {xs : Vector α n} (h : n ≤ i) : xs[i]? = none := by
theorem getElem?_eq_some_iff {xs : Vector α n} : xs[i]? = some b h : i < n, xs[i] = b := by
simp [getElem?_def]
theorem getElem_of_getElem? {xs : Vector α n} : xs[i]? = some a h : i < n, xs[i] = a :=
getElem?_eq_some_iff.mp
theorem some_eq_getElem?_iff {xs : Vector α n} : some b = xs[i]? h : i < n, xs[i] = b := by
rw [eq_comm, getElem?_eq_some_iff]
@@ -1179,20 +1182,20 @@ theorem all_bne' [BEq α] [PartialEquivBEq α] {xs : Vector α n} :
theorem mem_of_contains_eq_true [BEq α] [LawfulBEq α] {a : α} {as : Vector α n} :
as.contains a = true a as := by
rcases as with as, rfl
simp [Array.mem_of_contains_eq_true]
simp
theorem contains_eq_true_of_mem [BEq α] [LawfulBEq α] {a : α} {as : Vector α n} (h : a as) :
as.contains a = true := by
rcases as with as, rfl
simp only [mem_mk] at h
simp [Array.contains_eq_true_of_mem, h]
instance [BEq α] [LawfulBEq α] (a : α) (as : Vector α n) : Decidable (a as) :=
decidable_of_decidable_of_iff (Iff.intro mem_of_contains_eq_true contains_eq_true_of_mem)
simp [h]
theorem contains_iff [BEq α] [LawfulBEq α] {a : α} {as : Vector α n} :
as.contains a = true a as := mem_of_contains_eq_true, contains_eq_true_of_mem
instance [BEq α] [LawfulBEq α] (a : α) (as : Vector α n) : Decidable (a as) :=
decidable_of_decidable_of_iff contains_iff
@[simp] theorem contains_eq_mem [BEq α] [LawfulBEq α] {a : α} {as : Vector α n} :
as.contains a = decide (a as) := by
rw [Bool.eq_iff_iff, contains_iff, decide_eq_true_iff]
@@ -1348,36 +1351,31 @@ abbrev mkVector_beq_mkVector := @replicate_beq_replicate
· intro h
constructor
rintro xs, h
simpa using Array.isEqv_self_beq ..
simp
@[simp] theorem lawfulBEq_iff [BEq α] [NeZero n] : LawfulBEq (Vector α n) LawfulBEq α := by
match n, NeZero.ne n with
| n + 1, _ =>
constructor
· intro h
have : ReflBEq α := reflBEq_iff.mp h.toReflBEq
constructor
· intro a b h
have := replicate_inj (n := n+1) (a := a) (b := b)
simp only [Nat.add_one_ne_zero, false_or] at this
rw [ this]
apply eq_of_beq
rw [replicate_beq_replicate]
simpa
· intro a
suffices (replicate (n + 1) a == replicate (n + 1) a) = true by
rw [replicate_beq_replicate] at this
simpa
simp
intro a b h
have := replicate_inj (n := n+1) (a := a) (b := b)
simp only [Nat.add_one_ne_zero, false_or] at this
rw [ this]
apply eq_of_beq
rw [replicate_beq_replicate]
simpa
· intro h
have : ReflBEq (Vector α (n + 1)) := reflBEq_iff.mpr inferInstance
constructor
· rintro as, ha bs, hb h
simp_all
· rintro as, ha
simp
rintro as, ha bs, hb h
simp_all
/-! ### isEqv -/
@[simp] theorem isEqv_eq [DecidableEq α] {xs ys : Vector α n} : xs.isEqv ys (· == ·) = (xs = ys) := by
@[simp] theorem isEqv_eq [BEq α] [LawfulBEq α] {xs ys : Vector α n} : xs.isEqv ys (· == ·) = (xs = ys) := by
cases xs
cases ys
simp
@@ -1717,12 +1715,12 @@ theorem append_eq_append_iff {ws : Vector α n} {xs : Vector α m} {ys : Vector
constructor
· rintro (as, rfl, rfl | cs, rfl, rfl)
· rw [dif_pos (by simp)]
exact as.toVector.cast (by simp; omega), by simp
exact as.toVector.cast (by simp), by simp
· split <;> rename_i h
· have hc : cs.size = 0 := by simp at h; omega
simp at hc
exact #v[].cast (by simp; omega), by simp_all
· exact cs.toVector.cast (by simp; omega), by simp
exact #v[].cast (by simp), by simp_all
· exact cs.toVector.cast (by simp), by simp
· split <;> rename_i h
· rintro as, hc, rfl
left
@@ -2677,12 +2675,7 @@ variable [LawfulBEq α]
theorem getElem?_replace {xs : Vector α n} {i : Nat} :
(xs.replace a b)[i]? = if xs[i]? == some a then if a xs.take i then some a else some b else xs[i]? := by
rcases xs with xs, rfl
simp [Array.getElem?_replace]
split <;> rename_i h
· rw (occs := [2]) [if_pos]
simpa using h
· rw [if_neg]
simpa using h
simp [Array.getElem?_replace, -beq_iff_eq]
theorem getElem?_replace_of_ne {xs : Vector α n} {i : Nat} (h : xs[i]? some a) :
(xs.replace a b)[i]? = xs[i]? := by

View File

@@ -148,13 +148,13 @@ protected theorem le_iff_lt_or_eq [DecidableEq α] [LT α] [DecidableLT α]
{xs ys : Vector α n} : xs ys xs < ys xs = ys := by
simpa using Array.le_iff_lt_or_eq (xs := xs.toArray) (ys := ys.toArray)
@[simp] theorem lex_eq_true_iff_lt [DecidableEq α] [LT α] [DecidableLT α]
@[simp] theorem lex_eq_true_iff_lt [BEq α] [LawfulBEq α] [LT α] [DecidableLT α]
{xs ys : Vector α n} : lex xs ys = true xs < ys := by
cases xs
cases ys
simp
@[simp] theorem lex_eq_false_iff_ge [DecidableEq α] [LT α] [DecidableLT α]
@[simp] theorem lex_eq_false_iff_ge [BEq α] [LawfulBEq α] [LT α] [DecidableLT α]
{xs ys : Vector α n} : lex xs ys = false ys xs := by
cases xs
cases ys

View File

@@ -0,0 +1,104 @@
/-
Copyright (c) 2024 Lean FRO. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
prelude
import Init.Data.Array.Perm
import Init.Data.Vector.Lemmas
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
namespace Vector
open List Array
/--
`Perm as bs` asserts that `as` and `bs` are permutations of each other.
This is a wrapper around `List.Perm`, and for now has much less API.
For more complicated verification, use `perm_iff_toList_perm` and the `List` API.
-/
def Perm (as bs : Vector α n) : Prop :=
as.toArray ~ bs.toArray
@[inherit_doc] scoped infixl:50 " ~ " => Perm
theorem perm_iff_toList_perm {as bs : Vector α n} : as ~ bs as.toList ~ bs.toList := Iff.rfl
theorem perm_iff_toArray_perm {as bs : Vector α n} : as ~ bs as.toArray ~ bs.toArray := Iff.rfl
@[simp] theorem perm_mk (as bs : Array α) (ha : as.size = n) (hb : bs.size = n) :
mk as ha ~ mk bs hb as ~ bs := by
simp [perm_iff_toArray_perm, ha, hb]
theorem toArray_perm_iff (xs : Vector α n) (ys : Array α) : xs.toArray ~ ys h, xs ~ Vector.mk ys h := by
constructor
· intro h
refine by simp [ h.length_eq], h
· intro h, p
exact p
theorem perm_toArray_iff (xs : Array α) (ys : Vector α n) : xs ~ ys.toArray h, Vector.mk xs h ~ ys := by
constructor
· intro h
refine by simp [h.length_eq], h
· intro h, p
exact p
@[simp, refl] protected theorem Perm.refl (xs : Vector α n) : xs ~ xs := by
cases xs
simp
protected theorem Perm.rfl {xs : List α} : xs ~ xs := .refl _
theorem Perm.of_eq {xs ys : Vector α n} (h : xs = ys) : xs ~ ys := h .rfl
protected theorem Perm.symm {xs ys : Vector α n} (h : xs ~ ys) : ys ~ xs := by
cases xs; cases ys
simp only [perm_mk] at h
simpa using h.symm
protected theorem Perm.trans {xs ys zs : Vector α n} (h₁ : xs ~ ys) (h₂ : ys ~ zs) : xs ~ zs := by
cases xs; cases ys; cases zs
simp only [perm_mk] at h₁ h₂
simpa using h₁.trans h₂
instance : Trans (Perm (α := α) (n := n)) (Perm (α := α) (n := n)) (Perm (α := α) (n := n)) where
trans h₁ h₂ := Perm.trans h₁ h₂
theorem perm_comm {xs ys : Vector α n} : xs ~ ys ys ~ xs := Perm.symm, Perm.symm
theorem Perm.mem_iff {a : α} {xs ys : Vector α n} (p : xs ~ ys) : a xs a ys := by
rcases xs with xs
rcases ys with ys
simp at p
simpa using p.mem_iff
theorem Perm.push (x y : α) {xs ys : Vector α n} (p : xs ~ ys) :
(xs.push x).push y ~ (ys.push y).push x := by
rcases xs with xs, rfl
rcases ys with ys, h
simp only [perm_mk] at p
simp only [push_mk, List.append_assoc, singleton_append, perm_toArray]
simpa using Array.Perm.push x y p
theorem swap_perm {xs : Vector α n} {i j : Nat} (h₁ : i < n) (h₂ : j < n) :
xs.swap i j ~ xs := by
rcases xs with xs, rfl
simp only [swap, perm_iff_toList_perm, toList_set]
apply set_set_perm
namespace Perm
set_option linter.indexVariables false in
theorem extract {xs ys : Vector α n} (h : xs ~ ys) {lo hi : Nat}
(wlo : i, i < lo xs[i]? = ys[i]?) (whi : i, hi i xs[i]? = ys[i]?) :
(xs.extract lo hi) ~ (ys.extract lo hi) := by
rcases xs with xs, rfl
rcases ys with ys, h
exact Array.Perm.extract h (by simpa using wlo) (by simpa using whi)
end Perm
end Vector

View File

@@ -101,9 +101,8 @@ theorem range'_eq_append_iff : range' s (n + m) = xs ++ ys ↔ xs = range' s n
simp_all
subst w
simp_all
omega
· rintro h₁, h₂
exact n, by omega, by simp_all; omega
exact n, by omega, by simp_all
@[simp] theorem find?_range'_eq_some {s n : Nat} {i : Nat} {p : Nat Bool} :
(range' s n).find? p = some i p i i range' s n j, s j j < i !p j := by

View File

@@ -13,3 +13,4 @@ import Init.Grind.Util
import Init.Grind.Offset
import Init.Grind.PP
import Init.Grind.CommRing
import Init.Grind.Ext

View File

@@ -6,6 +6,7 @@ Authors: Kim Morrison
prelude
import Init.Data.Zero
import Init.Data.Int.DivMod.Lemmas
import Init.Data.Int.Pow
import Init.TacticsExtra
/-!
@@ -43,6 +44,12 @@ class CommRing (α : Type u) extends Add α, Mul α, Neg α, Sub α, HPow α Nat
pow_succ : a : α, n : Nat, a ^ (n + 1) = (a ^ n) * a
ofNat_succ : a : Nat, OfNat.ofNat (α := α) (a + 1) = OfNat.ofNat a + 1 := by intros; rfl
-- We reduce the priority of these parent instances,
-- so that in downstream libraries with their own `CommRing` class,
-- the path `CommRing -> Add` is found before `CommRing -> Lean.Grind.CommRing -> Add`.
-- (And similarly for the other parents.)
attribute [instance 100] CommRing.toAdd CommRing.toMul CommRing.toNeg CommRing.toSub CommRing.toHPow
-- This is a low-priority instance, to avoid conflicts with existing `OfNat` instances.
attribute [instance 100] CommRing.ofNat
@@ -69,6 +76,9 @@ theorem zero_add (a : α) : 0 + a = a := by
theorem add_neg_cancel (a : α) : a + -a = 0 := by
rw [add_comm, neg_add_cancel]
theorem add_left_comm (a b c : α) : a + (b + c) = b + (a + c) := by
rw [ add_assoc, add_assoc, add_comm a]
theorem one_mul (a : α) : 1 * a = a := by
rw [mul_comm, mul_one]
@@ -78,6 +88,9 @@ theorem right_distrib (a b c : α) : (a + b) * c = a * c + b * c := by
theorem mul_zero (a : α) : a * 0 = 0 := by
rw [mul_comm, zero_mul]
theorem mul_left_comm (a b c : α) : a * (b * c) = b * (a * c) := by
rw [ mul_assoc, mul_assoc, mul_comm a]
theorem ofNat_mul (a b : Nat) : OfNat.ofNat (α := α) (a * b) = OfNat.ofNat a * OfNat.ofNat b := by
induction b with
| zero => simp [Nat.mul_zero, mul_zero]
@@ -190,6 +203,16 @@ theorem intCast_mul (x y : Int) : ((x * y : Int) : α) = ((x : α) * (y : α)) :
rw [Int.neg_mul_neg, intCast_neg, intCast_neg, neg_mul, mul_neg, neg_neg, intCast_nat_mul,
intCast_ofNat, intCast_ofNat]
theorem intCast_pow (x : Int) (k : Nat) : ((x ^ k : Int) : α) = (x : α) ^ k := by
induction k
next => simp [pow_zero, Int.pow_zero, intCast_one]
next k ih => simp [pow_succ, Int.pow_succ, intCast_mul, *]
theorem pow_add (a : α) (k₁ k₂ : Nat) : a ^ (k₁ + k₂) = a^k₁ * a^k₂ := by
induction k₂
next => simp [pow_zero, mul_one]
next k₂ ih => rw [Nat.add_succ, pow_succ, pow_succ, ih, mul_assoc]
end CommRing
open CommRing
@@ -216,7 +239,7 @@ theorem intCast_eq_zero_iff (x : Int) : (x : α) = 0 ↔ x % p = 0 :=
rw [ofNat_eq_natCast] at this
rw [this]
simp only [Int.ofNat_dvd]
simp only [ Nat.dvd_iff_mod_eq_zero, Int.natAbs_ofNat, Int.natCast_add,
simp only [ Nat.dvd_iff_mod_eq_zero, Int.natAbs_natCast, Int.natCast_add,
Int.cast_ofNat_Int, ite_eq_left_iff]
by_cases h : p x + 1
· simp [h]

View File

@@ -0,0 +1,751 @@
/-
Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
prelude
import Init.Data.Nat.Lemmas
import Init.Data.Ord
import Init.Grind.CommRing.Basic
namespace Lean.Grind
namespace CommRing
abbrev Var := Nat
inductive Expr where
| num (v : Int)
| var (i : Var)
| neg (a : Expr)
| add (a b : Expr)
| sub (a b : Expr)
| mul (a b : Expr)
| pow (a : Expr) (k : Nat)
deriving Inhabited, BEq
-- TODO: add support for universes to Lean.RArray
inductive RArray (α : Type u) : Type u where
| leaf : α RArray α
| branch : Nat RArray α RArray α RArray α
def RArray.get (a : RArray α) (n : Nat) : α :=
match a with
| .leaf x => x
| .branch p l r => if n < p then l.get n else r.get n
abbrev Context (α : Type u) := RArray α
def Var.denote {α} (ctx : Context α) (v : Var) : α :=
ctx.get v
def Expr.denote {α} [CommRing α] (ctx : Context α) : Expr α
| .add a b => denote ctx a + denote ctx b
| .sub a b => denote ctx a - denote ctx b
| .mul a b => denote ctx a * denote ctx b
| .neg a => -denote ctx a
| .num k => k
| .var v => v.denote ctx
| .pow a k => denote ctx a ^ k
structure Power where
x : Var
k : Nat
deriving BEq, Repr
def Power.varLt (p₁ p₂ : Power) : Bool :=
p₁.x.blt p₂.x
def Power.denote {α} [CommRing α] (ctx : Context α) : Power α
| {x, k} =>
match k with
| 0 => 1
| 1 => x.denote ctx
| k => x.denote ctx ^ k
inductive Mon where
| leaf (p : Power)
| cons (p : Power) (m : Mon)
deriving BEq, Repr
def Mon.denote {α} [CommRing α] (ctx : Context α) : Mon α
| .leaf p => p.denote ctx
| .cons p m => p.denote ctx * denote ctx m
def Mon.denote' {α} [CommRing α] (ctx : Context α) : Mon α
| .leaf p => p.denote ctx
| .cons p m => go (p.denote ctx) m
where
go (acc : α) : Mon α
| .leaf p => acc * p.denote ctx
| .cons p m => go (acc * p.denote ctx) m
def Mon.ofVar (x : Var) : Mon :=
.leaf { x, k := 1 }
def Mon.concat (m₁ m₂ : Mon) : Mon :=
match m₁ with
| .leaf p => .cons p m₂
| .cons p m₁ => .cons p (concat m₁ m₂)
def Mon.mulPow (p : Power) (m : Mon) : Mon :=
match m with
| .leaf p' =>
bif p.varLt p' then
.cons p m
else bif p'.varLt p then
.cons p' (.leaf p)
else
.leaf { x := p.x, k := p.k + p'.k }
| .cons p' m =>
bif p.varLt p' then
.cons p (.cons p' m)
else bif p'.varLt p then
.cons p' (mulPow p m)
else
.cons { x := p.x, k := p.k + p'.k } m
def Mon.length : Mon Nat
| .leaf _ => 1
| .cons _ m => 1 + length m
def hugeFuel := 1000000
def Mon.mul (m₁ m₂ : Mon) : Mon :=
-- We could use `m₁.length + m₂.length` to avoid hugeFuel
go hugeFuel m₁ m₂
where
go (fuel : Nat) (m₁ m₂ : Mon) : Mon :=
match fuel with
| 0 => concat m₁ m₂
| fuel + 1 =>
match m₁, m₂ with
| m₁, .leaf p => m₁.mulPow p
| .leaf p, m₂ => m₂.mulPow p
| .cons p₁ m₁, .cons p₂ m₂ =>
bif p₁.varLt p₂ then
.cons p₁ (go fuel m₁ (.cons p₂ m₂))
else bif p₂.varLt p₁ then
.cons p₂ (go fuel (.cons p₁ m₁) m₂)
else
.cons { x := p₁.x, k := p₁.k + p₂.k } (go fuel m₁ m₂)
def Mon.degree : Mon Nat
| .leaf p => p.k
| .cons p m => p.k + degree m
def Var.revlex (x y : Var) : Ordering :=
bif x.blt y then .gt
else bif y.blt x then .lt
else .eq
def powerRevlex (k₁ k₂ : Nat) : Ordering :=
bif k₁.blt k₂ then .gt
else bif k₂.blt k₁ then .lt
else .eq
def Power.revlex (p₁ p₂ : Power) : Ordering :=
p₁.x.revlex p₂.x |>.then (powerRevlex p₁.k p₂.k)
def Mon.revlexWF (m₁ m₂ : Mon) : Ordering :=
match m₁, m₂ with
| .leaf p₁, .leaf p₂ => p₁.revlex p₂
| .leaf p₁, .cons p₂ m₂ =>
bif p₁.x.ble p₂.x then
.gt
else
revlexWF (.leaf p₁) m₂ |>.then .gt
| .cons p₁ m₁, .leaf p₂ =>
bif p₂.x.ble p₁.x then
.lt
else
revlexWF m₁ (.leaf p₂) |>.then .lt
| .cons p₁ m₁, .cons p₂ m₂ =>
bif p₁.x == p₂.x then
revlexWF m₁ m₂ |>.then (powerRevlex p₁.k p₂.k)
else bif p₁.x.blt p₂.x then
revlexWF m₁ (.cons p₂ m₂) |>.then .gt
else
revlexWF (.cons p₁ m₁) m₂ |>.then .lt
def Mon.revlexFuel (fuel : Nat) (m₁ m₂ : Mon) : Ordering :=
match fuel with
| 0 =>
-- This branch is not reachable in practice, but we add it here
-- to ensure we can prove helper theorems
revlexWF m₁ m₂
| fuel + 1 =>
match m₁, m₂ with
| .leaf p₁, .leaf p₂ => p₁.revlex p₂
| .leaf p₁, .cons p₂ m₂ =>
bif p₁.x.ble p₂.x then
.gt
else
revlexFuel fuel (.leaf p₁) m₂ |>.then .gt
| .cons p₁ m₁, .leaf p₂ =>
bif p₂.x.ble p₁.x then
.lt
else
revlexFuel fuel m₁ (.leaf p₂) |>.then .lt
| .cons p₁ m₁, .cons p₂ m₂ =>
bif p₁.x == p₂.x then
revlexFuel fuel m₁ m₂ |>.then (powerRevlex p₁.k p₂.k)
else bif p₁.x.blt p₂.x then
revlexFuel fuel m₁ (.cons p₂ m₂) |>.then .gt
else
revlexFuel fuel (.cons p₁ m₁) m₂ |>.then .lt
def Mon.revlex (m₁ m₂ : Mon) : Ordering :=
revlexFuel hugeFuel m₁ m₂
def Mon.grevlex (m₁ m₂ : Mon) : Ordering :=
compare m₁.degree m₂.degree |>.then (revlex m₁ m₂)
inductive Poly where
| num (k : Int)
| add (k : Int) (v : Mon) (p : Poly)
deriving BEq
def Poly.denote [CommRing α] (ctx : Context α) (p : Poly) : α :=
match p with
| .num k => Int.cast k
| .add k m p => Int.cast k * m.denote ctx + denote ctx p
def Poly.ofMon (m : Mon) : Poly :=
.add 1 m (.num 0)
def Poly.ofVar (x : Var) : Poly :=
ofMon (Mon.ofVar x)
def Poly.addConst (p : Poly) (k : Int) : Poly :=
match p with
| .num k' => .num (k' + k)
| .add k' m p => .add k' m (addConst p k)
def Poly.insert (k : Int) (m : Mon) (p : Poly) : Poly :=
bif k == 0 then
p
else
go p
where
go : Poly Poly
| .num k' => .add k m (.num k')
| .add k' m' p =>
match m.grevlex m' with
| .eq =>
let k'' := k + k'
bif k'' == 0 then
p
else
.add k'' m p
| .lt => .add k m (.add k' m' p)
| .gt => .add k' m' (go p)
def Poly.concat (p₁ p₂ : Poly) : Poly :=
match p₁ with
| .num k₁ => p₂.addConst k₁
| .add k m p₁ => .add k m (concat p₁ p₂)
def Poly.mulConst (k : Int) (p : Poly) : Poly :=
bif k == 0 then
.num 0
else bif k == 1 then
p
else
go p
where
go : Poly Poly
| .num k' => .num (k*k')
| .add k' m p => .add (k*k') m (go p)
def Poly.mulMon (k : Int) (m : Mon) (p : Poly) : Poly :=
bif k == 0 then
.num 0
else
go p
where
go : Poly Poly
| .num k' => .add (k*k') m (.num 0)
| .add k' m' p => .add (k*k') (m.mul m') (go p)
def Poly.combine (p₁ p₂ : Poly) : Poly :=
go hugeFuel p₁ p₂
where
go (fuel : Nat) (p₁ p₂ : Poly) : Poly :=
match fuel with
| 0 => p₁.concat p₂
| fuel + 1 => match p₁, p₂ with
| .num k₁, .num k₂ => .num (k₁ + k₂)
| .num k₁, .add k₂ m₂ p₂ => addConst (.add k₂ m₂ p₂) k₁
| .add k₁ m₁ p₁, .num k₂ => addConst (.add k₁ m₁ p₁) k₂
| .add k₁ m₁ p₁, .add k₂ m₂ p₂ =>
match m₁.grevlex m₂ with
| .eq =>
let k := k₁ + k₂
bif k == 0 then
go fuel p₁ p₂
else
.add k m₁ (go fuel p₁ p₂)
| .lt => .add k₁ m₁ (go fuel p₁ (.add k₂ m₂ p₂))
| .gt => .add k₂ m₂ (go fuel (.add k₁ m₁ p₁) p₂)
def Poly.mul (p₁ : Poly) (p₂ : Poly) : Poly :=
go p₁ (.num 0)
where
go (p₁ : Poly) (acc : Poly) : Poly :=
match p₁ with
| .num k => acc.combine (p₂.mulConst k)
| .add k m p₁ => go p₁ (acc.combine (p₂.mulMon k m))
def Poly.pow (p : Poly) (k : Nat) : Poly :=
match k with
| 0 => .num 1
| 1 => p
| k+1 => p.mul (pow p k)
def Expr.toPoly : Expr Poly
| .num n => .num n
| .var x => Poly.ofVar x
| .add a b => a.toPoly.combine b.toPoly
| .mul a b => a.toPoly.mul b.toPoly
| .neg a => a.toPoly.mulConst (-1)
| .sub a b => a.toPoly.combine (b.toPoly.mulConst (-1))
| .pow a k =>
match a with
| .num n => .num (n^k)
| .var x => Poly.ofMon (.leaf {x, k})
| _ => a.toPoly.pow k
/-!
**Definitions for the `IsCharP` case**
We considered using a single set of definitions parameterized by `Option c`, but decided against it to avoid
unnecessary kernelreduction overhead. Once we can specialize definitions before they reach the kernel,
we can merge the two versions. Until then, the `IsCharP` definitions will carry the `C` suffix.
-/
def Poly.addConstC (p : Poly) (k : Int) (c : Nat) : Poly :=
match p with
| .num k' => .num ((k' + k) % c)
| .add k' m p => .add k' m (addConstC p k c)
def Poly.insertC (k : Int) (m : Mon) (p : Poly) (c : Nat) : Poly :=
let k := k % c
bif k == 0 then
p
else
go k p
where
go (k : Int) : Poly Poly
| .num k' => .add k m (.num k')
| .add k' m' p =>
match m.grevlex m' with
| .eq =>
let k'' := (k + k') % c
bif k'' == 0 then
p
else
.add k'' m p
| .lt => .add k m (.add k' m' p)
| .gt => .add k' m' (go k p)
def Poly.mulConstC (k : Int) (p : Poly) (c : Nat) : Poly :=
let k := k % c
bif k == 0 then
.num 0
else bif k == 1 then
p
else
go p
where
go : Poly Poly
| .num k' => .num ((k*k') % c)
| .add k' m p =>
let k := (k*k') % c
bif k == 0 then
go p
else
.add k m (go p)
def Poly.mulMonC (k : Int) (m : Mon) (p : Poly) (c : Nat) : Poly :=
let k := k % c
bif k == 0 then
.num 0
else
go p
where
go : Poly Poly
| .num k' =>
let k := (k*k') % c
bif k == 0 then
.num 0
else
.add k m (.num 0)
| .add k' m' p =>
let k := (k*k') % c
bif k == 0 then
go p
else
.add k (m.mul m') (go p)
def Poly.combineC (p₁ p₂ : Poly) (c : Nat) : Poly :=
go hugeFuel p₁ p₂
where
go (fuel : Nat) (p₁ p₂ : Poly) : Poly :=
match fuel with
| 0 => p₁.concat p₂
| fuel + 1 => match p₁, p₂ with
| .num k₁, .num k₂ => .num ((k₁ + k₂) % c)
| .num k₁, .add k₂ m₂ p₂ => addConstC (.add k₂ m₂ p₂) k₁ c
| .add k₁ m₁ p₁, .num k₂ => addConstC (.add k₁ m₁ p₁) k₂ c
| .add k₁ m₁ p₁, .add k₂ m₂ p₂ =>
match m₁.grevlex m₂ with
| .eq =>
let k := (k₁ + k₂) % c
bif k == 0 then
go fuel p₁ p₂
else
.add k m₁ (go fuel p₁ p₂)
| .lt => .add k₁ m₁ (go fuel p₁ (.add k₂ m₂ p₂))
| .gt => .add k₂ m₂ (go fuel (.add k₁ m₁ p₁) p₂)
def Poly.mulC (p₁ : Poly) (p₂ : Poly) (c : Nat) : Poly :=
go p₁ (.num 0)
where
go (p₁ : Poly) (acc : Poly) : Poly :=
match p₁ with
| .num k => acc.combineC (p₂.mulConstC k c) c
| .add k m p₁ => go p₁ (acc.combineC (p₂.mulMonC k m c) c)
def Poly.powC (p : Poly) (k : Nat) (c : Nat) : Poly :=
match k with
| 0 => .num 1
| 1 => p
| k+1 => p.mulC (powC p k c) c
def Expr.toPolyC (e : Expr) (c : Nat) : Poly :=
go e
where
go : Expr Poly
| .num n => .num (n % c)
| .var x => Poly.ofVar x
| .add a b => (go a).combineC (go b) c
| .mul a b => (go a).mulC (go b) c
| .neg a => (go a).mulConstC (-1) c
| .sub a b => (go a).combineC ((go b).mulConstC (-1) c) c
| .pow a k =>
match a with
| .num n => .num ((n^k) % c)
| .var x => Poly.ofMon (.leaf {x, k})
| _ => (go a).powC k c
/-!
Theorems for justifying the procedure for commutative rings in `grind`.
-/
theorem Power.denote_eq {α} [CommRing α] (ctx : Context α) (p : Power)
: p.denote ctx = p.x.denote ctx ^ p.k := by
cases p <;> simp [Power.denote] <;> split <;> simp [pow_zero, pow_succ, one_mul]
theorem Mon.denote'_go_eq_denote {α} [CommRing α] (ctx : Context α) (a : α) (m : Mon)
: denote'.go ctx a m = a * denote ctx m := by
induction m generalizing a <;> simp [Mon.denote, Mon.denote'.go]
next p' m ih =>
simp [Mon.denote] at ih
rw [ih, mul_assoc]
theorem Mon.denote'_eq_denote {α} [CommRing α] (ctx : Context α) (m : Mon)
: denote' ctx m = denote ctx m := by
cases m <;> simp [Mon.denote, Mon.denote']
next p m => apply denote'_go_eq_denote
theorem Mon.denote_ofVar {α} [CommRing α] (ctx : Context α) (x : Var)
: denote ctx (ofVar x) = x.denote ctx := by
simp [denote, ofVar, Power.denote_eq, pow_succ, pow_zero, one_mul]
theorem Mon.denote_concat {α} [CommRing α] (ctx : Context α) (m₁ m₂ : Mon)
: denote ctx (concat m₁ m₂) = m₁.denote ctx * m₂.denote ctx := by
induction m₁ <;> simp [concat, denote, *]
next p₁ m₁ ih => rw [mul_assoc]
private theorem le_of_blt_false {a b : Nat} : a.blt b = false b a := by
intro h; apply Nat.le_of_not_gt; show ¬a < b
rw [ Nat.blt_eq, h]; simp
private theorem eq_of_blt_false {a b : Nat} : a.blt b = false b.blt a = false a = b := by
intro h₁ h₂
replace h₁ := le_of_blt_false h₁
replace h₂ := le_of_blt_false h₂
exact Nat.le_antisymm h₂ h₁
theorem Mon.denote_mulPow {α} [CommRing α] (ctx : Context α) (p : Power) (m : Mon)
: denote ctx (mulPow p m) = p.denote ctx * m.denote ctx := by
fun_induction mulPow <;> simp [mulPow, *]
next => simp [denote]
next => simp [denote]; rw [mul_comm]
next p' h₁ h₂ =>
have := eq_of_blt_false h₁ h₂
simp [denote, Power.denote_eq, this, pow_add]
next => simp [denote]
next => simp [denote, mul_assoc, mul_comm, mul_left_comm, *]
next h₁ h₂ =>
have := eq_of_blt_false h₁ h₂
simp [denote, Power.denote_eq, pow_add, this, mul_assoc]
theorem Mon.denote_mul {α} [CommRing α] (ctx : Context α) (m₁ m₂ : Mon)
: denote ctx (mul m₁ m₂) = m₁.denote ctx * m₂.denote ctx := by
unfold mul
generalize hugeFuel = fuel
fun_induction mul.go <;> simp [mul.go, denote, denote_concat, denote_mulPow, *]
next => rw [mul_comm]
next => simp [mul_assoc]
next => simp [mul_assoc, mul_left_comm, mul_comm]
next h₁ h₂ _ =>
have := eq_of_blt_false h₁ h₂
simp [Power.denote_eq, pow_add, mul_assoc, mul_left_comm, mul_comm, this]
theorem Var.eq_of_revlex {x₁ x₂ : Var} : x₁.revlex x₂ = .eq x₁ = x₂ := by
simp [revlex, cond_eq_if] <;> split <;> simp
next h₁ => intro h₂; exact Nat.le_antisymm h₂ (Nat.ge_of_not_lt h₁)
theorem eq_of_powerRevlex {k₁ k₂ : Nat} : powerRevlex k₁ k₂ = .eq k₁ = k₂ := by
simp [powerRevlex, cond_eq_if] <;> split <;> simp
next h₁ => intro h₂; exact Nat.le_antisymm h₂ (Nat.ge_of_not_lt h₁)
theorem Power.eq_of_revlex (p₁ p₂ : Power) : p₁.revlex p₂ = .eq p₁ = p₂ := by
cases p₁; cases p₂; simp [revlex, Ordering.then]; split
next h₁ => intro h₂; simp [Var.eq_of_revlex h₁, eq_of_powerRevlex h₂]
next h₁ => intro h₂; simp [h₂] at h₁
private theorem then_gt (o : Ordering) : ¬ o.then .gt = .eq := by
cases o <;> simp [Ordering.then]
private theorem then_lt (o : Ordering) : ¬ o.then .lt = .eq := by
cases o <;> simp [Ordering.then]
private theorem then_eq (o₁ o₂ : Ordering) : o₁.then o₂ = .eq o₁ = .eq o₂ = .eq := by
cases o₁ <;> cases o₂ <;> simp [Ordering.then]
theorem Mon.eq_of_revlexWF {m₁ m₂ : Mon} : m₁.revlexWF m₂ = .eq m₁ = m₂ := by
fun_induction revlexWF <;> simp [revlexWF, *, then_gt, then_lt, then_eq]
next => apply Power.eq_of_revlex
next p₁ m₁ p₂ m₂ h ih =>
cases p₁; cases p₂; intro h₁ h₂; simp [ih h₁, h]
simp at h h₂
simp [h, eq_of_powerRevlex h₂]
theorem Mon.eq_of_revlexFuel {fuel : Nat} {m₁ m₂ : Mon} : revlexFuel fuel m₁ m₂ = .eq m₁ = m₂ := by
fun_induction revlexFuel <;> simp [revlexFuel, *, then_gt, then_lt, then_eq]
next => apply eq_of_revlexWF
next => apply Power.eq_of_revlex
next p₁ m₁ p₂ m₂ h ih =>
cases p₁; cases p₂; intro h₁ h₂; simp [ih h₁, h]
simp at h h₂
simp [h, eq_of_powerRevlex h₂]
theorem Mon.eq_of_revlex {m₁ m₂ : Mon} : revlex m₁ m₂ = .eq m₁ = m₂ := by
apply eq_of_revlexFuel
theorem Mon.eq_of_grevlex {m₁ m₂ : Mon} : grevlex m₁ m₂ = .eq m₁ = m₂ := by
simp [grevlex, then_eq]; intro; apply eq_of_revlex
theorem Poly.denote_ofMon {α} [CommRing α] (ctx : Context α) (m : Mon)
: denote ctx (ofMon m) = m.denote ctx := by
simp [ofMon, denote, intCast_one, intCast_zero, one_mul, add_zero]
theorem Poly.denote_ofVar {α} [CommRing α] (ctx : Context α) (x : Var)
: denote ctx (ofVar x) = x.denote ctx := by
simp [ofVar, denote_ofMon, Mon.denote_ofVar]
theorem Poly.denote_addConst {α} [CommRing α] (ctx : Context α) (p : Poly) (k : Int) : (addConst p k).denote ctx = p.denote ctx + k := by
fun_induction addConst <;> simp [addConst, denote, *]
next => rw [intCast_add]
next => simp [add_comm, add_left_comm, add_assoc]
theorem Poly.denote_insert {α} [CommRing α] (ctx : Context α) (k : Int) (m : Mon) (p : Poly)
: (insert k m p).denote ctx = k * m.denote ctx + p.denote ctx := by
simp [insert, cond_eq_if] <;> split
next => simp [*, intCast_zero, zero_mul, zero_add]
next =>
fun_induction insert.go <;> simp_all +zetaDelta [insert.go, denote, cond_eq_if]
next h₁ _ h₂ =>
rw [ add_assoc, Mon.eq_of_grevlex h₁, right_distrib, intCast_add, h₂, intCast_zero, zero_mul, zero_add]
next h₁ _ _ =>
rw [intCast_add, right_distrib, add_assoc, Mon.eq_of_grevlex h₁]
next =>
rw [add_left_comm]
theorem Poly.denote_concat {α} [CommRing α] (ctx : Context α) (p₁ p₂ : Poly)
: (concat p₁ p₂).denote ctx = p₁.denote ctx + p₂.denote ctx := by
fun_induction concat <;> simp [concat, *, denote_addConst, denote]
next => rw [add_comm]
next => rw [add_assoc]
theorem Poly.denote_mulConst {α} [CommRing α] (ctx : Context α) (k : Int) (p : Poly)
: (mulConst k p).denote ctx = k * p.denote ctx := by
simp [mulConst, cond_eq_if] <;> split
next => simp [denote, *, intCast_zero, zero_mul]
next =>
split <;> try simp [*, intCast_one, one_mul]
fun_induction mulConst.go <;> simp [mulConst.go, denote, *]
next => rw [intCast_mul]
next => rw [intCast_mul, left_distrib, mul_assoc]
theorem Poly.denote_mulMon {α} [CommRing α] (ctx : Context α) (k : Int) (m : Mon) (p : Poly)
: (mulMon k m p).denote ctx = k * m.denote ctx * p.denote ctx := by
simp [mulMon, cond_eq_if] <;> split
next => simp [denote, *, intCast_zero, zero_mul]
next =>
fun_induction mulMon.go <;> simp [mulMon.go, denote, *]
next => simp [intCast_mul, intCast_zero, add_zero, mul_comm, mul_left_comm, mul_assoc]
next => simp [Mon.denote_mul, intCast_mul, left_distrib, mul_comm, mul_left_comm, mul_assoc]
theorem Poly.denote_combine {α} [CommRing α] (ctx : Context α) (p₁ p₂ : Poly)
: (combine p₁ p₂).denote ctx = p₁.denote ctx + p₂.denote ctx := by
unfold combine; generalize hugeFuel = fuel
fun_induction combine.go
<;> simp [combine.go, *, denote_concat, denote_addConst, denote, intCast_add, cond_eq_if, add_comm, add_left_comm, add_assoc]
next hg _ h _ =>
simp +zetaDelta at h; simp [*]
rw [ add_assoc, Mon.eq_of_grevlex hg, right_distrib, intCast_add, h, intCast_zero, zero_mul, zero_add]
next hg _ h _ =>
simp +zetaDelta at h; simp [*, denote, intCast_add]
rw [right_distrib, Mon.eq_of_grevlex hg, add_assoc]
theorem Poly.denote_mul_go {α} [CommRing α] (ctx : Context α) (p₁ p₂ acc : Poly)
: (mul.go p₂ p₁ acc).denote ctx = acc.denote ctx + p₁.denote ctx * p₂.denote ctx := by
fun_induction mul.go
<;> simp [mul.go, denote_combine, denote_mulConst, denote, *, right_distrib, denote_mulMon, add_assoc]
theorem Poly.denote_mul {α} [CommRing α] (ctx : Context α) (p₁ p₂ : Poly)
: (mul p₁ p₂).denote ctx = p₁.denote ctx * p₂.denote ctx := by
simp [mul, denote_mul_go, denote, intCast_zero, zero_add]
theorem Poly.denote_pow {α} [CommRing α] (ctx : Context α) (p : Poly) (k : Nat)
: (pow p k).denote ctx = p.denote ctx ^ k := by
fun_induction pow <;> simp [pow, denote, intCast_one, pow_zero]
next => simp [pow_succ, pow_zero, one_mul]
next => simp [denote_mul, *, pow_succ, mul_comm]
theorem Expr.denote_toPoly {α} [CommRing α] (ctx : Context α) (e : Expr)
: e.toPoly.denote ctx = e.denote ctx := by
fun_induction toPoly
<;> simp [toPoly, denote, Poly.denote, Poly.denote_ofVar, Poly.denote_combine,
Poly.denote_mul, Poly.denote_mulConst, Poly.denote_pow, *]
next => rw [intCast_neg, neg_mul, intCast_one, one_mul]
next => rw [intCast_neg, neg_mul, intCast_one, one_mul, sub_eq_add_neg]
next => rw [intCast_pow]
next => simp [Poly.denote_ofMon, Mon.denote, Power.denote_eq]
theorem Poly.denote_addConstC {α c} [CommRing α] [IsCharP α c] (ctx : Context α) (p : Poly) (k : Int) : (addConstC p k c).denote ctx = p.denote ctx + k := by
fun_induction addConstC <;> simp [addConstC, denote, *]
next => rw [IsCharP.intCast_emod, intCast_add]
next => simp [add_comm, add_left_comm, add_assoc]
theorem Poly.denote_insertC {α c} [CommRing α] [IsCharP α c] (ctx : Context α) (k : Int) (m : Mon) (p : Poly)
: (insertC k m p c).denote ctx = k * m.denote ctx + p.denote ctx := by
simp [insertC, cond_eq_if] <;> split
next =>
rw [ IsCharP.intCast_emod (p := c)]
simp +zetaDelta [*, intCast_zero, zero_mul, zero_add]
next =>
fun_induction insertC.go <;> simp_all +zetaDelta [insertC.go, denote, cond_eq_if]
next h₁ _ h₂ => rw [IsCharP.intCast_emod]
next h₁ _ h₂ =>
rw [ add_assoc, Mon.eq_of_grevlex h₁, right_distrib, intCast_add, IsCharP.intCast_emod (p := c), h₂,
intCast_zero, zero_mul, zero_add]
next h₁ _ _ =>
rw [IsCharP.intCast_emod, intCast_add, right_distrib, add_assoc, Mon.eq_of_grevlex h₁]
next => rw [IsCharP.intCast_emod]
next => rw [add_left_comm]
theorem Poly.denote_mulConstC {α c} [CommRing α] [IsCharP α c] (ctx : Context α) (k : Int) (p : Poly)
: (mulConstC k p c).denote ctx = k * p.denote ctx := by
simp [mulConstC, cond_eq_if] <;> split
next =>
rw [ IsCharP.intCast_emod (p := c)]
simp [denote, *, intCast_zero, zero_mul]
next =>
split
next =>
rw [ IsCharP.intCast_emod (p := c)]
simp [*, intCast_one, one_mul]
next =>
fun_induction mulConstC.go <;> simp [mulConstC.go, denote, IsCharP.intCast_emod, cond_eq_if, *]
next => rw [intCast_mul]
next h _ =>
simp +zetaDelta at h; simp [*]
rw [left_distrib, mul_assoc, intCast_mul, IsCharP.intCast_emod (x := k * _) (p := c),
h, intCast_zero, zero_mul, zero_add]
next h _ =>
simp +zetaDelta at h
simp [*, denote, IsCharP.intCast_emod, intCast_mul, mul_assoc, left_distrib]
theorem Poly.denote_mulMonC {α c} [CommRing α] [IsCharP α c] (ctx : Context α) (k : Int) (m : Mon) (p : Poly)
: (mulMonC k m p c).denote ctx = k * m.denote ctx * p.denote ctx := by
simp [mulMonC, cond_eq_if] <;> split
next =>
rw [ IsCharP.intCast_emod (p := c)]
simp [denote, *, intCast_zero, zero_mul]
next =>
fun_induction mulMonC.go <;> simp [mulMonC.go, denote, *, cond_eq_if]
next h =>
simp +zetaDelta at h; simp [*, denote]
rw [mul_assoc, mul_left_comm, intCast_mul, IsCharP.intCast_emod (x := k * _) (p := c), h]
simp [intCast_zero, mul_zero]
next h =>
simp +zetaDelta at h; simp [*, denote, IsCharP.intCast_emod]
simp [intCast_mul, intCast_zero, add_zero, mul_comm, mul_left_comm, mul_assoc]
next h _ =>
simp +zetaDelta at h; simp [*, denote, left_distrib]
rw [mul_left_comm]
conv => rhs; rw [ mul_assoc, mul_assoc, intCast_mul, IsCharP.intCast_emod (p := c)]
rw [Int.mul_comm] at h
simp [h, intCast_zero, zero_mul, zero_add]
next h _ =>
simp +zetaDelta at h
simp [*, denote, IsCharP.intCast_emod, Mon.denote_mul, intCast_mul, left_distrib,
mul_comm, mul_left_comm, mul_assoc]
theorem Poly.denote_combineC {α c} [CommRing α] [IsCharP α c] (ctx : Context α) (p₁ p₂ : Poly)
: (combineC p₁ p₂ c).denote ctx = p₁.denote ctx + p₂.denote ctx := by
unfold combineC; generalize hugeFuel = fuel
fun_induction combineC.go
<;> simp [combineC.go, *, denote_concat, denote_addConstC, denote, intCast_add,
cond_eq_if, add_comm, add_left_comm, add_assoc, IsCharP.intCast_emod]
next hg _ h _ =>
simp +zetaDelta at h; simp [*]
rw [ add_assoc, Mon.eq_of_grevlex hg, right_distrib, intCast_add,
IsCharP.intCast_emod (p := c),
h, intCast_zero, zero_mul, zero_add]
next hg _ h _ =>
simp +zetaDelta at h; simp [*, denote, intCast_add, IsCharP.intCast_emod]
rw [right_distrib, Mon.eq_of_grevlex hg, add_assoc]
theorem Poly.denote_mulC_go {α c} [CommRing α] [IsCharP α c] (ctx : Context α) (p₁ p₂ acc : Poly)
: (mulC.go p₂ c p₁ acc).denote ctx = acc.denote ctx + p₁.denote ctx * p₂.denote ctx := by
fun_induction mulC.go
<;> simp [mulC.go, denote_combineC, denote_mulConstC, denote, *, right_distrib, denote_mulMonC, add_assoc]
theorem Poly.denote_mulC {α c} [CommRing α] [IsCharP α c] (ctx : Context α) (p₁ p₂ : Poly)
: (mulC p₁ p₂ c).denote ctx = p₁.denote ctx * p₂.denote ctx := by
simp [mulC, denote_mulC_go, denote, intCast_zero, zero_add]
theorem Poly.denote_powC {α c} [CommRing α] [IsCharP α c] (ctx : Context α) (p : Poly) (k : Nat)
: (powC p k c).denote ctx = p.denote ctx ^ k := by
fun_induction powC <;> simp [powC, denote, intCast_one, pow_zero]
next => simp [pow_succ, pow_zero, one_mul]
next => simp [denote_mulC, *, pow_succ, mul_comm]
theorem Expr.denote_toPolyC {α c} [CommRing α] [IsCharP α c] (ctx : Context α) (e : Expr)
: (e.toPolyC c).denote ctx = e.denote ctx := by
unfold toPolyC
fun_induction toPolyC.go
<;> simp [toPolyC.go, denote, Poly.denote, Poly.denote_ofVar, Poly.denote_combineC,
Poly.denote_mulC, Poly.denote_mulConstC, Poly.denote_powC, *]
next => rw [IsCharP.intCast_emod]
next => rw [intCast_neg, neg_mul, intCast_one, one_mul]
next => rw [intCast_neg, neg_mul, intCast_one, one_mul, sub_eq_add_neg]
next => rw [IsCharP.intCast_emod, intCast_pow]
next => simp [Poly.denote_ofMon, Mon.denote, Power.denote_eq]
end CommRing
end Lean.Grind

10
src/Init/Grind/Ext.lean Normal file
View File

@@ -0,0 +1,10 @@
/-
Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
prelude
import Init.Ext
import Init.Grind.Tactics
attribute [grind ext] funext

View File

@@ -165,4 +165,9 @@ theorem of_decide_eq_false {p : Prop} {_ : Decidable p} : decide p = false → p
theorem decide_eq_true {p : Prop} {_ : Decidable p} : p = True decide p = true := by simp
theorem decide_eq_false {p : Prop} {_ : Decidable p} : p = False decide p = false := by simp
/-! Lookahead -/
theorem of_lookahead (p : Prop) (h : (¬ p) False) : p = True := by
simp at h; simp [h]
end Lean.Grind

View File

@@ -25,7 +25,8 @@ syntax grindUsr := &"usr "
syntax grindCases := &"cases "
syntax grindCasesEager := atomic(&"cases" &"eager ")
syntax grindIntro := &"intro "
syntax grindMod := grindEqBoth <|> grindEqRhs <|> grindEq <|> grindEqBwd <|> grindBwd <|> grindFwd <|> grindRL <|> grindLR <|> grindUsr <|> grindCasesEager <|> grindCases <|> grindIntro
syntax grindExt := &"ext "
syntax grindMod := grindEqBoth <|> grindEqRhs <|> grindEq <|> grindEqBwd <|> grindBwd <|> grindFwd <|> grindRL <|> grindLR <|> grindUsr <|> grindCasesEager <|> grindCases <|> grindIntro <|> grindExt
syntax (name := grind) "grind" (grindMod)? : attr
end Attr
end Lean.Parser
@@ -68,13 +69,17 @@ structure Config where
failures : Nat := 1
/-- Maximum number of heartbeats (in thousands) the canonicalizer can spend per definitional equality test. -/
canonHeartbeats : Nat := 1000
/-- If `ext` is `true`, `grind` uses extensionality theorems available in the environment. -/
/-- If `ext` is `true`, `grind` uses extensionality theorems that have been marked with `[grind ext]`. -/
ext : Bool := true
/-- If `extAll` is `true`, `grind` uses any extensionality theorems available in the environment. -/
extAll : Bool := false
/--
If `funext` is `true`, `grind` creates new opportunities for applying function extensionality by case-splitting
on equalities between lambda expressions.
-/
funext : Bool := true
/-- TODO -/
lookahead : Bool := true
/-- If `verbose` is `false`, additional diagnostics information is not collected. -/
verbose : Bool := true
/-- If `clean` is `true`, `grind` uses `expose_names` and only generates accessible names. -/

View File

@@ -275,6 +275,14 @@ class MonadNameGenerator (m : Type → Type) where
export MonadNameGenerator (getNGen setNGen)
/--
Creates a globally unique `Name`, without any semantic interpretation.
The names are not intended to be user-visible.
With the default name generator, names use `_uniq` as a base and have a numeric suffix.
This is used for example by `Lean.mkFreshFVarId`, `Lean.mkFreshMVarId`, and `Lean.mkFreshLMVarId`.
To create fresh user-visible identifiers, use functions such as `Lean.Core.mkFreshUserName` instead.
-/
def mkFreshId {m : Type Type} [Monad m] [MonadNameGenerator m] : m Name := do
let ngen getNGen
let r := ngen.curr

View File

@@ -286,7 +286,7 @@ theorem gcd_cons_div_right : gcd (x::xs) gcd xs := by
apply Nat.gcd_dvd_right
theorem gcd_cons_div_right' : (gcd (x::xs) : Int) (gcd xs : Int) := by
rw [Int.ofNat_dvd_left, Int.natAbs_ofNat]
rw [Int.ofNat_dvd_left, Int.natAbs_natCast]
exact gcd_cons_div_right
theorem gcd_dvd (xs : IntList) {a : Int} (m : a xs) : (xs.gcd : Int) a := by

View File

@@ -1003,7 +1003,7 @@ class BEq (α : Type u) where
open BEq (beq)
instance [DecidableEq α] : BEq α where
instance (priority := 500) [DecidableEq α] : BEq α where
beq a b := decide (Eq a b)

View File

@@ -287,8 +287,8 @@ theorem not_decide_eq_true [h : Decidable p] : ((!decide p) = true) = ¬ p := by
@[simp] theorem cond_true (a b : α) : cond true a b = a := rfl
@[simp] theorem cond_false (a b : α) : cond false a b = b := rfl
@[simp] theorem beq_self_eq_true [BEq α] [LawfulBEq α] (a : α) : (a == a) = true := LawfulBEq.rfl
theorem beq_self_eq_true' [DecidableEq α] (a : α) : (a == a) = true := by simp
theorem beq_self_eq_true [BEq α] [ReflBEq α] (a : α) : (a == a) = true := BEq.rfl
theorem beq_self_eq_true' [DecidableEq α] (a : α) : (a == a) = true := BEq.rfl
@[simp] theorem bne_self_eq_false [BEq α] [LawfulBEq α] (a : α) : (a != a) = false := by simp [bne]
theorem bne_self_eq_false' [DecidableEq α] (a : α) : (a != a) = false := by simp

View File

@@ -1355,6 +1355,8 @@ structure SpawnArgs extends StdioConfig where
and `some` sets the variable to the new value, adding it if necessary. Variables are processed from left to right.
-/
env : Array (String × Option String) := #[]
/-- Inherit environment variables from the spawning process. -/
inheritEnv : Bool := true
/--
Starts the child process in a new session and process group using `setsid`. Currently a no-op on
non-POSIX platforms.

View File

@@ -819,12 +819,12 @@ The left hand side of an induction arm, `| foo a b c` or `| @foo a b c`
where `foo` is a constructor of the inductive type and `a b c` are the arguments
to the constructor.
-/
syntax inductionAltLHS := "| " (("@"? ident) <|> hole) (ident <|> hole)*
syntax inductionAltLHS := withPosition("| " (("@"? ident) <|> hole) (colGt (ident <|> hole))*)
/--
In induction alternative, which can have 1 or more cases on the left
and `_`, `?_`, or a tactic sequence after the `=>`.
-/
syntax inductionAlt := ppDedent(ppLine) inductionAltLHS+ " => " (hole <|> syntheticHole <|> tacticSeq)
syntax inductionAlt := ppDedent(ppLine) inductionAltLHS+ (" => " (hole <|> syntheticHole <|> tacticSeq))?
/--
After `with`, there is an optional tactic that runs on all branches, and
then a list of alternatives.
@@ -1752,7 +1752,7 @@ attribute @[simp ←] and_assoc
```
When multiple simp theorems are applicable, the simplifier uses the one with highest priority.
The equational theorems of function are applied at very low priority (100 and below).
The equational theorems of functions are applied at very low priority (100 and below).
If there are several with the same priority, it is uses the "most recent one". Example:
```lean
@[simp high] theorem cond_true (a b : α) : cond true a b = a := rfl

View File

@@ -17,9 +17,16 @@ private opaque getLeancExtraFlags : Unit → String
private def flagsStringToArray (s : String) : Array String :=
s.splitOn.toArray |>.filter (· "")
/--
Return C compiler flags for including Lean's headers.
Unlike `getCFlags`, this does not contain the Lean include directory.
-/
def getCFlags' : Array String :=
flagsStringToArray (getLeancExtraFlags ())
/-- Return C compiler flags for including Lean's headers. -/
def getCFlags (leanSysroot : FilePath) : Array String :=
#["-I", (leanSysroot / "include").toString] ++ flagsStringToArray (getLeancExtraFlags ())
#["-I", (leanSysroot / "include").toString] ++ getCFlags'
@[extern "lean_get_leanc_internal_flags"]
private opaque getLeancInternalFlags : Unit String
@@ -31,9 +38,16 @@ def getInternalCFlags (leanSysroot : FilePath) : Array String :=
@[extern "lean_get_linker_flags"]
private opaque getBuiltinLinkerFlags (linkStatic : Bool) : String
/--
Return linker flags for linking against Lean's libraries.
Unlike `getLinkerFlags`, this does not contain the Lean library directory.
-/
def getLinkerFlags' (linkStatic := true) : Array String :=
flagsStringToArray (getBuiltinLinkerFlags linkStatic)
/-- Return linker flags for linking against Lean's libraries. -/
def getLinkerFlags (leanSysroot : FilePath) (linkStatic := true) : Array String :=
#["-L", (leanSysroot / "lib" / "lean").toString] ++ flagsStringToArray (getBuiltinLinkerFlags linkStatic)
#["-L", (leanSysroot / "lib" / "lean").toString] ++ getLinkerFlags' linkStatic
@[extern "lean_get_internal_linker_flags"]
private opaque getBuiltinInternalLinkerFlags : Unit String

View File

@@ -44,7 +44,7 @@ def replaceFun (decl : FunDecl) (fvarId : FVarId) : M Unit := do
eraseFunDecl decl
addFVarSubst decl.fvarId fvarId
partial def _root_.Lean.Compiler.LCNF.Code.cse (code : Code) : CompilerM Code :=
partial def _root_.Lean.Compiler.LCNF.Code.cse (shouldElimFunDecls : Bool) (code : Code) : CompilerM Code :=
go code |>.run' {}
where
goFunDecl (decl : FunDecl) : M FunDecl := do
@@ -67,14 +67,18 @@ where
addEntry key decl.fvarId
return code.updateLet! decl ( go k)
| .fun decl k =>
let decl goFunDecl decl
let value := decl.toExpr
match ( get).map.find? value with
| some fvarId' =>
replaceFun decl fvarId'
go k
| none =>
addEntry value decl.fvarId
if shouldElimFunDecls then
let decl goFunDecl decl
let value := decl.toExpr
match ( get).map.find? value with
| some fvarId' =>
replaceFun decl fvarId'
go k
| none =>
addEntry value decl.fvarId
return code.updateFun! decl ( go k)
else
let decl goFunDecl decl
return code.updateFun! decl ( go k)
| .jp decl k =>
let decl goFunDecl decl
@@ -101,12 +105,12 @@ end CSE
/--
Common sub-expression elimination
-/
def Decl.cse (decl : Decl) : CompilerM Decl := do
let value decl.value.mapCodeM (·.cse)
def Decl.cse (shouldElimFunDecls : Bool) (decl : Decl) : CompilerM Decl := do
let value decl.value.mapCodeM (·.cse shouldElimFunDecls)
return { decl with value }
def cse (phase : Phase := .base) (occurrence := 0) : Pass :=
.mkPerDeclaration `cse Decl.cse phase occurrence
def cse (phase : Phase := .base) (shouldElimFunDecls := false) (occurrence := 0) : Pass :=
.mkPerDeclaration `cse (Decl.cse shouldElimFunDecls) phase occurrence
builtin_initialize
registerTraceClass `Compiler.cse (inherited := true)

View File

@@ -142,7 +142,7 @@ mutual
fType := instantiateRevRangeArgs fType j i args |>.headBeta
match fType with
| .forallE _ _ b _ => j := i; fType := b
| _ => return erasedExpr
| _ => return anyExpr
return instantiateRevRangeArgs fType j args.size args |>.headBeta
partial def inferAppType (e : Expr) : InferTypeM Expr := do
@@ -157,7 +157,7 @@ mutual
fType := fType.instantiateRevRange j i args |>.headBeta
match fType with
| .forallE _ _ b _ => j := i; fType := b
| _ => return erasedExpr
| _ => return anyExpr
return fType.instantiateRevRange j args.size args |>.headBeta
partial def inferProjType (structName : Name) (idx : Nat) (s : FVarId) : InferTypeM Expr := do
@@ -167,6 +167,8 @@ mutual
if structType.isErased then
/- TODO: after we erase universe variables, we can just extract a better type using just `structName` and `idx`. -/
return erasedExpr
else if structType.isAny then
return anyExpr
else
matchConstStructure structType.getAppFn failed fun structVal structLvls ctorVal =>
let structTypeArgs := structType.getAppArgs
@@ -179,7 +181,7 @@ mutual
| .forallE _ _ body _ =>
if body.hasLooseBVars then
-- This can happen when one of the fields is a type or type former.
ctorType := body.instantiate1 erasedExpr
ctorType := body.instantiate1 anyExpr
else
ctorType := body
| _ =>

View File

@@ -178,16 +178,8 @@ def eagerLambdaLifting : Pass where
name := `eagerLambdaLifting
run := fun decls => do
decls.foldlM (init := #[]) fun decls decl => do
if ( Meta.isInstance decl.name) then
/-
Recall that we lambda lift local functions in instances to control code blowup, and make sure they are cheap to inline.
It is not worth to lift tiny ones. TODO: evaluate whether we should add a compiler option to control the min size.
Recall that when performing eager lambda lifting in instances, we progatate the `[inline]` annotations to the new auxiliary functions.
Note: we have tried `if decl.inlineable then return decls.push decl`, but it didn't help in our preliminary experiments.
-/
return decls ++ ( decl.lambdaLifting (liftInstParamOnly := false) (suffix := `_elam) (inheritInlineAttrs := true) (minSize := 3))
if decl.inlineAttr || ( Meta.isInstance decl.name) then
return decls.push decl
else
return decls ++ ( decl.lambdaLifting (liftInstParamOnly := true) (suffix := `_elam))

View File

@@ -85,7 +85,11 @@ partial def toMonoType (type : Expr) : CoreM Expr := do
where
visitApp (f : Expr) (args : Array Expr) : CoreM Expr := do
match f with
| .const ``lcErased _ => return erasedExpr
| .const ``lcErased _ =>
if args.all (·.isErased) then
return erasedExpr
else
return anyExpr
| .const ``lcAny _ => return anyExpr
| .const ``Decidable _ => return mkConst ``Bool
| .const declName us =>
@@ -101,7 +105,7 @@ where
if d matches .const ``lcErased _ | .sort _ then
result := mkApp result ( toMonoType arg)
else
result := mkApp result erasedExpr
result := mkApp result anyExpr
type := b.instantiate1 arg
return result
| _ => return anyExpr

View File

@@ -46,7 +46,7 @@ def builtinPassManager : PassManager := {
passes := #[
init,
pullInstances,
cse,
cse (shouldElimFunDecls := false),
simp,
floatLetIn,
findJoinPoints,
@@ -61,7 +61,7 @@ def builtinPassManager : PassManager := {
eagerLambdaLifting,
specialize,
simp (occurrence := 2),
cse (occurrence := 1),
cse (shouldElimFunDecls := false) (occurrence := 1),
saveBase, -- End of base phase
toMono,
simp (occurrence := 3) (phase := .mono),

View File

@@ -69,9 +69,14 @@ mutual
partial def pullDecls (code : Code) : PullM Code := do
match code with
| .cases c =>
withCheckpoint do
let alts c.alts.mapMonoM pullAlt
return code.updateAlts! alts
-- At the present time, we can't correctly enforce the dependencies required for lifting
-- out of a cases expression on Decidable, so we disable this optimization.
if c.typeName == ``Decidable then
return code
else
withCheckpoint do
let alts c.alts.mapMonoM pullAlt
return code.updateAlts! alts
| .let decl k =>
if ( shouldPull decl) then
pullDecls k

View File

@@ -42,30 +42,13 @@ def Decl.simp? (decl : Decl) : SimpM (Option Decl) := do
partial def Decl.simp (decl : Decl) (config : Config) : CompilerM Decl := do
let mut config := config
if ( isTemplateLike decl) then
let mut inlineDefs := config.inlineDefs
/-
At the base phase, we don't inline definitions occurring in instances.
Reason: we eagerly lambda lift local functions occurring at instances before saving their code at the end of the base
phase. The goal is to make them cheap to inline in actual code. By inlining definitions we would be just generating extra
work for the lambda lifter.
There is an exception: inlineable instances. This is important for auxiliary instances such as
```
@[always_inline]
instance : Monad TermElabM := let i := inferInstanceAs (Monad TermElabM); { pure := i.pure, bind := i.bind }
```
by keeping `inlineDefs := true`, we can pre-compute the `pure` and `bind` methods for `TermElabM`.
-/
if ( inBasePhase <&&> Meta.isInstance decl.name) then
unless decl.inlineable do
inlineDefs := false
/-
We do not eta-expand or inline partial applications in template like code.
Recall we don't want to generate code for them.
Remark: by eta-expanding partial applications in instances, we also make the simplifier
work harder when inlining instance projections.
-/
config := { config with etaPoly := false, inlinePartial := false, inlineDefs }
config := { config with etaPoly := false, inlinePartial := false }
go decl config
where
go (decl : Decl) (config : Config) : CompilerM Decl := do

View File

@@ -261,6 +261,9 @@ def getRemainingArgs (paramsInfo : Array SpecParamInfo) (args : Array Arg) : Arr
result := result.push arg
return result ++ args[paramsInfo.size:]
def paramsToVarSet (params : Array Param) : FVarIdSet :=
params.foldl (fun r p => r.insert p.fvarId) {}
mutual
/--
Try to specialize the function application in the given let-declaration.
@@ -295,7 +298,8 @@ mutual
specDecl.saveBase
let specDecl specDecl.simp {}
let specDecl specDecl.simp { etaPoly := true, inlinePartial := true, implementedBy := true }
let value withReader (fun _ => { declName := specDecl.name }) do
let ground := paramsToVarSet specDecl.params
let value withReader (fun _ => { declName := specDecl.name, ground }) do
withParams specDecl.params <| specDecl.value.mapCodeM visitCode
let specDecl := { specDecl with value }
modify fun s => { s with decls := s.decls.push specDecl }
@@ -337,7 +341,8 @@ def main (decl : Decl) : SpecializeM Decl := do
end Specialize
partial def Decl.specialize (decl : Decl) : CompilerM (Array Decl) := do
let (decl, s) Specialize.main decl |>.run { declName := decl.name } |>.run {}
let ground := Specialize.paramsToVarSet decl.params
let (decl, s) Specialize.main decl |>.run { declName := decl.name, ground } |>.run {}
return s.decls.push decl
def specialize : Pass where

View File

@@ -8,6 +8,7 @@ import Lean.ProjFns
import Lean.Meta.CtorRecognizer
import Lean.Compiler.BorrowedAnnotation
import Lean.Compiler.CSimpAttr
import Lean.Compiler.ImplementedByAttr
import Lean.Compiler.LCNF.Types
import Lean.Compiler.LCNF.Bind
import Lean.Compiler.LCNF.InferType
@@ -302,7 +303,7 @@ are type formers. This can happen when we have a field whose type is, for exampl
def applyToAny (type : Expr) : M Expr := do
let toAny := ( get).toAny
return type.replace fun
| .fvar fvarId => if toAny.contains fvarId then some erasedExpr else none
| .fvar fvarId => if toAny.contains fvarId then some anyExpr else none
| _ => none
def toLCNFType (type : Expr) : M Expr := do
@@ -567,6 +568,33 @@ where
let result := .fvar auxDecl.fvarId
mkOverApplication result args casesInfo.arity
visitCasesImplementedBy (casesInfo : CasesInfo) (f : Expr) (args : Array Expr) : M Arg := do
let mut args := args
let discr := args[casesInfo.discrPos]!
if discr matches .fvar _ then
let typeName := casesInfo.declName.getPrefix
let .inductInfo indVal getConstInfo typeName | unreachable!
args args.mapIdxM fun i arg => do
unless casesInfo.altsRange.start <= i && i < casesInfo.altsRange.stop do return arg
let altIdx := i - casesInfo.altsRange.start
let numParams := casesInfo.altNumParams[altIdx]!
let ctorName := indVal.ctors[altIdx]!
-- We simplify `casesOn` arguments that simply reconstruct the discriminant and replace
-- them with the actual discriminant. This is required for hash consing to work correctly,
-- and should eventually be fixed by changing the elaborated term to use the original
-- variable.
Meta.MetaM.run' <| Meta.lambdaBoundedTelescope arg numParams fun paramExprs body => do
let fn := body.getAppFn
let args := body.getAppArgs
let args := args.map fun arg =>
if arg.getAppFn.constName? == some ctorName && arg.getAppArgs == paramExprs then
discr
else
arg
Meta.mkLambdaFVars paramExprs (mkAppN fn args)
visitAppDefaultConst f args
visitCtor (arity : Nat) (e : Expr) : M Arg :=
etaIfUnderApplied e arity do
visitAppDefaultConst e.getAppFn e.getAppArgs
@@ -671,7 +699,7 @@ where
visitApp (e : Expr) : M Arg := do
if let some (args, n, t, v, b) := e.letFunAppArgs? then
visitCore <| mkAppN (.letE n t v b (nonDep := true)) args
else if let .const declName _ := CSimp.replaceConstants ( getEnv) e.getAppFn then
else if let .const declName us := CSimp.replaceConstants ( getEnv) e.getAppFn then
if declName == ``Quot.lift then
visitQuotLift e
else if declName == ``Quot.mk then
@@ -687,7 +715,10 @@ where
else if declName == ``False.rec || declName == ``Empty.rec || declName == ``False.casesOn || declName == ``Empty.casesOn then
visitFalseRec e
else if let some casesInfo getCasesInfo? declName then
visitCases casesInfo e
if (getImplementedBy? ( getEnv) declName).isSome then
e.withApp (visitCasesImplementedBy casesInfo)
else
visitCases casesInfo e
else if let some arity getCtorArity? declName then
visitCtor arity e
else if isNoConfusion ( getEnv) declName then

View File

@@ -18,6 +18,9 @@ def anyExpr := mkConst ``lcAny
def _root_.Lean.Expr.isErased (e : Expr) :=
e.isAppOf ``lcErased
def _root_.Lean.Expr.isAny (e : Expr) :=
e.isAppOf ``lcAny
def isPropFormerTypeQuick : Expr Bool
| .forallE _ _ b _ => isPropFormerTypeQuick b
| .sort .zero => true
@@ -132,7 +135,7 @@ partial def toLCNFType (type : Expr) : MetaM Expr := do
| .forallE .. => visitForall type #[]
| .app .. => type.withApp visitApp
| .fvar .. => visitApp type #[]
| _ => return erasedExpr
| _ => return mkConst ``lcAny
where
whnfEta (type : Expr) : MetaM Expr := do
let type whnf type
@@ -156,10 +159,10 @@ where
visitApp (f : Expr) (args : Array Expr) := do
let fNew match f with
| .const declName us =>
let .inductInfo _ getConstInfo declName | return erasedExpr
let .inductInfo _ getConstInfo declName | return anyExpr
pure <| .const declName us
| .fvar .. => pure f
| _ => return erasedExpr
| _ => return anyExpr
let mut result := fNew
for arg in args do
if ( isProp arg) then
@@ -169,13 +172,13 @@ where
else if ( isTypeFormer arg) then
result := mkApp result ( toLCNFType arg)
else
result := mkApp result erasedExpr
result := mkApp result (mkConst ``lcAny)
return result
mutual
partial def joinTypes (a b : Expr) : Expr :=
joinTypes? a b |>.getD erasedExpr
joinTypes? a b |>.getD (mkConst ``lcAny)
partial def joinTypes? (a b : Expr) : Option Expr := do
if a.isErased || b.isErased then
@@ -194,16 +197,16 @@ partial def joinTypes? (a b : Expr) : Option Expr := do
| .app f a, .app g b =>
(do return .app ( joinTypes? f g) ( joinTypes? a b))
<|>
return erasedExpr
return (mkConst ``lcAny)
| .forallE n d₁ b₁ _, .forallE _ d₂ b₂ _ =>
(do return .forallE n ( joinTypes? d₁ d₂) (joinTypes b₁ b₂) .default)
<|>
return erasedExpr
return (mkConst ``lcAny)
| .lam n d₁ b₁ _, .lam _ d₂ b₂ _ =>
(do return .lam n ( joinTypes? d₁ d₂) (joinTypes b₁ b₂) .default)
<|>
return erasedExpr
| _, _ => return erasedExpr
return (mkConst ``lcAny)
| _, _ => return (mkConst ``lcAny)
end

View File

@@ -303,6 +303,17 @@ private def mkFreshNameImp (n : Name) : CoreM Name := do
let fresh modifyGet fun s => (s.nextMacroScope, { s with nextMacroScope := s.nextMacroScope + 1 })
return addMacroScope ( getEnv).mainModule n fresh
/--
Creates a name from `n` that is guaranteed to be unique.
This is intended to be used for creating inaccessible user names for free variables and constants.
It works by adding a fresh macro scope to `n`.
Applying `Lean.Name.eraseMacroScopes` to the resulting name yields `n`.
See also `Lean.LocalContext.getUnusedName` (for creating a new accessible user name that is
unused in the local context) and `Lean.Meta.mkFreshBinderNameForTactic` (for creating names
that are conditionally inaccessible, depending on the current value of the `tactic.hygiene` option).
-/
def mkFreshUserName (n : Name) : CoreM Name :=
mkFreshNameImp n

View File

@@ -6,6 +6,8 @@ Authors: Joachim Breitner
prelude
import Init.Data.RArray
import Lean.Meta.InferType
import Lean.Meta.DecLevel
import Lean.ToExpr
/-!
@@ -54,22 +56,20 @@ theorem RArray.size_ofFn {n : Nat} (f : Fin n → α) (h : 0 < n) :
where
go lb ub h1 h2 : (ofFn.go f lb ub h1 h2).size = ub - lb := by
induction lb, ub, h1, h2 using RArray.ofFn.go.induct (n := n)
case case1 => simp [ofFn.go, size]; omega
case case1 => simp [ofFn.go, size]
case case2 ih1 ih2 hiu => rw [ofFn.go]; simp +zetaDelta [size, *]; omega
section Meta
open Lean
def RArray.toExpr (ty : Expr) (f : α Expr) : RArray α Expr
| .leaf x =>
mkApp2 (mkConst ``RArray.leaf) ty (f x)
| .branch p l r =>
mkApp4 (mkConst ``RArray.branch) ty (mkRawNatLit p) (l.toExpr ty f) (r.toExpr ty f)
instance [ToExpr α] : ToExpr (RArray α) where
toTypeExpr := mkApp (mkConst ``RArray) (toTypeExpr α)
toExpr a := a.toExpr (toTypeExpr α) toExpr
end Meta
open Meta in
def RArray.toExpr (ty : Expr) (f : α Expr) (a : RArray α) : MetaM Expr := do
let u getDecLevel ty
let leaf := mkConst ``RArray.leaf [u]
let branch := mkConst ``RArray.branch [u]
let rec go (a : RArray α) : MetaM Expr := do
match a with
| .leaf x =>
return mkApp2 leaf ty (f x)
| .branch p l r =>
return mkApp4 branch ty (mkRawNatLit p) ( go l) ( go r)
go a
end Lean

View File

@@ -288,7 +288,7 @@ where
let ras := Lean.RArray.ofArray as h
let packedType := mkConst ``BVExpr.PackedBitVec
let pack := fun (width, expr) => mkApp2 (mkConst ``BVExpr.PackedBitVec.mk) (toExpr width) expr
let newAtomsAssignment := ras.toExpr packedType pack
let newAtomsAssignment ras.toExpr packedType pack
modify fun s => { s with atomsAssignmentCache := some newAtomsAssignment }
return newAtomsAssignment
else

View File

@@ -45,24 +45,32 @@ def isSupportedMatch (declName : Name) : MetaM (Option MatchKind) := do
let retTypeOk a.withApp fun fn arg =>
return fn == motive && arg.size == 1 && arg[0]! == discr
if !retTypeOk then return none
let numCtors := inductiveInfo.numCtors
/-
At this point the control flow splits and tries to establish that the match is one of the kinds
that we support.
-/
if xs.size == numCtors + 2 then
-- Probably a full match
/-
This situation is most likely a full match but it could also be a match like:
```
inductive Foo where
| a
| b
-- Check that all parameters are `h_n EnumInductive.ctor`
let mut handledCtors := Array.mkEmpty numCtors
for i in [0:numCtors] do
let argType inferType xs[i + 2]!
let some (.const ``Unit [], (.app m (.const c []))) := argType.arrow? | return none
if m != motive then return none
let .ctorInfo ctorInfo getConstInfo c | return none
handledCtors := handledCtors.push ctorInfo
def isA (f : Foo) : Bool :=
match f with
| .a => true
| _ => false
```
Where we have as many arms as constructors but the last arm is a default.
-/
if !( verifySimpleEnum defnInfo inductiveInfo handledCtors) then return none
if let some kind trySimpleEnum defnInfo inductiveInfo xs numCtors motive then
return kind
return some <| .simpleEnum inductiveInfo handledCtors
else if xs.size > 2 then
if xs.size > 2 then
-- Probably a match with default case
-- Check that all parameters except the last are `h_n EnumInductive.ctor`
@@ -90,6 +98,21 @@ def isSupportedMatch (declName : Name) : MetaM (Option MatchKind) := do
else
return none
where
trySimpleEnum (defnInfo : DefinitionVal) (inductiveInfo : InductiveVal) (xs : Array Expr)
(numCtors : Nat) (motive : Expr) : MetaM (Option MatchKind) := do
-- Check that all parameters are `h_n EnumInductive.ctor`
let mut handledCtors := Array.mkEmpty numCtors
for i in [0:numCtors] do
let argType inferType xs[i + 2]!
let some (.const ``Unit [], (.app m (.const c []))) := argType.arrow? | return none
if m != motive then return none
let .ctorInfo ctorInfo getConstInfo c | return none
handledCtors := handledCtors.push ctorInfo
if !( verifySimpleEnum defnInfo inductiveInfo handledCtors) then return none
return some <| .simpleEnum inductiveInfo handledCtors
verifySimpleCasesOnApp (inductiveInfo : InductiveVal) (fn : Expr) (args : Array Expr)
(params : Array Expr) : MetaM Bool := do
-- Body is an application of `EnumInductive.casesOn`

View File

@@ -191,10 +191,10 @@ private def getOptRotation (stx : Syntax) : Nat :=
let mvarIds getGoals
let mut mvarIdsNew := #[]
let mut abort := false
let mut mctxSaved getMCtx
for mvarId in mvarIds do
unless ( mvarId.isAssigned) do
setGoals [mvarId]
let saved saveState
abort Tactic.tryCatch
(do
evalTactic stx[1]
@@ -202,13 +202,15 @@ private def getOptRotation (stx : Syntax) : Nat :=
(fun ex => do
if ( read).recover then
logException ex
let msgLog Core.getMessageLog
saved.restore
Core.setMessageLog msgLog
admitGoal mvarId
pure true
else
throw ex)
mvarIdsNew := mvarIdsNew ++ ( getUnsolvedGoals)
if abort then
setMCtx mctxSaved
mvarIds.forM fun mvarId => unless ( mvarId.isAssigned) do admitGoal mvarId
throwAbortTactic
setGoals mvarIdsNew.toList

View File

@@ -89,6 +89,8 @@ def elabGrindParams (params : Grind.Params) (ps : TSyntaxArray ``Parser.Tactic.
params withRef p <| addEMatchTheorem params ctor .default
else
throwError "invalid use of `intro` modifier, `{declName}` is not an inductive predicate"
| .ext =>
throwError "`[grind ext]` cannot be set using parameters"
| .infer =>
if let some declName Grind.isCasesAttrCandidate? declName false then
params := { params with casesTypes := params.casesTypes.insert declName false }

View File

@@ -26,8 +26,10 @@ open Meta
Given an `inductionAlt` of the form
```
syntax inductionAltLHS := "| " (group("@"? ident) <|> hole) (ident <|> hole)*
syntax inductionAlt := ppDedent(ppLine) inductionAltLHS+ " => " (hole <|> syntheticHole <|> tacticSeq)
syntax inductionAlt := ppDedent(ppLine) inductionAltLHS+ (" => " (hole <|> syntheticHole <|> tacticSeq))?
```
We assume that the syntax has been expanded. There is exactly one `inductionAltLHS`,
and `" => " (hole <|> syntheticHole <|> tacticSeq)` is present
-/
private def getAltLhses (alt : Syntax) : Syntax :=
alt[0]
@@ -49,36 +51,40 @@ private def altHasExplicitModifier (alt : Syntax) : Bool :=
private def getAltVars (alt : Syntax) : Array Syntax :=
let lhs := getFirstAltLhs alt
lhs[2].getArgs
private def hasAltRHS (alt : Syntax) : Bool :=
alt[1].getNumArgs > 0
private def getAltRHS (alt : Syntax) : Syntax :=
alt[2]
alt[1][1]
private def getAltDArrow (alt : Syntax) : Syntax :=
alt[1]
alt[1][0]
-- Return true if `stx` is a term occurring in the RHS of the induction/cases tactic
def isHoleRHS (rhs : Syntax) : Bool :=
rhs.isOfKind ``Parser.Term.syntheticHole || rhs.isOfKind ``Parser.Term.hole
def evalAlt (mvarId : MVarId) (alt : Syntax) (addInfo : TermElabM Unit) : TacticM Unit :=
let rhs := getAltRHS alt
withCaseRef (getAltDArrow alt) rhs do
if isHoleRHS rhs then
addInfo
mvarId.withContext <| withTacticInfoContext rhs do
let mvarDecl mvarId.getDecl
let val elabTermEnsuringType rhs mvarDecl.type
mvarId.assign val
let gs' getMVarsNoDelayed val
tagUntaggedGoals mvarDecl.userName `induction gs'.toList
setGoals <| ( getGoals) ++ gs'.toList
else
def evalAlt (mvarId : MVarId) (alt : Syntax) (addInfo : TermElabM Unit) : TacticM Unit := do
if !hasAltRHS alt then
throwErrorAt alt "(internal error) RHS was not expanded"
else
let rhs := getAltRHS alt
withCaseRef (getAltDArrow alt) rhs do
let goals getGoals
setGoals []
try
setGoals [mvarId]
closeUsingOrAdmit <|
withTacticInfoContext (mkNullNode #[getAltLhses alt, getAltDArrow alt]) <|
(addInfo *> evalTactic rhs)
withTacticInfoContext (mkNullNode #[getAltLhses alt, getAltDArrow alt]) do
addInfo
if isHoleRHS rhs then
mvarId.withContext do
let mvarDecl mvarId.getDecl
-- Elaborate ensuring that `_` is interpreted as `?_`.
let (val, gs') elabTermWithHoles rhs mvarDecl.type `induction (parentTag? := mvarDecl.userName) (allowNaturalHoles := true)
mvarId.assign val
setGoals gs'
else
closeUsingOrAdmit <| evalTactic rhs
finally
setGoals goals
pushGoals goals
/-!
Helper method for creating an user-defined eliminator/recursor application.
@@ -432,7 +438,8 @@ where
-- all previous alternatives have to be unchanged for reuse
Term.withNarrowedArgTacticReuse (stx := mkNullNode altStxs) (argIdx := altStxIdx) fun altStx => do
-- everything up to rhs has to be unchanged for reuse
Term.withNarrowedArgTacticReuse (stx := altStx) (argIdx := 2) fun _rhs => do
Term.withNarrowedArgTacticReuse (stx := altStx) (argIdx := 1) fun rhs? => do
Term.withNarrowedArgTacticReuse (stx := rhs?) (argIdx := 1) fun _rhs => do
-- disable reuse if rhs is run multiple times
Term.withoutTacticIncrementality (altMVarIds.length != 1 || isWildcard altStx) do
for altMVarId' in altMVarIds do
@@ -531,13 +538,25 @@ private def withAltsOfOptInductionAlts (optInductionAlts : Syntax)
private def getOptPreTacOfOptInductionAlts (optInductionAlts : Syntax) : Syntax :=
if optInductionAlts.isNone then mkNullNode else optInductionAlts[0][1]
private def isMultiAlt (alt : Syntax) : Bool :=
alt[0].getNumArgs > 1
/--
Returns true if the `Lean.Parser.Tactic.inductionAlt` either has more than one alternative
or has no RHS.
-/
private def shouldExpandAlt (alt : Syntax) : Bool :=
alt[0].getNumArgs > 1 || (1 < alt.getNumArgs && alt[1].getNumArgs == 0)
/-- Return `some #[alt_1, ..., alt_n]` if `alt` has multiple LHSs. -/
private def expandMultiAlt? (alt : Syntax) : Option (Array Syntax) := Id.run do
if isMultiAlt alt then
some <| alt[0].getArgs.map fun lhs => alt.setArg 0 (mkNullNode #[lhs])
/--
Returns `some #[alt_1, ..., alt_n]` if `alt` has multiple LHSs or if `alt` has no RHS.
If there is no RHS, it is filled in with a hole.
-/
private def expandAlt? (alt : Syntax) : Option (Array Syntax) := Id.run do
if shouldExpandAlt alt then
some <| alt[0].getArgs.map fun lhs =>
let alt := alt.setArg 0 (mkNullNode #[lhs])
if 1 < alt.getNumArgs && alt[1].getNumArgs == 0 then
alt.setArg 1 <| mkNullNode #[mkAtomFrom lhs "=>", mkHole lhs]
else
alt
else
none
@@ -546,17 +565,17 @@ Given `inductionAlts` of the form
```
syntax inductionAlts := "with " (tactic)? withPosition( (colGe inductionAlt)*)
```
Return `some inductionAlts'` if one of the alternatives have multiple LHSs, in the new `inductionAlts'`
all alternatives have a single LHS.
Return `some inductionAlts'` if one of the alternatives has multiple LHSs or no RHS.
In the new `inductionAlts'` all alternatives have a single LHS.
Remark: the `RHS` of alternatives with multi LHSs is copied.
-/
private def expandInductionAlts? (inductionAlts : Syntax) : Option Syntax := Id.run do
let alts := getAltsOfInductionAlts inductionAlts
if alts.any isMultiAlt then
if alts.any shouldExpandAlt then
let mut altsNew := #[]
for alt in alts do
if let some alt' := expandMultiAlt? alt then
if let some alt' := expandAlt? alt then
altsNew := altsNew ++ alt'
else
altsNew := altsNew.push alt

View File

@@ -674,16 +674,19 @@ open Lean Elab Tactic Parser.Tactic
def omegaTactic (cfg : OmegaConfig) : TacticM Unit := do
let declName? Term.getDeclName?
liftMetaFinishingTactic fun g => do
let some g g.falseOrByContra | return ()
g.withContext do
let type g.getType
let g' mkFreshExprSyntheticOpaqueMVar type
let hyps := ( getLocalHyps).toList
trace[omega] "analyzing {hyps.length} hypotheses:\n{← hyps.mapM inferType}"
omega hyps g'.mvarId! cfg
-- Omega proofs are typically rather large, so hide them in a separate definition
let e mkAuxTheorem (prefix? := declName?) type ( instantiateMVarsProfiling g') (zetaDelta := true)
g.assign e
if debug.terminalTacticsAsSorry.get ( getOptions) then
g.admit
else
let some g g.falseOrByContra | return ()
g.withContext do
let type g.getType
let g' mkFreshExprSyntheticOpaqueMVar type
let hyps := ( getLocalHyps).toList
trace[omega] "analyzing {hyps.length} hypotheses:\n{← hyps.mapM inferType}"
omega hyps g'.mvarId! cfg
-- Omega proofs are typically rather large, so hide them in a separate definition
let e mkAuxTheorem (prefix? := declName?) type ( instantiateMVarsProfiling g') (zetaDelta := true)
g.assign e
/-- The `omega` tactic, for resolving integer and natural linear arithmetic problems. This

View File

@@ -908,8 +908,11 @@ def levelMVarToParam (e : Expr) (except : LMVarId → Bool := fun _ => false) :
return r.expr
/--
Auxiliary method for creating fresh binder names.
Do not confuse with the method for creating fresh free/meta variable ids. -/
Creates a fresh inaccessible binder name based on `x`.
Equivalent to ``Lean.Core.mkFreshUserName `x``.
Do not confuse with `Lean.mkFreshId`, for creating fresh free variable and metavariable ids.
-/
def mkFreshBinderName [Monad m] [MonadQuotation m] : m Name :=
withFreshMacroScope <| MonadQuotation.addMacroScope `x

View File

@@ -62,6 +62,15 @@ This is triggered by `attribute [-ext] name`.
def ExtTheorems.eraseCore (d : ExtTheorems) (declName : Name) : ExtTheorems :=
{ d with erased := d.erased.insert declName }
/-- Returns `true` if `d` contains theorem with name `declName`. -/
def ExtTheorems.contains (d : ExtTheorems) (declName : Name) : Bool :=
d.tree.containsValueP (·.declName == declName) && !d.erased.contains declName
/-- Returns `true` if `declName` is tagged with `[ext]` attribute. -/
def isExtTheorem (declName : Name) : CoreM Bool := do
let extTheorems := extExtension.getState ( getEnv)
return extTheorems.contains declName
/--
Erases a name marked as a `ext` attribute.
Check that it does in fact have the `ext` attribute by making sure it names a `ExtTheorem`
@@ -69,7 +78,7 @@ found somewhere in the state's tree, and is not erased.
-/
def ExtTheorems.erase [Monad m] [MonadError m] (d : ExtTheorems) (declName : Name) :
m ExtTheorems := do
unless d.tree.containsValueP (·.declName == declName) && !d.erased.contains declName do
unless d.contains declName do
throwError "'{declName}' does not have [ext] attribute"
return d.eraseCore declName

View File

@@ -189,6 +189,16 @@ def lambdaTelescope1 {n} [MonadControlT MetaM n] [MonadError n] [MonadNameGenera
throwError "lambdaTelescope1: expected lambda, got {e}"
k xs[0]!.fvarId! body
/-- There are multiple variants of this function around in the code base, maybe unify at some point. -/
private def elimTypeAnnotations (type : Expr) : CoreM Expr := do
Core.transform type fun e =>
if e.isOptParam || e.isAutoParam then
return .visit e.appFn!.appArg!
else if e.isOutParam || e.isSemiOutParam then
return .visit e.appArg!
else
return .continue
/--
A monad to help collecting inductive hypothesis.
@@ -746,7 +756,7 @@ where doRealize (inductName : Name) := do
check e'
let eTyp inferType e'
let eTyp elimOptParam eTyp
let eTyp elimTypeAnnotations eTyp
-- logInfo m!"eTyp: {eTyp}"
let levelParams := (collectLevelParams {} eTyp).params
-- Prune unused level parameters, preserving the original order
@@ -1121,7 +1131,7 @@ where doRealize inductName := do
check e'
let eTyp inferType e'
let eTyp elimOptParam eTyp
let eTyp elimTypeAnnotations eTyp
-- logInfo m!"eTyp: {eTyp}"
let levelParams := (collectLevelParams {} eTyp).params
-- Prune unused level parameters, preserving the original order
@@ -1199,7 +1209,7 @@ def deriveCases (name : Name) : MetaM Unit := do
check e'
let eTyp inferType e'
let eTyp elimOptParam eTyp
let eTyp elimTypeAnnotations eTyp
-- logInfo m!"eTyp: {eTyp}"
let levelParams := (collectLevelParams {} eTyp).params
-- Prune unused level parameters, preserving the original order

View File

@@ -30,6 +30,7 @@ import Lean.Meta.Tactic.Grind.MatchCond
import Lean.Meta.Tactic.Grind.MatchDiscrOnly
import Lean.Meta.Tactic.Grind.Diseq
import Lean.Meta.Tactic.Grind.MBTC
import Lean.Meta.Tactic.Grind.Lookahead
namespace Lean
@@ -54,6 +55,10 @@ builtin_initialize registerTraceClass `grind.beta
builtin_initialize registerTraceClass `grind.mbtc
builtin_initialize registerTraceClass `grind.ext
builtin_initialize registerTraceClass `grind.ext.candidate
builtin_initialize registerTraceClass `grind.lookahead
builtin_initialize registerTraceClass `grind.lookahead.add (inherited := true)
builtin_initialize registerTraceClass `grind.lookahead.try (inherited := true)
builtin_initialize registerTraceClass `grind.lookahead.assert (inherited := true)
/-! Trace options for `grind` developers -/
builtin_initialize registerTraceClass `grind.debug

View File

@@ -82,14 +82,14 @@ private def mkLetOfMap {_ : Hashable α} {_ : BEq α} (m : Std.HashMap α Expr)
i := i - 1
return e
private def toContextExprCore (vars : PArray Expr) (type : Expr) : Expr :=
private def toContextExprCore (vars : PArray Expr) (type : Expr) : MetaM Expr :=
if h : 0 < vars.size then
RArray.toExpr type id (RArray.ofFn (vars[·]) h)
else
RArray.toExpr type id (RArray.leaf (mkIntLit 0))
private def toContextExpr : GoalM Expr := do
return toContextExprCore ( getVars) (mkConst ``Int)
toContextExprCore ( getVars) (mkConst ``Int)
private def withForeignContexts (k : Std.HashMap ForeignType Expr GoalM α) : GoalM α := do
go 1 ( get').foreignVars.toList {}
@@ -99,8 +99,8 @@ where
| [] => k r
| (type, ctx) :: ctxs =>
let typeExpr := type.denoteType
let ctxExpr := toContextExprCore ctx typeExpr
withLetDecl ((`ctx).appendIndexAfter i) (mkApp (mkConst ``RArray) typeExpr) ctxExpr fun ctx => do
let ctxExpr toContextExprCore ctx typeExpr
withLetDecl ((`ctx).appendIndexAfter i) (mkApp (mkConst ``RArray [levelZero]) typeExpr) ctxExpr fun ctx => do
go (i+1) ctxs (r.insert type ctx)
private def getLetCtxVars : ProofM (Array Expr) := do
@@ -110,7 +110,7 @@ private def getLetCtxVars : ProofM (Array Expr) := do
return r
private abbrev withProofContext (x : ProofM Expr) : GoalM Expr := do
withLetDecl `ctx (mkApp (mkConst ``RArray) (mkConst ``Int)) ( toContextExpr) fun ctx =>
withLetDecl `ctx (mkApp (mkConst ``RArray [levelZero]) (mkConst ``Int)) ( toContextExpr) fun ctx =>
withForeignContexts fun foreignCtxs =>
go { ctx, foreignCtxs } |>.run' {}
where

View File

@@ -6,6 +6,7 @@ Authors: Leonardo de Moura
prelude
import Lean.Meta.Tactic.Grind.EMatchTheorem
import Lean.Meta.Tactic.Grind.Cases
import Lean.Meta.Tactic.Grind.ExtAttr
namespace Lean.Meta.Grind
@@ -14,6 +15,7 @@ inductive AttrKind where
| cases (eager : Bool)
| intro
| infer
| ext
/-- Return theorem kind for `stx` of the form `Attr.grindThmMod` -/
def getAttrKindCore (stx : Syntax) : CoreM AttrKind := do
@@ -34,6 +36,7 @@ def getAttrKindCore (stx : Syntax) : CoreM AttrKind := do
| `(Parser.Attr.grindMod| cases) => return .cases false
| `(Parser.Attr.grindMod| cases eager) => return .cases true
| `(Parser.Attr.grindMod| intro) => return .intro
| `(Parser.Attr.grindMod| ext) => return .ext
| _ => throwError "unexpected `grind` theorem kind: `{stx}`"
/-- Return theorem kind for `stx` of the form `(Attr.grindMod)?` -/
@@ -78,6 +81,7 @@ builtin_initialize
addEMatchAttr ctor attrKind .default
else
throwError "invalid `[grind intro]`, `{declName}` is not an inductive predicate"
| .ext => addExtAttr declName attrKind
| .infer =>
if let some declName isCasesAttrCandidate? declName false then
addCasesAttr declName false attrKind
@@ -91,6 +95,8 @@ builtin_initialize
erase := fun declName => MetaM.run' do
if ( isCasesAttrCandidate declName false) then
eraseCasesAttr declName
else if ( isExtTheorem declName) then
eraseExtAttr declName
else
eraseEMatchAttr declName
}

View File

@@ -0,0 +1,43 @@
/-
Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
prelude
import Lean.Meta.Tactic.Ext
namespace Lean.Meta.Grind
/-! Grind extensionality attribute to mark which `[ext]` theorems should be used. -/
/-- Extensionality theorems that can be used by `grind` -/
abbrev ExtTheorems := PHashSet Name
builtin_initialize extTheoremsExt : SimpleScopedEnvExtension Name ExtTheorems
registerSimpleScopedEnvExtension {
initial := {}
addEntry := fun s declName => s.insert declName
}
def validateExtAttr (declName : Name) : CoreM Unit := do
unless ( Ext.isExtTheorem declName) do
throwError "invalid `[grind ext]`, `{declName}` is not tagged with `[ext]`"
def addExtAttr (declName : Name) (attrKind : AttributeKind) : CoreM Unit := do
validateExtAttr declName
extTheoremsExt.add declName attrKind
private def eraseDecl (s : ExtTheorems) (declName : Name) : CoreM ExtTheorems := do
if s.contains declName then
return s.erase declName
else
throwError "`{declName}` is not marked with the `[grind ext]` attribute"
def eraseExtAttr (declName : Name) : CoreM Unit := do
let s := extTheoremsExt.getState ( getEnv)
let s eraseDecl s declName
modifyEnv fun env => extTheoremsExt.modifyState env fun _ => s
def isExtTheorem (declName : Name) : CoreM Bool := do
return extTheoremsExt.getState ( getEnv) |>.contains declName
end Lean.Meta.Grind

View File

@@ -62,10 +62,10 @@ private def checkAndAddSplitCandidate (e : Expr) : GoalM Unit := do
match e with
| .app .. =>
if ( getConfig).splitIte && (e.isIte || e.isDIte) then
addSplitCandidate e
addSplitCandidate (.default e)
return ()
if isMorallyIff e then
addSplitCandidate e
addSplitCandidate (.default e)
return ()
if ( getConfig).splitMatch then
if ( isMatcherApp e) then
@@ -74,7 +74,7 @@ private def checkAndAddSplitCandidate (e : Expr) : GoalM Unit := do
-- and consequently don't need to be split.
return ()
else
addSplitCandidate e
addSplitCandidate (.default e)
return ()
let .const declName _ := e.getAppFn | return ()
if forbiddenSplitTypes.contains declName then
@@ -82,16 +82,21 @@ private def checkAndAddSplitCandidate (e : Expr) : GoalM Unit := do
unless ( isInductivePredicate declName) do
return ()
if ( get).split.casesTypes.isSplit declName then
addSplitCandidate e
addSplitCandidate (.default e)
else if ( getConfig).splitIndPred then
addSplitCandidate e
addSplitCandidate (.default e)
| .fvar .. =>
let .const declName _ := ( whnfD ( inferType e)).getAppFn | return ()
if ( get).split.casesTypes.isSplit declName then
addSplitCandidate e
addSplitCandidate (.default e)
| .forallE _ d _ _ =>
if Arith.isRelevantPred d || ( getConfig).splitImp then
addSplitCandidate e
if ( getConfig).splitImp then
addSplitCandidate (.default e)
else if Arith.isRelevantPred d then
if ( getConfig).lookahead then
addLookaheadCandidate (.default e)
else
addSplitCandidate (.default e)
| _ => pure ()
/--
@@ -167,7 +172,7 @@ private def activateTheoremPatterns (fName : Name) (generation : Nat) : GoalM Un
modify fun s => { s with ematch.thmMap := thmMap }
let appMap := ( get).appMap
for thm in thms do
trace[grind.debug.ematch.activate] "`{fName}` => `{thm.origin.key}`"
trace_goal[grind.debug.ematch.activate] "`{fName}` => `{thm.origin.key}`"
unless ( get).ematch.thmMap.isErased thm.origin do
let symbols := thm.symbols.filter fun sym => !appMap.contains sym
let thm := { thm with symbols }
@@ -208,13 +213,13 @@ private def extParentsToIgnore (declName : Name) : Bool :=
|| declName == ``Exists || declName == ``Subtype
/--
Given a term `e` that occurs as the argument at position `i` of an `f`-application `parent?`,
we consider `e` as a candidate for case-splitting. For every other argument `e'` that also appears
at position `i` in an `f`-application and has the same type as `e`, we add the case-split candidate `e = e'`.
Given a term `arg` that occurs as the argument at position `i` of an `f`-application `parent?`,
we consider `arg` as a candidate for case-splitting. For every other argument `arg'` that also appears
at position `i` in an `f`-application and has the same type as `e`, we add the case-split candidate `arg = arg'`.
When performing the case split, we consider the following two cases:
- `e = e'`, which may introduce a new congruence between the corresponding `f`-applications.
- `¬(e = e')`, which may trigger extensionality theorems for the type of `e`.
- `arg = arg'`, which may introduce a new congruence between the corresponding `f`-applications.
- `¬(arg = arg')`, which may trigger extensionality theorems for the type of `arg`.
This feature enables `grind` to solve examples such as:
```lean
@@ -222,13 +227,13 @@ example (f : (Nat → Nat) → Nat) : a = b → f (fun x => a + x) = f (fun x =>
grind
```
-/
private def addSplitCandidatesForExt (e : Expr) (generation : Nat) (parent? : Option Expr := none) : GoalM Unit := do
private def addSplitCandidatesForExt (arg : Expr) (generation : Nat) (parent? : Option Expr := none) : GoalM Unit := do
let some parent := parent? | return ()
unless parent.isApp do return ()
let f := parent.getAppFn
if let .const declName _ := f then
if extParentsToIgnore declName then return ()
let type inferType e
let type inferType arg
-- Remark: we currently do not perform function extensionality on functions that produce a type that is not a proposition.
-- We may add an option to enable that in the future.
let u? typeFormerTypeLevel type
@@ -238,28 +243,31 @@ private def addSplitCandidatesForExt (e : Expr) (generation : Nat) (parent? : Op
repeat
if !it.isApp then return ()
i := i - 1
let arg := it.appArg!
if isSameExpr arg e then
found f i type
if isSameExpr arg it.appArg! then
found f i type parent
it := it.appFn!
where
found (f : Expr) (i : Nat) (type : Expr) : GoalM Unit := do
trace[grind.debug.ext] "{f}, {i}, {e}"
let others := ( get).termsAt.find? (f, i) |>.getD []
for (e', type') in others do
if ( withDefault <| isDefEq type type') then
let eq := mkApp3 (mkConst ``Eq [ getLevel type]) type e e'
found (f : Expr) (i : Nat) (type : Expr) (parent : Expr) : GoalM Unit := do
trace_goal[grind.debug.ext] "{f}, {i}, {arg}"
let others := ( get).split.argsAt.find? (f, i) |>.getD []
for other in others do
if ( withDefault <| isDefEq type other.type) then
let eq := mkApp3 (mkConst ``Eq [ getLevel type]) type arg other.arg
let eq shareCommon eq
internalize eq generation
trace_goal[grind.ext.candidate] "{eq}"
addSplitCandidate eq
modify fun s => { s with termsAt := s.termsAt.insert (f, i) ((e, type) :: others) }
-- We do not use lookahead here because it is too incomplete.
-- if (← getConfig).lookahead then
-- addLookaheadCandidate (.arg other.app parent i eq)
-- else
addSplitCandidate (.arg other.app parent i eq)
modify fun s => { s with split.argsAt := s.split.argsAt.insert (f, i) ({ arg, type, app := parent } :: others) }
return ()
/-- Applies `addSplitCandidatesForExt` if `funext` is enabled. -/
private def addSplitCandidatesForFunext (e : Expr) (generation : Nat) (parent? : Option Expr := none) : GoalM Unit := do
private def addSplitCandidatesForFunext (arg : Expr) (generation : Nat) (parent? : Option Expr := none) : GoalM Unit := do
unless ( getConfig).funext do return ()
addSplitCandidatesForExt e generation parent?
addSplitCandidatesForExt arg generation parent?
@[export lean_grind_internalize]
private partial def internalizeImpl (e : Expr) (generation : Nat) (parent? : Option Expr := none) : GoalM Unit := withIncRecDepth do

View File

@@ -178,6 +178,12 @@ private def isEagerCasesCandidate (goal : Goal) (type : Expr) : Bool := Id.run d
let .const declName _ := type.getAppFn | return false
return goal.split.casesTypes.isEagerSplit declName
/-- Returns `true` if `type` is an inductive type with at most one constructor. -/
private def isCheapInductive (type : Expr) : CoreM Bool := do
let .const declName _ := type.getAppFn | return false
let .inductInfo info getConstInfo declName | return false
return info.numCtors <= 1
private def applyCases? (goal : Goal) (fvarId : FVarId) : GrindM (Option (List Goal)) := goal.mvarId.withContext do
/-
Remark: we used to use `whnfD`. This was a mistake, we don't want to unfold user-defined abstractions.
@@ -185,6 +191,9 @@ private def applyCases? (goal : Goal) (fvarId : FVarId) : GrindM (Option (List G
-/
let type whnf ( fvarId.getType)
if isEagerCasesCandidate goal type then
if ( cheapCasesOnly) then
unless ( isCheapInductive type) do
return none
if let .const declName _ := type.getAppFn then
saveCases declName true
let mvarIds cases goal.mvarId (mkFVar fvarId)
@@ -205,7 +214,7 @@ private def exfalsoIfNotProp (goal : Goal) : MetaM Goal := goal.mvarId.withConte
return { goal with mvarId := ( goal.mvarId.exfalso) }
/-- Introduce new hypotheses (and apply `by_contra`) until goal is of the form `... ⊢ False` -/
partial def intros (generation : Nat) : GrindTactic' := fun goal => do
partial def intros (generation : Nat) : GrindTactic' := fun goal => do
let rec go (goal : Goal) : StateRefT (Array Goal) GrindM Unit := do
if goal.inconsistent then
return ()

View File

@@ -0,0 +1,101 @@
/-
Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
prelude
import Lean.Meta.Tactic.Grind.Types
import Lean.Meta.Tactic.Grind.Intro
import Lean.Meta.Tactic.Grind.Arith
import Lean.Meta.Tactic.Grind.Split
import Lean.Meta.Tactic.Grind.EMatch
namespace Lean.Meta.Grind
private partial def solve (generation : Nat) (goal : Goal) : GrindM Bool := do
cont ( intros generation goal)
where
cont (goals : List Goal) : GrindM Bool := do
match goals with
| [] => return true
| [goal] => loop goal
| _ => throwError "`grind` lookahead internal error, unexpected number of goals"
loop (goal : Goal) : GrindM Bool := withIncRecDepth do
if goal.inconsistent then
return true
else if let some goals assertNext goal then
cont goals
else if let some goals Arith.check goal then
cont goals
else if let some goals splitNext goal then
cont goals
else if let some goals ematchAndAssert goal then
cont goals
else
return false
private def tryLookahead (e : Expr) : GoalM Bool := do
-- TODO: if `e` is an arithmetic expression, we can avoid creating an auxiliary goal.
-- We can assert it directly to the arithmetic module.
-- Remark: We can simplify this code because the lookahead only really worked for arithmetic.
trace_goal[grind.lookahead.try] "{e}"
let proof? withoutModifyingState do
let goal get
let tag goal.mvarId.getTag
let target mkArrow (mkNot e) ( getFalseExpr)
let mvar mkFreshExprMVar target .syntheticOpaque tag
let gen getGeneration e
if ( solve gen { goal with mvarId := mvar.mvarId! }) then
return some ( instantiateMVars mvar)
else
return none
if let some proof := proof? then
trace[grind.lookahead.assert] "{e}"
pushEqTrue e <| mkApp2 (mkConst ``Grind.of_lookahead) e proof
processNewFacts
return true
else
return false
private def withLookaheadConfig (x : GrindM α) : GrindM α := do
withTheReader Grind.Context
(fun ctx => { ctx with config.qlia := true, cheapCases := true })
x
def lookahead : GrindTactic := fun goal => do
unless ( getConfig).lookahead do
return none
if goal.split.lookaheads.isEmpty then
return none
withLookaheadConfig do
let (progress, goal) GoalM.run goal do
let mut postponed := []
let mut progress := false
let infos := ( get).split.lookaheads
modify fun s => { s with split.lookaheads := [] }
for info in infos do
if ( isInconsistent) then
return true
match ( checkSplitStatus info) with
| .resolved => progress := true
| .ready _ _ true
| .notReady => postponed := info :: postponed
| .ready _ _ false =>
if ( tryLookahead info.getExpr) then
progress := true
else
postponed := info :: postponed
if progress then
modify fun s => { s with
split.lookaheads := s.split.lookaheads ++ postponed.reverse
}
return true
else
return false
if progress then
return some [goal]
else
return none
end Lean.Meta.Grind

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