Compare commits

...

41 Commits

Author SHA1 Message Date
Kim Morrison
eb896cabfa Merge remote-tracking branch 'origin/master' into align_extract 2025-02-17 15:37:00 +11:00
Kim Morrison
353eaf7014 feat: align Array/Vector.extract lemmas with List 2025-02-17 15:35:56 +11:00
Luisa Cicolini
6a17e62523 feat: add BitVec.[(getMsbD, msb)_extractLsb', (getLsbD, getMsbD, msb)_extractLsb] , add and_eq_decide, or_eq_decide, decide_eq_true_iff to bool_to_prop (#6792)
This PR adds theorems `BitVec.(getMsbD, msb)_(extractLsb', extractLsb),
getMsbD_extractLsb'_eq_getLsbD`.

---------

Co-authored-by: Siddharth <siddu.druid@gmail.com>
Co-authored-by: Alex Keizer <alex@keizer.dev>
Co-authored-by: Kim Morrison <kim@tqft.net>
Co-authored-by: Tobias Grosser <tobias@grosser.es>
Co-authored-by: Tobias Grosser <github@grosser.es>
2025-02-17 03:02:37 +00:00
Kim Morrison
1ce7047bf5 feat: cleanup of get and back functions on List/Array (#7059)
This PR moves away from using `List.get` / `List.get?` / `List.get!` and
`Array.get!`, in favour of using the `GetElem` mediated getters. In
particular it deprecates `List.get?`, `List.get!` and `Array.get?`. Also
adds `Array.back`, taking a proof, matching `List.getLast`.
2025-02-17 01:43:45 +00:00
Leonardo de Moura
ef759d874f fix: grind using reducible transparency setting (#7102)
This PR modifies `grind` to run with the `reducible` transparency
setting. We do not want `grind` to unfold arbitrary terms during
definitional equality tests. This PR also fixes several issues
introduced by this change. The most common problem was the lack of a
hint in proofs, particularly in those constructed using proof by
reflection. This PR also introduces new sanity checks when `set_option
grind.debug true` is used.
2025-02-16 22:30:04 +00:00
Kitamado
6f5bb3e896 fix: allow trailing comma in array syntax (#7055)
This PR improves array and vector literal syntax by allowing trailing
commas. For example, `#[1, 2, 3,]`.

see: [Why Are Trailing Commas Not Allowed in Array
Literals?](https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/Why.20Are.20Trailing.20Commas.20Not.20Allowed.20in.20Array.20Literals.3F)

Note: we need to preserve the current name for the array syntax
(`«term#[_,]»`) to avoid a bootstrapping issue. The `FromJson`/`ToJson`
deriving handlers use array syntax in macros, and the stage0 version is
used in most of the prelude.
2025-02-16 19:26:23 +00:00
Joachim Breitner
96c6f9dc96 feat: fun_induction and fun_cases tactics (#7069)
This PR adds the `fun_induction` and `fun_cases` tactics, which add
convenience around using functional induction and functional cases
principles.

```
fun_induction foo  x y z
```
elaborates `foo x y z`, then looks up `foo.induct`, and then essentially
does
```
induction z using foo.induct y
```
including and in particular figuring out which arguments are parameters,
targets or dropped. This only works for non-mutual functions so far.

Likewise there is the `fun_cases` tactic using `foo.fun_cases`.
2025-02-16 10:59:56 +00:00
Leonardo de Moura
f50b863868 feat: cutsat helper functions (#7098)
This PR adds some helper functions for cutsat in the `grind` tactic.
2025-02-16 05:32:46 +00:00
Leonardo de Moura
dd3652ecdc feat: cutsat preparations (#7097)
This PR implements several modifications for the cutsat procedure in
`grind`.
- The maximal variable is now at the beginning of linear polynomials. 
- The old `LinearArith.Solver` was deleted, and the normalizer was moved
to `Simp`.
- cutsat first files were created, and basic infrastructure for
representing divisibility constraints was added.
2025-02-16 02:52:14 +00:00
Tobias Grosser
a9efbf04f4 feat: make BitVec.getElem the simp normal form and use it in ext (#5498)
This PR makes `BitVec.getElem` the simp normal form in case a proof is
available and changes `ext` to return `x[i]` + a hypothesis that proves
that we are in-bounds. This aligns `BitVec` further with the API
conventions of the Lean standard datatypes.

We move our proofs to this new normal form, which results in slightly
smaller proofs. With the exception of `getElem_ofFin`, no new API
surface is added as the `getElem` API has already been completed over
the previous months. We also move `getElem_shiftConcat_*` a bit higher
as they are needed in earlier proofs. To keep the changeset small, we do
not update the API of `BVDecide` but insert `←
BitVec.getLsbD_eq_getElem` at the few locations where it is needed.
Finally, we add a simproc for getElem, mirroring the existing ones for
getLsbD/getMsdD.

---------

Co-authored-by: Alex Keizer <alex@keizer.dev>
2025-02-16 00:04:56 +00:00
Leonardo de Moura
3a76ac5620 chore: cleanup and missing grind normalization rules (#7095)
This PR adds missing `grind` normalization rules, and removes dead
theorems.
2025-02-15 23:45:35 +00:00
Leonardo de Moura
747ea91c3a refactor: add denote' functions to Int/Linear.lean (#7094)
This PR adds the functions `Poly.denote'`, `RelCnstr.denote'`, and
`DvdCnstr.denote'`. These functions are useful for representing the
denotation of normalized results in `simp +arith` and the `grind`
preprocessor. This PR also adjusts all auxiliary normalization theorems
to use them to represent the normalized constraints. Previously, we were
converting `RelCnstr` and `DvdCnstr` back into raw constraints. While
this overhead was reasonable for `simp +arith`, it is not for the cutsat
procedure, which has no need for raw constraints. All constraints have
already been normalized by the time they reach cutsat.
2025-02-15 22:10:23 +00:00
Leonardo de Moura
ecdc2d57f2 refactor: Int.Linear module (#7093)
This PR cleans up the `Int.Linear` module by normalizing function and
type names and adding documentation strings. We will use it to implement
cutsat in the `grind` tactic.
2025-02-15 19:20:18 +00:00
Leonardo de Moura
f4afcfc923 feat: divisibility constraint normalizer (#7092)
This PR implements divisibility constraint normalization in `simp
+arith`.
2025-02-15 04:20:40 +00:00
jrr6
9cce0ce8d9 fix: ensure get_elem_tactic works in absence of goals (#7088)
This PR fixes the behavior of the indexed-access notation `xs[i]` in
cases where the proof of `i`'s validity is filled in during unification.

Closes #6999.
2025-02-15 03:00:36 +00:00
Leonardo de Moura
57aadf8af9 feat: add helper theorems for normalizing divisibility constraints (#7091)
This PR adds helper theorems for normalizing divisibility constraints.
They are going to be used to implement the cutsat procedure in the
`grind` tactic.
2025-02-15 02:44:49 +00:00
Kyle Miller
1babe9fc67 feat: make binders in #check be hoverable (#7074)
This PR modifies the signature pretty printer to add hover information
for parameters in binders. This makes the binders be consistent with the
hovers in pi types.

Suggested by @david-christiansen
2025-02-14 17:28:54 +00:00
Markus Himmel
dd1a4188a0 feat: Fin.toNat (#7079)
This PR introduces `Fin.toNat` as an alias for `Fin.val`. We add this
function for discoverability and consistency reasons. The normal form
for proofs remains `Fin.val`, and there is a `simp` lemma rewriting
`Fin.toNat` to `Fin.val`.
2025-02-14 11:59:44 +00:00
Markus Himmel
ed42d068d4 feat: UIntX.ofNatTruncate (#7080)
This PR adds the functions `UIntX.ofNatTruncate` (the version for
`UInt32` already exists).
2025-02-14 11:59:41 +00:00
Markus Himmel
784444c7a9 feat: IntX.minValue, IntX.maxValue, IntX.ofIntLE, IntX.ofIntTruncate (#7081)
This PR adds functions `IntX.ofIntLE`, `IntX.ofIntTruncate`, which are
analogous to the unsigned counterparts `UIntX.ofNatLT` and
`UInt.ofNatTruncate`.
2025-02-14 11:59:37 +00:00
Marc Huisinga
05fb67af90 feat: request cancellation (#7054)
This PR adds language server support for request cancellation to the
following expensive requests: Code actions, auto-completion, document
symbols, folding ranges and semantic highlighting. This means that when
the client informs the language server that a request is stale (e.g.
because it belongs to a previous state of the document), the language
server will now prematurely cancel the computation of the response in
order to reduce the CPU load for requests that will be discarded by the
client anyways.
2025-02-14 11:55:43 +00:00
Marc Huisinga
22d1d04059 fix: incremental goal state requests select incomplete snapshot (#6887)
This PR fixes a bug where the goal state selection would sometimes
select incomplete incremental snapshots on whitespace, leading to an
incorrect "no goals" response. Fixes #6594, a regression that was
originally introduced in 4.11.0 by #4727.

The fundamental cause of #6594 was that the snapshot selection would
always select the first snapshot with a range that contains the cursor
position. For tactics, whitespace had to be included in this range.
However, in the test case of #6594, this meant that the snapshot
selection would also sometimes pick a snapshot before the cursor that
still contains the cursor in its whitespace, but which also does not
necessarily contain all the information needed to produce a correct goal
state. Specifically, at the `InfoTree`-level, when the cursor is in
whitespace, we distinguish competing goal states by their level of
indentation. The snapshot selection did not have access to this
information, so it necessarily had to do the wrong thing in some cases.

This PR fixes the issue by adjusting the snapshot selection for goals to
explicitly account for whitespace and indentation, and refactoring the
language processor architecture to thread enough information through to
the snapshot selection so that it can decide which snapshots to use
without having to force too many tasks, which would destroy
incrementality in goal state requests.

Specifically, this PR makes the following adjustments:
- Refactor `SnapshotTask` to contain both a `Syntax` and a `Range`.
Before, `SnapshotTask`s had a single range that was used both for
displaying file progress information and for selecting snapshots in
server requests. For most snapshots, this range did not include
whitespace, though for tactics it did. Now, the `reportingRange` field
of `SnapshotTask` is intended exclusively for reporting file progress
information, and the `Syntax` is used for selecting snapshots in server
requests. Importantly, the `Syntax` contains the full range information
of the snapshot, i.e. its regular range and its range including
whitespace.
- Adjust all call-sites of `SnapshotTask` to produce a reasonable
`Syntax`.
- Adjust the goal snapshot selection to account for whitespace and
indentation, as the `InfoTree` goal selection does.
- Fix a bug in the snapshot tree tracing that would cause it to render
the `Info` of a snapshot at the wrong location when `trace.Elab.info`
was also set.

This PR is based on #6329.
2025-02-14 11:53:24 +00:00
Paul Reichert
36ac6eb912 feat: insertMany, ofList, ofArray, foldr, foldM functions for the tree map (#7051)
This PR implements the methods `insertMany`, `ofList`, `ofArray`,
`foldr` and `foldrM` on the tree map.

---------

Co-authored-by: Paul Reichert <6992158+datokrat@users.noreply.github.com>
2025-02-14 08:24:33 +00:00
Markus Himmel
47548aa171 chore: rename UIntX.ofNatCore, UIntX.ofNat' -> UIntX.ofNatLT (#7071)
This PR unifies the existing functions `UIntX.ofNatCore` and
`UIntX.ofNat'` under a new name, `UIntX.ofNatLT`.
2025-02-14 06:58:15 +00:00
Leonardo de Moura
b26b781992 feat: simprocs for Int and Nat divides predicates (#7078)
This PR implements simprocs for `Int` and `Nat` divides predicates.
2025-02-14 05:43:38 +00:00
Mac Malone
c9c3366521 feat: lake: support plugins (#7001)
This PR adds support for plugins to Lake. Precompiled modules are now
loaded as plugins rather than via `--load-dynlib`.

Additional plugins can be added through an experimental `plugins`
configuration option. The syntax for specifying this is not yet
convenient, and will be improved in future changes. A parallel `dynlibs`
configuration option has been added for specifying additional dynamic
libraries to build and pass to `--load-dynlib`.

This PR also changes the default directory for `.olean`, `.ilean`, and
module dynamic libraries (i.e., `leanLibDir`) to `lib/lean` instead of
the previous default of `lib`. This avoids potential name clashes
between single module shared libraries and the shared libraries of a
full `lean_lib`.

On non-Windows systems, module dynamic libraries are no longer linked to
their imports or external symbols. Symbols from those libraries are left
unresolved until load time. This avoids nesting these dependencies
within the shared library and means Lake no longer needs to augment the
shared library path to allow Lean to resolve such nested dependencies on
load.
2025-02-14 04:57:31 +00:00
Leonardo de Moura
2c2a3a65b2 feat: support theorems for cutsat Div-Solve rule (#7077)
This PR proves the helper theorems for justifying the "Div-Solve" rule
in the cutsat procedure.
2025-02-14 04:55:58 +00:00
Kim Morrison
8cefb2cf65 feat: premise selection API (#7061)
This PR provides a basic API for a premise selection tool, which can be
provided in downstream libraries. It does not implement premise
selection itself!
2025-02-14 04:08:18 +00:00
Lean stage0 autoupdater
80c8837f49 chore: update stage0 2025-02-13 16:00:29 +00:00
Markus Himmel
40c6dfa3ae chore: dsimproc for UIntX.ofNatLT (#7068)
This PR is a follow-up to #7057 and adds a builtin dsimproc for
`UIntX.ofNatLT` which it turns out we need in stage0 before we can get
the deprecation of `UIntX.ofNatCore` in favor of `UIntX.ofNatLT` off the
ground.
2025-02-13 14:51:42 +00:00
Bulhwi Cha
cc76c46244 doc: fix typo (#7067) 2025-02-13 13:21:18 +00:00
Markus Himmel
b38da34db2 chore: rename BitVec.ofNatLt -> BitVec.ofNatLT (#7064)
This PR renames `BitVec.ofNatLt` to `BitVec.ofNatLT` and sets up
deprecations for the old name.
2025-02-13 12:52:31 +00:00
Markus Himmel
4a900cc65c chore: rename IntX.toNat -> IntX.toNatClampNeg (#7066)
This PR renames `IntX.toNat` to `IntX.toNatClampNeg` (to reduce
surprises) and sets up a deprecation.
2025-02-13 12:14:28 +00:00
Markus Himmel
a3fd2eb0fe chore: make IntX constructor private, provide UIntX.toIntX (#7062)
This PR introduces the functions `UIntX.toIntX` as the public API to
obtain the `IntX` that is 2's complement equivalent to a given `UIntX`.
2025-02-13 11:29:31 +00:00
Paul Reichert
6ac530aa1a feat: deprecated find, fold, foldM, mergeBy functions for the tree map (#7036)
This PR adds some deprecated function aliases to the tree map in order
to ease the transition from the `RBMap` to the tree map.

---------

Co-authored-by: Paul Reichert <6992158+datokrat@users.noreply.github.com>
2025-02-13 11:12:22 +00:00
Markus Himmel
04fe72fee0 feat: missing conversion functions for ISize (#7063)
This PR adds `ISize.toInt8`, `ISize.toInt16`, `Int8.toISize`,
`Int16.toISize`.
2025-02-13 11:02:00 +00:00
Joachim Breitner
a833afa935 feat: binderNameHint in congr (#7053)
This PR makes `simp` heed the `binderNameHint` also in the assumptions
of congruence rules. Fixes #7052.
2025-02-13 09:38:42 +00:00
Markus Himmel
7c9454edd2 feat: UIntX.ofFin (#7056)
This PR adds the `UIntX.ofFin` conversion functions.
2025-02-13 08:45:01 +00:00
Markus Himmel
1ecb4a43ae chore: rename UIntX.val -> UIntX.toFin (#7050)
This PR renames the functions `UIntX.val` to `UIntX.toFin`.
2025-02-13 07:50:47 +00:00
Kim Morrison
ae9d12aeaa chore: upstream an Int lemma (#7060) 2025-02-13 03:19:02 +00:00
Leonardo de Moura
e617ce7e4f refactor: move grind offset constraint module to Grind/Arith/Offset (#7058)
This PR moves the `grind` offset constraint module to the
`Grind/Arith/Offset` subdirectory in preparation to the full linear
integer arithmetic module.
2025-02-12 23:16:07 +00:00
266 changed files with 6448 additions and 2656 deletions

View File

@@ -31,8 +31,12 @@ example (names : List String) : names.all (fun name => "Waldo".isPrefixOf name)
If `binder` is not a binder, then the name of `v` attains a macro scope. This only matters when the
resulting term is used in a non-hygienic way, e.g. in termination proofs for well-founded recursion.
This gadget is supported by `simp`, `dsimp` and `rw` in the right-hand-side of an equation, but not
in hypotheses or by other tactics.
This gadget is supported by
* `simp`, `dsimp` and `rw` in the right-hand-side of an equation
* `simp` in the assumptions of congruence rules
It is ineffective in other positions (hyptheses of rewrite rules) or when used by other tactics
(e.g. `apply`).
-/
@[simp ]
def binderNameHint {α : Sort u} {β : Sort v} {γ : Sort w} (v : α) (binder : β) (e : γ) : γ := e

View File

@@ -195,7 +195,7 @@ end Classical
/- Export for Mathlib compat. -/
export Classical (imp_iff_right_iff imp_and_neg_imp_iff and_or_imp not_imp)
/-- Extract an element from a existential statement, using `Classical.choose`. -/
/-- Extract an element from an existential statement, using `Classical.choose`. -/
-- This enables projection notation.
@[reducible] noncomputable def Exists.choose {p : α Prop} (P : a, p a) : α := Classical.choose P

View File

@@ -39,7 +39,7 @@ class EvalInformation (α : Sort u) (β : Sort v) where
evalVar : α Nat β
def Context.var (ctx : Context α) (idx : Nat) : Variable ctx.op :=
ctx.vars.getD idx ctx.arbitrary, none
ctx.vars[idx]?.getD ctx.arbitrary, none
instance : ContextInformation (Context α) where
isNeutral ctx x := ctx.var x |>.neutral.isSome

View File

@@ -27,3 +27,4 @@ import Init.Data.Array.Range
import Init.Data.Array.Erase
import Init.Data.Array.Zip
import Init.Data.Array.InsertIdx
import Init.Data.Array.Extract

View File

@@ -19,7 +19,7 @@ universe u v w
/-! ### Array literal syntax -/
/-- Syntax for `Array α`. -/
syntax "#[" withoutPosition(sepBy(term, ", ")) "]" : term
syntax (name := «term#[_,]») "#[" withoutPosition(term,*,?) "]" : term
macro_rules
| `(#[ $elems,* ]) => `(List.toArray [ $elems,* ])
@@ -48,7 +48,7 @@ theorem ext (a b : Array α)
: a = b := by
let rec extAux (a b : List α)
(h₁ : a.length = b.length)
(h₂ : (i : Nat) (hi₁ : i < a.length) (hi₂ : i < b.length) a.get i, hi₁ = b.get i, hi₂)
(h₂ : (i : Nat) (hi₁ : i < a.length) (hi₂ : i < b.length) a[i] = b[i])
: a = b := by
induction a generalizing b with
| nil =>
@@ -63,11 +63,11 @@ theorem ext (a b : Array α)
have hz₂ : 0 < (b::bs).length := by rw [List.length_cons]; apply Nat.zero_lt_succ
have headEq : a = b := h₂ 0 hz₁ hz₂
have h₁' : as.length = bs.length := by rw [List.length_cons, List.length_cons] at h₁; injection h₁
have h₂' : (i : Nat) (hi₁ : i < as.length) (hi₂ : i < bs.length) as.get i, hi₁ = bs.get i, hi₂ := by
have h₂' : (i : Nat) (hi₁ : i < as.length) (hi₂ : i < bs.length) as[i] = bs[i] := by
intro i hi₁ hi₂
have hi₁' : i+1 < (a::as).length := by rw [List.length_cons]; apply Nat.succ_lt_succ; assumption
have hi₂' : i+1 < (b::bs).length := by rw [List.length_cons]; apply Nat.succ_lt_succ; assumption
have : (a::as).get i+1, hi₁' = (b::bs).get i+1, hi₂' := h₂ (i+1) hi₁' hi₂'
have : (a::as)[i+1] = (b::bs)[i+1] := h₂ (i+1) hi₁' hi₂'
apply this
have tailEq : as = bs := ih bs h₁' h₂'
rw [headEq, tailEq]
@@ -123,7 +123,8 @@ namespace List
@[simp] theorem getElem?_toArray {a : List α} {i : Nat} : a.toArray[i]? = a[i]? := rfl
@[simp] theorem getElem!_toArray [Inhabited α] {a : List α} {i : Nat} :
a.toArray[i]! = a[i]! := rfl
a.toArray[i]! = a[i]! := by
simp [getElem!_def]
end List
@@ -254,17 +255,37 @@ def range' (start size : Nat) (step : Nat := 1) : Array Nat :=
@[inline] protected def singleton (v : α) : Array α := #[v]
/--
Return the last element of an array, or panic if the array is empty.
See `back` for the version that requires a proof the array is non-empty,
or `back?` for the version that returns an option.
-/
def back! [Inhabited α] (a : Array α) : α :=
a[a.size - 1]!
@[deprecated back! (since := "2024-10-31")] abbrev back := @back!
/--
Return the last element of an array, given a proof that the array is not empty.
def get? (a : Array α) (i : Nat) : Option α :=
if h : i < a.size then some a[i] else none
See `back!` for the version that panics if the array is empty,
or `back?` for the version that returns an option.
-/
def back (a : Array α) (h : 0 < a.size := by get_elem_tactic) : α :=
a[a.size - 1]'(Nat.sub_one_lt_of_lt h)
/--
Return the last element of an array, or `none` if the array is empty.
See `back!` for the version that panics if the array is empty,
or `back` for the version that requires a proof the array is non-empty.
-/
def back? (a : Array α) : Option α :=
a[a.size - 1]?
@[deprecated "Use `a[i]?` instead." (since := "2025-02-12")]
def get? (a : Array α) (i : Nat) : Option α :=
if h : i < a.size then some a[i] else none
@[inline] def swapAt (a : Array α) (i : Nat) (v : α) (hi : i < a.size := by get_elem_tactic) : α × Array α :=
let e := a[i]
let a := a.set i v
@@ -881,6 +902,10 @@ def popWhile (p : α → Bool) (as : Array α) : Array α :=
as
decreasing_by simp_wf; decreasing_trivial_pre_omega
@[simp] theorem popWhile_empty (p : α Bool) :
popWhile p #[] = #[] := by
simp [popWhile]
def takeWhile (p : α Bool) (as : Array α) : Array α :=
let rec @[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
go (i : Nat) (r : Array α) : Array α :=

View File

@@ -0,0 +1,427 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
prelude
import Init.Data.Array.Lemmas
import Init.Data.List.Nat.TakeDrop
/-!
# Lemmas about `Array.extract`
This file follows the contents of `Init.Data.List.TakeDrop` and `Init.Data.List.Nat.TakeDrop`.
-/
open Nat
namespace Array
/-! ### extract -/
@[simp] theorem extract_of_size_lt {as : Array α} {i j : Nat} (h : as.size < j) :
as.extract i j = as.extract i as.size := by
ext l h₁ h₂
· simp
omega
· simp only [size_extract] at h₁ h₂
simp [h]
theorem size_extract_le {as : Array α} {i j : Nat} :
(as.extract i j).size j - i := by
simp
omega
theorem size_extract_le' {as : Array α} {i j : Nat} :
(as.extract i j).size as.size - i := by
simp
omega
theorem size_extract_of_le {as : Array α} {i j : Nat} (h : j as.size) :
(as.extract i j).size = j - i := by
simp
omega
@[simp]
theorem extract_push {as : Array α} {b : α} {start stop : Nat} (h : stop as.size) :
(as.push b).extract start stop = as.extract start stop := by
ext i h₁ h₂
· simp
omega
· simp only [size_extract, size_push] at h₁ h₂
simp only [getElem_extract, getElem_push]
rw [dif_pos (by omega)]
@[simp]
theorem extract_eq_pop {as : Array α} {stop : Nat} (h : stop = as.size - 1) :
as.extract 0 stop = as.pop := by
ext i h₁ h₂
· simp
omega
· simp only [size_extract, size_pop] at h₁ h₂
simp [getElem_extract, getElem_pop]
@[simp]
theorem extract_append_extract {as : Array α} {i j k : Nat} :
as.extract i j ++ as.extract j k = as.extract (min i j) (max j k) := by
ext l h₁ h₂
· simp
omega
· simp only [size_append, size_extract] at h₁ h₂
simp only [getElem_append, size_extract, getElem_extract]
split <;>
· congr 1
omega
@[simp]
theorem extract_eq_empty_iff {as : Array α} :
as.extract i j = #[] min j as.size i := by
constructor
· intro h
replace h := congrArg Array.size h
simp at h
omega
· intro h
exact eq_empty_of_size_eq_zero (by simp; omega)
theorem extract_eq_empty_of_le {as : Array α} (h : min j as.size i) :
as.extract i j = #[] :=
extract_eq_empty_iff.2 h
theorem lt_of_extract_ne_empty {as : Array α} (h : as.extract i j #[]) :
i < min j as.size :=
gt_of_not_le (mt extract_eq_empty_of_le h)
@[simp]
theorem extract_eq_self_iff {as : Array α} :
as.extract i j = as as.size = 0 i = 0 as.size j := by
constructor
· intro h
replace h := congrArg Array.size h
simp at h
omega
· intro h
ext l h₁ h₂
· simp
omega
· simp only [size_extract] at h₁
simp only [getElem_extract]
congr 1
omega
theorem extract_eq_self_of_le {as : Array α} (h : as.size j) :
as.extract 0 j = as :=
extract_eq_self_iff.2 (.inr rfl, h)
theorem le_of_extract_eq_self {as : Array α} (h : as.extract i j = as) :
as.size j := by
replace h := congrArg Array.size h
simp at h
omega
@[simp]
theorem extract_size_left {as : Array α} :
as.extract as.size j = #[] := by
simp
omega
@[simp]
theorem push_extract_getElem {as : Array α} {i j : Nat} (h : j < as.size) :
(as.extract i j).push as[j] = as.extract (min i j) (j + 1) := by
ext l h₁ h₂
· simp
omega
· simp only [size_push, size_extract] at h₁ h₂
simp only [getElem_push, size_extract, getElem_extract]
split <;>
· congr
omega
theorem extract_succ_right {as : Array α} {i j : Nat} (w : i < j + 1) (h : j < as.size) :
as.extract i (j + 1) = (as.extract i j).push as[j] := by
ext l h₁ h₂
· simp
omega
· simp only [size_extract, push_extract_getElem] at h₁ h₂
simp only [getElem_extract, push_extract_getElem]
congr
omega
theorem extract_sub_one {as : Array α} {i j : Nat} (h : j < as.size) :
as.extract i (j - 1) = (as.extract i j).pop := by
ext l h₁ h₂
· simp
omega
· simp only [size_extract, size_pop] at h₁ h₂
simp only [getElem_extract, getElem_pop]
@[simp]
theorem getElem?_extract_of_lt {as : Array α} {i j k : Nat} (h : k < min j as.size - i) :
(as.extract i j)[k]? = some (as[i + k]'(by omega)) := by
simp [getElem?_extract, h]
theorem getElem?_extract_of_succ {as : Array α} {j : Nat} :
(as.extract 0 (j + 1))[j]? = as[j]? := by
simp [getElem?_extract]
omega
@[simp] theorem extract_extract {as : Array α} {i j k l : Nat} :
(as.extract i j).extract k l = as.extract (i + k) (min (i + l) j) := by
ext m h₁ h₂
· simp
omega
· simp only [size_extract] at h₁ h₂
simp [Nat.add_assoc]
theorem extract_eq_empty_of_eq_empty {as : Array α} {i j : Nat} (h : as = #[]) :
as.extract i j = #[] := by
simp [h]
theorem ne_empty_of_extract_ne_empty {as : Array α} {i j : Nat} (h : as.extract i j #[]) :
as #[] :=
mt extract_eq_empty_of_eq_empty h
theorem extract_set {as : Array α} {i j k : Nat} (h : k < as.size) {a : α} :
(as.set k a).extract i j =
if _ : k < i then
as.extract i j
else if _ : k < min j as.size then
(as.extract i j).set (k - i) a (by simp; omega)
else as.extract i j := by
split
· ext l h₁ h₂
· simp
· simp at h₁ h₂
simp [getElem_set]
omega
· split
· ext l h₁ h₂
· simp
· simp only [getElem_extract, getElem_set]
split
· rw [if_pos]; omega
· rw [if_neg]; omega
· ext l h₁ h₂
· simp
· simp at h₁ h₂
simp [getElem_set]
omega
theorem set_extract {as : Array α} {i j k : Nat} (h : k < (as.extract i j).size) {a : α} :
(as.extract i j).set k a = (as.set (i + k) a (by simp at h; omega)).extract i j := by
ext l h₁ h₂
· simp
· simp_all [getElem_set]
@[simp]
theorem extract_append {as bs : Array α} {i j : Nat} :
(as ++ bs).extract i j = as.extract i j ++ bs.extract (i - as.size) (j - as.size) := by
ext l h₁ h₂
· simp
omega
· simp only [size_extract, size_append] at h₁ h₂
simp only [getElem_extract, getElem_append, size_extract]
split
· split
· rfl
· omega
· split
· omega
· congr 1
omega
theorem extract_append_left {as bs : Array α} :
(as ++ bs).extract 0 as.size = as.extract 0 as.size := by
simp
@[simp] theorem extract_append_right {as bs : Array α} :
(as ++ bs).extract as.size (as.size + i) = bs.extract 0 i := by
simp only [extract_append, extract_size_left, Nat.sub_self, empty_append]
congr 1
omega
@[simp] theorem map_extract {as : Array α} {i j : Nat} :
(as.extract i j).map f = (as.map f).extract i j := by
ext l h₁ h₂
· simp
· simp only [size_map, size_extract] at h₁ h₂
simp only [getElem_map, getElem_extract]
@[simp] theorem extract_mkArray {a : α} {n i j : Nat} :
(mkArray n a).extract i j = mkArray (min j n - i) a := by
ext l h₁ h₂
· simp
· simp only [size_extract, size_mkArray] at h₁ h₂
simp only [getElem_extract, getElem_mkArray]
theorem extract_eq_extract_right {as : Array α} {i j j' : Nat} :
as.extract i j = as.extract i j' min (j - i) (as.size - i) = min (j' - i) (as.size - i) := by
rcases as with as
simp
theorem extract_eq_extract_left {as : Array α} {i i' j : Nat} :
as.extract i j = as.extract i' j min j as.size - i = min j as.size - i' := by
constructor
· intro h
replace h := congrArg Array.size h
simpa using h
· intro h
ext l h₁ h₂
· simpa
· simp only [size_extract] at h₁ h₂
simp only [getElem_extract]
congr 1
omega
theorem extract_add_left {as : Array α} {i j k : Nat} :
as.extract (i + j) k = (as.extract i k).extract j (k - i) := by
simp [extract_eq_extract_right]
omega
theorem mem_extract_iff_getElem {as : Array α} {a : α} {i j : Nat} :
a as.extract i j (k : Nat) (hm : k < min j as.size - i), as[i + k] = a := by
rcases as with as
simp [List.mem_take_iff_getElem]
constructor <;>
· rintro k, h, rfl
exact k, by omega, rfl
theorem set_eq_push_extract_append_extract {as : Array α} {i : Nat} (h : i < as.size) {a : α} :
as.set i a = (as.extract 0 i).push a ++ (as.extract (i + 1) as.size) := by
rcases as with as
simp at h
simp [List.set_eq_take_append_cons_drop, h, List.take_of_length_le]
theorem extract_reverse {as : Array α} {i j : Nat} :
as.reverse.extract i j = (as.extract (as.size - j) (as.size - i)).reverse := by
ext l h₁ h₂
· simp
omega
· simp only [size_extract, size_reverse] at h₁ h₂
simp only [getElem_extract, getElem_reverse, size_extract]
congr 1
omega
theorem reverse_extract {as : Array α} {i j : Nat} :
(as.extract i j).reverse = as.reverse.extract (as.size - j) (as.size - i) := by
rw [extract_reverse]
simp
by_cases h : j as.size
· have : as.size - (as.size - j) = j := by omega
simp [this, extract_eq_extract_left]
omega
· have : as.size - (as.size - j) = as.size := by omega
simp only [Nat.not_le] at h
simp [h, this, extract_eq_extract_left]
omega
/-! ### takeWhile -/
theorem takeWhile_map (f : α β) (p : β Bool) (as : Array α) :
(as.map f).takeWhile p = (as.takeWhile (p f)).map f := by
rcases as with as
simp [List.takeWhile_map]
theorem popWhile_map (f : α β) (p : β Bool) (as : Array α) :
(as.map f).popWhile p = (as.popWhile (p f)).map f := by
rcases as with as
simp [List.dropWhile_map, List.map_reverse]
theorem takeWhile_filterMap (f : α Option β) (p : β Bool) (as : Array α) :
(as.filterMap f).takeWhile p = (as.takeWhile fun a => (f a).all p).filterMap f := by
rcases as with as
simp [List.takeWhile_filterMap]
theorem popWhile_filterMap (f : α Option β) (p : β Bool) (as : Array α) :
(as.filterMap f).popWhile p = (as.popWhile fun a => (f a).all p).filterMap f := by
rcases as with as
simp [List.dropWhile_filterMap, List.filterMap_reverse]
theorem takeWhile_filter (p q : α Bool) (as : Array α) :
(as.filter p).takeWhile q = (as.takeWhile fun a => !p a || q a).filter p := by
rcases as with as
simp [List.takeWhile_filter]
theorem popWhile_filter (p q : α Bool) (as : Array α) :
(as.filter p).popWhile q = (as.popWhile fun a => !p a || q a).filter p := by
rcases as with as
simp [List.dropWhile_filter, List.filter_reverse]
theorem takeWhile_append {xs ys : Array α} :
(xs ++ ys).takeWhile p =
if (xs.takeWhile p).size = xs.size then xs ++ ys.takeWhile p else xs.takeWhile p := by
rcases xs with xs
rcases ys with ys
simp only [List.append_toArray, List.takeWhile_toArray, List.takeWhile_append, size_toArray]
split <;> rfl
@[simp] theorem takeWhile_append_of_pos {p : α Bool} {l₁ l₂ : Array α} (h : a l₁, p a) :
(l₁ ++ l₂).takeWhile p = l₁ ++ l₂.takeWhile p := by
rcases l₁ with l₁
rcases l₂ with l₂
simp at h
simp [List.takeWhile_append_of_pos h]
theorem popWhile_append {xs ys : Array α} :
(xs ++ ys).popWhile p =
if (ys.popWhile p).isEmpty then xs.popWhile p else xs ++ ys.popWhile p := by
rcases xs with xs
rcases ys with ys
simp only [List.append_toArray, List.popWhile_toArray, List.reverse_append, List.dropWhile_append,
List.isEmpty_eq_true, List.isEmpty_toArray, List.isEmpty_reverse]
-- Why do these not fire with `simp`?
rw [List.popWhile_toArray, List.isEmpty_toArray, List.isEmpty_reverse]
split
· rfl
· simp
@[simp] theorem popWhile_append_of_pos {p : α Bool} {l₁ l₂ : Array α} (h : a l₂, p a) :
(l₁ ++ l₂).popWhile p = l₁.popWhile p := by
rcases l₁ with l₁
rcases l₂ with l₂
simp at h
simp only [List.append_toArray, List.popWhile_toArray, List.reverse_append, mk.injEq,
List.reverse_inj]
rw [List.dropWhile_append_of_pos]
simpa
@[simp] theorem takeWhile_mkArray_eq_filter (p : α Bool) :
(mkArray n a).takeWhile p = (mkArray n a).filter p := by
simp [ List.toArray_replicate]
theorem takeWhile_mkArray (p : α Bool) :
(mkArray n a).takeWhile p = if p a then mkArray n a else #[] := by
simp [takeWhile_mkArray_eq_filter, filter_mkArray]
@[simp] theorem popWhile_mkArray_eq_filter_not (p : α Bool) :
(mkArray n a).popWhile p = (mkArray n a).filter (fun a => !p a) := by
simp [ List.toArray_replicate, List.filter_reverse]
theorem popWhile_mkArray (p : α Bool) :
(mkArray n a).popWhile p = if p a then #[] else mkArray n a := by
simp only [popWhile_mkArray_eq_filter_not, size_mkArray, filter_mkArray, Bool.not_eq_eq_eq_not,
Bool.not_true]
split <;> simp_all
theorem extract_takeWhile {as : Array α} {i : Nat} :
(as.takeWhile p).extract 0 i = (as.extract 0 i).takeWhile p := by
rcases as with as
simp [List.take_takeWhile]
@[simp] theorem all_takeWhile {as : Array α} :
(as.takeWhile p).all p = true := by
rcases as with as
rw [List.takeWhile_toArray] -- Not sure why this doesn't fire with `simp`.
simp
@[simp] theorem any_popWhile {as : Array α} :
(as.popWhile p).any (fun a => !p a) = !as.all p := by
rcases as with as
rw [List.popWhile_toArray] -- Not sure why this doesn't fire with `simp`.
simp
theorem takeWhile_eq_extract_findIdx_not {xs : Array α} {p : α Bool} :
takeWhile p xs = xs.extract 0 (xs.findIdx (fun a => !p a)) := by
rcases xs with xs
simp [List.takeWhile_eq_take_findIdx_not]
end Array

View File

@@ -412,6 +412,21 @@ theorem findIdx_le_findIdx {l : Array α} {p q : α → Bool} (h : ∀ x ∈ l,
cases l
simp [hf]
theorem false_of_mem_extract_findIdx {xs : Array α} {p : α Bool} (h : x xs.extract 0 (xs.findIdx p)) :
p x = false := by
rcases xs with xs
exact List.false_of_mem_take_findIdx (by simpa using h)
@[simp] theorem findIdx_extract {xs : Array α} {i : Nat} {p : α Bool} :
(xs.extract 0 i).findIdx p = min i (xs.findIdx p) := by
cases xs
simp
@[simp] theorem min_findIdx_findIdx {xs : Array α} {p q : α Bool} :
min (xs.findIdx p) (xs.findIdx q) = xs.findIdx (fun a => p a || q a) := by
cases xs
simp
/-! ### findIdx? -/
@[simp] theorem findIdx?_empty : (#[] : Array α).findIdx? p = none := rfl
@@ -525,6 +540,11 @@ theorem findIdx?_eq_some_le_of_findIdx?_eq_some {xs : Array α} {p q : α → Bo
cases l
simp [hf]
@[simp] theorem findIdx?_take {xs : Array α} {i : Nat} {p : α Bool} :
(xs.take i).findIdx? p = (xs.findIdx? p).bind (Option.guard (fun j => j < i)) := by
cases xs
simp
/-! ### idxOf
The verification API for `idxOf` is still incomplete.

View File

@@ -2385,6 +2385,10 @@ theorem getElem?_swap (a : Array α) (i j : Nat) (hi hj) (k : Nat) : (a.swap i j
theorem reverse_ne_empty_iff {xs : Array α} : xs.reverse #[] xs #[] :=
not_congr reverse_eq_empty_iff
@[simp] theorem isEmpty_reverse {xs : Array α} : xs.reverse.isEmpty = xs.isEmpty := by
cases xs
simp
/-- Variant of `getElem?_reverse` with a hypothesis giving the linear relation between the indices. -/
theorem getElem?_reverse' {l : Array α} (i j) (h : i + j + 1 = l.size) : l.reverse[i]? = l[j]? := by
rcases l with l
@@ -3226,7 +3230,9 @@ theorem getElem?_lt
theorem getElem?_ge
(a : Array α) {i : Nat} (h : i a.size) : a[i]? = none := dif_neg (Nat.not_lt_of_le h)
@[simp] theorem get?_eq_getElem? (a : Array α) (i : Nat) : a.get? i = a[i]? := rfl
set_option linter.deprecated false in
@[deprecated "`get?` is deprecated" (since := "2025-02-12"), simp]
theorem get?_eq_getElem? (a : Array α) (i : Nat) : a.get? i = a[i]? := rfl
@[deprecated getElem?_eq_none (since := "2024-12-11")]
theorem getElem?_len_le (a : Array α) {i : Nat} (h : a.size i) : a[i]? = none := by
@@ -3234,15 +3240,26 @@ theorem getElem?_len_le (a : Array α) {i : Nat} (h : a.size ≤ i) : a[i]? = no
@[deprecated getD_getElem? (since := "2024-12-11")] abbrev getD_get? := @getD_getElem?
@[simp] theorem getD_eq_get? (a : Array α) (i d) : a.getD i d = (a[i]?).getD d := by
simp only [getD, get_eq_getElem, get?_eq_getElem?]; split <;> simp [getD_getElem?, *]
@[simp] theorem getD_eq_getD_getElem? (a : Array α) (i d) : a.getD i d = a[i]?.getD d := by
simp only [getD, get_eq_getElem]; split <;> simp [getD_getElem?, *]
@[deprecated getD_eq_getD_getElem? (since := "2025-02-12")] abbrev getD_eq_get? := @getD_eq_getD_getElem?
theorem getElem!_eq_getD [Inhabited α] (a : Array α) : a[i]! = a.getD i default := by
simp only [ get!_eq_getElem!]
rfl
@[deprecated getElem!_eq_getD (since := "2025-02-12")]
theorem get!_eq_getD [Inhabited α] (a : Array α) : a.get! n = a.getD n default := rfl
theorem get!_eq_getElem? [Inhabited α] (a : Array α) (i : Nat) :
a.get! i = (a.get? i).getD default := by
@[deprecated "Use `a[i]!` instead of `a.get! i`." (since := "2025-02-12")]
theorem get!_eq_getD_getElem? [Inhabited α] (a : Array α) (i : Nat) :
a.get! i = a[i]?.getD default := by
by_cases p : i < a.size <;>
simp only [get!_eq_getD, getD_eq_get?, getD_getElem?, p, get?_eq_getElem?]
simp only [get!_eq_getElem!, getElem!_eq_getD, getD_eq_getD_getElem?, getD_getElem?, p]
set_option linter.deprecated false in
@[deprecated get!_eq_getD_getElem? (since := "2025-02-12")] abbrev get!_eq_getElem? := @get!_eq_getD_getElem?
/-! # ofFn -/
@@ -3325,11 +3342,13 @@ theorem getElem?_size_le (a : Array α) (i : Nat) (h : a.size ≤ i) : a[i]? = n
theorem getElem_mem_toList (a : Array α) (h : i < a.size) : a[i] a.toList := by
simp only [ getElem_toList, List.getElem_mem]
set_option linter.deprecated false in
@[deprecated "`Array.get?` is deprecated, use `a[i]?` instead." (since := "2025-02-12")]
theorem get?_eq_get?_toList (a : Array α) (i : Nat) : a.get? i = a.toList.get? i := by
simp [ getElem?_toList]
theorem get!_eq_get? [Inhabited α] (a : Array α) : a.get! n = (a.get? n).getD default := by
simp only [get!_eq_getElem?, get?_eq_getElem?]
set_option linter.deprecated false in
@[deprecated get!_eq_getD_getElem? (since := "2025-02-12")] abbrev get!_eq_get? := @get!_eq_getD_getElem?
theorem back!_eq_back? [Inhabited α] (a : Array α) : a.back! = a.back?.getD default := by
simp [back!, back?, getElem!_def, Option.getD]; rfl
@@ -3939,8 +3958,6 @@ end Array
namespace List
@[deprecated back!_toArray (since := "2024-10-31")] abbrev back_toArray := @back!_toArray
@[deprecated setIfInBounds_toArray (since := "2024-11-24")] abbrev setD_toArray := @setIfInBounds_toArray
end List
@@ -3962,9 +3979,6 @@ abbrev getElem_fin_eq_toList_get := @getElem_fin_eq_getElem_toList
@[deprecated "Use reverse direction of `getElem?_toList`" (since := "2024-10-17")]
abbrev getElem?_eq_toList_getElem? := @getElem?_toList
@[deprecated get?_eq_get?_toList (since := "2024-10-17")]
abbrev get?_eq_toList_get? := @get?_eq_get?_toList
@[deprecated getElem?_swap (since := "2024-10-17")] abbrev get?_swap := @getElem?_swap
@[deprecated getElem_push (since := "2024-10-21")] abbrev get_push := @getElem_push

View File

@@ -418,6 +418,11 @@ theorem mapIdx_eq_mapIdx_iff {l : Array α} :
rcases l with l
simp [List.getLast?_mapIdx]
@[simp] theorem back_mapIdx {l : Array α} {f : Nat α β} (h) :
(l.mapIdx f).back h = f (l.size - 1) (l.back (by simpa using h)) := by
rcases l with l
simp [List.getLast_mapIdx]
@[simp] theorem mapIdx_mapIdx {l : Array α} {f : Nat α β} {g : Nat β γ} :
(l.mapIdx f).mapIdx g = l.mapIdx (fun i => g i f i) := by
simp [mapIdx_eq_iff]

View File

@@ -7,6 +7,20 @@ prelude
import Init.Data.Array.Lemmas
import Init.Data.List.Nat.TakeDrop
/-!
These lemmas are used in the internals of HashMap.
They should find a new home and/or be reformulated.
-/
namespace List
theorem exists_of_set {i : Nat} {a' : α} {l : List α} (h : i < l.length) :
l₁ l₂, l = l₁ ++ l[i] :: l₂ l₁.length = i l.set i a' = l₁ ++ a' :: l₂ := by
refine l.take i, l.drop (i + 1), by simp, length_take_of_le (Nat.le_of_lt h), ?_
simp [set_eq_take_append_cons_drop, h]
end List
namespace Array
theorem exists_of_uset (self : Array α) (i d h) :

View File

@@ -25,6 +25,10 @@ set_option linter.missingDocs true
namespace BitVec
@[inline, deprecated BitVec.ofNatLT (since := "2025-02-13"), inherit_doc BitVec.ofNatLT]
protected def ofNatLt {n : Nat} (i : Nat) (p : i < 2 ^ n) : BitVec n :=
BitVec.ofNatLT i p
section Nat
instance natCastInst : NatCast (BitVec w) := BitVec.ofNat w
@@ -55,12 +59,12 @@ end subsingleton
section zero_allOnes
/-- Return a bitvector `0` of size `n`. This is the bitvector with all zero bits. -/
protected def zero (n : Nat) : BitVec n := .ofNatLt 0 (Nat.two_pow_pos n)
protected def zero (n : Nat) : BitVec n := .ofNatLT 0 (Nat.two_pow_pos n)
instance : Inhabited (BitVec n) where default := .zero n
/-- Bit vector of size `n` where all bits are `1`s -/
def allOnes (n : Nat) : BitVec n :=
.ofNatLt (2^n - 1) (Nat.le_of_eq (Nat.sub_add_cancel (Nat.two_pow_pos n)))
.ofNatLT (2^n - 1) (Nat.le_of_eq (Nat.sub_add_cancel (Nat.two_pow_pos n)))
end zero_allOnes
@@ -123,6 +127,7 @@ instance : GetElem (BitVec w) Nat Bool fun _ i => i < w where
theorem getElem_eq_testBit_toNat (x : BitVec w) (i : Nat) (h : i < w) :
x[i] = x.toNat.testBit i := rfl
@[simp]
theorem getLsbD_eq_getElem {x : BitVec w} {i : Nat} (h : i < w) :
x.getLsbD i = x[i] := rfl
@@ -138,7 +143,7 @@ protected def toInt (x : BitVec n) : Int :=
(x.toNat : Int) - (2^n : Nat)
/-- The `BitVec` with value `(2^n + (i mod 2^n)) mod 2^n`. -/
protected def ofInt (n : Nat) (i : Int) : BitVec n := .ofNatLt (i % (Int.ofNat (2^n))).toNat (by
protected def ofInt (n : Nat) (i : Int) : BitVec n := .ofNatLT (i % (Int.ofNat (2^n))).toNat (by
apply (Int.toNat_lt _).mpr
· apply Int.emod_lt_of_pos
exact Int.ofNat_pos.mpr (Nat.two_pow_pos _)
@@ -167,12 +172,12 @@ recommended_spelling "one" for "1#n" in [BitVec.ofNat, «term__#__»]
| `($(_) $n $i:num) => `($i:num#$n)
| _ => throw ()
/-- Notation for bit vector literals without truncation. `i#'lt` is a shorthand for `BitVec.ofNatLt i lt`. -/
/-- Notation for bit vector literals without truncation. `i#'lt` is a shorthand for `BitVec.ofNatLT i lt`. -/
scoped syntax:max term:max noWs "#'" noWs term:max : term
macro_rules | `($i#'$p) => `(BitVec.ofNatLt $i $p)
macro_rules | `($i#'$p) => `(BitVec.ofNatLT $i $p)
/-- Unexpander for bit vector literals without truncation. -/
@[app_unexpander BitVec.ofNatLt] def unexpandBitVecOfNatLt : Lean.PrettyPrinter.Unexpander
@[app_unexpander BitVec.ofNatLT] def unexpandBitVecOfNatLt : Lean.PrettyPrinter.Unexpander
| `($(_) $i $p) => `($i#'$p)
| _ => throw ()
@@ -356,7 +361,7 @@ end relations
section cast
/-- `cast eq x` embeds `x` into an equal `BitVec` type. -/
@[inline] protected def cast (eq : n = m) (x : BitVec n) : BitVec m := .ofNatLt x.toNat (eq x.isLt)
@[inline] protected def cast (eq : n = m) (x : BitVec n) : BitVec m := .ofNatLT x.toNat (eq x.isLt)
@[simp] theorem cast_ofNat {n m : Nat} (h : n = m) (x : Nat) :
(BitVec.ofNat n x).cast h = BitVec.ofNat m x := by

View File

@@ -285,7 +285,7 @@ theorem adc_spec (x y : BitVec w) (c : Bool) :
simp [carry, Nat.mod_one]
cases c <;> rfl
case step =>
simp [adcb, Prod.mk.injEq, carry_succ, getLsbD_add_add_bool]
simp [adcb, Prod.mk.injEq, carry_succ, getElem_add_add_bool]
theorem add_eq_adc (w : Nat) (x y : BitVec w) : x + y = (adc x y false).snd := by
simp [adc_spec]
@@ -295,7 +295,7 @@ theorem add_eq_adc (w : Nat) (x y : BitVec w) : x + y = (adc x y false).snd := b
theorem getMsbD_add {i : Nat} {i_lt : i < w} {x y : BitVec w} :
getMsbD (x + y) i =
Bool.xor (getMsbD x i) (Bool.xor (getMsbD y i) (carry (w - 1 - i) x y false)) := by
simp [getMsbD, getLsbD_add, i_lt, show w - 1 - i < w by omega]
simp [getMsbD, getElem_add, i_lt, show w - 1 - i < w by omega]
theorem msb_add {w : Nat} {x y: BitVec w} :
(x + y).msb =
@@ -359,24 +359,25 @@ theorem msb_sub {x y: BitVec w} :
/-! ### Negation -/
theorem bit_not_testBit (x : BitVec w) (i : Fin w) :
getLsbD (((iunfoldr (fun (i : Fin w) c => (c, !(x.getLsbD i)))) ()).snd) i.val = !(getLsbD x i.val) := by
(((iunfoldr (fun (i : Fin w) c => (c, !(x[i.val])))) ()).snd)[i.val] = !(getLsbD x i.val) := by
apply iunfoldr_getLsbD (fun _ => ()) i (by simp)
theorem bit_not_add_self (x : BitVec w) :
((iunfoldr (fun (i : Fin w) c => (c, !(x.getLsbD i)))) ()).snd + x = -1 := by
((iunfoldr (fun (i : Fin w) c => (c, !(x[i.val])))) ()).snd + x = -1 := by
simp only [add_eq_adc]
apply iunfoldr_replace_snd (fun _ => false) (-1) false rfl
intro i; simp only [ BitVec.not, adcb, testBit_toNat]
rw [iunfoldr_replace_snd (fun _ => ()) (((iunfoldr (fun i c => (c, !(x.getLsbD i)))) ()).snd)]
<;> simp [bit_not_testBit, negOne_eq_allOnes, getLsbD_allOnes]
intro i; simp only [adcb, Fin.is_lt, getLsbD_eq_getElem, atLeastTwo_false_right, bne_false,
ofNat_eq_ofNat, Fin.getElem_fin, Prod.mk.injEq, and_eq_false_imp]
rw [iunfoldr_replace_snd (fun _ => ()) (((iunfoldr (fun i c => (c, !(x[i.val])))) ()).snd)]
<;> simp [bit_not_testBit, negOne_eq_allOnes, getElem_allOnes]
theorem bit_not_eq_not (x : BitVec w) :
((iunfoldr (fun i c => (c, !(x.getLsbD i)))) ()).snd = ~~~ x := by
((iunfoldr (fun i c => (c, !(x[i])))) ()).snd = ~~~ x := by
simp [allOnes_sub_eq_not, BitVec.eq_sub_iff_add_eq.mpr (bit_not_add_self x), negOne_eq_allOnes]
theorem bit_neg_eq_neg (x : BitVec w) : -x = (adc (((iunfoldr (fun (i : Fin w) c => (c, !(x.getLsbD i)))) ()).snd) (BitVec.ofNat w 1) false).snd:= by
theorem bit_neg_eq_neg (x : BitVec w) : -x = (adc (((iunfoldr (fun (i : Fin w) c => (c, !(x[i.val])))) ()).snd) (BitVec.ofNat w 1) false).snd:= by
simp only [ add_eq_adc]
rw [iunfoldr_replace_snd ((fun _ => ())) (((iunfoldr (fun (i : Fin w) c => (c, !(x.getLsbD i)))) ()).snd) _ rfl]
rw [iunfoldr_replace_snd ((fun _ => ())) (((iunfoldr (fun (i : Fin w) c => (c, !(x[i.val])))) ()).snd) _ rfl]
· rw [BitVec.eq_sub_iff_add_eq.mpr (bit_not_add_self x), sub_toAdd, BitVec.add_comm _ (-x)]
simp [ sub_toAdd, BitVec.sub_add_cancel]
· simp [bit_not_testBit x _]
@@ -575,16 +576,18 @@ theorem setWidth_setWidth_succ_eq_setWidth_setWidth_add_twoPow (x : BitVec w) (i
setWidth w (x.setWidth i) + (x &&& twoPow w i) := by
rw [add_eq_or_of_and_eq_zero]
· ext k h
simp only [getLsbD_setWidth, h, decide_true, Bool.true_and, getLsbD_or, getLsbD_and]
simp only [getElem_setWidth, getLsbD_setWidth, h, getLsbD_eq_getElem, getElem_or, getElem_and,
getElem_twoPow]
by_cases hik : i = k
· subst hik
simp [h]
· simp only [getLsbD_twoPow, hik, decide_false, Bool.and_false, Bool.or_false]
by_cases hik' : k < (i + 1)
· by_cases hik' : k < (i + 1)
· have hik'' : k < i := by omega
simp [hik', hik'']
omega
· have hik'' : ¬ (k < i) := by omega
simp [hik', hik'']
omega
· ext k
simp only [and_twoPow, getLsbD_and, getLsbD_setWidth, Fin.is_lt, decide_true, Bool.true_and,
getLsbD_zero, and_eq_false_imp, and_eq_true, decide_eq_true_eq, and_imp]

View File

@@ -101,14 +101,14 @@ Correctness theorem for `iunfoldr`.
theorem iunfoldr_replace
{f : Fin w α α × Bool} (state : Nat α) (value : BitVec w) (a : α)
(init : state 0 = a)
(step : (i : Fin w), f i (state i.val) = (state (i.val+1), value.getLsbD i.val)) :
(step : (i : Fin w), f i (state i.val) = (state (i.val+1), value[i.val])) :
iunfoldr f a = (state w, value) := by
simp [iunfoldr.eq_test state value a init step]
theorem iunfoldr_replace_snd
{f : Fin w α α × Bool} (state : Nat α) (value : BitVec w) (a : α)
(init : state 0 = a)
(step : (i : Fin w), f i (state i.val) = (state (i.val+1), value.getLsbD i.val)) :
(step : (i : Fin w), f i (state i.val) = (state (i.val+1), value[i.val])) :
(iunfoldr f a).snd = value := by
simp [iunfoldr.eq_test state value a init step]

View File

@@ -22,6 +22,9 @@ namespace BitVec
@[simp] theorem getLsbD_ofFin (x : Fin (2^n)) (i : Nat) :
getLsbD (BitVec.ofFin x) i = x.val.testBit i := rfl
@[simp] theorem getElem_ofFin (x : Fin (2^n)) (i : Nat) (h : i < n) :
(BitVec.ofFin x)[i] = x.val.testBit i := rfl
@[simp] theorem getLsbD_ge (x : BitVec w) (i : Nat) (ge : w i) : getLsbD x i = false := by
let x, x_lt := x
simp only [getLsbD_ofFin]
@@ -90,6 +93,11 @@ theorem getLsbD_eq_getElem?_getD {x : BitVec w} {i : Nat} :
· rfl
· simp_all
@[simp]
theorem getElem_of_getLsbD_eq_true {x : BitVec w} {i : Nat} (h : x.getLsbD i = true) :
(x[i]'(lt_of_getLsbD h) = true) = True := by
simp [ BitVec.getLsbD_eq_getElem, h]
/--
This normalized a bitvec using `ofFin` to `ofNat`.
-/
@@ -178,9 +186,7 @@ theorem getMsbD_eq_getMsb?_getD (x : BitVec w) (i : Nat) :
intros
omega
-- We choose `eq_of_getLsbD_eq` as the `@[ext]` theorem for `BitVec`
-- somewhat arbitrarily over `eq_of_getMsbD_eq`.
@[ext] theorem eq_of_getLsbD_eq {x y : BitVec w}
theorem eq_of_getLsbD_eq {x y : BitVec w}
(pred : i, i < w x.getLsbD i = y.getLsbD i) : x = y := by
apply eq_of_toNat_eq
apply Nat.eq_of_testBit_eq
@@ -191,6 +197,21 @@ theorem getMsbD_eq_getMsb?_getD (x : BitVec w) (i : Nat) :
have p : i w := Nat.le_of_not_gt i_lt
simp [testBit_toNat, getLsbD_ge _ _ p]
@[ext] theorem eq_of_getElem_eq {x y : BitVec n} :
( i (hi : i < n), x[i] = y[i]) x = y :=
fun h => BitVec.eq_of_getLsbD_eq (h ·)
theorem eq_of_getLsbD_eq_iff {w : Nat} {x y : BitVec w} :
x = y (i : Nat), i < w x.getLsbD i = y.getLsbD i := by
have iff := @BitVec.eq_of_getElem_eq_iff w x y
constructor
· intros heq i lt
have hext := iff.mp heq i lt
simp only [ getLsbD_eq_getElem] at hext
exact hext
· intros heq
exact iff.mpr heq
theorem eq_of_getMsbD_eq {x y : BitVec w}
(pred : i, i < w x.getMsbD i = y.getMsbD i) : x = y := by
simp only [getMsbD] at pred
@@ -211,7 +232,7 @@ theorem eq_of_getMsbD_eq {x y : BitVec w}
simpa [q_lt, Nat.sub_sub_self, r] using q
-- This cannot be a `@[simp]` lemma, as it would be tried at every term.
theorem of_length_zero {x : BitVec 0} : x = 0#0 := by ext; simp
theorem of_length_zero {x : BitVec 0} : x = 0#0 := by ext; simp [ getLsbD_eq_getElem]
theorem toNat_zero_length (x : BitVec 0) : x.toNat = 0 := by simp [of_length_zero]
theorem getLsbD_zero_length (x : BitVec 0) : x.getLsbD i = false := by simp
@@ -274,16 +295,27 @@ theorem ofBool_eq_iff_eq : ∀ {b b' : Bool}, BitVec.ofBool b = BitVec.ofBool b'
@[simp, bitvec_to_nat] theorem toNat_ofFin (x : Fin (2^n)) : (BitVec.ofFin x).toNat = x.val := rfl
@[simp] theorem toNat_ofNatLt (x : Nat) (p : x < 2^w) : (x#'p).toNat = x := rfl
@[simp] theorem toNat_ofNatLT (x : Nat) (p : x < 2^w) : (x#'p).toNat = x := rfl
@[simp] theorem getLsbD_ofNatLt {n : Nat} (x : Nat) (lt : x < 2^n) (i : Nat) :
@[deprecated toNat_ofNatLT (since := "2025-02-13")]
theorem toNat_ofNatLt (x : Nat) (p : x < 2^w) : (x#'p).toNat = x := rfl
@[simp] theorem getLsbD_ofNatLT {n : Nat} (x : Nat) (lt : x < 2^n) (i : Nat) :
getLsbD (x#'lt) i = x.testBit i := by
simp [getLsbD, BitVec.ofNatLt]
simp [getLsbD, BitVec.ofNatLT]
@[simp] theorem getMsbD_ofNatLt {n x i : Nat} (h : x < 2^n) :
@[deprecated getLsbD_ofNatLT (since := "2025-02-13")]
theorem getLsbD_ofNatLt {n : Nat} (x : Nat) (lt : x < 2^n) (i : Nat) :
getLsbD (x#'lt) i = x.testBit i := getLsbD_ofNatLT x lt i
@[simp] theorem getMsbD_ofNatLT {n x i : Nat} (h : x < 2^n) :
getMsbD (x#'h) i = (decide (i < n) && x.testBit (n - 1 - i)) := by
simp [getMsbD, getLsbD]
@[deprecated getMsbD_ofNatLT (since := "2025-02-13")]
theorem getMsbD_ofNatLt {n x i : Nat} (h : x < 2^n) :
getMsbD (x#'h) i = (decide (i < n) && x.testBit (n - 1 - i)) := getMsbD_ofNatLT h
@[simp, bitvec_to_nat] theorem toNat_ofNat (x w : Nat) : (BitVec.ofNat w x).toNat = x % 2^w := by
simp [BitVec.toNat, BitVec.ofNat, Fin.ofNat']
@@ -374,7 +406,12 @@ theorem getLsbD_ofBool (b : Bool) (i : Nat) : (ofBool b).getLsbD i = ((i = 0) &&
· simp only [ofBool, ofNat_eq_ofNat, cond_true, getLsbD_ofNat, Bool.and_true]
by_cases hi : i = 0 <;> simp [hi] <;> omega
theorem getElem_ofBool {b : Bool} : (ofBool b)[0] = b := by simp
@[simp] theorem getElem_ofBool_zero {b : Bool} : (ofBool b)[0] = b := by simp
@[simp]
theorem getElem_ofBool {b : Bool} {h : i < 1}: (ofBool b)[i] = b := by
simp [ getLsbD_eq_getElem]
omega
@[simp] theorem getMsbD_ofBool (b : Bool) : (ofBool b).getMsbD i = (decide (i = 0) && b) := by
cases b <;> simp [getMsbD]
@@ -535,7 +572,7 @@ theorem toInt_ofNat {n : Nat} (x : 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, h, BitVec.toInt]
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
@@ -750,10 +787,8 @@ theorem setWidth'_eq {x : BitVec w} (h : w ≤ v) : x.setWidth' h = x.setWidth v
@[simp] theorem setWidth_setWidth_of_le (x : BitVec w) (h : k l) :
(x.setWidth l).setWidth k = x.setWidth k := by
ext i
simp only [getLsbD_setWidth, Fin.is_lt, decide_true, Bool.true_and]
have p := lt_of_getLsbD (x := x) (i := i)
revert p
cases getLsbD x i <;> simp; omega
simp [getElem_setWidth, Fin.is_lt, decide_true, Bool.true_and]
omega
@[simp] theorem setWidth_cast {x : BitVec w} {h : w = v} : (x.cast h).setWidth k = x.setWidth k := by
apply eq_of_getLsbD_eq
@@ -781,11 +816,10 @@ theorem setWidth_one_eq_ofBool_getLsb_zero (x : BitVec w) :
theorem setWidth_ofNat_one_eq_ofNat_one_of_lt {v w : Nat} (hv : 0 < v) :
(BitVec.ofNat v 1).setWidth w = BitVec.ofNat w 1 := by
ext i h
simp only [getLsbD_setWidth, h, decide_true, getLsbD_ofNat, Bool.true_and,
simp only [getElem_setWidth, h, decide_true, getLsbD_ofNat, Bool.true_and,
Bool.and_iff_right_iff_imp, decide_eq_true_eq]
intros hi₁
have hv := Nat.testBit_one_eq_true_iff_self_eq_zero.mp hi₁
omega
have hv := (@Nat.testBit_one_eq_true_iff_self_eq_zero i)
by_cases h : Nat.testBit 1 i = true <;> simp_all
/-- Truncating to width 1 produces a bitvector equal to the least significant bit. -/
theorem setWidth_one {x : BitVec w} :
@@ -808,12 +842,9 @@ and the second `setWidth` is a non-trivial extension.
-- `simp` can discharge the side condition itself.
@[simp] theorem setWidth_setWidth {x : BitVec u} {w v : Nat} (h : ¬ (v < u v < w)) :
setWidth w (setWidth v x) = setWidth w x := by
ext
simp_all only [getLsbD_setWidth, decide_true, Bool.true_and, Bool.and_iff_right_iff_imp,
decide_eq_true_eq]
intro h
replace h := lt_of_getLsbD h
omega
ext i ih
have := @lt_of_getLsbD u x i
by_cases h' : x.getLsbD i = true <;> simp [h'] at * <;> omega
/-! ## extractLsb -/
@@ -841,6 +872,85 @@ protected theorem extractLsb_ofNat (x n : Nat) (hi lo : Nat) :
(extractLsb' start len x).getLsbD i = (i < len && x.getLsbD (start+i)) := by
simp [getLsbD, Nat.lt_succ]
/--
Get the most significant bit after `extractLsb'`. With `extractLsb'`, we extract
a `BitVec len` `x'` with length `len` from `BitVec w` `x`, starting from the
element at position `start`. The function `getMsb` extracts a bit counting from
the most significant bit. Assuming certain conditions,
`(@extractLsbD' w x start len).getMsbD i` is equal to
`@getMsbD w x (w - (start + len - i))`.
Example (w := 10, start := 3, len := 4):
|---| = w - (start + len) = 3
|start + len| = 7
|start| = 3
| len | = 4
let x = 9 8 7 6 5 4 3 2 1 0
let x' = x.extractLsb' 3 4 = 6 5 4 3
| |
| x'.getMsbD 1 =
x.getMsbD (i := w - (start + len - i) = 10 - (3 + 4 - 1) = 4)
|
x'.getMsbD 0 =
x.getMsbD (i := w - (start + len - i) = 10 - (3 + 4 - 0) = 3)
# Condition 1: `i < len`
The index `i` must be within the range of `len`.
# Condition 2: `start + len - i ≤ w`
If `start + len` is larger than `w`, the high bits at `i` with `w ≤ i` are filled with 0,
meaning that `getMsbD[i] = false` for these `i`.
If `i` is large enough, `getMsbD[i]` is again within the bounds `x`.
The precise condition is:
`start + len - i ≤ w`
Example (w := 10, start := 7, len := 5):
|= w - (start + len) = 0
| start + len | = 12
| start | = 7
| len | = 5
let x = 9 8 7 6 5 4 3 2 1 0
let x' = x.extractLsb' 7 5 = _ _ 9 8 7
| |
| x'.getMsbD (i := 2) =
| x.getMsbD (i := w - (start + len - i) = 10 - (7 + 5 - 2)) =
| x.getMsbD 0
| ✅ start + len - i ≤ w
| 7 + 5 - 2 = 10 ≤ 10
|
x'.getMsbD (i := 0) =
x.getMsbD (i := w - (start + len - i) = 10 - (7 + 5 - 0)) =
x.getMsbD (i := w - (start + len - i) = x.getMsbD (i := -2) -- in Nat becomes 0
❌ start + len - i ≤ w
7 + 5 - 0 ≤ w
-/
@[simp] theorem getMsbD_extractLsb' {start len : Nat} {x : BitVec w} {i : Nat} :
(extractLsb' start len x).getMsbD i =
(decide (i < len) &&
(decide (start + len - i w) &&
x.getMsbD (w - (start + len - i)))) := by
rw [getMsbD_eq_getLsbD, getLsbD_extractLsb', getLsbD_eq_getMsbD]
simp only [bool_to_prop]
constructor
· rintro h₁, h₂, h₃, h₄
simp [show w - (start + len - i) = w - 1 - (start + (len - 1 - i)) by omega, h₄]
omega
· rintro h₁, h₂, h₃
simp [show w - 1 - (start + (len - 1 - i)) = w - (start + len - i) by omega, h₃]
omega
@[simp] theorem msb_extractLsb' {start len : Nat} {x : BitVec w} :
(extractLsb' start len x).msb =
(decide (0 < len) &&
(decide (start + len w) &&
x.getMsbD (w - (start + len)))) := by
simp [BitVec.msb, getMsbD_extractLsb']
@[simp] theorem getElem_extract {hi lo : Nat} {x : BitVec n} {i : Nat} (h : i < hi - lo + 1) :
(extractLsb hi lo x)[i] = getLsbD x (lo+i) := by
simp [getElem_eq_testBit_toNat, getLsbD, h]
@@ -849,6 +959,34 @@ protected theorem extractLsb_ofNat (x n : Nat) (hi lo : Nat) :
getLsbD (extractLsb hi lo x) i = (i (hi-lo) && getLsbD x (lo+i)) := by
simp [getLsbD, Nat.lt_succ]
@[simp] theorem getLsbD_extractLsb {hi lo : Nat} {x : BitVec n} {i : Nat} :
(extractLsb hi lo x).getLsbD i = (decide (i < hi - lo + 1) && x.getLsbD (lo + i)) := by
rw [extractLsb, getLsbD_extractLsb']
@[simp] theorem getMsbD_extractLsb {hi lo : Nat} {x : BitVec w} {i : Nat} :
(extractLsb hi lo x).getMsbD i =
(decide (i < hi - lo + 1) &&
(decide (max hi lo - i < w) &&
x.getMsbD (w - 1 - (max hi lo - i)))) := by
rw [getMsbD_eq_getLsbD, getLsbD_extractLsb, getLsbD_eq_getMsbD]
simp only [bool_to_prop]
constructor
· rintro h₁, h₂, h₃, h₄
have p : w - 1 - (lo + (hi - lo + 1 - 1 - i)) = w - 1 - (max hi lo - i) := by omega
rw [p] at h₄
simp [h₄]
omega
· rintro h₁, h₂, h₃
have p : w - 1 - (lo + (hi - lo + 1 - 1 - i)) = w - 1 - (max hi lo - i) := by omega
rw [ p] at h₃
rw [h₃]
simp
omega
@[simp] theorem msb_extractLsb {hi lo : Nat} {x : BitVec w} :
(extractLsb hi lo x).msb = (decide (max hi lo < w) && x.getMsbD (w - 1 - max hi lo)) := by
simp [BitVec.msb]
theorem extractLsb'_eq_extractLsb {w : Nat} (x : BitVec w) (start len : Nat) (h : len > 0) :
x.extractLsb' start len = (x.extractLsb (len - 1 + start) start).cast (by omega) := by
apply eq_of_toNat_eq
@@ -885,12 +1023,13 @@ theorem extractLsb'_eq_extractLsb {w : Nat} (x : BitVec w) (start len : Nat) (h
@[simp] theorem ofFin_add_rev (x : Fin (2^n)) : ofFin (x + x.rev) = allOnes n := by
ext
simp only [Fin.rev, getLsbD_ofFin, getLsbD_allOnes, Fin.is_lt, decide_true]
simp only [Fin.rev, getElem_ofFin, getElem_allOnes, Fin.is_lt, decide_true]
rw [Fin.add_def]
simp only [Nat.testBit_mod_two_pow, Fin.is_lt, decide_true, Bool.true_and]
have h : (x : Nat) + (2 ^ n - (x + 1)) = 2 ^ n - 1 := by omega
rw [h, Nat.testBit_two_pow_sub_one]
simp
omega
/-! ### or -/
@@ -972,8 +1111,8 @@ theorem or_eq_zero_iff {x y : BitVec w} : (x ||| y) = 0#w ↔ x = 0#w ∧ y = 0#
constructor
all_goals
· ext i ih
have := BitVec.eq_of_getLsbD_eq_iff.mp h i ih
simp only [getLsbD_or, getLsbD_zero, Bool.or_eq_false_iff] at this
have := BitVec.eq_of_getElem_eq_iff.mp h i ih
simp only [getElem_or, getElem_zero, Bool.or_eq_false_iff] at this
simp [this]
· intro h
simp [h]
@@ -1069,8 +1208,8 @@ theorem and_eq_allOnes_iff {x y : BitVec w} :
constructor
all_goals
· ext i ih
have := BitVec.eq_of_getLsbD_eq_iff.mp h i ih
simp only [getLsbD_and, getLsbD_allOnes, ih, decide_true, Bool.and_eq_true] at this
have := BitVec.eq_of_getElem_eq_iff.mp h i ih
simp only [getElem_and, getElem_allOnes, Bool.and_eq_true] at this
simp [this, ih]
· intro h
simp [h]
@@ -1155,8 +1294,8 @@ theorem xor_left_inj {x y : BitVec w} (z : BitVec w) : (x ^^^ z = y ^^^ z) ↔ x
constructor
· intro h
ext i ih
have := BitVec.eq_of_getLsbD_eq_iff.mp h i
simp only [getLsbD_xor, Bool.xor_left_inj] at this
have := BitVec.eq_of_getElem_eq_iff.mp h i
simp only [getElem_xor, Bool.xor_left_inj] at this
exact this ih
· intro h
rw [h]
@@ -1217,7 +1356,7 @@ theorem not_def {x : BitVec v} : ~~~x = allOnes v ^^^ x := rfl
@[simp] theorem ofInt_negSucc_eq_not_ofNat {w n : Nat} :
BitVec.ofInt w (Int.negSucc n) = ~~~.ofNat w n := by
simp only [BitVec.ofInt, Int.toNat, Int.ofNat_eq_coe, toNat_eq, toNat_ofNatLt, toNat_not,
simp only [BitVec.ofInt, Int.toNat, Int.ofNat_eq_coe, toNat_eq, toNat_ofNatLT, toNat_not,
toNat_ofNat]
cases h : Int.negSucc n % ((2 ^ w : Nat) : Int)
case ofNat =>
@@ -1384,19 +1523,19 @@ theorem zero_shiftLeft (n : Nat) : 0#w <<< n = 0#w := by
theorem shiftLeft_xor_distrib (x y : BitVec w) (n : Nat) :
(x ^^^ y) <<< n = (x <<< n) ^^^ (y <<< n) := by
ext i h
simp only [getLsbD_shiftLeft, h, decide_true, Bool.true_and, getLsbD_xor]
simp only [getElem_shiftLeft, h, decide_true, Bool.true_and, getLsbD_xor]
by_cases h' : i < n <;> simp [h']
theorem shiftLeft_and_distrib (x y : BitVec w) (n : Nat) :
(x &&& y) <<< n = (x <<< n) &&& (y <<< n) := by
ext i h
simp only [getLsbD_shiftLeft, h, decide_true, Bool.true_and, getLsbD_and]
simp only [getElem_shiftLeft, h, decide_true, Bool.true_and, getLsbD_and]
by_cases h' : i < n <;> simp [h']
theorem shiftLeft_or_distrib (x y : BitVec w) (n : Nat) :
(x ||| y) <<< n = (x <<< n) ||| (y <<< n) := by
ext i h
simp only [getLsbD_shiftLeft, h, decide_true, Bool.true_and, getLsbD_or]
simp only [getElem_shiftLeft, h, decide_true, Bool.true_and, getLsbD_or]
by_cases h' : i < n <;> simp [h']
@[simp] theorem getMsbD_shiftLeft (x : BitVec w) (i) :
@@ -1418,7 +1557,7 @@ theorem shiftLeftZeroExtend_eq {x : BitVec w} :
apply eq_of_toNat_eq
rw [shiftLeftZeroExtend, setWidth]
split
· simp only [toNat_ofNatLt, toNat_shiftLeft, toNat_setWidth']
· simp only [toNat_ofNatLT, toNat_shiftLeft, toNat_setWidth']
rw [Nat.mod_eq_of_lt]
rw [Nat.shiftLeft_eq, Nat.pow_add]
exact Nat.mul_lt_mul_of_pos_right x.isLt (Nat.two_pow_pos _)
@@ -1455,7 +1594,7 @@ theorem shiftLeftZeroExtend_eq {x : BitVec w} :
theorem shiftLeft_add {w : Nat} (x : BitVec w) (n m : Nat) :
x <<< (n + m) = (x <<< n) <<< m := by
ext i
simp only [getLsbD_shiftLeft, Fin.is_lt, decide_true, Bool.true_and]
simp only [getElem_shiftLeft, Fin.is_lt, decide_true, Bool.true_and]
rw [show i - (n + m) = (i - m - n) by omega]
cases h₂ : decide (i < m) <;>
cases h₃ : decide (i - m < w) <;>
@@ -1712,7 +1851,7 @@ theorem getElem_sshiftRight {x : BitVec w} {s i : Nat} (h : i < w) :
theorem sshiftRight_xor_distrib (x y : BitVec w) (n : Nat) :
(x ^^^ y).sshiftRight n = (x.sshiftRight n) ^^^ (y.sshiftRight n) := by
ext i
simp only [getLsbD_sshiftRight, getLsbD_xor, msb_xor]
simp only [getElem_sshiftRight, getElem_xor, msb_xor]
split
<;> by_cases w i
<;> simp [*]
@@ -1720,7 +1859,7 @@ theorem sshiftRight_xor_distrib (x y : BitVec w) (n : Nat) :
theorem sshiftRight_and_distrib (x y : BitVec w) (n : Nat) :
(x &&& y).sshiftRight n = (x.sshiftRight n) &&& (y.sshiftRight n) := by
ext i
simp only [getLsbD_sshiftRight, getLsbD_and, msb_and]
simp only [getElem_sshiftRight, getElem_and, msb_and]
split
<;> by_cases w i
<;> simp [*]
@@ -1728,7 +1867,7 @@ theorem sshiftRight_and_distrib (x y : BitVec w) (n : Nat) :
theorem sshiftRight_or_distrib (x y : BitVec w) (n : Nat) :
(x ||| y).sshiftRight n = (x.sshiftRight n) ||| (y.sshiftRight n) := by
ext i
simp only [getLsbD_sshiftRight, getLsbD_or, msb_or]
simp only [getElem_sshiftRight, getElem_or, msb_or]
split
<;> by_cases w i
<;> simp [*]
@@ -1750,31 +1889,28 @@ theorem msb_sshiftRight {n : Nat} {x : BitVec w} :
@[simp] theorem sshiftRight_zero {x : BitVec w} : x.sshiftRight 0 = x := by
ext i h
simp [getLsbD_sshiftRight, h]
simp [getElem_sshiftRight, h]
@[simp] theorem zero_sshiftRight {n : Nat} : (0#w).sshiftRight n = 0#w := by
ext i h
simp [getLsbD_sshiftRight, h]
simp [getElem_sshiftRight, h]
theorem sshiftRight_add {x : BitVec w} {m n : Nat} :
x.sshiftRight (m + n) = (x.sshiftRight m).sshiftRight n := by
ext i
simp only [getLsbD_sshiftRight, Nat.add_assoc]
by_cases h : w (i : Nat)
· simp [h]
· simp only [h, decide_false, Bool.not_false, Bool.true_and]
by_cases h : n + i < w
· simp [h]
· simp only [h₂, reduceIte]
by_cases h₃ : m + (n + i) < w
· simp [h₃]
omega
· simp [h₃, msb_sshiftRight]
simp [getElem_sshiftRight, getLsbD_sshiftRight, Nat.add_assoc]
by_cases h : n + i < w
· simp [h]
· simp only [h, reduceIte]
by_cases h : m + (n + i) < w
· simp [h]
omega
· simp [h₃, msb_sshiftRight]
theorem not_sshiftRight {b : BitVec w} :
~~~b.sshiftRight n = (~~~b).sshiftRight n := by
ext i
simp only [getLsbD_not, Fin.is_lt, decide_true, getLsbD_sshiftRight, Bool.not_and, Bool.not_not,
simp only [getElem_not, Fin.is_lt, decide_true, getElem_sshiftRight, Bool.not_and, Bool.not_not,
Bool.true_and, msb_not]
by_cases h : w i
<;> by_cases h' : n + i < w
@@ -1845,12 +1981,10 @@ theorem signExtend_eq_setWidth_of_msb_false {x : BitVec w} {v : Nat} (hmsb : x.m
x.signExtend v = x.setWidth v := by
ext i
by_cases hv : i < v
· simp only [signExtend, getLsbD, getLsbD_setWidth, hv, decide_true, Bool.true_and, toNat_ofInt,
· simp only [signExtend, getLsbD, getElem_setWidth, hv, decide_true, Bool.true_and, toNat_ofInt,
BitVec.toInt_eq_msb_cond, hmsb, reduceIte, reduceCtorEq]
rw [Int.ofNat_mod_ofNat, Int.toNat_ofNat, Nat.testBit_mod_two_pow]
simp [BitVec.testBit_toNat]
· simp only [getLsbD_setWidth, hv, decide_false, Bool.false_and]
apply getLsbD_ge
· simp only [getElem_setWidth, hv, decide_false, Bool.false_and]
omega
/--
@@ -1906,7 +2040,7 @@ theorem msb_signExtend {x : BitVec w} :
theorem signExtend_eq_setWidth_of_lt (x : BitVec w) {v : Nat} (hv : v w):
x.signExtend v = x.setWidth v := by
ext i h
simp only [getLsbD_signExtend, h, decide_true, Bool.true_and, getLsbD_setWidth,
simp only [getElem_signExtend, h, decide_true, Bool.true_and, getElem_setWidth,
ite_eq_left_iff, Nat.not_lt]
omega
@@ -2007,11 +2141,11 @@ theorem getLsbD_append {x : BitVec n} {y : BitVec m} :
· simp_all [h]
theorem getElem_append {x : BitVec n} {y : BitVec m} (h : i < n + m) :
(x ++ y)[i] = if i < m then getLsbD y i else getLsbD x (i - m) := by
simp only [append_def, getElem_or, getElem_shiftLeftZeroExtend, getElem_setWidth']
(x ++ y)[i] = if h : i < m then y[i] else x[i - m] := by
simp only [append_def]
by_cases h' : i < m
· simp [h']
· simp_all [h']
· simp [h', show m i by omega, show i - m < n by omega]
@[simp] theorem getMsbD_append {x : BitVec n} {y : BitVec m} :
getMsbD (x ++ y) i = if n i then getMsbD y (i - n) else getMsbD x i := by
@@ -2033,23 +2167,22 @@ theorem msb_append {x : BitVec w} {y : BitVec v} :
simp [h, q, t, BitVec.msb, getMsbD]
@[simp] theorem append_zero_width (x : BitVec w) (y : BitVec 0) : x ++ y = x := by
ext
rw [getLsbD_append] -- Why does this not work with `simp [getLsbD_append]`?
simp
ext i ih
rw [getElem_append] -- Why does this not work with `simp [getElem_append]`?
simp [show i < w by omega]
@[simp] theorem zero_width_append (x : BitVec 0) (y : BitVec v) : x ++ y = y.cast (by omega) := by
ext
rw [getLsbD_append]
simpa using lt_of_getLsbD
ext i ih
simp [getElem_append, show i < v by omega]
@[simp] theorem zero_append_zero : 0#v ++ 0#w = 0#(v + w) := by
ext
simp only [getLsbD_append, getLsbD_zero, ite_self]
simp [getElem_append]
@[simp] theorem cast_append_right (h : w + v = w + v') (x : BitVec w) (y : BitVec v) :
(x ++ y).cast h = x ++ y.cast (by omega) := by
ext
simp only [getLsbD_cast, getLsbD_append, cond_eq_if, decide_eq_true_eq]
simp only [getElem_cast, getElem_append]
split <;> split
· rfl
· omega
@@ -2060,57 +2193,54 @@ theorem msb_append {x : BitVec w} {y : BitVec v} :
@[simp] theorem cast_append_left (h : w + v = w' + v) (x : BitVec w) (y : BitVec v) :
(x ++ y).cast h = x.cast (by omega) ++ y := by
ext
simp [getLsbD_append]
simp [getElem_append]
theorem setWidth_append {x : BitVec w} {y : BitVec v} :
(x ++ y).setWidth k = if h : k v then y.setWidth k else (x.setWidth (k - v) ++ y).cast (by omega) := by
ext i h
simp only [getLsbD_setWidth, h, getLsbD_append]
simp only [getElem_setWidth, h, getLsbD_append, getElem_append]
split <;> rename_i h₁ <;> split <;> rename_i h₂
· simp [h]
· simp [getLsbD_append, h₁]
· simp [getElem_append, h₁]
· omega
· simp [getLsbD_append, h₁]
omega
· simp [getElem_append, h₁]
@[simp] theorem setWidth_append_of_eq {x : BitVec v} {y : BitVec w} (h : w' = w) : setWidth (v' + w') (x ++ y) = setWidth v' x ++ setWidth w' y := by
@[simp] theorem setWidth_append_of_eq {x : BitVec v} {y : BitVec w} (h : w' = w) :
setWidth (v' + w') (x ++ y) = setWidth v' x ++ setWidth w' y := by
subst h
ext i h
simp only [getLsbD_setWidth, h, decide_true, getLsbD_append, cond_eq_if,
decide_eq_true_eq, Bool.true_and, setWidth_eq]
ext i h'
simp only [getElem_setWidth, getLsbD_append, getElem_append]
split
· simp_all
· simp_all only [Bool.iff_and_self, decide_eq_true_eq]
intro h
omega
· simp
· simp
@[simp] theorem setWidth_cons {x : BitVec w} : (cons a x).setWidth w = x := by
simp [cons, setWidth_append]
@[simp] theorem not_append {x : BitVec w} {y : BitVec v} : ~~~ (x ++ y) = (~~~ x) ++ (~~~ y) := by
ext i
simp only [getLsbD_not, getLsbD_append, cond_eq_if]
simp only [getElem_not, getElem_append, cond_eq_if]
split
· simp_all
· simp_all; omega
· simp_all
@[simp] theorem and_append {x₁ x₂ : BitVec w} {y₁ y₂ : BitVec v} :
(x₁ ++ y₁) &&& (x₂ ++ y₂) = (x₁ &&& x₂) ++ (y₁ &&& y₂) := by
ext i
simp only [getLsbD_append, cond_eq_if]
split <;> simp [getLsbD_append, *]
simp only [getElem_and, getElem_append]
split <;> simp
@[simp] theorem or_append {x₁ x₂ : BitVec w} {y₁ y₂ : BitVec v} :
(x₁ ++ y₁) ||| (x₂ ++ y₂) = (x₁ ||| x₂) ++ (y₁ ||| y₂) := by
ext i
simp only [getLsbD_append, cond_eq_if]
split <;> simp [getLsbD_append, *]
simp only [getElem_or, getElem_append]
split <;> simp
@[simp] theorem xor_append {x₁ x₂ : BitVec w} {y₁ y₂ : BitVec v} :
(x₁ ++ y₁) ^^^ (x₂ ++ y₂) = (x₁ ^^^ x₂) ++ (y₁ ^^^ y₂) := by
ext i
simp only [getLsbD_append, cond_eq_if]
split <;> simp [getLsbD_append, *]
simp only [getElem_xor, getElem_append]
split <;> simp
theorem shiftRight_add {w : Nat} (x : BitVec w) (n m : Nat) :
x >>> (n + m) = (x >>> n) >>> m:= by
@@ -2140,21 +2270,19 @@ theorem msb_shiftLeft {x : BitVec w} {n : Nat} :
theorem ushiftRight_eq_extractLsb'_of_lt {x : BitVec w} {n : Nat} (hn : n < w) :
x >>> n = ((0#n) ++ (x.extractLsb' n (w - n))).cast (by omega) := by
ext i hi
simp only [getLsbD_ushiftRight, getLsbD_cast, getLsbD_append, getLsbD_extractLsb', getLsbD_zero,
Bool.if_false_right, Bool.and_self_left, Bool.iff_and_self, decide_eq_true_eq]
intros h
have := lt_of_getLsbD h
omega
simp only [getElem_cast, getElem_append, getElem_zero, getElem_ushiftRight, getElem_extractLsb']
split
· simp
· exact getLsbD_ge x (n+i) (by omega)
theorem shiftLeft_eq_concat_of_lt {x : BitVec w} {n : Nat} (hn : n < w) :
x <<< n = (x.extractLsb' 0 (w - n) ++ 0#n).cast (by omega) := by
ext i hi
simp only [getLsbD_shiftLeft, hi, decide_true, Bool.true_and, getLsbD_cast, getLsbD_append,
getLsbD_zero, getLsbD_extractLsb', Nat.zero_add, Bool.if_false_left]
simp only [getElem_shiftLeft, getElem_cast, getElem_append, getLsbD_zero, getLsbD_extractLsb',
Nat.zero_add, Bool.if_false_left]
by_cases hi' : i < n
· simp [hi']
· simp [hi']
omega
/-! ### rev -/
@@ -2230,7 +2358,7 @@ theorem getElem_cons {b : Bool} {n} {x : BitVec n} {i : Nat} (h : i < n + 1) :
theorem setWidth_succ (x : BitVec w) :
setWidth (i+1) x = cons (getLsbD x i) (setWidth i x) := by
ext j h
simp only [getLsbD_setWidth, getLsbD_cons, h, decide_true, Bool.true_and]
simp only [getElem_setWidth, getElem_cons]
if j_eq : j = i then
simp [j_eq]
else
@@ -2239,7 +2367,7 @@ theorem setWidth_succ (x : BitVec w) :
@[simp] theorem cons_msb_setWidth (x : BitVec (w+1)) : (cons x.msb (x.setWidth w)) = x := by
ext i
simp only [getLsbD_cons]
simp only [getElem_cons]
split <;> rename_i h
· simp [BitVec.msb, getMsbD, h]
· by_cases h' : i < w
@@ -2278,7 +2406,7 @@ theorem cons_append (x : BitVec w₁) (y : BitVec w₂) (a : Bool) :
theorem cons_append_append (x : BitVec w₁) (y : BitVec w₂) (z : BitVec w₃) (a : Bool) :
(cons a x) ++ y ++ z = (cons a (x ++ y ++ z)).cast (by omega) := by
ext i h
simp only [cons, getLsbD_append, getLsbD_cast, getLsbD_ofBool, cast_cast]
simp only [cons, getElem_append, getElem_cast, getElem_ofBool, cast_cast, getLsbD_append, getLsbD_cast, getLsbD_ofBool]
by_cases h₀ : i < w₁ + w₂ + w₃
· simp only [h₀, reduceIte]
by_cases h₁ : i < w₃
@@ -2286,8 +2414,7 @@ theorem cons_append_append (x : BitVec w₁) (y : BitVec w₂) (z : BitVec w₃)
· simp only [h₁, reduceIte]
by_cases h₂ : i - w₃ < w₂
· simp [h₂]
· simp [h₂]
omega
· simp [h₂, show i - w₃ - w₂ < w₁ by omega]
· simp only [show ¬i - w₃ - w₂ < w₁ by omega, reduceIte, show i - w₃ - w₂ - w₁ = 0 by omega,
decide_true, Bool.true_and, h₀, show i - (w₁ + w₂ + w₃) = 0 by omega]
by_cases h₂ : i < w₃
@@ -2321,7 +2448,7 @@ theorem getElem_concat (x : BitVec w) (b : Bool) (i : Nat) (h : i < w + 1) :
· simp [Nat.div_eq_of_lt b.toNat_lt, Nat.testBit_add_one]
@[simp] theorem getLsbD_concat_zero : (concat x b).getLsbD 0 = b := by
simp [getLsbD_concat]
simp [getElem_concat]
@[simp] theorem getElem_concat_zero : (concat x b)[0] = b := by
simp [getElem_concat]
@@ -2391,7 +2518,7 @@ theorem msb_concat {w : Nat} {b : Bool} {x : BitVec w} :
@[simp] theorem zero_concat_false : concat 0#w false = 0#(w + 1) := by
ext
simp [getLsbD_concat]
simp [getElem_concat]
/-! ### shiftConcat -/
@@ -2400,6 +2527,20 @@ theorem getLsbD_shiftConcat (x : BitVec w) (b : Bool) (i : Nat) :
= (decide (i < w) && (if (i = 0) then b else x.getLsbD (i - 1))) := by
simp only [shiftConcat, getLsbD_setWidth, getLsbD_concat]
theorem getElem_shiftConcat {x : BitVec w} {b : Bool} (h : i < w) :
(x.shiftConcat b)[i] = if i = 0 then b else x[i-1] := by
rw [ getLsbD_eq_getElem, getLsbD_shiftConcat, getLsbD_eq_getElem, decide_eq_true h, Bool.true_and]
@[simp]
theorem getElem_shiftConcat_zero {x : BitVec w} (b : Bool) (h : 0 < w) :
(x.shiftConcat b)[0] = b := by
simp [getElem_shiftConcat]
@[simp]
theorem getElem_shiftConcat_succ {x : BitVec w} {b : Bool} (h : i + 1 < w) :
(x.shiftConcat b)[i+1] = x[i] := by
simp [getElem_shiftConcat]
theorem getLsbD_shiftConcat_eq_decide (x : BitVec w) (b : Bool) (i : Nat) :
(shiftConcat x b).getLsbD i
= (decide (i < w) && ((decide (i = 0) && b) || (decide (0 < i) && x.getLsbD (i - 1)))) := by
@@ -2409,7 +2550,7 @@ theorem getLsbD_shiftConcat_eq_decide (x : BitVec w) (b : Bool) (i : Nat) :
theorem shiftRight_sub_one_eq_shiftConcat (n : BitVec w) (hwn : 0 < wn) :
n >>> (wn - 1) = (n >>> wn).shiftConcat (n.getLsbD (wn - 1)) := by
ext i h
simp only [getLsbD_ushiftRight, getLsbD_shiftConcat, h, decide_true, Bool.true_and]
simp only [getElem_ushiftRight, getElem_shiftConcat, h, decide_true, Bool.true_and]
split
· simp [*]
· congr 1; omega
@@ -2437,20 +2578,6 @@ theorem toNat_shiftConcat_lt_of_lt {x : BitVec w} {b : Bool} {k : Nat}
have := Bool.toNat_lt b
omega
theorem getElem_shiftConcat {x : BitVec w} {b : Bool} (h : i < w) :
(x.shiftConcat b)[i] = if i = 0 then b else x[i-1] := by
rw [ getLsbD_eq_getElem, getLsbD_shiftConcat, getLsbD_eq_getElem, decide_eq_true h, Bool.true_and]
@[simp]
theorem getElem_shiftConcat_zero {x : BitVec w} (b : Bool) (h : 0 < w) :
(x.shiftConcat b)[0] = b := by
simp [getElem_shiftConcat]
@[simp]
theorem getElem_shiftConcat_succ {x : BitVec w} {b : Bool} (h : i + 1 < w) :
(x.shiftConcat b)[i+1] = x[i] := by
simp [getElem_shiftConcat]
/-! ### add -/
theorem add_def {n} (x y : BitVec n) : x + y = .ofNat n (x.toNat + y.toNat) := rfl
@@ -2901,7 +3028,7 @@ protected theorem ne_of_lt {x y : BitVec n} : x < y → x ≠ y := by
apply Nat.ne_of_lt
protected theorem umod_lt (x : BitVec n) {y : BitVec n} : 0 < y x % y < y := by
simp only [ofNat_eq_ofNat, lt_def, toNat_ofNat, Nat.zero_mod, umod, toNat_ofNatLt]
simp only [ofNat_eq_ofNat, lt_def, toNat_ofNat, Nat.zero_mod, umod, toNat_ofNatLT]
apply Nat.mod_lt
theorem not_lt_iff_le {x y : BitVec w} : (¬ x < y) y x := by
@@ -3243,7 +3370,7 @@ theorem toNat_smod {x y : BitVec w} : (x.smod y).toNat =
by_cases h : x.msb <;> by_cases h' : y.msb
<;> by_cases h'' : (-x).umod y = 0#w <;> by_cases h''' : x.umod (-y) = 0#w
<;> simp only [h, h', h'', h''']
<;> simp only [umod, toNat_eq, toNat_ofNatLt, toNat_ofNat, Nat.zero_mod] at h'' h'''
<;> simp only [umod, toNat_eq, toNat_ofNatLT, toNat_ofNat, Nat.zero_mod] at h'' h'''
<;> simp [h'', h''']
@[simp]
@@ -3521,8 +3648,7 @@ theorem getLsbD_rotateRight {x : BitVec w} {r i : Nat} :
@[simp]
theorem getElem_rotateRight {x : BitVec w} {r i : Nat} (h : i < w) :
(x.rotateRight r)[i] = if h' : i < w - (r % w) then x[(r % w) + i] else x[(i - (w - (r % w)))] := by
simp only [ BitVec.getLsbD_eq_getElem]
simp [getLsbD_rotateRight, h]
simp [ BitVec.getLsbD_eq_getElem, getLsbD_rotateRight, h]
theorem getMsbD_rotateRightAux_of_lt {x : BitVec w} {r : Nat} {i : Nat} (hi : i < r) :
(x.rotateRightAux r).getMsbD i = x.getMsbD (i + (w - r)) := by
@@ -3633,9 +3759,9 @@ theorem msb_twoPow {i w: Nat} :
theorem and_twoPow (x : BitVec w) (i : Nat) :
x &&& (twoPow w i) = if x.getLsbD i then twoPow w i else 0#w := by
ext j
simp only [getLsbD_and, getLsbD_twoPow]
by_cases hj : i = j <;> by_cases hx : x.getLsbD i <;> simp_all
ext j h
simp only [getElem_and, getLsbD_twoPow]
by_cases hj : i = j <;> by_cases hx : x.getLsbD i <;> simp_all <;> omega
theorem twoPow_and (x : BitVec w) (i : Nat) :
(twoPow w i) &&& x = if x.getLsbD i then twoPow w i else 0#w := by
@@ -3686,16 +3812,15 @@ theorem udiv_twoPow_eq_of_lt {w : Nat} {x : BitVec w} {k : Nat} (hk : k < w) : x
@[simp] theorem true_cons_zero : cons true 0#w = twoPow (w + 1) w := by
ext
simp [getLsbD_cons]
omega
simp [getElem_cons]
@[simp] theorem false_cons_zero : cons false 0#w = 0#(w + 1) := by
ext
simp [getLsbD_cons]
simp [getElem_cons]
@[simp] theorem zero_concat_true : concat 0#w true = 1#(w + 1) := by
ext
simp [getLsbD_concat]
simp [getElem_concat]
/- ### setWidth, setWidth, and bitwise operations -/
@@ -3708,7 +3833,6 @@ theorem setWidth_setWidth_succ_eq_setWidth_setWidth_of_getLsbD_false
setWidth w (x.setWidth (i + 1)) =
setWidth w (x.setWidth i) := by
ext k h
simp only [getLsbD_setWidth, h, decide_true, Bool.true_and, getLsbD_or, getLsbD_and]
by_cases hik : i = k
· subst hik
simp [hx]
@@ -3724,16 +3848,18 @@ theorem setWidth_setWidth_succ_eq_setWidth_setWidth_or_twoPow_of_getLsbD_true
setWidth w (x.setWidth (i + 1)) =
setWidth w (x.setWidth i) ||| (twoPow w i) := by
ext k h
simp only [getLsbD_setWidth, h, decide_true, Bool.true_and, getLsbD_or, getLsbD_and]
simp only [getElem_setWidth, h, getElem_or, getElem_twoPow]
by_cases hik : i = k
· subst hik
simp [hx, h]
· by_cases hik' : k < i + 1 <;> simp [hik, hik'] <;> omega
simp [hx]
· by_cases hik' : k < i + 1
<;> simp [hik, hik', show ¬ (k = i) by omega]
<;> omega
/-- Bitwise and of `(x : BitVec w)` with `1#w` equals zero extending `x.lsb` to `w`. -/
theorem and_one_eq_setWidth_ofBool_getLsbD {x : BitVec w} :
(x &&& 1#w) = setWidth w (ofBool (x.getLsbD 0)) := by
ext (_ | i) h <;> simp [Bool.and_comm]
ext (_ | i) h <;> simp [Bool.and_comm, h]
@[simp]
theorem replicate_zero {x : BitVec w} : x.replicate 0 = 0#0 := by
@@ -3761,7 +3887,8 @@ theorem getLsbD_replicate {n w : Nat} {x : BitVec w} :
by_cases hi : i < w * (n + 1)
· simp only [hi, decide_true, Bool.true_and]
by_cases hi' : i < w * n
· simp [hi', ih]
· rw [ih]
simp_all
· simp only [hi', reduceIte]
rw [Nat.sub_mul_eq_mod_of_lt_of_le (by omega) (by omega)]
· rw [Nat.mul_succ] at hi
@@ -3782,7 +3909,7 @@ theorem append_assoc {x₁ : BitVec w₁} {x₂ : BitVec w₂} {x₃ : BitVec w
specialize @ih (setWidth n x₁)
rw [ cons_msb_setWidth x₁, cons_append_append, ih, cons_append]
ext j h
simp [getLsbD_cons, show n + w₂ + w₃ = n + (w₂ + w₃) by omega]
simp [getElem_cons, show n + w₂ + w₃ = n + (w₂ + w₃) by omega]
theorem replicate_append_self {x : BitVec w} :
x ++ x.replicate n = (x.replicate n ++ x).cast (by omega) := by
@@ -4101,6 +4228,10 @@ theorem getLsbD_reverse {i : Nat} {x : BitVec w} :
simp only [show n - (n + 1) = 0 by omega, Nat.zero_le, decide_true, Bool.true_and]
congr; omega
theorem getElem_reverse (x : BitVec w) (h : i < w) :
x.reverse[i] = x.getMsbD i := by
rw [ getLsbD_eq_getElem, getLsbD_reverse]
theorem getMsbD_reverse {i : Nat} {x : BitVec w} :
(x.reverse).getMsbD i = x.getLsbD i := by
simp only [getMsbD_eq_getLsbD, getLsbD_reverse]
@@ -4116,14 +4247,14 @@ theorem msb_reverse {x : BitVec w} :
theorem reverse_append {x : BitVec w} {y : BitVec v} :
(x ++ y).reverse = (y.reverse ++ x.reverse).cast (by omega) := by
ext i h
simp only [getLsbD_append, getLsbD_reverse]
simp only [getElem_reverse, getElem_cast, getElem_append]
by_cases hi : i < v
· by_cases hw : w i
· simp [getMsbD_append, getLsbD_cast, getLsbD_append, getLsbD_reverse, hw]
· simp [getMsbD_append, getLsbD_cast, getLsbD_append, getLsbD_reverse, hw, show i < w by omega]
· simp [getLsbD_reverse, hw]
· simp [getElem_reverse, hw, show i < w by omega]
· by_cases hw : w i
· simp [getMsbD_append, getLsbD_cast, getLsbD_append, hw, show ¬ i < w by omega, getLsbD_reverse]
· simp [getMsbD_append, getLsbD_cast, getLsbD_append, hw, show i < w by omega, getLsbD_reverse]
· simp [hw, show ¬ i < w by omega, getLsbD_reverse]
· simp [hw, show i < w by omega, getElem_reverse]
@[simp]
theorem reverse_cast {w v : Nat} (h : w = v) (x : BitVec w) :

View File

@@ -581,14 +581,10 @@ protected theorem decide_coe (b : Bool) [Decidable (b = true)] : decide (b = tru
cases dp with | _ p => simp [p]
@[bool_to_prop]
theorem and_eq_decide (p q : Prop) [dpq : Decidable (p q)] [dp : Decidable p] [dq : Decidable q] :
(p && q) = decide (p q) := by
cases dp with | _ p => simp [p]
theorem and_eq_decide (p q : Bool) : (p && q) = decide (p q) := by simp
@[bool_to_prop]
theorem or_eq_decide (p q : Prop) [dpq : Decidable (p q)] [dp : Decidable p] [dq : Decidable q] :
(p || q) = decide (p q) := by
cases dp with | _ p => simp [p]
theorem or_eq_decide (p q : Bool) : (p || q) = decide (p q) := by simp
@[bool_to_prop]
theorem decide_beq_decide (p q : Prop) [dpq : Decidable (p q)] [dp : Decidable p] [dq : Decidable q] :

View File

@@ -47,7 +47,7 @@ def uget : (a : @& ByteArray) → (i : USize) → (h : i.toNat < a.size := by ge
@[extern "lean_byte_array_get"]
def get! : (@& ByteArray) (@& Nat) UInt8
| bs, i => bs.get! i
| bs, i => bs[i]!
@[extern "lean_byte_array_fget"]
def get : (a : @& ByteArray) (i : @& Nat) (h : i < a.size := by get_elem_tactic) UInt8
@@ -56,7 +56,7 @@ def get : (a : @& ByteArray) → (i : @& Nat) → (h : i < a.size := by get_elem
instance : GetElem ByteArray Nat UInt8 fun xs i => i < xs.size where
getElem xs i h := xs.get i
instance : GetElem ByteArray USize UInt8 fun xs i => i.val < xs.size where
instance : GetElem ByteArray USize UInt8 fun xs i => i.toFin < xs.size where
getElem xs i h := xs.uget i h
@[extern "lean_byte_array_set"]

View File

@@ -40,12 +40,12 @@ theorem isValidUInt32 (n : Nat) (h : isValidCharNat n) : n < UInt32.size := by
apply Nat.lt_trans h₂
decide
theorem isValidChar_of_isValidCharNat (n : Nat) (h : isValidCharNat n) : isValidChar (UInt32.ofNat' n (isValidUInt32 n h)) :=
theorem isValidChar_of_isValidCharNat (n : Nat) (h : isValidCharNat n) : isValidChar (UInt32.ofNatLT n (isValidUInt32 n h)) :=
match h with
| Or.inl h =>
Or.inl (UInt32.ofNat'_lt_of_lt _ (by decide) h)
Or.inl (UInt32.ofNatLT_lt_of_lt _ (by decide) h)
| Or.inr h₁, h₂ =>
Or.inr UInt32.lt_ofNat'_of_lt _ (by decide) h₁, UInt32.ofNat'_lt_of_lt _ (by decide) h₂
Or.inr UInt32.lt_ofNatLT_of_lt _ (by decide) h₁, UInt32.ofNatLT_lt_of_lt _ (by decide) h₂
theorem isValidChar_zero : isValidChar 0 :=
Or.inl (by decide)

View File

@@ -51,6 +51,14 @@ Returns `a` modulo `n + 1` as a `Fin n.succ`.
protected def ofNat {n : Nat} (a : Nat) : Fin (n + 1) :=
a % (n+1), Nat.mod_lt _ (Nat.zero_lt_succ _)
-- We provide this because other similar types have a `toNat` function, but `simp` rewrites
-- `i.toNat` to `i.val`.
@[inline, inherit_doc val]
protected def toNat (i : Fin n) : Nat :=
i.val
@[simp] theorem toNat_eq_val {i : Fin n} : i.toNat = i.val := rfl
private theorem mlt {b : Nat} : {a : Nat} a < n b % n < n
| 0, h => Nat.mod_lt _ h
| _+1, h =>

View File

@@ -51,7 +51,7 @@ def get : (ds : @& FloatArray) → (i : @& Nat) → (h : i < ds.size := by get_e
@[extern "lean_float_array_get"]
def get! : (@& FloatArray) (@& Nat) Float
| ds, i => ds.get! i
| ds, i => ds[i]!
def get? (ds : FloatArray) (i : Nat) : Option Float :=
if h : i < ds.size then
@@ -62,7 +62,7 @@ def get? (ds : FloatArray) (i : Nat) : Option Float :=
instance : GetElem FloatArray Nat Float fun xs i => i < xs.size where
getElem xs i h := xs.get i h
instance : GetElem FloatArray USize Float fun xs i => i.val < xs.size where
instance : GetElem FloatArray USize Float fun xs i => i.toNat < xs.size where
getElem xs i h := xs.uget i h
@[extern "lean_float_array_uset"]

View File

@@ -15,3 +15,4 @@ import Init.Data.Int.Order
import Init.Data.Int.Pow
import Init.Data.Int.Cooper
import Init.Data.Int.Linear
import Init.Data.Int.Cutsat

View File

@@ -0,0 +1,68 @@
/-
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.AC
import Init.Data.Int.Gcd
namespace Int.Linear
/-!
Helper theorems for solving divisibility constraints.
The two theorems are used to justify the `Div-Solve` rule
in the section "Strong Conflict Resolution" in the paper
"Cutting to the Chase: Solving Linear Integer Arithmetic".
-/
theorem dvd_solve_1 {x : Int} {d₁ a₁ p₁ : Int} {d₂ a₂ p₂ : Int} {α β d : Int}
(h : α*a₁*d₂ + β*a₂*d₁ = d)
(h₁ : d₁ a₁*x + p₁)
(h₂ : d₂ a₂*x + p₂)
: d₁*d₂ d*x + α*d₂*p₁ + β*d₁*p₂ := by
rcases h₁ with k₁, h₁
replace h₁ : α*a₁*d₂*x + α*d₂*p₁ = d₁*d₂*(α*k₁) := by
have ac₁ : d₁*d₂*(α*k₁) = α*d₂*(d₁*k₁) := by ac_rfl
have ac₂ : α * a₁ * d₂ * x = α * d₂ * (a₁ * x) := by ac_rfl
rw [ac₁, h₁, Int.mul_add, ac₂]
rcases h₂ with k₂, h₂
replace h₂ : β*a₂*d₁*x + β*d₁*p₂ = d₁*d₂*(β*k₂) := by
have ac₁ : d₁*d₂*(β*k₂) = β*d₁*(d₂*k₂) := by ac_rfl
have ac₂ : β * a₂ * d₁ * x = β * d₁ * (a₂ * x) := by ac_rfl
rw [ac₁, h₂, Int.mul_add, ac₂]
replace h₁ : d₁*d₂ α*a₁*d₂*x + α*d₂*p₁ := α*k₁, h₁
replace h₂ : d₁*d₂ β*a₂*d₁*x + β*d₁*p₂ := β*k₂, h₂
have h' := Int.dvd_add h₁ h₂; clear h₁ h₂ k₁ k₂
replace h : d*x = α*a₁*d₂*x + β*a₂*d₁*x := by
rw [h, Int.add_mul]
have ac :
α * a₁ * d₂ * x + α * d₂ * p₁ + (β * a₂ * d₁ * x + β * d₁ * p₂)
=
α * a₁ * d₂ * x + β * a₂ * d₁ * x + α * d₂ * p₁ + β * d₁ * p₂ := by ac_rfl
rw [h, ac]
assumption
theorem dvd_solve_2 {x : Int} {d₁ a₁ p₁ : Int} {d₂ a₂ p₂ : Int} {d : Int}
(h : d = Int.gcd (a₁*d₂) (a₂*d₁))
(h₁ : d₁ a₁*x + p₁)
(h₂ : d₂ a₂*x + p₂)
: d a₂*p₁ - a₁*p₂ := by
rcases h₁ with k₁, h₁
rcases h₂ with k₂, h₂
have h₃ : d a₁*d₂ := by
rw [h]; apply Int.gcd_dvd_left
have h₄ : d a₂*d₁ := by
rw [h]; apply Int.gcd_dvd_right
rcases h₃ with k₃, h₃
rcases h₄ with k₄, h₄
have : a₂*p₁ - a₁*p₂ = a₂*d₁*k₁ - a₁*d₂*k₂ := by
have ac₁ : a₂*d₁*k₁ = a₂*(d₁*k₁) := by ac_rfl
have ac₂ : a₁*d₂*k₂ = a₁*(d₂*k₂) := by ac_rfl
have ac₃ : a₁*(a₂*x) = a₂*(a₁*x) := by ac_rfl
rw [ac₁, ac₂, h₁, h₂, Int.mul_add, Int.mul_add, ac₃, Int.sub_sub, Int.add_comm, Int.add_sub_assoc]
simp
rw [h₃, h₄, Int.mul_assoc, Int.mul_assoc, Int.mul_sub] at this
exact k₄ * k₁ - k₃ * k₂, this
end Int.Linear

View File

@@ -22,11 +22,11 @@ namespace Int
protected theorem dvd_def (a b : Int) : (a b) = Exists (fun c => b = a * c) := rfl
protected theorem dvd_zero (n : Int) : n 0 := 0, (Int.mul_zero _).symm
@[simp] protected theorem dvd_zero (n : Int) : n 0 := 0, (Int.mul_zero _).symm
protected theorem dvd_refl (n : Int) : n n := 1, (Int.mul_one _).symm
@[simp] protected theorem dvd_refl (n : Int) : n n := 1, (Int.mul_one _).symm
protected theorem one_dvd (n : Int) : 1 n := n, (Int.one_mul n).symm
@[simp] protected theorem one_dvd (n : Int) : 1 n := n, (Int.one_mul n).symm
protected theorem dvd_trans : {a b c : Int}, a b b c a c
| _, _, _, d, rfl, e, rfl => Exists.intro (d * e) (by rw [Int.mul_assoc])
@@ -1347,3 +1347,14 @@ theorem bmod_natAbs_plus_one (x : Int) (w : 1 < x.natAbs) : bmod x (x.natAbs + 1
theorem bmod_neg_bmod : bmod (-(bmod x n)) n = bmod (-x) n := by
apply (bmod_add_cancel_right x).mp
rw [Int.add_left_neg, add_bmod_bmod, Int.add_left_neg]
/-! Helper theorems for `dvd` simproc -/
protected theorem dvd_eq_true_of_mod_eq_zero {a b : Int} (h : b % a == 0) : (a b) = True := by
simp [Int.dvd_of_emod_eq_zero, eq_of_beq h]
protected theorem dvd_eq_false_of_mod_ne_zero {a b : Int} (h : b % a != 0) : (a b) = False := by
simp [eq_of_beq] at h
simp [Int.dvd_iff_emod_eq_zero, h]
end Int

View File

@@ -326,6 +326,10 @@ theorem toNat_sub (m n : Nat) : toNat (m - n) = m - n := by
· exact (Nat.add_sub_cancel_left ..).symm
· dsimp; rw [Nat.add_assoc, Nat.sub_eq_zero_of_le (Nat.le_add_right ..)]; rfl
theorem toNat_of_nonpos : {z : Int}, z 0 z.toNat = 0
| 0, _ => rfl
| -[_+1], _ => rfl
/- ## add/sub injectivity -/
protected theorem add_left_inj {i j : Int} (k : Int) : (i + k = j + k) i = j := by

View File

@@ -9,7 +9,9 @@ import Init.Data.Prod
import Init.Data.Int.Lemmas
import Init.Data.Int.LemmasAux
import Init.Data.Int.DivModLemmas
import Init.Data.Int.Gcd
import Init.Data.RArray
import Init.Data.AC
namespace Int.Linear
@@ -50,6 +52,32 @@ def Poly.denote (ctx : Context) (p : Poly) : Int :=
| .num k => k
| .add k v p => Int.add (Int.mul k (v.denote ctx)) (denote ctx p)
/--
Similar to `Poly.denote`, but produces a denotation better for `simp +arith`.
Remark: we used to convert `Poly` back into `Expr` to achieve that.
-/
def Poly.denote' (ctx : Context) (p : Poly) : Int :=
match p with
| .num k => k
| .add 1 v p => go (v.denote ctx) p
| .add k v p => go (Int.mul k (v.denote ctx)) p
where
go (r : Int) (p : Poly) : Int :=
match p with
| .num 0 => r
| .num k => Int.add r k
| .add 1 v p => go (Int.add r (v.denote ctx)) p
| .add k v p => go (Int.add r (Int.mul k (v.denote ctx))) p
theorem Poly.denote'_go_eq_denote (ctx : Context) (p : Poly) (r : Int) : denote'.go ctx r p = p.denote ctx + r := by
induction r, p using denote'.go.induct ctx <;> simp [denote'.go, denote]
next => rw [Int.add_comm]
next ih => simp [denote'.go] at ih; rw [ih]; ac_rfl
next ih => simp [denote'.go] at ih; rw [ih]; ac_rfl
theorem Poly.denote'_eq_denote (ctx : Context) (p : Poly) : p.denote' ctx = p.denote ctx := by
unfold denote' <;> split <;> simp [denote, denote'_go_eq_denote] <;> ac_rfl
def Poly.addConst (p : Poly) (k : Int) : Poly :=
match p with
| .num k' => .num (k+k')
@@ -59,7 +87,7 @@ def Poly.insert (k : Int) (v : Var) (p : Poly) : Poly :=
match p with
| .num k' => .add k v (.num k')
| .add k' v' p =>
bif Nat.blt v v' then
bif Nat.blt v' v then
.add k v <| .add k' v' p
else bif Nat.beq v v' then
if Int.add k k' == 0 then
@@ -69,12 +97,14 @@ def Poly.insert (k : Int) (v : Var) (p : Poly) : Poly :=
else
.add k' v' (insert k v p)
/-- Normalizes the given polynomial by fusing monomial and constants. -/
def Poly.norm (p : Poly) : Poly :=
match p with
| .num k => .num k
| .add k v p => (norm p).insert k v
def Expr.toPoly' (e : Expr) :=
/-- Converts the given expression into a polynomial. -/
def Expr.toPoly' (e : Expr) : Poly :=
go 1 e (.num 0)
where
go (coeff : Int) : Expr (Poly Poly)
@@ -86,21 +116,49 @@ where
| .mulR a k => bif k == 0 then id else go (Int.mul coeff k) a
| .neg a => go (-coeff) a
/-- Converts the given expression into a polynomial, and then normalizes it. -/
def Expr.toPoly (e : Expr) : Poly :=
e.toPoly'.norm
inductive PolyCnstr where
| eq (p : Poly)
| le (p : Poly)
/-- Relational contraints: equality and inequality. -/
inductive RelCnstr where
| /-- `p = 0` constraint. -/
eq (p : Poly)
| /-- `p ≤ 0` contraint. -/
le (p : Poly)
deriving BEq
def PolyCnstr.denote (ctx : Context) : PolyCnstr Prop
def RelCnstr.denote (ctx : Context) : RelCnstr Prop
| .eq p => p.denote ctx = 0
| .le p => p.denote ctx 0
def RelCnstr.denote' (ctx : Context) : RelCnstr Prop
| .eq p => p.denote' ctx = 0
| .le p => p.denote' ctx 0
theorem RelCnstr.denote'_eq_denote (ctx : Context) (c : RelCnstr) : c.denote' ctx = c.denote ctx := by
cases c <;> simp [denote, denote', Poly.denote'_eq_denote]
/--
Returns the ceiling of the division `a / b`. That is, the result is equivalent to `⌈a / b⌉`.
Examples:
- `cdiv 7 3` returns `3`
- `cdiv (-7) 3` returns `-2`.
-/
def cdiv (a b : Int) : Int :=
-((-a)/b)
/--
Returns the ceiling-compatible remainder of the division `a / b`.
This function ensures that the remainder is consistent with `cdiv`, meaning:
```
a = b * cdiv a b + cmod a b
```
See theorem `cdiv_add_cmod`. We also have
```
-b < cmod a b ≤ 0
```
-/
def cmod (a b : Int) : Int :=
-((-a)%b)
@@ -126,7 +184,11 @@ theorem cmod_eq_zero_iff_emod_eq_zero (a b : Int) : cmod a b = 0 ↔ a%b = 0 :=
simp at this
simp [Int.neg_emod, this, Eq.comm]
theorem cdiv_eq_div_of_divides {a b : Int} (h : (a/b)*b = a) : a/b = cdiv a b := by
private abbrev div_mul_cancel_of_mod_zero :=
@Int.ediv_mul_cancel_of_emod_eq_zero
theorem cdiv_eq_div_of_divides {a b : Int} (h : a % b = 0) : a/b = cdiv a b := by
replace h := div_mul_cancel_of_mod_zero h
have hz : a % b = 0 := by
have := Int.ediv_add_emod a b
conv at this => rhs; rw [ Int.add_zero a]
@@ -143,60 +205,121 @@ theorem cdiv_eq_div_of_divides {a b : Int} (h : (a/b)*b = a) : a/b = cdiv a b :=
next => simp[cdiv, h]
next => rw [Int.mul_eq_mul_right_iff h] at this; assumption
def Poly.div (k : Int) : Poly Poly
| .num k' => .num (cdiv k' k)
| .add k' x p => .add (k'/k) x (div k p)
def Poly.divAll (k : Int) : Poly Bool
| .num k' => (k'/k)*k == k'
| .add k' _ p => (k'/k)*k == k' && divAll k p
def Poly.divCoeffs (k : Int) : Poly Bool
| .num _ => true
| .add k' _ p => (k'/k)*k == k' && divCoeffs k p
/-- Returns the constant of the given linear polynomial. -/
def Poly.getConst : Poly Int
| .num k => k
| .add _ _ p => getConst p
def PolyCnstr.norm : PolyCnstr PolyCnstr
/--
`p.div k` divides all coefficients of the polynomial `p` by `k`, but
rounds up the constant using `cdiv`.
Notes:
- We only use this function with `k`s that divides all coefficients.
- We use `cdiv` for the constant to implement the inequality tightening rule.
-/
def Poly.div (k : Int) : Poly Poly
| .num k' => .num (cdiv k' k)
| .add k' x p => .add (k'/k) x (div k p)
/--
Returns `true` if `k` divides all coefficients and the constant of the given
linear polynomial.
-/
def Poly.divAll (k : Int) : Poly Bool
| .num k' => k' % k == 0
| .add k' _ p => k' % k == 0 && divAll k p
/--
Returns `true` if `k` divides all coefficients of the given linear polynomial.
-/
def Poly.divCoeffs (k : Int) : Poly Bool
| .num _ => true
| .add k' _ p => k' % k == 0 && divCoeffs k p
/--
`p.mul k` multiplies all coefficients and constant of the polynomial `p` by `k`.
-/
def Poly.mul (p : Poly) (k : Int) : Poly :=
match p with
| .num k' => .num (k*k')
| .add k' v p => .add (k*k') v (mul p k)
@[simp] theorem Poly.denote_mul (ctx : Context) (p : Poly) (k : Int) : (p.mul k).denote ctx = k * p.denote ctx := by
induction p <;> simp [mul, denote, *]
rw [Int.mul_assoc, Int.mul_add]
/-- Normalizes the polynomial of the given relational constraint. -/
def RelCnstr.norm : RelCnstr RelCnstr
| .eq p => .eq p.norm
| .le p => .le p.norm
def PolyCnstr.divAll (k : Int) : PolyCnstr Bool
/-- Returns `true` if `k` divides all coefficients and constant of the given relational constraint. -/
def RelCnstr.divAll (k : Int) : RelCnstr Bool
| .eq p | .le p => p.divAll k
def PolyCnstr.divCoeffs (k : Int) : PolyCnstr Bool
/-- Returns `true` if `k` divides all coefficients of the given relational constraint. -/
def RelCnstr.divCoeffs (k : Int) : RelCnstr Bool
| .eq p | .le p => p.divCoeffs k
def PolyCnstr.isLe : PolyCnstr Bool
/-- Returns `true` if the given relational constraint is an inequality constraint of the form `p ≤ 0`. -/
def RelCnstr.isLe : RelCnstr Bool
| .eq _ => false
| .le _ => true
def PolyCnstr.div (k : Int) : PolyCnstr PolyCnstr
/--
Divides all coefficients and constants in the linear polynomial of the given constraint by `k`.
We rounds up the constant using `cdiv`.
-/
def RelCnstr.div (k : Int) : RelCnstr RelCnstr
| .eq p => .eq <| p.div k
| .le p => .le <| p.div k
inductive ExprCnstr where
/--
Multiplies all coefficients and constants in the linear polynomial of the given constraint by `k`.
-/
def RelCnstr.mul (k : Int) : RelCnstr RelCnstr
| .eq p => .eq <| p.mul k
| .le p => .le <| p.mul k
@[simp] theorem RelCnstr.denote_mul (ctx : Context) (c : RelCnstr) (k : Int) (h : k > 0) : (c.mul k).denote ctx = c.denote ctx := by
cases c <;> simp [mul, denote]
next =>
constructor
· intro h₁; cases (Int.mul_eq_zero.mp h₁)
next hz => simp [hz] at h
next => assumption
· intro h'; simp [*]
next =>
constructor
· intro h₁
conv at h₁ => rhs; rw [ Int.mul_zero k]
exact Int.le_of_mul_le_mul_left h₁ h
· intro h₂
have := Int.mul_le_mul_of_nonneg_left h₂ (Int.le_of_lt h)
simp at this; assumption
/-- Raw relational constraint. They are later converted into `RelCnstr`. -/
inductive RawRelCnstr where
| eq (p₁ p₂ : Expr)
| le (p₁ p₂ : Expr)
deriving Inhabited, BEq
def ExprCnstr.isLe : ExprCnstr Bool
/-- Returns `true` if the given relational constraint is an inequality constraint of the form `e₁ ≤ e₂`. -/
def RawRelCnstr.isLe : RawRelCnstr Bool
| .eq .. => false
| .le .. => true
def ExprCnstr.denote (ctx : Context) : ExprCnstr Prop
def RawRelCnstr.denote (ctx : Context) : RawRelCnstr Prop
| .eq e₁ e₂ => e₁.denote ctx = e₂.denote ctx
| .le e₁ e₂ => e₁.denote ctx e₂.denote ctx
def ExprCnstr.toPoly : ExprCnstr PolyCnstr
def RawRelCnstr.norm : RawRelCnstr RelCnstr
| .eq e₁ e₂ => .eq (e₁.sub e₂).toPoly.norm
| .le e₁ e₂ => .le (e₁.sub e₂).toPoly.norm
-- Certificate for normalizing the coefficients of a constraint
def divBy (e e' : ExprCnstr) (k : Int) : Bool :=
k > 0 && e.toPoly.divAll k && e'.toPoly == e.toPoly.div k
/-- A certificate for normalizing the coefficients of a raw relational constraint. -/
def divBy (c : RawRelCnstr) (c' : RelCnstr) (k : Int) : Bool :=
k > 0 && c.norm == c'.mul k
attribute [local simp] Int.add_comm Int.add_assoc Int.add_left_comm Int.add_mul Int.mul_add
attribute [local simp] Poly.insert Poly.denote Poly.norm Poly.addConst
@@ -210,7 +333,7 @@ theorem Poly.denote_insert (ctx : Context) (k : Int) (v : Var) (p : Poly) :
(p.insert k v).denote ctx = p.denote ctx + k * v.denote ctx := by
induction p <;> simp [*]
next k' v' p' ih =>
by_cases h₁ : Nat.blt v v' <;> simp [*]
by_cases h₁ : Nat.blt v' v <;> simp [*]
by_cases h₂ : Nat.beq v v' <;> simp [*]
by_cases h₃ : k + k' = 0 <;> simp [*, Nat.eq_of_beq_eq_true h₂]
rw [ Int.add_mul]
@@ -223,18 +346,19 @@ theorem Poly.denote_norm (ctx : Context) (p : Poly) : p.norm.denote ctx = p.deno
attribute [local simp] Poly.denote_norm
private theorem sub_fold (a b : Int) : a.sub b = a - b := rfl
private theorem neg_fold (a : Int) : a.neg = -a := rfl
theorem sub_fold (a b : Int) : a.sub b = a - b := rfl
theorem neg_fold (a : Int) : a.neg = -a := rfl
attribute [local simp] sub_fold neg_fold
attribute [local simp] Poly.div Poly.divAll PolyCnstr.denote
attribute [local simp] Poly.div Poly.divAll RelCnstr.denote
theorem Poly.denote_div_eq_of_divAll (ctx : Context) (p : Poly) (k : Int) : p.divAll k (p.div k).denote ctx * k = p.denote ctx := by
induction p with
| num _ => simp; intro h; rw [ cdiv_eq_div_of_divides h]; assumption
| num _ => simp; intro h; rw [ cdiv_eq_div_of_divides h]; exact div_mul_cancel_of_mod_zero h
| add k' v p ih =>
simp; intro h₁ h₂
replace h₁ := div_mul_cancel_of_mod_zero h₁
have ih := ih h₂
simp [ih]
apply congrArg (denote ctx p + ·)
@@ -247,10 +371,11 @@ theorem Poly.denote_div_eq_of_divCoeffs (ctx : Context) (p : Poly) (k : Int) : p
| num k' => simp; rw [Int.mul_comm, cdiv_add_cmod]
| add k' v p ih =>
simp; intro h₁ h₂
replace h₁ := div_mul_cancel_of_mod_zero h₁
rw [ ih h₂]
rw [Int.mul_right_comm, h₁, Int.add_assoc]
attribute [local simp] ExprCnstr.denote ExprCnstr.toPoly Expr.denote
attribute [local simp] RawRelCnstr.denote RawRelCnstr.norm Expr.denote
theorem Expr.denote_toPoly'_go (ctx : Context) (e : Expr) :
(toPoly'.go k e p).denote ctx = k * e.denote ctx + p.denote ctx := by
@@ -279,9 +404,9 @@ theorem Expr.denote_toPoly'_go (ctx : Context) (e : Expr) :
theorem Expr.denote_toPoly (ctx : Context) (e : Expr) : e.toPoly.denote ctx = e.denote ctx := by
simp [toPoly, toPoly', Expr.denote_toPoly'_go]
attribute [local simp] Expr.denote_toPoly PolyCnstr.denote
attribute [local simp] Expr.denote_toPoly RelCnstr.denote
theorem ExprCnstr.denote_toPoly (ctx : Context) (c : ExprCnstr) : c.toPoly.denote ctx = c.denote ctx := by
theorem RawRelCnstr.denote_norm (ctx : Context) (c : RawRelCnstr) : c.norm.denote ctx = c.denote ctx := by
cases c <;> simp
· rw [Int.sub_eq_zero]
· constructor
@@ -291,16 +416,15 @@ theorem ExprCnstr.denote_toPoly (ctx : Context) (c : ExprCnstr) : c.toPoly.denot
instance : LawfulBEq Poly where
eq_of_beq {a} := by
induction a <;> intro b <;> cases b <;> simp_all! [BEq.beq]
· rename_i k₁ v₁ p₁ k₂ v₂ p₂ ih
next ih =>
intro _ _ h
exact ih h
rfl := by
intro a
induction a <;> simp! [BEq.beq]
· rename_i k v p ih
exact ih
assumption
instance : LawfulBEq PolyCnstr where
instance : LawfulBEq RelCnstr where
eq_of_beq {a b} := by
cases a <;> cases b <;> rename_i p₁ p₂ <;> simp_all! [BEq.beq]
· show (p₁ == p₂) = true _
@@ -316,79 +440,38 @@ theorem Expr.eq_of_toPoly_eq (ctx : Context) (e e' : Expr) (h : e.toPoly == e'.t
simp [Poly.norm] at h
assumption
theorem ExprCnstr.eq_of_toPoly_eq (ctx : Context) (c c' : ExprCnstr) (h : c.toPoly == c'.toPoly) : c.denote ctx = c'.denote ctx := by
have h := congrArg (PolyCnstr.denote ctx) (eq_of_beq h)
rw [denote_toPoly, denote_toPoly] at h
assumption
theorem RawRelCnstr.eq_of_norm_eq (ctx : Context) (c : RawRelCnstr) (c' : RelCnstr) (h : c.norm == c') : c.denote ctx = c'.denote' ctx := by
have h := congrArg (RelCnstr.denote ctx) (eq_of_beq h)
rw [denote_norm] at h
rw [RelCnstr.denote'_eq_denote, h]
theorem ExprCnstr.eq_of_toPoly_eq_var (ctx : Context) (x y : Var) (c : ExprCnstr) (h : c.toPoly == .eq (.add 1 x (.add (-1) y (.num 0))))
theorem RawRelCnstr.eq_of_norm_eq_var (ctx : Context) (x y : Var) (c : RawRelCnstr) (h : c.norm == .eq (.add 1 x (.add (-1) y (.num 0))))
: c.denote ctx = (x.denote ctx = y.denote ctx) := by
have h := congrArg (PolyCnstr.denote ctx) (eq_of_beq h)
rw [denote_toPoly] at h
have h := congrArg (RelCnstr.denote ctx) (eq_of_beq h)
rw [denote_norm] at h
rw [h]; simp
rw [ Int.sub_eq_add_neg, Int.sub_eq_zero]
theorem ExprCnstr.eq_of_toPoly_eq_const (ctx : Context) (x : Var) (k : Int) (c : ExprCnstr) (h : c.toPoly == .eq (.add 1 x (.num (-k))))
theorem RawRelCnstr.eq_of_norm_eq_const (ctx : Context) (x : Var) (k : Int) (c : RawRelCnstr) (h : c.norm == .eq (.add 1 x (.num (-k))))
: c.denote ctx = (x.denote ctx = k) := by
have h := congrArg (PolyCnstr.denote ctx) (eq_of_beq h)
rw [denote_toPoly] at h
have h := congrArg (RelCnstr.denote ctx) (eq_of_beq h)
rw [denote_norm] at h
rw [h]; simp
rw [Int.add_comm, Int.sub_eq_add_neg, Int.sub_eq_zero]
private theorem mul_eq_zero_iff_eq_zero (a b : Int) : b 0 (a * b = 0 a = 0) := by
intro h
constructor
· intro h'
cases Int.mul_eq_zero.mp h'
· assumption
· contradiction
· intro; simp [*]
private theorem eq_mul_le_zero {a b : Int} : 0 < b (a 0 a * b 0) := by
intro h
have : 0 = 0 * b := by simp
constructor
· intro h'
rw [this]
apply Int.mul_le_mul h' <;> try simp
apply Int.le_of_lt h
· intro h'
rw [this] at h'
exact Int.le_of_mul_le_mul_right h' h
attribute [local simp] PolyCnstr.divAll PolyCnstr.div
theorem ExprCnstr.eq_of_toPoly_eq_of_divBy' (ctx : Context) (e e' : ExprCnstr) (p : PolyCnstr) (k : Int) : k > 0 p.divAll k e.toPoly = p e'.toPoly = p.div k e.denote ctx = e'.denote ctx := by
intro h₀ h₁ h₂ h₃
have hz : k 0 := Int.ne_of_gt h₀
cases p <;> simp at h₁
next p =>
replace h₁ := Poly.denote_div_eq_of_divAll ctx p k h₁
replace h₂ := congrArg (PolyCnstr.denote ctx) h₂
simp only [PolyCnstr.denote.eq_1, h₁] at h₂
replace h₃ := congrArg (PolyCnstr.denote ctx) h₃
simp only [PolyCnstr.denote.eq_1, PolyCnstr.div] at h₃
rw [mul_eq_zero_iff_eq_zero _ _ hz] at h₂
have := Eq.trans h₂ h₃.symm
rw [denote_toPoly, denote_toPoly] at this
exact this
next p =>
-- TODO: this is correct but we can simplify `p ≤ 0` if `p.divCoeffs k` and `p.getConst % k > 0`. Here, we are simplifying only the case `p.getConst % k = 0`
replace h₁ := Poly.denote_div_eq_of_divAll ctx p k h₁
replace h₂ := congrArg (PolyCnstr.denote ctx) h₂
simp only [PolyCnstr.denote.eq_2, h₁] at h₂
replace h₃ := congrArg (PolyCnstr.denote ctx) h₃
simp only [PolyCnstr.denote.eq_2, PolyCnstr.div] at h₃
rw [eq_mul_le_zero h₀] at h₃
have := Eq.trans h₂ h₃.symm
rw [denote_toPoly, denote_toPoly] at this
exact this
theorem ExprCnstr.eq_of_divBy (ctx : Context) (e e' : ExprCnstr) (k : Int) : divBy e e' k e.denote ctx = e'.denote ctx := by
attribute [local simp] RelCnstr.divAll RelCnstr.div RelCnstr.mul
theorem RawRelCnstr.eq_of_norm_eq_mul (ctx : Context) (c : RawRelCnstr) (c' : RelCnstr) (k : Int) (hz : k > 0) (h : c.norm = c'.mul k) : c.denote ctx = c'.denote ctx := by
replace h := congrArg (RelCnstr.denote ctx) h
simp only [RawRelCnstr.denote_norm, RelCnstr.denote_mul, *] at h
assumption
theorem RawRelCnstr.eq_of_divBy (ctx : Context) (c : RawRelCnstr) (c' : RelCnstr) (k : Int) : divBy c c' k c.denote ctx = c'.denote' ctx := by
intro h
simp only [RelCnstr.denote'_eq_denote]
simp only [divBy, Bool.and_eq_true, bne_iff_ne, ne_eq, beq_iff_eq, decide_eq_true_eq] at h
have h₁, h₂, h₃ := h
exact ExprCnstr.eq_of_toPoly_eq_of_divBy' ctx e e' e.toPoly k h₁ h₂ rfl h₃
have h₁, h₂ := h
exact eq_of_norm_eq_mul ctx c c' k h₁ h₂
private theorem mul_add_cmod_le_iff {a k b : Int} (h : k > 0) : a*k + cmod b k 0 a 0 := by
constructor
@@ -414,68 +497,58 @@ private theorem mul_add_cmod_le_iff {a k b : Int} (h : k > 0) : a*k + cmod b k
simp at this
assumption
theorem ExprCnstr.eq_of_toPoly_eq_of_divCoeffs (ctx : Context) (e e' : ExprCnstr) (p : PolyCnstr) (k : Int) : k > 0 p.divCoeffs k p.isLe e.toPoly = p e'.toPoly = p.div k e.denote ctx = e'.denote ctx := by
theorem RawRelCnstr.eq_of_norm_eq_of_divCoeffs (ctx : Context) (c₁ : RawRelCnstr) (c₂ : RelCnstr) (c₃ : RelCnstr) (k : Int)
: k > 0 c₂.divCoeffs k c₂.isLe c₁.norm = c₂ c₃ = c₂.div k c₁.denote ctx = c₃.denote ctx := by
intro h₀ h₁ h₂ h₃ h₄
have hz : k 0 := Int.ne_of_gt h₀
cases p <;> simp [PolyCnstr.isLe] at h₂
cases c₂ <;> simp [RelCnstr.isLe] at h₂
clear h₂
next p =>
simp [PolyCnstr.divCoeffs] at h₁
simp [RelCnstr.divCoeffs] at h₁
replace h₁ := Poly.denote_div_eq_of_divCoeffs ctx p k h₁
replace h₃ := congrArg (PolyCnstr.denote ctx) h₃
simp only [PolyCnstr.denote.eq_2, h₁] at h₃
replace h₄ := congrArg (PolyCnstr.denote ctx) h₄
simp only [PolyCnstr.denote.eq_2, PolyCnstr.div] at h₄
rw [denote_toPoly] at h₃ h₄
replace h₃ := congrArg (RelCnstr.denote ctx) h₃
simp only [RelCnstr.denote.eq_2, h₁] at h₃
replace h₄ := congrArg (RelCnstr.denote ctx) h₄
simp only [RelCnstr.denote.eq_2, RelCnstr.div] at h₄
rw [denote_norm] at h₃
rw [h₃, h₄]
apply propext
apply mul_add_cmod_le_iff
exact h₀
-- Certificate for normalizing the coefficients of inequality constraint with bound tightening
def divByLe (e e' : ExprCnstr) (k : Int) : Bool :=
k > 0 && e.isLe && e.toPoly.divCoeffs k && e'.toPoly == e.toPoly.div k
/-- Certificate for normalizing the coefficients of inequality constraint with bound tightening. -/
def divByLe (c : RawRelCnstr) (c' : RelCnstr) (k : Int) : Bool :=
k > 0 && c.isLe && c.norm.divCoeffs k && c' == c.norm.div k
theorem ExprCnstr.eq_of_divByLe (ctx : Context) (e e' : ExprCnstr) (k : Int) : divByLe e e' k e.denote ctx = e'.denote ctx := by
theorem RawRelCnstr.eq_of_divByLe (ctx : Context) (c : RawRelCnstr) (c' : RelCnstr) (k : Int) : divByLe c c' k c.denote ctx = c'.denote' ctx := by
intro h
simp only [RelCnstr.denote'_eq_denote]
simp only [divByLe, Bool.and_eq_true, bne_iff_ne, ne_eq, beq_iff_eq, decide_eq_true_eq] at h
have h₀, h₁, h₂, h₃ := h
have hle : e.toPoly.isLe := by
cases e <;> simp [ExprCnstr.isLe] at h₁
simp [PolyCnstr.isLe]
apply ExprCnstr.eq_of_toPoly_eq_of_divCoeffs ctx e e' e.toPoly k h₀ h₂ hle rfl h₃
have hle : c.norm.isLe := by
cases c <;> simp [RawRelCnstr.isLe] at h₁
simp [RelCnstr.isLe]
apply eq_of_norm_eq_of_divCoeffs ctx c c.norm c' k h₀ h₂ hle rfl h₃
def PolyCnstr.isUnsat : PolyCnstr Bool
def RelCnstr.isUnsat : RelCnstr Bool
| .eq (.num k) => k != 0
| .eq _ => false
| .le (.num k) => k > 0
| .le _ => false
theorem PolyCnstr.eq_false_of_isUnsat (ctx : Context) (p : PolyCnstr) : p.isUnsat p.denote ctx = False := by
theorem RelCnstr.eq_false_of_isUnsat (ctx : Context) (c : RelCnstr) : c.isUnsat c.denote ctx = False := by
unfold isUnsat <;> split <;> simp <;> try contradiction
apply Int.not_le_of_gt
theorem ExprCnstr.eq_false_of_isUnsat (ctx : Context) (c : ExprCnstr) (h : c.toPoly.isUnsat) : c.denote ctx = False := by
have := PolyCnstr.eq_false_of_isUnsat ctx (c.toPoly) h
rw [ExprCnstr.denote_toPoly] at this
theorem RawRelCnstr.eq_false_of_isUnsat (ctx : Context) (c : RawRelCnstr) (h : c.norm.isUnsat) : c.denote ctx = False := by
have := RelCnstr.eq_false_of_isUnsat ctx c.norm h
rw [RawRelCnstr.denote_norm] at this
assumption
def PolyCnstr.isUnsatCoeff (k : Int) : PolyCnstr Bool
def RelCnstr.isUnsatCoeff (k : Int) : RelCnstr Bool
| .eq p => p.divCoeffs k && k > 0 && cmod p.getConst k < 0
| .le _ => false
private theorem contra_old {a b k : Int} (h₀ : 0 < k) (h₁ : 0 < b) (h₂ : b < k) (h₃ : a*k + b = 0) : False := by
have : b = -a*k := by
rw [ Int.neg_eq_of_add_eq_zero h₃, Int.neg_mul]
rw [this] at h₁ h₂
conv at h₂ => rhs; rw [ Int.one_mul k]
have high := Int.lt_of_mul_lt_mul_right h₂ (Int.le_of_lt h₀)
rw [ Int.zero_mul k] at h₁
have low := Int.lt_of_mul_lt_mul_right h₁ (Int.le_of_lt h₀)
replace low : 1 -a := low
have : (1 : Int) < 1 := Int.lt_of_le_of_lt low high
contradiction
private theorem contra {a b k : Int} (h₀ : 0 < k) (h₁ : -k < b) (h₂ : b < 0) (h₃ : a*k + b = 0) : False := by
have : b = -a*k := by
rw [ Int.neg_eq_of_add_eq_zero h₃, Int.neg_mul]
@@ -491,7 +564,7 @@ private theorem contra {a b k : Int} (h₀ : 0 < k) (h₁ : -k < b) (h₂ : b <
have : (1 : Int) < 1 := Int.lt_of_le_of_lt h₂ h₁
contradiction
private theorem PolyCnstr.eq_false (ctx : Context) (p : Poly) (k : Int) : p.divCoeffs k k > 0 cmod p.getConst k < 0 (PolyCnstr.eq p).denote ctx = False := by
private theorem RelCnstr.eq_false (ctx : Context) (p : Poly) (k : Int) : p.divCoeffs k k > 0 cmod p.getConst k < 0 (RelCnstr.eq p).denote ctx = False := by
simp
intro h₁ h₂ h₃ h
have hnz : k 0 := by intro h; rw [h] at h₂; contradiction
@@ -501,37 +574,143 @@ private theorem PolyCnstr.eq_false (ctx : Context) (p : Poly) (k : Int) : p.divC
have high := h₃
exact contra h₂ low high this
theorem ExprCnstr.eq_false_of_isUnsat_coeff (ctx : Context) (c : ExprCnstr) (k : Int) : c.toPoly.isUnsatCoeff k c.denote ctx = False := by
theorem RawRelCnstr.eq_false_of_isUnsat_coeff (ctx : Context) (c : RawRelCnstr) (k : Int) : c.norm.isUnsatCoeff k c.denote ctx = False := by
intro h
cases c <;> simp [toPoly, PolyCnstr.isUnsatCoeff] at h
cases c <;> simp [norm, RelCnstr.isUnsatCoeff] at h
next e₁ e₂ =>
have h₁, h₂, h₃ := h
have := PolyCnstr.eq_false ctx _ _ h₁ h₂ h₃
have := RelCnstr.eq_false ctx _ _ h₁ h₂ h₃
simp at this
simp
intro he
simp [he] at this
def PolyCnstr.isValid : PolyCnstr Bool
def RelCnstr.isValid : RelCnstr Bool
| .eq (.num k) => k == 0
| .eq _ => false
| .le (.num k) => k 0
| .le _ => false
theorem PolyCnstr.eq_true_of_isValid (ctx : Context) (p : PolyCnstr) : p.isValid p.denote ctx = True := by
theorem RelCnstr.eq_true_of_isValid (ctx : Context) (c : RelCnstr) : c.isValid c.denote ctx = True := by
unfold isValid <;> split <;> simp
theorem ExprCnstr.eq_true_of_isValid (ctx : Context) (c : ExprCnstr) (h : c.toPoly.isValid) : c.denote ctx = True := by
have := PolyCnstr.eq_true_of_isValid ctx (c.toPoly) h
rw [ExprCnstr.denote_toPoly] at this
theorem RawRelCnstr.eq_true_of_isValid (ctx : Context) (c : RawRelCnstr) (h : c.norm.isValid) : c.denote ctx = True := by
have := RelCnstr.eq_true_of_isValid ctx c.norm h
rw [RawRelCnstr.denote_norm] at this
assumption
private def gcd (a b : Int) : Int :=
Int.ofNat <| Int.gcd a b
private theorem gcd_dvd_left (a b : Int) : gcd a b a := by
simp [gcd, Int.gcd_dvd_left]
private theorem gcd_dvd_right (a b : Int) : gcd a b b := by
simp [gcd, Int.gcd_dvd_right]
private theorem gcd_dvd_step {k a b x : Int} (h : k a*x + b) : gcd a k b := by
have h₁ : gcd a k a*x + b := Int.dvd_trans (gcd_dvd_right a k) h
have h₂ : gcd a k a*x := Int.dvd_trans (gcd_dvd_left a k) (Int.dvd_mul_right a x)
exact Int.dvd_iff_dvd_of_dvd_add h₁ |>.mp h₂
def Poly.gcdCoeffs : Poly Int Int
| .num _, k => k
| .add k' _ p, k => gcdCoeffs p (gcd k' k)
theorem Poly.gcd_dvd_const {ctx : Context} {p : Poly} {k : Int} (h : k p.denote ctx) : p.gcdCoeffs k p.getConst := by
induction p generalizing k <;> simp_all [gcdCoeffs]
next k' x p ih =>
rw [Int.add_comm] at h
exact ih (gcd_dvd_step h)
/-- Divibility constraint of the form `k p`. -/
structure DvdCnstr where
k : Int
p : Poly
def DvdCnstr.denote (ctx : Context) (c : DvdCnstr) : Prop :=
c.k c.p.denote ctx
def DvdCnstr.denote' (ctx : Context) (c : DvdCnstr) : Prop :=
c.k c.p.denote' ctx
theorem DvdCnstr.denote'_eq_denote (ctx : Context) (c : DvdCnstr) : c.denote' ctx = c.denote ctx := by
simp [denote', denote, Poly.denote'_eq_denote]
def DvdCnstr.isUnsat (c : DvdCnstr) : Bool :=
c.p.getConst % c.p.gcdCoeffs c.k != 0
def DvdCnstr.isEqv (c₁ c₂ : DvdCnstr) (k : Int) : Bool :=
k != 0 && c₁.k == k*c₂.k && c₁.p == c₂.p.mul k
def DvdCnstr.div (k' : Int) : DvdCnstr DvdCnstr
| { k, p } => { k := k / k', p := p.div k' }
private theorem not_dvd_of_not_mod_zero {a b : Int} (h : ¬ b % a = 0) : ¬ a b := by
intro h; have := Int.emod_eq_zero_of_dvd h; contradiction
def DvdCnstr.eq_false_of_isUnsat (ctx : Context) (c : DvdCnstr) : c.isUnsat c.denote ctx = False := by
rcases c with a, p
simp [isUnsat, denote]
intro h₁ h₂
have := Poly.gcd_dvd_const h₂
have := not_dvd_of_not_mod_zero h₁
contradiction
@[local simp] private theorem mul_dvd_mul_eq {a b c : Int} (hnz : a 0) : a * b a * c b c := by
constructor
· intro h
rcases h with k, h
rw [Int.mul_assoc a] at h
replace h := Int.eq_of_mul_eq_mul_left hnz h
exists k
· intro h
rcases h with k, h
exists k
rw [h, Int.mul_assoc]
@[local simp] theorem DvdCnstr.eq_of_isEqv (ctx : Context) (c₁ c₂ : DvdCnstr) (k : Int) (h : isEqv c₁ c₂ k) : c₁.denote ctx = c₂.denote ctx := by
rcases c₁ with a₁, e₁
rcases c₂ with a₂, e₂
simp [isEqv] at h
rcases h with h₁, h₂, h₃
replace h₃ := congrArg (Poly.denote ctx) h₃
simp at h₃
simp [denote, *]
/-- Raw divisibility constraint of the form `k e`. -/
structure RawDvdCnstr where
k : Int
e : Expr
deriving BEq
def RawDvdCnstr.denote (ctx : Context) (c : RawDvdCnstr) : Prop :=
c.k c.e.denote ctx
def RawDvdCnstr.norm (c : RawDvdCnstr) : DvdCnstr :=
{ k := c.k, p := c.e.toPoly }
@[simp] theorem RawDvdCnstr.denote_norm_eq (ctx : Context) (c : RawDvdCnstr) : c.denote ctx = c.norm.denote ctx := by
simp [norm, denote, DvdCnstr.denote]
def RawDvdCnstr.isEqv (c : RawDvdCnstr) (c' : DvdCnstr) (k : Int) : Bool :=
c.norm.isEqv c' k
def RawDvdCnstr.isUnsat (c : RawDvdCnstr) : Bool :=
c.norm.isUnsat
theorem RawDvdCnstr.eq_of_isEqv (ctx : Context) (c : RawDvdCnstr) (c' : DvdCnstr) (k : Int) (h : isEqv c c' k) : c.denote ctx = c'.denote' ctx := by
simp [DvdCnstr.eq_of_isEqv ctx c.norm c' k h, DvdCnstr.denote'_eq_denote]
theorem RawDvdCnstr.eq_false_of_isUnsat (ctx : Context) (c : RawDvdCnstr) (h : c.isUnsat) : c.denote ctx = False := by
simp [DvdCnstr.eq_false_of_isUnsat ctx c.norm h]
end Int.Linear
theorem Int.not_le_eq (a b : Int) : (¬a b) = (b + 1 a) := by
apply propext; constructor
· intro h; have h := Int.add_one_le_of_lt (Int.lt_of_not_ge h); assumption
· intro h; apply Int.not_le_of_gt; exact h
· intro h; exact Int.add_one_le_of_lt (Int.lt_of_not_ge h)
· exact Int.not_le_of_gt
theorem Int.not_ge_eq (a b : Int) : (¬a b) = (a + 1 b) := by
apply Int.not_le_eq

View File

@@ -239,6 +239,8 @@ theorem getElem?_pmap {p : α → Prop} (f : ∀ a, p a → β) {l : List α} (h
· simp_all
· simp_all
set_option linter.deprecated false in
@[deprecated List.getElem?_pmap (since := "2025-02-12")]
theorem get?_pmap {p : α Prop} (f : a, p a β) {l : List α} (h : a l, p a) (n : Nat) :
get? (pmap f l h) n = Option.pmap f (get? l n) fun x H => h x (mem_of_get? H) := by
simp only [get?_eq_getElem?]
@@ -259,6 +261,7 @@ theorem getElem_pmap {p : α → Prop} (f : ∀ a, p a → β) {l : List α} (h
· simp
· simp [hl]
@[deprecated getElem_pmap (since := "2025-02-13")]
theorem get_pmap {p : α Prop} (f : a, p a β) {l : List α} (h : a l, p a) {n : Nat}
(hn : n < (pmap f l h).length) :
get (pmap f l h) n, hn =

View File

@@ -246,46 +246,6 @@ theorem lex_cons_cons [BEq α] {a b} {as bs : List α} :
/-! ## Alternative getters -/
/-! ### get? -/
/--
Returns the `i`-th element in the list (zero-based).
If the index is out of bounds (`i ≥ as.length`), this function returns `none`.
Also see `get`, `getD` and `get!`.
-/
def get? : (as : List α) (i : Nat) Option α
| a::_, 0 => some a
| _::as, n+1 => get? as n
| _, _ => none
@[simp] theorem get?_nil : @get? α [] n = none := rfl
@[simp] theorem get?_cons_zero : @get? α (a::l) 0 = some a := rfl
@[simp] theorem get?_cons_succ : @get? α (a::l) (n+1) = get? l n := rfl
theorem ext_get? : {l₁ l₂ : List α}, ( n, l₁.get? n = l₂.get? n) l₁ = l₂
| [], [], _ => rfl
| _ :: _, [], h => nomatch h 0
| [], _ :: _, h => nomatch h 0
| a :: l₁, a' :: l₂, h => by
have h0 : some a = some a' := h 0
injection h0 with aa; simp only [aa, ext_get? fun n => h (n+1)]
/-! ### getD -/
/--
Returns the `i`-th element in the list (zero-based).
If the index is out of bounds (`i ≥ as.length`), this function returns `fallback`.
See also `get?` and `get!`.
-/
def getD (as : List α) (i : Nat) (fallback : α) : α :=
(as.get? i).getD fallback
@[simp] theorem getD_nil : getD [] n d = d := rfl
@[simp] theorem getD_cons_zero : getD (x :: xs) 0 d = x := rfl
@[simp] theorem getD_cons_succ : getD (x :: xs) (n + 1) d = getD xs n d := rfl
/-! ### getLast -/
/--

View File

@@ -14,6 +14,53 @@ namespace List
/-! ## Alternative getters -/
/-! ### get? -/
/--
Returns the `i`-th element in the list (zero-based).
If the index is out of bounds (`i ≥ as.length`), this function returns `none`.
Also see `get`, `getD` and `get!`.
-/
@[deprecated "Use `a[i]?` instead." (since := "2025-02-12")]
def get? : (as : List α) (i : Nat) Option α
| a::_, 0 => some a
| _::as, n+1 => get? as n
| _, _ => none
set_option linter.deprecated false in
@[deprecated "Use `a[i]?` instead." (since := "2025-02-12"), simp]
theorem get?_nil : @get? α [] n = none := rfl
set_option linter.deprecated false in
@[deprecated "Use `a[i]?` instead." (since := "2025-02-12"), simp]
theorem get?_cons_zero : @get? α (a::l) 0 = some a := rfl
set_option linter.deprecated false in
@[deprecated "Use `a[i]?` instead." (since := "2025-02-12"), simp]
theorem get?_cons_succ : @get? α (a::l) (n+1) = get? l n := rfl
set_option linter.deprecated false in
@[deprecated "Use `List.ext_getElem?`." (since := "2025-02-12")]
theorem ext_get? : {l₁ l₂ : List α}, ( n, l₁.get? n = l₂.get? n) l₁ = l₂
| [], [], _ => rfl
| _ :: _, [], h => nomatch h 0
| [], _ :: _, h => nomatch h 0
| a :: l₁, a' :: l₂, h => by
have h0 : some a = some a' := h 0
injection h0 with aa; simp only [aa, ext_get? fun n => h (n+1)]
/-! ### getD -/
/--
Returns the `i`-th element in the list (zero-based).
If the index is out of bounds (`i ≥ as.length`), this function returns `fallback`.
See also `get?` and `get!`.
-/
def getD (as : List α) (i : Nat) (fallback : α) : α :=
as[i]?.getD fallback
@[simp] theorem getD_nil : getD [] n d = d := rfl
/-! ### get! -/
/--
@@ -22,14 +69,21 @@ Returns the `i`-th element in the list (zero-based).
If the index is out of bounds (`i ≥ as.length`), this function panics when executed, and returns
`default`. See `get?` and `getD` for safer alternatives.
-/
@[deprecated "Use `a[i]!` instead." (since := "2025-02-12")]
def get! [Inhabited α] : (as : List α) (i : Nat) α
| a::_, 0 => a
| _::as, n+1 => get! as n
| _, _ => panic! "invalid index"
set_option linter.deprecated false in
@[deprecated "Use `a[i]!` instead." (since := "2025-02-12")]
theorem get!_nil [Inhabited α] (n : Nat) : [].get! n = (default : α) := rfl
set_option linter.deprecated false in
@[deprecated "Use `a[i]!` instead." (since := "2025-02-12")]
theorem get!_cons_succ [Inhabited α] (l : List α) (a : α) (n : Nat) :
(a::l).get! (n+1) = get! l n := rfl
set_option linter.deprecated false in
@[deprecated "Use `a[i]!` instead." (since := "2025-02-12")]
theorem get!_cons_zero [Inhabited α] (l : List α) (a : α) : (a::l).get! 0 = a := rfl
/-! ### getLast! -/
@@ -170,9 +224,10 @@ theorem getElem_append_right {as bs : List α} {i : Nat} (h₁ : as.length ≤ i
induction as generalizing i with
| nil => trivial
| cons a as ih =>
cases i with simp [get, Nat.succ_sub_succ] <;> simp [Nat.succ_sub_succ] at h₁
cases i with simp [Nat.succ_sub_succ] <;> simp [Nat.succ_sub_succ] at h₁
| succ i => apply ih; simp [h₁]
@[deprecated "Deprecated without replacement." (since := "2025-02-13")]
theorem get_last {as : List α} {i : Fin (length (as ++ [a]))} (h : ¬ i.1 < as.length) : (as ++ [a] : List _).get i = a := by
cases i; rename_i i h'
induction as generalizing i with

View File

@@ -577,10 +577,6 @@ theorem findIdx_getElem {xs : List α} {w : xs.findIdx p < xs.length} :
p xs[xs.findIdx p] :=
xs.findIdx_of_getElem?_eq_some (getElem?_eq_getElem w)
@[deprecated findIdx_of_getElem?_eq_some (since := "2024-08-12")]
theorem findIdx_of_get?_eq_some {xs : List α} (w : xs.get? (xs.findIdx p) = some y) : p y :=
findIdx_of_getElem?_eq_some (by simpa using w)
@[deprecated findIdx_getElem (since := "2024-08-12")]
theorem findIdx_get {xs : List α} {w : xs.findIdx p < xs.length} :
p (xs.get xs.findIdx p, w) :=
@@ -603,11 +599,6 @@ theorem findIdx_getElem?_eq_getElem_of_exists {xs : List α} (h : ∃ x ∈ xs,
xs[xs.findIdx p]? = some (xs[xs.findIdx p]'(xs.findIdx_lt_length_of_exists h)) :=
getElem?_eq_getElem (findIdx_lt_length_of_exists h)
@[deprecated findIdx_getElem?_eq_getElem_of_exists (since := "2024-08-12")]
theorem findIdx_get?_eq_get_of_exists {xs : List α} (h : x xs, p x) :
xs.get? (xs.findIdx p) = some (xs.get xs.findIdx p, xs.findIdx_lt_length_of_exists h) :=
get?_eq_get (findIdx_lt_length_of_exists h)
@[simp]
theorem findIdx_eq_length {p : α Bool} {xs : List α} :
xs.findIdx p = xs.length x xs, p x = false := by

View File

@@ -167,51 +167,38 @@ We simplify `l.get i` to `l[i.1]'i.2` and `l.get? i` to `l[i]?`.
@[simp] theorem get_eq_getElem (l : List α) (i : Fin l.length) : l.get i = l[i.1]'i.2 := rfl
set_option linter.deprecated false in
@[deprecated "Use `a[i]?` instead." (since := "2025-02-12")]
theorem get?_eq_none : {l : List α} {n}, length l n l.get? n = none
| [], _, _ => rfl
| _ :: l, _+1, h => get?_eq_none (l := l) <| Nat.le_of_succ_le_succ h
set_option linter.deprecated false in
@[deprecated "Use `a[i]?` instead." (since := "2025-02-12")]
theorem get?_eq_get : {l : List α} {n} (h : n < l.length), l.get? n = some (get l n, h)
| _ :: _, 0, _ => rfl
| _ :: l, _+1, _ => get?_eq_get (l := l) _
set_option linter.deprecated false in
@[deprecated "Use `a[i]?` instead." (since := "2025-02-12")]
theorem get?_eq_some_iff : l.get? n = some a h, get l n, h = a :=
fun e =>
have : n < length l := Nat.gt_of_not_le fun hn => by cases get?_eq_none hn e
this, by rwa [get?_eq_get this, Option.some.injEq] at e,
fun _, e => e get?_eq_get _
set_option linter.deprecated false in
@[deprecated "Use `a[i]?` instead." (since := "2025-02-12")]
theorem get?_eq_none_iff : l.get? n = none length l n :=
fun e => Nat.ge_of_not_lt (fun h' => by cases e get?_eq_some_iff.2 h', rfl), get?_eq_none
@[simp] theorem get?_eq_getElem? (l : List α) (i : Nat) : l.get? i = l[i]? := by
set_option linter.deprecated false in
@[deprecated "Use `a[i]?` instead." (since := "2025-02-12"), simp]
theorem get?_eq_getElem? (l : List α) (i : Nat) : l.get? i = l[i]? := by
simp only [getElem?_def]; split
· exact (get?_eq_get _)
· exact (get?_eq_none_iff.2 <| Nat.not_lt.1 _)
/-! ### getD
We simplify away `getD`, replacing `getD l n a` with `(l[n]?).getD a`.
Because of this, there is only minimal API for `getD`.
-/
@[simp] theorem getD_eq_getElem?_getD (l) (i) (a : α) : getD l i a = (l[i]?).getD a := by
simp [getD]
/-! ### get!
We simplify `l.get! i` to `l[i]!`.
-/
theorem get!_eq_getD [Inhabited α] : (l : List α) i, l.get! i = l.getD i default
| [], _ => rfl
| _a::_, 0 => rfl
| _a::l, n+1 => get!_eq_getD l n
@[simp] theorem get!_eq_getElem! [Inhabited α] (l : List α) (i) : l.get! i = l[i]! := by
simp [get!_eq_getD]
rfl
/-! ### getElem!
We simplify `l[i]!` to `(l[i]?).getD default`.
@@ -226,8 +213,26 @@ We simplify `l[i]!` to `(l[i]?).getD default`.
/-! ### getElem? and getElem -/
@[simp] theorem getElem?_eq_none_iff : l[i]? = none length l i := by
simp only [ get?_eq_getElem?, get?_eq_none_iff]
@[simp] theorem getElem?_nil {i : Nat} : ([] : List α)[i]? = none := rfl
theorem getElem_cons {l : List α} (w : i < (a :: l).length) :
(a :: l)[i] =
if h : i = 0 then a else l[i-1]'(match i, h with | i+1, _ => succ_lt_succ_iff.mp w) := by
cases i <;> simp
theorem getElem?_cons_zero {l : List α} : (a::l)[0]? = some a := by
simp [getElem?]
@[simp] theorem getElem?_cons_succ {l : List α} : (a::l)[i+1]? = l[i]? := by
simp [getElem?, decidableGetElem?, Nat.succ_lt_succ_iff]
theorem getElem?_cons : (a :: l)[i]? = if i = 0 then some a else l[i-1]? := by
cases i <;> simp [getElem?_cons_zero]
@[simp] theorem getElem?_eq_none_iff : l[i]? = none length l i :=
match l with
| [] => by simp
| _ :: l => by simp
@[simp] theorem none_eq_getElem?_iff {l : List α} {i : Nat} : none = l[i]? length l i := by
simp [eq_comm (a := none)]
@@ -237,8 +242,15 @@ theorem getElem?_eq_none (h : length l ≤ i) : l[i]? = none := getElem?_eq_none
@[simp] theorem getElem?_eq_getElem {l : List α} {i} (h : i < l.length) : l[i]? = some l[i] :=
getElem?_pos ..
theorem getElem?_eq_some_iff {l : List α} : l[i]? = some a h : i < l.length, l[i] = a := by
simp only [ get?_eq_getElem?, get?_eq_some_iff, get_eq_getElem]
theorem getElem?_eq_some_iff {l : List α} : l[i]? = some a h : i < l.length, l[i] = a :=
match l with
| [] => by simp
| _ :: l => by
simp only [getElem?_cons, length_cons]
split <;> rename_i h
· simp_all
· match i, h with
| i + 1, h => simp [getElem?_eq_some_iff, Nat.succ_lt_succ_iff]
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]
@@ -267,22 +279,6 @@ theorem getD_getElem? (l : List α) (i : Nat) (d : α) :
have p : i l.length := Nat.le_of_not_gt h
simp [getElem?_eq_none p, h]
@[simp] theorem getElem?_nil {i : Nat} : ([] : List α)[i]? = none := rfl
theorem getElem_cons {l : List α} (w : i < (a :: l).length) :
(a :: l)[i] =
if h : i = 0 then a else l[i-1]'(match i, h with | i+1, _ => succ_lt_succ_iff.mp w) := by
cases i <;> simp
theorem getElem?_cons_zero {l : List α} : (a::l)[0]? = some a := by simp
@[simp] theorem getElem?_cons_succ {l : List α} : (a::l)[i+1]? = l[i]? := by
simp only [ get?_eq_getElem?]
rfl
theorem getElem?_cons : (a :: l)[i]? = if i = 0 then some a else l[i-1]? := by
cases i <;> simp
@[simp] theorem getElem_singleton (a : α) (h : i < 1) : [a][i] = a :=
match i, h with
| 0, _ => rfl
@@ -304,7 +300,13 @@ theorem getElem_zero {l : List α} (h : 0 < l.length) : l[0] = l.head (length_po
| _ :: _, _ => rfl
@[ext] theorem ext_getElem? {l₁ l₂ : List α} (h : i : Nat, l₁[i]? = l₂[i]?) : l₁ = l₂ :=
ext_get? fun n => by simp_all
match l₁, l₂, h with
| [], [], _ => rfl
| _ :: _, [], h => by simpa using h 0
| [], _ :: _, h => by simpa using h 0
| a :: l₁, a' :: l₂, h => by
have h0 : some a = some a' := by simpa using h 0
injection h0 with aa; simp only [aa, ext_getElem? fun n => by simpa using h (n+1)]
theorem ext_getElem {l₁ l₂ : List α} (hl : length l₁ = length l₂)
(h : (i : Nat) (h₁ : i < l₁.length) (h₂ : i < l₂.length), l₁[i]'h₁ = l₂[i]'h₂) : l₁ = l₂ :=
@@ -322,6 +324,35 @@ theorem ext_getElem {l₁ l₂ : List α} (hl : length l₁ = length l₂)
theorem getElem?_concat_length (l : List α) (a : α) : (l ++ [a])[l.length]? = some a := by
simp
/-! ### getD
We simplify away `getD`, replacing `getD l n a` with `(l[n]?).getD a`.
Because of this, there is only minimal API for `getD`.
-/
@[simp] theorem getD_eq_getElem?_getD (l) (i) (a : α) : getD l i a = (l[i]?).getD a := by
simp [getD]
theorem getD_cons_zero : getD (x :: xs) 0 d = x := by simp
theorem getD_cons_succ : getD (x :: xs) (n + 1) d = getD xs n d := by simp
/-! ### get!
We simplify `l.get! i` to `l[i]!`.
-/
set_option linter.deprecated false in
@[deprecated "Use `a[i]!` instead." (since := "2025-02-12")]
theorem get!_eq_getD [Inhabited α] : (l : List α) i, l.get! i = l.getD i default
| [], _ => rfl
| _a::_, 0 => by simp [get!]
| _a::l, n+1 => by simpa using get!_eq_getD l n
set_option linter.deprecated false in
@[deprecated "Use `a[i]!` instead." (since := "2025-02-12"), simp]
theorem get!_eq_getElem! [Inhabited α] (l : List α) (i) : l.get! i = l[i]! := by
simp [get!_eq_getD]
/-! ### mem -/
@[simp] theorem not_mem_nil (a : α) : ¬ a [] := nofun
@@ -771,7 +802,7 @@ theorem getLast_eq_getElem : ∀ (l : List α) (h : l ≠ []),
| a :: l => exact Nat.le_refl _)
| [_], _ => rfl
| _ :: _ :: _, _ => by
simp [getLast, get, Nat.succ_sub_succ, getLast_eq_getElem]
simp [getLast, Nat.succ_sub_succ, getLast_eq_getElem]
theorem getElem_length_sub_one_eq_getLast (l : List α) (h : l.length - 1 < l.length) :
l[l.length - 1] = getLast l (by cases l; simp at h; simp) := by
@@ -844,10 +875,6 @@ theorem getLast?_cons {a : α} : (a::l).getLast? = l.getLast?.getD a := by
@[simp] theorem getLast?_cons_cons : (a :: b :: l).getLast? = (b :: l).getLast? := by
simp [getLast?_cons]
@[deprecated getLast?_eq_getElem? (since := "2024-07-07")]
theorem getLast?_eq_get? (l : List α) : getLast? l = l.get? (l.length - 1) := by
simp [getLast?_eq_getElem?]
theorem getLast?_concat (l : List α) : getLast? (l ++ [a]) = some a := by
simp [getLast?_eq_getElem?, Nat.succ_sub_succ]
@@ -2368,6 +2395,9 @@ theorem mem_reverseAux {x : α} : ∀ {as bs}, x ∈ reverseAux as bs ↔ x ∈
theorem reverse_ne_nil_iff {xs : List α} : xs.reverse [] xs [] :=
not_congr reverse_eq_nil_iff
@[simp] theorem isEmpty_reverse {xs : List α} : xs.reverse.isEmpty = xs.isEmpty := by
cases xs <;> simp
/-- Variant of `getElem?_reverse` with a hypothesis giving the linear relation between the indices. -/
theorem getElem?_reverse' : {l : List α} (i j), i + j + 1 = length l
l.reverse[i]? = l[j]?
@@ -3399,6 +3429,8 @@ theorem get_cons_succ' {as : List α} {i : Fin as.length} :
theorem get_mk_zero : {l : List α} (h : 0 < l.length), l.get 0, h = l.head (length_pos.mp h)
| _::_, _ => rfl
set_option linter.deprecated false in
@[deprecated "Use `a[0]?` instead." (since := "2025-02-12")]
theorem get?_zero (l : List α) : l.get? 0 = l.head? := by cases l <;> rfl
/--
@@ -3410,10 +3442,14 @@ such a rewrite, with `rw [get_of_eq h]`.
theorem get_of_eq {l l' : List α} (h : l = l') (i : Fin l.length) :
get l i = get l' i, h i.2 := by cases h; rfl
set_option linter.deprecated false in
@[deprecated "Use `a[i]?` instead." (since := "2025-02-12")]
theorem get!_of_get? [Inhabited α] : {l : List α} {n}, get? l n = some a get! l n = a
| _a::_, 0, rfl => rfl
| _::l, _+1, e => get!_of_get? (l := l) e
set_option linter.deprecated false in
@[deprecated "Use `a[i]!` instead." (since := "2025-02-12")]
theorem get!_len_le [Inhabited α] : {l : List α} {n}, length l n l.get! n = (default : α)
| [], _, _ => rfl
| _ :: l, _+1, h => get!_len_le (l := l) <| Nat.le_of_succ_le_succ h
@@ -3443,6 +3479,8 @@ theorem get_of_mem {a} {l : List α} (h : a ∈ l) : ∃ n, get l n = a := by
obtain n, h, e := getElem_of_mem h
exact n, h, e
set_option linter.deprecated false in
@[deprecated getElem?_of_mem (since := "2025-02-12")]
theorem get?_of_mem {a} {l : List α} (h : a l) : n, l.get? n = some a :=
let n, _, e := get_of_mem h; n, e get?_eq_get _
@@ -3450,12 +3488,16 @@ theorem get_mem : ∀ (l : List α) n, get l n ∈ l
| _ :: _, 0, _ => .head ..
| _ :: l, _+1, _ => .tail _ (get_mem l ..)
set_option linter.deprecated false in
@[deprecated mem_of_getElem? (since := "2025-02-12")]
theorem mem_of_get? {l : List α} {n a} (e : l.get? n = some a) : a l :=
let _, e := get?_eq_some_iff.1 e; e get_mem ..
theorem mem_iff_get {a} {l : List α} : a l n, get l n = a :=
get_of_mem, fun _, e => e get_mem ..
set_option linter.deprecated false in
@[deprecated mem_iff_getElem? (since := "2025-02-12")]
theorem mem_iff_get? {a} {l : List α} : a l n, l.get? n = some a := by
simp [getElem?_eq_some_iff, Fin.exists_iff, mem_iff_get]
@@ -3477,7 +3519,6 @@ theorem join_map_filter (p : α → Bool) (l : List (List α)) :
@[deprecated flatten_eq_cons_iff (since := "2024-09-05")] abbrev join_eq_cons := @flatten_eq_cons_iff
@[deprecated flatten_eq_append_iff (since := "2024-09-05")] abbrev join_eq_append := @flatten_eq_append_iff
@[deprecated mem_of_getElem? (since := "2024-09-06")] abbrev getElem?_mem := @mem_of_getElem?
@[deprecated mem_of_get? (since := "2024-09-06")] abbrev get?_mem := @mem_of_get?
@[deprecated getElem_set_self (since := "2024-09-04")] abbrev getElem_set_eq := @getElem_set_self
@[deprecated getElem?_set_self (since := "2024-09-04")] abbrev getElem?_set_eq := @getElem?_set_self
@[deprecated set_eq_nil_iff (since := "2024-09-05")] abbrev set_eq_nil := @set_eq_nil_iff
@@ -3538,11 +3579,11 @@ theorem join_map_filter (p : α → Bool) (l : List (List α)) :
@[deprecated any_flatMap (since := "2024-10-16")] abbrev any_bind := @any_flatMap
@[deprecated all_flatMap (since := "2024-10-16")] abbrev all_bind := @all_flatMap
@[deprecated get?_eq_none (since := "2024-11-29")] abbrev get?_len_le := @get?_eq_none
@[deprecated get?_eq_none (since := "2024-11-29")] abbrev get?_len_le := @getElem?_eq_none
@[deprecated getElem?_eq_some_iff (since := "2024-11-29")]
abbrev getElem?_eq_some := @getElem?_eq_some_iff
@[deprecated get?_eq_some_iff (since := "2024-11-29")]
abbrev get?_eq_some := @get?_eq_some_iff
abbrev get?_eq_some := @getElem?_eq_some_iff
@[deprecated LawfulGetElem.getElem?_def (since := "2024-11-29")]
theorem getElem?_eq (l : List α) (i : Nat) :
l[i]? = if h : i < l.length then some l[i] else none :=

View File

@@ -132,7 +132,7 @@ theorem getElem_insertIdx_of_lt {l : List α} {x : α} {n k : Nat} (hn : k < n)
| nil => simp
| cons _ _=>
cases k
· simp [get]
· simp
· rw [Nat.succ_lt_succ_iff] at hn
simpa using ih hn _

View File

@@ -368,7 +368,7 @@ theorem mk_mem_zipIdx_iff_le_and_getElem?_sub {k i : Nat} {x : α} {l : List α}
simp [mk_add_mem_zipIdx_iff_getElem?, Nat.add_sub_cancel_left]
else
have : m, k + m i := by rintro _ rfl; simp at h
simp [h, mem_iff_get?, this]
simp [h, mem_iff_getElem?, this]
/-- Variant of `mk_mem_zipIdx_iff_le_and_getElem?_sub` specialized at `k = 0`,
to avoid the inequality and the subtraction. -/

View File

@@ -148,20 +148,23 @@ theorem take_append {l₁ l₂ : List α} (i : Nat) :
rw [take_append_eq_append_take, take_of_length_le (Nat.le_add_right _ _), Nat.add_sub_cancel_left]
@[simp]
theorem take_eq_take :
theorem take_eq_take_iff :
{l : List α} {i j : Nat}, l.take i = l.take j min i l.length = min j l.length
| [], i, j => by simp [Nat.min_zero]
| _ :: xs, 0, 0 => by simp
| x :: xs, i + 1, 0 => by simp [Nat.zero_min, succ_min_succ]
| x :: xs, 0, j + 1 => by simp [Nat.zero_min, succ_min_succ]
| x :: xs, i + 1, j + 1 => by simp [succ_min_succ, take_eq_take]
| x :: xs, i + 1, j + 1 => by simp [succ_min_succ, take_eq_take_iff]
@[deprecated take_eq_take_iff (since := "2025-02-16")]
abbrev take_eq_take := @take_eq_take_iff
theorem take_add (l : List α) (i j : Nat) : l.take (i + j) = l.take i ++ (l.drop i).take j := by
suffices take (i + j) (take i l ++ drop i l) = take i l ++ take j (drop i l) by
rw [take_append_drop] at this
assumption
rw [take_append_eq_append_take, take_of_length_le, append_right_inj]
· simp only [take_eq_take, length_take, length_drop]
· simp only [take_eq_take_iff, length_take, length_drop]
omega
apply Nat.le_trans (m := i)
· apply length_take_le
@@ -350,11 +353,6 @@ theorem set_eq_take_append_cons_drop (l : List α) (i : Nat) (a : α) :
· rw [set_eq_of_length_le]
omega
theorem exists_of_set {i : Nat} {a' : α} {l : List α} (h : i < l.length) :
l₁ l₂, l = l₁ ++ l[i] :: l₂ l₁.length = i l.set i a' = l₁ ++ a' :: l₂ := by
refine l.take i, l.drop (i + 1), by simp, length_take_of_le (Nat.le_of_lt h), ?_
simp [set_eq_take_append_cons_drop, h]
theorem drop_set_of_lt (a : α) {i j : Nat} (l : List α)
(hnm : i < j) : drop j (l.set i a) = l.drop j :=
ext_getElem? fun k => by simpa only [getElem?_drop] using getElem?_set_ne (by omega)
@@ -474,6 +472,16 @@ theorem false_of_mem_take_findIdx {xs : List α} {p : α → Bool} (h : x ∈ xs
· simp
· rw [Nat.add_min_add_right]
@[simp] theorem min_findIdx_findIdx {xs : List α} {p q : α Bool} :
min (xs.findIdx p) (xs.findIdx q) = xs.findIdx (fun a => p a || q a) := by
induction xs with
| nil => simp
| cons x xs ih =>
simp [findIdx_cons, cond_eq_if, Bool.not_eq_eq_eq_not, Bool.not_true]
split <;> split <;> simp_all [Nat.add_min_add_right]
/-! ### findIdx? -/
@[simp] theorem findIdx?_take {xs : List α} {i : Nat} {p : α Bool} :
(xs.take i).findIdx? p = (xs.findIdx? p).bind (Option.guard (fun j => j < i)) := by
induction xs generalizing i with
@@ -486,14 +494,6 @@ theorem false_of_mem_take_findIdx {xs : List α} {p : α → Bool} (h : x ∈ xs
· simp
· simp [ih, Option.guard_comp, Option.bind_map]
@[simp] theorem min_findIdx_findIdx {xs : List α} {p q : α Bool} :
min (xs.findIdx p) (xs.findIdx q) = xs.findIdx (fun a => p a || q a) := by
induction xs with
| nil => simp
| cons x xs ih =>
simp [findIdx_cons, cond_eq_if, Bool.not_eq_eq_eq_not, Bool.not_true]
split <;> split <;> simp_all [Nat.add_min_add_right]
/-! ### takeWhile -/
theorem takeWhile_eq_take_findIdx_not {xs : List α} {p : α Bool} :

View File

@@ -301,11 +301,10 @@ theorem getElem?_inj {xs : List α}
| i+1, 0 => ?_
| 0, j+1 => ?_
all_goals
simp only [get?_eq_getElem?, getElem?_cons_zero, getElem?_cons_succ] at h₂
simp only [getElem?_cons_zero, getElem?_cons_succ] at h₂
cases h₁; rename_i h' h
have := h x ?_ rfl; cases this
rw [mem_iff_get?]
simp only [get?_eq_getElem?]
rw [mem_iff_getElem?]
exact _, h₂; exact _ , h₂.symm
@[simp] theorem nodup_replicate {n : Nat} {a : α} :

View File

@@ -149,7 +149,7 @@ theorem take_eq_nil_of_eq_nil : ∀ {as : List α} {i}, as = [] → as.take i =
theorem ne_nil_of_take_ne_nil {as : List α} {i : Nat} (h : as.take i []) : as [] :=
mt take_eq_nil_of_eq_nil h
theorem set_take {l : List α} {i j : Nat} {a : α} :
theorem take_set {l : List α} {i j : Nat} {a : α} :
(l.set j a).take i = (l.take i).set j a := by
induction i generalizing l j with
| zero => simp
@@ -158,6 +158,9 @@ theorem set_take {l : List α} {i j : Nat} {a : α} :
| nil => simp
| cons hd tl => cases j <;> simp_all
@[deprecated take_set (since := "2025-02-17")]
abbrev set_take := @take_set
theorem drop_set {l : List α} {i j : Nat} {a : α} :
(l.set j a).drop i = if j < i then l.drop i else (l.drop i).set (j - i) a := by
induction i generalizing l j with

View File

@@ -73,6 +73,10 @@ theorem toArray_cons (a : α) (l : List α) : (a :: l).toArray = #[a] ++ l.toArr
@[simp] theorem back?_toArray (l : List α) : l.toArray.back? = l.getLast? := by
simp [back?, List.getLast?_eq_getElem?]
@[simp] theorem back_toArray (l : List α) (h) :
l.toArray.back = l.getLast (by simp at h; exact ne_nil_of_length_pos h) := by
simp [back, List.getLast_eq_getElem]
@[simp] theorem set_toArray (l : List α) (i : Nat) (a : α) (h : i < l.length) :
(l.toArray.set i a) = (l.set i a).toArray := rfl
@@ -485,6 +489,21 @@ theorem takeWhile_go_toArray (p : α → Bool) (l : List α) (i : Nat) :
l.toArray.takeWhile p = (l.takeWhile p).toArray := by
simp [Array.takeWhile, takeWhile_go_toArray]
private theorem popWhile_toArray_aux (p : α Bool) (l : List α) :
l.reverse.toArray.popWhile p = (l.dropWhile p).reverse.toArray := by
induction l with
| nil => simp
| cons a l ih =>
unfold popWhile
simp [ih, dropWhile_cons]
split
· rfl
· simp
@[simp] theorem popWhile_toArray (p : α Bool) (l : List α) :
l.toArray.popWhile p = (l.reverse.dropWhile p).reverse.toArray := by
simp [ popWhile_toArray_aux]
@[simp] theorem setIfInBounds_toArray (l : List α) (i : Nat) (a : α) :
l.toArray.setIfInBounds i a = (l.set i a).toArray := by
apply ext'

View File

@@ -9,9 +9,9 @@ import Init.Meta
namespace Nat
protected theorem dvd_refl (a : Nat) : a a := 1, by simp
@[simp] protected theorem dvd_refl (a : Nat) : a a := 1, by simp
protected theorem dvd_zero (a : Nat) : a 0 := 0, by simp
@[simp] protected theorem dvd_zero (a : Nat) : a 0 := 0, by simp
protected theorem dvd_mul_left (a b : Nat) : a b * a := b, Nat.mul_comm b a
protected theorem dvd_mul_right (a b : Nat) : a a * b := b, rfl
@@ -129,4 +129,13 @@ protected theorem mul_div_assoc (m : Nat) (H : k n) : m * n / k = m * (n / k
have h1 : m * n / k = m * (n / k * k) / k := by rw [Nat.div_mul_cancel H]
rw [h1, Nat.mul_assoc, Nat.mul_div_cancel _ hpos]
/-! Helper theorems for `dvd` simproc -/
protected theorem dvd_eq_true_of_mod_eq_zero {m n : Nat} (h : n % m == 0) : (m n) = True := by
simp [Nat.dvd_of_mod_eq_zero, eq_of_beq h]
protected theorem dvd_eq_false_of_mod_ne_zero {m n : Nat} (h : n % m != 0) : (m n) = False := by
simp [eq_of_beq] at h
simp [dvd_iff_mod_eq_zero, h]
end Nat

View File

@@ -163,7 +163,7 @@ private def reprArray : Array String := Id.run do
private def reprFast (n : Nat) : String :=
if h : n < 128 then Nat.reprArray.get n h else
if h : n < USize.size then (USize.ofNatCore n h).repr
if h : n < USize.size then (USize.ofNatLT n h).repr
else (toDigits 10 n).asString
@[implemented_by reprFast]

View File

@@ -17,6 +17,7 @@ The type of signed 8-bit integers. This type has special support in the
compiler to make it actually 8 bits rather than wrapping a `Nat`.
-/
structure Int8 where
private ofUInt8 ::
/--
Obtain the `UInt8` that is 2's complement equivalent to the `Int8`.
-/
@@ -27,6 +28,7 @@ The type of signed 16-bit integers. This type has special support in the
compiler to make it actually 16 bits rather than wrapping a `Nat`.
-/
structure Int16 where
private ofUInt16 ::
/--
Obtain the `UInt16` that is 2's complement equivalent to the `Int16`.
-/
@@ -37,6 +39,7 @@ The type of signed 32-bit integers. This type has special support in the
compiler to make it actually 32 bits rather than wrapping a `Nat`.
-/
structure Int32 where
private ofUInt32 ::
/--
Obtain the `UInt32` that is 2's complement equivalent to the `Int32`.
-/
@@ -47,6 +50,7 @@ The type of signed 64-bit integers. This type has special support in the
compiler to make it actually 64 bits rather than wrapping a `Nat`.
-/
structure Int64 where
private ofUInt64 ::
/--
Obtain the `UInt64` that is 2's complement equivalent to the `Int64`.
-/
@@ -59,6 +63,7 @@ For example, if running on a 32-bit machine, ISize is equivalent to `Int32`.
Or on a 64-bit machine, `Int64`.
-/
structure ISize where
private ofUSize ::
/--
Obtain the `USize` that is 2's complement equivalent to the `ISize`.
-/
@@ -72,6 +77,10 @@ Obtain the `BitVec` that contains the 2's complement representation of the `Int8
-/
@[inline] def Int8.toBitVec (x : Int8) : BitVec 8 := x.toUInt8.toBitVec
/-- Obtains the `Int8` that is 2's complement equivalent to the `UInt8`. -/
@[inline] def UInt8.toInt8 (i : UInt8) : Int8 := Int8.ofUInt8 i
@[inline, deprecated UInt8.toInt8 (since := "2025-02-13"), inherit_doc UInt8.toInt8]
def Int8.mk (i : UInt8) : Int8 := UInt8.toInt8 i
@[extern "lean_int8_of_int"]
def Int8.ofInt (i : @& Int) : Int8 := BitVec.ofInt 8 i
@[extern "lean_int8_of_nat"]
@@ -84,7 +93,9 @@ def Int8.toInt (i : Int8) : Int := i.toBitVec.toInt
This function has the same behavior as `Int.toNat` for negative numbers.
If you want to obtain the 2's complement representation use `toBitVec`.
-/
@[inline] def Int8.toNat (i : Int8) : Nat := i.toInt.toNat
@[inline] def Int8.toNatClampNeg (i : Int8) : Nat := i.toInt.toNat
@[inline, deprecated Int8.toNatClampNeg (since := "2025-02-13"), inherit_doc Int8.toNatClampNeg]
def Int8.toNat (i : Int8) : Nat := i.toInt.toNat
/-- Obtains the `Int8` whose 2's complement representation is the given `BitVec 8`. -/
@[inline] def Int8.ofBitVec (b : BitVec 8) : Int8 := b
@[extern "lean_int8_neg"]
@@ -97,6 +108,24 @@ instance : OfNat Int8 n := ⟨Int8.ofNat n⟩
instance : Neg Int8 where
neg := Int8.neg
/-- The maximum value an `Int8` may attain, that is, `2^7 - 1 = 127`. -/
abbrev Int8.maxValue : Int8 := 127
/-- The minimum value an `Int8` may attain, that is, `-2^7 = -128`. -/
abbrev Int8.minValue : Int8 := -128
/-- Constructs an `Int8` from an `Int` which is known to be in bounds. -/
@[inline]
def Int8.ofIntLE (i : Int) (_hl : Int8.minValue.toInt i) (_hr : i Int8.maxValue.toInt) : Int8 :=
Int8.ofInt i
/-- Constructs an `Int8` from an `Int`, clamping if the value is too small or too large. -/
def Int8.ofIntTruncate (i : Int) : Int8 :=
if hl : Int8.minValue.toInt i then
if hr : i Int8.maxValue.toInt then
Int8.ofIntLE i hl hr
else
Int8.minValue
else
Int8.minValue
@[extern "lean_int8_add"]
def Int8.add (a b : Int8) : Int8 := a.toBitVec + b.toBitVec
@[extern "lean_int8_sub"]
@@ -174,6 +203,10 @@ Obtain the `BitVec` that contains the 2's complement representation of the `Int1
-/
@[inline] def Int16.toBitVec (x : Int16) : BitVec 16 := x.toUInt16.toBitVec
/-- Obtains the `Int16` that is 2's complement equivalent to the `UInt16`. -/
@[inline] def UInt16.toInt16 (i : UInt16) : Int16 := Int16.ofUInt16 i
@[inline, deprecated UInt16.toInt16 (since := "2025-02-13"), inherit_doc UInt16.toInt16]
def Int16.mk (i : UInt16) : Int16 := UInt16.toInt16 i
@[extern "lean_int16_of_int"]
def Int16.ofInt (i : @& Int) : Int16 := BitVec.ofInt 16 i
@[extern "lean_int16_of_nat"]
@@ -186,7 +219,9 @@ def Int16.toInt (i : Int16) : Int := i.toBitVec.toInt
This function has the same behavior as `Int.toNat` for negative numbers.
If you want to obtain the 2's complement representation use `toBitVec`.
-/
@[inline] def Int16.toNat (i : Int16) : Nat := i.toInt.toNat
@[inline] def Int16.toNatClampNeg (i : Int16) : Nat := i.toInt.toNat
@[inline, deprecated Int16.toNatClampNeg (since := "2025-02-13"), inherit_doc Int16.toNatClampNeg]
def Int16.toNat (i : Int16) : Nat := i.toInt.toNat
/-- Obtains the `Int16` whose 2's complement representation is the given `BitVec 16`. -/
@[inline] def Int16.ofBitVec (b : BitVec 16) : Int16 := b
@[extern "lean_int16_to_int8"]
@@ -203,6 +238,24 @@ instance : OfNat Int16 n := ⟨Int16.ofNat n⟩
instance : Neg Int16 where
neg := Int16.neg
/-- The maximum value an `Int16` may attain, that is, `2^15 - 1 = 32767`. -/
abbrev Int16.maxValue : Int16 := 32767
/-- The minimum value an `Int16` may attain, that is, `-2^15 = -32768`. -/
abbrev Int16.minValue : Int16 := -32768
/-- Constructs an `Int16` from an `Int` which is known to be in bounds. -/
@[inline]
def Int16.ofIntLE (i : Int) (_hl : Int16.minValue.toInt i) (_hr : i Int16.maxValue.toInt) : Int16 :=
Int16.ofInt i
/-- Constructs an `Int16` from an `Int`, clamping if the value is too small or too large. -/
def Int16.ofIntTruncate (i : Int) : Int16 :=
if hl : Int16.minValue.toInt i then
if hr : i Int16.maxValue.toInt then
Int16.ofIntLE i hl hr
else
Int16.minValue
else
Int16.minValue
@[extern "lean_int16_add"]
def Int16.add (a b : Int16) : Int16 := a.toBitVec + b.toBitVec
@[extern "lean_int16_sub"]
@@ -280,6 +333,10 @@ Obtain the `BitVec` that contains the 2's complement representation of the `Int3
-/
@[inline] def Int32.toBitVec (x : Int32) : BitVec 32 := x.toUInt32.toBitVec
/-- Obtains the `Int32` that is 2's complement equivalent to the `UInt32`. -/
@[inline] def UInt32.toInt32 (i : UInt32) : Int32 := Int32.ofUInt32 i
@[inline, deprecated UInt32.toInt32 (since := "2025-02-13"), inherit_doc UInt32.toInt32]
def Int32.mk (i : UInt32) : Int32 := UInt32.toInt32 i
@[extern "lean_int32_of_int"]
def Int32.ofInt (i : @& Int) : Int32 := BitVec.ofInt 32 i
@[extern "lean_int32_of_nat"]
@@ -292,7 +349,9 @@ def Int32.toInt (i : Int32) : Int := i.toBitVec.toInt
This function has the same behavior as `Int.toNat` for negative numbers.
If you want to obtain the 2's complement representation use `toBitVec`.
-/
@[inline] def Int32.toNat (i : Int32) : Nat := i.toInt.toNat
@[inline] def Int32.toNatClampNeg (i : Int32) : Nat := i.toInt.toNat
@[inline, deprecated Int32.toNatClampNeg (since := "2025-02-13"), inherit_doc Int32.toNatClampNeg]
def Int32.toNat (i : Int32) : Nat := i.toInt.toNat
/-- Obtains the `Int32` whose 2's complement representation is the given `BitVec 32`. -/
@[inline] def Int32.ofBitVec (b : BitVec 32) : Int32 := b
@[extern "lean_int32_to_int8"]
@@ -313,6 +372,24 @@ instance : OfNat Int32 n := ⟨Int32.ofNat n⟩
instance : Neg Int32 where
neg := Int32.neg
/-- The maximum value an `Int32` may attain, that is, `2^31 - 1 = 2147483647`. -/
abbrev Int32.maxValue : Int32 := 2147483647
/-- The minimum value an `Int32` may attain, that is, `-2^31 = -2147483648`. -/
abbrev Int32.minValue : Int32 := -2147483648
/-- Constructs an `Int32` from an `Int` which is known to be in bounds. -/
@[inline]
def Int32.ofIntLE (i : Int) (_hl : Int32.minValue.toInt i) (_hr : i Int32.maxValue.toInt) : Int32 :=
Int32.ofInt i
/-- Constructs an `Int32` from an `Int`, clamping if the value is too small or too large. -/
def Int32.ofIntTruncate (i : Int) : Int32 :=
if hl : Int32.minValue.toInt i then
if hr : i Int32.maxValue.toInt then
Int32.ofIntLE i hl hr
else
Int32.minValue
else
Int32.minValue
@[extern "lean_int32_add"]
def Int32.add (a b : Int32) : Int32 := a.toBitVec + b.toBitVec
@[extern "lean_int32_sub"]
@@ -390,6 +467,10 @@ Obtain the `BitVec` that contains the 2's complement representation of the `Int6
-/
@[inline] def Int64.toBitVec (x : Int64) : BitVec 64 := x.toUInt64.toBitVec
/-- Obtains the `Int64` that is 2's complement equivalent to the `UInt64`. -/
@[inline] def UInt64.toInt64 (i : UInt64) : Int64 := Int64.ofUInt64 i
@[inline, deprecated UInt64.toInt64 (since := "2025-02-13"), inherit_doc UInt64.toInt64]
def Int64.mk (i : UInt64) : Int64 := UInt64.toInt64 i
@[extern "lean_int64_of_int"]
def Int64.ofInt (i : @& Int) : Int64 := BitVec.ofInt 64 i
@[extern "lean_int64_of_nat"]
@@ -402,7 +483,9 @@ def Int64.toInt (i : Int64) : Int := i.toBitVec.toInt
This function has the same behavior as `Int.toNat` for negative numbers.
If you want to obtain the 2's complement representation use `toBitVec`.
-/
@[inline] def Int64.toNat (i : Int64) : Nat := i.toInt.toNat
@[inline] def Int64.toNatClampNeg (i : Int64) : Nat := i.toInt.toNat
@[inline, deprecated Int64.toNatClampNeg (since := "2025-02-13"), inherit_doc Int64.toNatClampNeg]
def Int64.toNat (i : Int64) : Nat := i.toInt.toNat
/-- Obtains the `Int64` whose 2's complement representation is the given `BitVec 64`. -/
@[inline] def Int64.ofBitVec (b : BitVec 64) : Int64 := b
@[extern "lean_int64_to_int8"]
@@ -427,6 +510,24 @@ instance : OfNat Int64 n := ⟨Int64.ofNat n⟩
instance : Neg Int64 where
neg := Int64.neg
/-- The maximum value an `Int64` may attain, that is, `2^63 - 1 = 9223372036854775807`. -/
abbrev Int64.maxValue : Int64 := 9223372036854775807
/-- The minimum value an `Int64` may attain, that is, `-2^63 = -9223372036854775808`. -/
abbrev Int64.minValue : Int64 := -9223372036854775808
/-- Constructs an `Int64` from an `Int` which is known to be in bounds. -/
@[inline]
def Int64.ofIntLE (i : Int) (_hl : Int64.minValue.toInt i) (_hr : i Int64.maxValue.toInt) : Int64 :=
Int64.ofInt i
/-- Constructs an `Int64` from an `Int`, clamping if the value is too small or too large. -/
def Int64.ofIntTruncate (i : Int) : Int64 :=
if hl : Int64.minValue.toInt i then
if hr : i Int64.maxValue.toInt then
Int64.ofIntLE i hl hr
else
Int64.minValue
else
Int64.minValue
@[extern "lean_int64_add"]
def Int64.add (a b : Int64) : Int64 := a.toBitVec + b.toBitVec
@[extern "lean_int64_sub"]
@@ -504,6 +605,10 @@ Obtain the `BitVec` that contains the 2's complement representation of the `ISiz
-/
@[inline] def ISize.toBitVec (x : ISize) : BitVec System.Platform.numBits := x.toUSize.toBitVec
/-- Obtains the `ISize` that is 2's complement equivalent to the `USize`. -/
@[inline] def USize.toISize (i : USize) : ISize := ISize.ofUSize i
@[inline, deprecated USize.toISize (since := "2025-02-13"), inherit_doc USize.toISize]
def ISize.mk (i : USize) : ISize := USize.toISize i
@[extern "lean_isize_of_int"]
def ISize.ofInt (i : @& Int) : ISize := BitVec.ofInt System.Platform.numBits i
@[extern "lean_isize_of_nat"]
@@ -516,18 +621,28 @@ def ISize.toInt (i : ISize) : Int := i.toBitVec.toInt
This function has the same behavior as `Int.toNat` for negative numbers.
If you want to obtain the 2's complement representation use `toBitVec`.
-/
@[inline] def ISize.toNat (i : ISize) : Nat := i.toInt.toNat
@[inline] def ISize.toNatClampNeg (i : ISize) : Nat := i.toInt.toNat
@[inline, deprecated ISize.toNatClampNeg (since := "2025-02-13"), inherit_doc ISize.toNatClampNeg]
def ISize.toNat (i : ISize) : Nat := i.toInt.toNat
/-- Obtains the `ISize` whose 2's complement representation is the given `BitVec`. -/
@[inline] def ISize.ofBitVec (b : BitVec System.Platform.numBits) : ISize := b
@[extern "lean_isize_to_int8"]
def ISize.toInt8 (a : ISize) : Int8 := a.toBitVec.signExtend 8
@[extern "lean_isize_to_int16"]
def ISize.toInt16 (a : ISize) : Int16 := a.toBitVec.signExtend 16
@[extern "lean_isize_to_int32"]
def ISize.toInt32 (a : ISize) : Int32 := a.toBitVec.signExtend 32
/--
Upcast `ISize` to `Int64`. This function is losless as `ISize` is either `Int32` or `Int64`.
Upcasts `ISize` to `Int64`. This function is lossless as `ISize` is either `Int32` or `Int64`.
-/
@[extern "lean_isize_to_int64"]
def ISize.toInt64 (a : ISize) : Int64 := a.toBitVec.signExtend 64
@[extern "lean_int8_to_isize"]
def Int8.toISize (a : Int8) : ISize := a.toBitVec.signExtend System.Platform.numBits
@[extern "lean_int16_to_isize"]
def Int16.toISize (a : Int16) : ISize := a.toBitVec.signExtend System.Platform.numBits
/--
Upcast `Int32` to `ISize`. This function is losless as `ISize` is either `Int32` or `Int64`.
Upcasts `Int32` to `ISize`. This function is lossless as `ISize` is either `Int32` or `Int64`.
-/
@[extern "lean_int32_to_isize"]
def Int32.toISize (a : Int32) : ISize := a.toBitVec.signExtend System.Platform.numBits
@@ -543,6 +658,26 @@ instance : OfNat ISize n := ⟨ISize.ofNat n⟩
instance : Neg ISize where
neg := ISize.neg
/-- The maximum value an `ISize` may attain, that is, `2^(System.Platform.numBits - 1) - 1`. -/
abbrev ISize.maxValue : ISize := .ofInt (2 ^ (System.Platform.numBits - 1) - 1)
-- 9223372036854775807
/-- The minimum value an `ISize` may attain, that is, `-2^(System.Platform.numBits - 1)`. -/
abbrev ISize.minValue : ISize := .ofInt (2 ^ (System.Platform.numBits - 1))
/-- Constructs an `ISize` from an `Int` which is known to be in bounds. -/
@[inline]
def ISize.ofIntLE (i : Int) (_hl : ISize.minValue.toInt i) (_hr : i ISize.maxValue.toInt) : ISize :=
ISize.ofInt i
/-- Constructs an `ISize` from an `Int`, clamping if the value is too small or too large. -/
def ISize.ofIntTruncate (i : Int) : ISize :=
if hl : ISize.minValue.toInt i then
if hr : i ISize.maxValue.toInt then
ISize.ofIntLE i hl hr
else
ISize.minValue
else
ISize.minValue
@[extern "lean_isize_add"]
def ISize.add (a b : ISize) : ISize := a.toBitVec + b.toBitVec
@[extern "lean_isize_sub"]

View File

@@ -9,9 +9,14 @@ import Init.Data.BitVec.Basic
open Nat
/-- Converts a `Fin UInt8.size` into the corresponding `UInt8`. -/
@[inline] def UInt8.ofFin (a : Fin UInt8.size) : UInt8 := a
@[deprecated UInt8.ofBitVec (since := "2025-02-12"), inherit_doc UInt8.ofBitVec]
def UInt8.mk (bitVec : BitVec 8) : UInt8 :=
UInt8.ofBitVec bitVec
@[inline, deprecated UInt8.ofNatLT (since := "2025-02-13"), inherit_doc UInt8.ofNatLT]
def UInt8.ofNatCore (n : Nat) (h : n < UInt8.size) : UInt8 :=
UInt8.ofNatLT n h
@[extern "lean_uint8_add"]
def UInt8.add (a b : UInt8) : UInt8 := a.toBitVec + b.toBitVec
@@ -24,7 +29,7 @@ def UInt8.div (a b : UInt8) : UInt8 := ⟨BitVec.udiv a.toBitVec b.toBitVec⟩
@[extern "lean_uint8_mod"]
def UInt8.mod (a b : UInt8) : UInt8 := BitVec.umod a.toBitVec b.toBitVec
@[deprecated UInt8.mod (since := "2024-09-23")]
def UInt8.modn (a : UInt8) (n : Nat) : UInt8 := Fin.modn a.val n
def UInt8.modn (a : UInt8) (n : Nat) : UInt8 := Fin.modn a.toFin n
@[extern "lean_uint8_land"]
def UInt8.land (a b : UInt8) : UInt8 := a.toBitVec &&& b.toBitVec
@[extern "lean_uint8_lor"]
@@ -76,9 +81,14 @@ instance (a b : UInt8) : Decidable (a ≤ b) := UInt8.decLe a b
instance : Max UInt8 := maxOfLe
instance : Min UInt8 := minOfLe
/-- Converts a `Fin UInt16.size` into the corresponding `UInt16`. -/
@[inline] def UInt16.ofFin (a : Fin UInt16.size) : UInt16 := a
@[deprecated UInt16.ofBitVec (since := "2025-02-12"), inherit_doc UInt16.ofBitVec]
def UInt16.mk (bitVec : BitVec 16) : UInt16 :=
UInt16.ofBitVec bitVec
@[inline, deprecated UInt16.ofNatLT (since := "2025-02-13"), inherit_doc UInt16.ofNatLT]
def UInt16.ofNatCore (n : Nat) (h : n < UInt16.size) : UInt16 :=
UInt16.ofNatLT n h
@[extern "lean_uint16_add"]
def UInt16.add (a b : UInt16) : UInt16 := a.toBitVec + b.toBitVec
@@ -91,7 +101,7 @@ def UInt16.div (a b : UInt16) : UInt16 := ⟨BitVec.udiv a.toBitVec b.toBitVec
@[extern "lean_uint16_mod"]
def UInt16.mod (a b : UInt16) : UInt16 := BitVec.umod a.toBitVec b.toBitVec
@[deprecated UInt16.mod (since := "2024-09-23")]
def UInt16.modn (a : UInt16) (n : Nat) : UInt16 := Fin.modn a.val n
def UInt16.modn (a : UInt16) (n : Nat) : UInt16 := Fin.modn a.toFin n
@[extern "lean_uint16_land"]
def UInt16.land (a b : UInt16) : UInt16 := a.toBitVec &&& b.toBitVec
@[extern "lean_uint16_lor"]
@@ -145,9 +155,14 @@ instance (a b : UInt16) : Decidable (a ≤ b) := UInt16.decLe a b
instance : Max UInt16 := maxOfLe
instance : Min UInt16 := minOfLe
/-- Converts a `Fin UInt32.size` into the corresponding `UInt32`. -/
@[inline] def UInt32.ofFin (a : Fin UInt32.size) : UInt32 := a
@[deprecated UInt32.ofBitVec (since := "2025-02-12"), inherit_doc UInt32.ofBitVec]
def UInt32.mk (bitVec : BitVec 32) : UInt32 :=
UInt32.ofBitVec bitVec
@[inline, deprecated UInt32.ofNatLT (since := "2025-02-13"), inherit_doc UInt32.ofNatLT]
def UInt32.ofNatCore (n : Nat) (h : n < UInt32.size) : UInt32 :=
UInt32.ofNatLT n h
@[extern "lean_uint32_add"]
def UInt32.add (a b : UInt32) : UInt32 := a.toBitVec + b.toBitVec
@@ -160,7 +175,7 @@ def UInt32.div (a b : UInt32) : UInt32 := ⟨BitVec.udiv a.toBitVec b.toBitVec
@[extern "lean_uint32_mod"]
def UInt32.mod (a b : UInt32) : UInt32 := BitVec.umod a.toBitVec b.toBitVec
@[deprecated UInt32.mod (since := "2024-09-23")]
def UInt32.modn (a : UInt32) (n : Nat) : UInt32 := Fin.modn a.val n
def UInt32.modn (a : UInt32) (n : Nat) : UInt32 := Fin.modn a.toFin n
@[extern "lean_uint32_land"]
def UInt32.land (a b : UInt32) : UInt32 := a.toBitVec &&& b.toBitVec
@[extern "lean_uint32_lor"]
@@ -199,9 +214,14 @@ instance : ShiftRight UInt32 := ⟨UInt32.shiftRight⟩
@[extern "lean_bool_to_uint32"]
def Bool.toUInt32 (b : Bool) : UInt32 := if b then 1 else 0
/-- Converts a `Fin UInt64.size` into the corresponding `UInt64`. -/
@[inline] def UInt64.ofFin (a : Fin UInt64.size) : UInt64 := a
@[deprecated UInt64.ofBitVec (since := "2025-02-12"), inherit_doc UInt64.ofBitVec]
def UInt64.mk (bitVec : BitVec 64) : UInt64 :=
UInt64.ofBitVec bitVec
@[inline, deprecated UInt64.ofNatLT (since := "2025-02-13"), inherit_doc UInt64.ofNatLT]
def UInt64.ofNatCore (n : Nat) (h : n < UInt64.size) : UInt64 :=
UInt64.ofNatLT n h
@[extern "lean_uint64_add"]
def UInt64.add (a b : UInt64) : UInt64 := a.toBitVec + b.toBitVec
@@ -214,7 +234,7 @@ def UInt64.div (a b : UInt64) : UInt64 := ⟨BitVec.udiv a.toBitVec b.toBitVec
@[extern "lean_uint64_mod"]
def UInt64.mod (a b : UInt64) : UInt64 := BitVec.umod a.toBitVec b.toBitVec
@[deprecated UInt64.mod (since := "2024-09-23")]
def UInt64.modn (a : UInt64) (n : Nat) : UInt64 := Fin.modn a.val n
def UInt64.modn (a : UInt64) (n : Nat) : UInt64 := Fin.modn a.toFin n
@[extern "lean_uint64_land"]
def UInt64.land (a b : UInt64) : UInt64 := a.toBitVec &&& b.toBitVec
@[extern "lean_uint64_lor"]
@@ -266,9 +286,14 @@ instance (a b : UInt64) : Decidable (a ≤ b) := UInt64.decLe a b
instance : Max UInt64 := maxOfLe
instance : Min UInt64 := minOfLe
/-- Converts a `Fin USize.size` into the corresponding `USize`. -/
@[inline] def USize.ofFin (a : Fin USize.size) : USize := a
@[deprecated USize.ofBitVec (since := "2025-02-12"), inherit_doc USize.ofBitVec]
def USize.mk (bitVec : BitVec System.Platform.numBits) : USize :=
USize.ofBitVec bitVec
@[inline, deprecated USize.ofNatLT (since := "2025-02-13"), inherit_doc USize.ofNatLT]
def USize.ofNatCore (n : Nat) (h : n < USize.size) : USize :=
USize.ofNatLT n h
theorem usize_size_le : USize.size 18446744073709551616 := by
cases usize_size_eq <;> next h => rw [h]; decide
@@ -283,7 +308,7 @@ def USize.div (a b : USize) : USize := ⟨a.toBitVec / b.toBitVec⟩
@[extern "lean_usize_mod"]
def USize.mod (a b : USize) : USize := a.toBitVec % b.toBitVec
@[deprecated USize.mod (since := "2024-09-23")]
def USize.modn (a : USize) (n : Nat) : USize := Fin.modn a.val n
def USize.modn (a : USize) (n : Nat) : USize := Fin.modn a.toFin n
@[extern "lean_usize_land"]
def USize.land (a b : USize) : USize := a.toBitVec &&& b.toBitVec
@[extern "lean_usize_lor"]
@@ -301,7 +326,7 @@ This function is overridden with a native implementation.
-/
@[extern "lean_usize_of_nat"]
def USize.ofNat32 (n : @& Nat) (h : n < 4294967296) : USize :=
USize.ofNatCore n (Nat.lt_of_lt_of_le h le_usize_size)
USize.ofNatLT n (Nat.lt_of_lt_of_le h le_usize_size)
@[extern "lean_uint8_to_usize"]
def UInt8.toUSize (a : UInt8) : USize :=
USize.ofNat32 a.toBitVec.toNat (Nat.lt_trans a.toBitVec.isLt (by decide))
@@ -326,7 +351,7 @@ This function is overridden with a native implementation.
-/
@[extern "lean_usize_to_uint64"]
def USize.toUInt64 (a : USize) : UInt64 :=
UInt64.ofNatCore a.toBitVec.toNat (Nat.lt_of_lt_of_le a.toBitVec.isLt usize_size_le)
UInt64.ofNatLT a.toBitVec.toNat (Nat.lt_of_lt_of_le a.toBitVec.isLt usize_size_le)
instance : Mul USize := USize.mul
instance : Mod USize := USize.mod

View File

@@ -16,18 +16,40 @@ This file thus breaks the import cycle that would be created by this dependency.
open Nat
def UInt8.val (x : UInt8) : Fin UInt8.size := x.toBitVec.toFin
/-- Converts a `UInt8` into the corresponding `Fin UInt8.size`. -/
def UInt8.toFin (x : UInt8) : Fin UInt8.size := x.toBitVec.toFin
@[deprecated UInt8.toFin (since := "2025-02-12"), inherit_doc UInt8.toFin]
def UInt8.val (x : UInt8) : Fin UInt8.size := x.toFin
@[extern "lean_uint8_of_nat"]
def UInt8.ofNat (n : @& Nat) : UInt8 := BitVec.ofNat 8 n
/--
Converts the given natural number to `UInt8`, but returns `2^8 - 1` for natural numbers `>= 2^8`.
-/
def UInt8.ofNatTruncate (n : Nat) : UInt8 :=
if h : n < UInt8.size then
UInt8.ofNatLT n h
else
UInt8.ofNatLT (UInt8.size - 1) (by decide)
abbrev Nat.toUInt8 := UInt8.ofNat
@[extern "lean_uint8_to_nat"]
def UInt8.toNat (n : UInt8) : Nat := n.toBitVec.toNat
instance UInt8.instOfNat : OfNat UInt8 n := UInt8.ofNat n
def UInt16.val (x : UInt16) : Fin UInt16.size := x.toBitVec.toFin
/-- Converts a `UInt16` into the corresponding `Fin UInt16.size`. -/
def UInt16.toFin (x : UInt16) : Fin UInt16.size := x.toBitVec.toFin
@[deprecated UInt16.toFin (since := "2025-02-12"), inherit_doc UInt16.toFin]
def UInt16.val (x : UInt16) : Fin UInt16.size := x.toFin
@[extern "lean_uint16_of_nat"]
def UInt16.ofNat (n : @& Nat) : UInt16 := BitVec.ofNat 16 n
/--
Converts the given natural number to `UInt16`, but returns `2^16 - 1` for natural numbers `>= 2^16`.
-/
def UInt16.ofNatTruncate (n : Nat) : UInt16 :=
if h : n < UInt16.size then
UInt16.ofNatLT n h
else
UInt16.ofNatLT (UInt16.size - 1) (by decide)
abbrev Nat.toUInt16 := UInt16.ofNat
@[extern "lean_uint16_to_nat"]
def UInt16.toNat (n : UInt16) : Nat := n.toBitVec.toNat
@@ -38,19 +60,22 @@ def UInt8.toUInt16 (a : UInt8) : UInt16 := ⟨⟨a.toNat, Nat.lt_trans a.toBitVe
instance UInt16.instOfNat : OfNat UInt16 n := UInt16.ofNat n
def UInt32.val (x : UInt32) : Fin UInt32.size := x.toBitVec.toFin
/-- Converts a `UInt32` into the corresponding `Fin UInt32.size`. -/
def UInt32.toFin (x : UInt32) : Fin UInt32.size := x.toBitVec.toFin
@[deprecated UInt32.toFin (since := "2025-02-12"), inherit_doc UInt32.toFin]
def UInt32.val (x : UInt32) : Fin UInt32.size := x.toFin
@[extern "lean_uint32_of_nat"]
def UInt32.ofNat (n : @& Nat) : UInt32 := BitVec.ofNat 32 n
@[extern "lean_uint32_of_nat"]
def UInt32.ofNat' (n : Nat) (h : n < UInt32.size) : UInt32 := BitVec.ofNatLt n h
@[inline, deprecated UInt32.ofNatLT (since := "2025-02-13"), inherit_doc UInt32.ofNatLT]
def UInt32.ofNat' (n : Nat) (h : n < UInt32.size) : UInt32 := UInt32.ofNatLT n h
/--
Converts the given natural number to `UInt32`, but returns `2^32 - 1` for natural numbers `>= 2^32`.
-/
def UInt32.ofNatTruncate (n : Nat) : UInt32 :=
if h : n < UInt32.size then
UInt32.ofNat' n h
UInt32.ofNatLT n h
else
UInt32.ofNat' (UInt32.size - 1) (by decide)
UInt32.ofNatLT (UInt32.size - 1) (by decide)
abbrev Nat.toUInt32 := UInt32.ofNat
@[extern "lean_uint32_to_uint8"]
def UInt32.toUInt8 (a : UInt32) : UInt8 := a.toNat.toUInt8
@@ -63,19 +88,38 @@ def UInt16.toUInt32 (a : UInt16) : UInt32 := ⟨⟨a.toNat, Nat.lt_trans a.toBit
instance UInt32.instOfNat : OfNat UInt32 n := UInt32.ofNat n
theorem UInt32.ofNatLT_lt_of_lt {n m : Nat} (h1 : n < UInt32.size) (h2 : m < UInt32.size) :
n < m UInt32.ofNatLT n h1 < UInt32.ofNat m := by
simp only [(· < ·), BitVec.toNat, ofNatLT, BitVec.ofNatLT, ofNat, BitVec.ofNat, Fin.ofNat',
Nat.mod_eq_of_lt h2, imp_self]
@[deprecated UInt32.ofNatLT_lt_of_lt (since := "2025-02-13")]
theorem UInt32.ofNat'_lt_of_lt {n m : Nat} (h1 : n < UInt32.size) (h2 : m < UInt32.size) :
n < m UInt32.ofNat' n h1 < UInt32.ofNat m := by
simp only [(· < ·), BitVec.toNat, ofNat', BitVec.ofNatLt, ofNat, BitVec.ofNat, Fin.ofNat',
n < m UInt32.ofNatLT n h1 < UInt32.ofNat m := UInt32.ofNatLT_lt_of_lt h1 h2
theorem UInt32.lt_ofNatLT_of_lt {n m : Nat} (h1 : n < UInt32.size) (h2 : m < UInt32.size) :
m < n UInt32.ofNat m < UInt32.ofNatLT n h1 := by
simp only [(· < ·), BitVec.toNat, ofNatLT, BitVec.ofNatLT, ofNat, BitVec.ofNat, Fin.ofNat',
Nat.mod_eq_of_lt h2, imp_self]
@[deprecated UInt32.lt_ofNatLT_of_lt (since := "2025-02-13")]
theorem UInt32.lt_ofNat'_of_lt {n m : Nat} (h1 : n < UInt32.size) (h2 : m < UInt32.size) :
m < n UInt32.ofNat m < UInt32.ofNat' n h1 := by
simp only [(· < ·), BitVec.toNat, ofNat', BitVec.ofNatLt, ofNat, BitVec.ofNat, Fin.ofNat',
Nat.mod_eq_of_lt h2, imp_self]
m < n UInt32.ofNat m < UInt32.ofNatLT n h1 := UInt32.lt_ofNatLT_of_lt h1 h2
def UInt64.val (x : UInt64) : Fin UInt64.size := x.toBitVec.toFin
/-- Converts a `UInt64` into the corresponding `Fin UInt64.size`. -/
def UInt64.toFin (x : UInt64) : Fin UInt64.size := x.toBitVec.toFin
@[deprecated UInt64.toFin (since := "2025-02-12"), inherit_doc UInt64.toFin]
def UInt64.val (x : UInt64) : Fin UInt64.size := x.toFin
@[extern "lean_uint64_of_nat"]
def UInt64.ofNat (n : @& Nat) : UInt64 := BitVec.ofNat 64 n
/--
Converts the given natural number to `UInt64`, but returns `2^64 - 1` for natural numbers `>= 2^64`.
-/
def UInt64.ofNatTruncate (n : Nat) : UInt64 :=
if h : n < UInt64.size then
UInt64.ofNatLT n h
else
UInt64.ofNatLT (UInt64.size - 1) (by decide)
abbrev Nat.toUInt64 := UInt64.ofNat
@[extern "lean_uint64_to_nat"]
def UInt64.toNat (n : UInt64) : Nat := n.toBitVec.toNat
@@ -97,9 +141,21 @@ instance UInt64.instOfNat : OfNat UInt64 n := ⟨UInt64.ofNat n⟩
@[deprecated usize_size_pos (since := "2024-11-24")] theorem usize_size_gt_zero : USize.size > 0 :=
usize_size_pos
def USize.val (x : USize) : Fin USize.size := x.toBitVec.toFin
/-- Converts a `USize` into the corresponding `Fin USize.size`. -/
def USize.toFin (x : USize) : Fin USize.size := x.toBitVec.toFin
@[deprecated USize.toFin (since := "2025-02-12"), inherit_doc USize.toFin]
def USize.val (x : USize) : Fin USize.size := x.toFin
@[extern "lean_usize_of_nat"]
def USize.ofNat (n : @& Nat) : USize := BitVec.ofNat _ n
/--
Converts the given natural number to `USize`, but returns `USize.size - 1` (i.e., `2^64 - 1` or
`2^32 - 1` depending on the platform) for natural numbers `>= USize.size`.
-/
def USize.ofNatTruncate (n : Nat) : USize :=
if h : n < USize.size then
USize.ofNatLT n h
else
USize.ofNatLT (USize.size - 1) (Nat.pred_lt (Nat.ne_zero_of_lt usize_size_pos))
abbrev Nat.toUSize := USize.ofNat
@[extern "lean_usize_to_nat"]
def USize.toNat (n : USize) : Nat := n.toBitVec.toNat

View File

@@ -29,9 +29,14 @@ macro "declare_uint_theorems" typeName:ident bits:term:arg : command => do
@[simp] theorem toNat_ofNat {n : Nat} : (ofNat n).toNat = n % 2 ^ $bits := BitVec.toNat_ofNat ..
@[simp] theorem toNat_ofNatCore {n : Nat} {h : n < size} : (ofNatCore n h).toNat = n := BitVec.toNat_ofNatLt ..
@[simp] theorem toNat_ofNatLT {n : Nat} {h : n < size} : (ofNatLT n h).toNat = n := BitVec.toNat_ofNatLT ..
@[simp] theorem val_val_eq_toNat (x : $typeName) : x.val.val = x.toNat := rfl
@[deprecated toNat_ofNatLT (since := "2025-02-13")]
theorem toNat_ofNatCore {n : Nat} {h : n < size} : (ofNatLT n h).toNat = n := BitVec.toNat_ofNatLT ..
@[simp] theorem toFin_val_eq_toNat (x : $typeName) : x.toFin.val = x.toNat := rfl
@[deprecated toFin_val_eq_toNat (since := "2025-02-12")]
theorem val_val_eq_toNat (x : $typeName) : x.toFin.val = x.toNat := rfl
theorem toNat_toBitVec_eq_toNat (x : $typeName) : x.toBitVec.toNat = x.toNat := rfl
@@ -86,13 +91,21 @@ macro "declare_uint_theorems" typeName:ident bits:term:arg : command => do
protected theorem eq_iff_toBitVec_eq {a b : $typeName} : a = b a.toBitVec = b.toBitVec :=
Iff.intro toBitVec_eq_of_eq eq_of_toBitVec_eq
open $typeName (eq_of_toBitVec_eq) in
protected theorem eq_of_val_eq {a b : $typeName} (h : a.val = b.val) : a = b := by
rcases a with _; rcases b with _; simp_all [val]
open $typeName (eq_of_toBitVec_eq toFin) in
protected theorem eq_of_toFin_eq {a b : $typeName} (h : a.toFin = b.toFin) : a = b := by
rcases a with _; rcases b with _; simp_all [toFin]
open $typeName (eq_of_toFin_eq) in
@[deprecated eq_of_toFin_eq (since := "2025-02-12")]
protected theorem eq_of_val_eq {a b : $typeName} (h : a.toFin = b.toFin) : a = b :=
eq_of_toFin_eq h
open $typeName (eq_of_val_eq) in
protected theorem val_inj {a b : $typeName} : a.val = b.val a = b :=
Iff.intro eq_of_val_eq (congrArg val)
open $typeName (eq_of_toFin_eq) in
protected theorem toFin_inj {a b : $typeName} : a.toFin = b.toFin a = b :=
Iff.intro eq_of_toFin_eq (congrArg toFin)
open $typeName (toFin_inj) in
@[deprecated toFin_inj (since := "2025-02-12")]
protected theorem val_inj {a b : $typeName} : a.toFin = b.toFin a = b :=
toFin_inj
open $typeName (eq_of_toBitVec_eq) in
protected theorem toBitVec_ne_of_ne {a b : $typeName} (h : a b) : a.toBitVec b.toBitVec :=
@@ -178,7 +191,9 @@ macro "declare_uint_theorems" typeName:ident bits:term:arg : command => do
simp [Nat.mod_eq_of_lt x.toNat_lt_size]
@[simp]
theorem val_ofNat (n : Nat) : val (no_index (OfNat.ofNat n)) = OfNat.ofNat n := rfl
theorem toFin_ofNat (n : Nat) : toFin (no_index (OfNat.ofNat n)) = OfNat.ofNat n := rfl
@[deprecated toFin_ofNat (since := "2025-02-12")]
theorem val_ofNat (n : Nat) : toFin (no_index (OfNat.ofNat n)) = OfNat.ofNat n := rfl
@[simp, int_toBitVec]
theorem toBitVec_ofNat (n : Nat) : toBitVec (no_index (OfNat.ofNat n)) = BitVec.ofNat _ n := rfl

View File

@@ -7,16 +7,16 @@ prelude
import Init.Data.Fin.Log2
@[extern "lean_uint8_log2"]
def UInt8.log2 (a : UInt8) : UInt8 := Fin.log2 a.val
def UInt8.log2 (a : UInt8) : UInt8 := Fin.log2 a.toFin
@[extern "lean_uint16_log2"]
def UInt16.log2 (a : UInt16) : UInt16 := Fin.log2 a.val
def UInt16.log2 (a : UInt16) : UInt16 := Fin.log2 a.toFin
@[extern "lean_uint32_log2"]
def UInt32.log2 (a : UInt32) : UInt32 := Fin.log2 a.val
def UInt32.log2 (a : UInt32) : UInt32 := Fin.log2 a.toFin
@[extern "lean_uint64_log2"]
def UInt64.log2 (a : UInt64) : UInt64 := Fin.log2 a.val
def UInt64.log2 (a : UInt64) : UInt64 := Fin.log2 a.toFin
@[extern "lean_usize_log2"]
def USize.log2 (a : USize) : USize := Fin.log2 a.val
def USize.log2 (a : USize) : USize := Fin.log2 a.toFin

View File

@@ -16,3 +16,4 @@ import Init.Data.Vector.Range
import Init.Data.Vector.Erase
import Init.Data.Vector.Monadic
import Init.Data.Vector.InsertIdx
import Init.Data.Vector.Extract

View File

@@ -31,7 +31,7 @@ abbrev Array.toVector (xs : Array α) : Vector α xs.size := .mk xs rfl
namespace Vector
/-- Syntax for `Vector α n` -/
syntax "#v[" withoutPosition(sepBy(term, ", ")) "]" : term
syntax (name := «term#v[_,]») "#v[" withoutPosition(term,*,?) "]" : term
open Lean in
macro_rules

View File

@@ -0,0 +1,166 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
prelude
import Init.Data.Vector.Lemmas
import Init.Data.Array.Extract
/-!
# Lemmas about `Vector.extract`
-/
open Nat
namespace Vector
/-! ### extract -/
@[simp] theorem extract_of_size_lt {as : Vector α n} {i j : Nat} (h : n < j) :
as.extract i j = (as.extract i n).cast (by omega) := by
rcases as with as, rfl
simp [h]
@[simp]
theorem extract_push {as : Vector α n} {b : α} {start stop : Nat} (h : stop n) :
(as.push b).extract start stop = (as.extract start stop).cast (by omega) := by
rcases as with as, rfl
simp [h]
@[simp]
theorem extract_eq_pop {as : Vector α n} {stop : Nat} (h : stop = n - 1) :
as.extract 0 stop = as.pop.cast (by omega) := by
rcases as with as, rfl
simp [h]
@[simp]
theorem extract_append_extract {as : Vector α n} {i j k : Nat} :
as.extract i j ++ as.extract j k =
(as.extract (min i j) (max j k)).cast (by omega) := by
rcases as with as, rfl
simp
@[simp]
theorem push_extract_getElem {as : Vector α n} {i j : Nat} (h : j < n) :
(as.extract i j).push as[j] = (as.extract (min i j) (j + 1)).cast (by omega) := by
rcases as with as, rfl
simp [h]
theorem extract_succ_right {as : Vector α n} {i j : Nat} (w : i < j + 1) (h : j < n) :
as.extract i (j + 1) = ((as.extract i j).push as[j]).cast (by omega) := by
rcases as with as, rfl
simp [Array.extract_succ_right, w, h]
theorem extract_sub_one {as : Vector α n} {i j : Nat} (h : j < n) :
as.extract i (j - 1) = (as.extract i j).pop.cast (by omega) := by
rcases as with as, rfl
simp [Array.extract_sub_one, h]
@[simp]
theorem getElem?_extract_of_lt {as : Vector α n} {i j k : Nat} (h : k < min j n - i) :
(as.extract i j)[k]? = some (as[i + k]'(by omega)) := by
simp [getElem?_extract, h]
theorem getElem?_extract_of_succ {as : Vector α n} {j : Nat} :
(as.extract 0 (j + 1))[j]? = as[j]? := by
simp only [Nat.sub_zero]
erw [getElem?_extract] -- Why does this not fire by `simp` or `rw`?
by_cases h : j < n
· rw [if_pos (by omega)]
simp
· rw [if_neg (by omega)]
simp_all
@[simp] theorem extract_extract {as : Vector α n} {i j k l : Nat} :
(as.extract i j).extract k l = (as.extract (i + k) (min (i + l) j)).cast (by omega) := by
rcases as with as, rfl
simp
theorem extract_set {as : Vector α n} {i j k : Nat} (h : k < n) {a : α} :
(as.set k a).extract i j =
if _ : k < i then
as.extract i j
else if _ : k < min j as.size then
(as.extract i j).set (k - i) a (by omega)
else as.extract i j := by
rcases as with as, rfl
simp only [set_mk, extract_mk, Array.extract_set]
split
· simp
· split <;> simp
theorem set_extract {as : Vector α n} {i j k : Nat} (h : k < min j n - i) {a : α} :
(as.extract i j).set k a = (as.set (i + k) a).extract i j := by
rcases as with as, rfl
simp [Array.set_extract]
@[simp]
theorem extract_append {as : Vector α n} {bs : Vector α m} {i j : Nat} :
(as ++ bs).extract i j =
(as.extract i j ++ bs.extract (i - n) (j - n)).cast (by omega) := by
rcases as with as, rfl
rcases bs with bs, rfl
simp
theorem extract_append_left {as : Vector α n} {bs : Vector α m} :
(as ++ bs).extract 0 n = (as.extract 0 n).cast (by omega) := by
ext i h
simp only [Nat.sub_zero, extract_append, extract_size, getElem_cast, getElem_append, Nat.min_self,
getElem_extract, Nat.zero_sub, Nat.zero_add, cast_cast]
split
· rfl
· omega
@[simp] theorem extract_append_right {as : Vector α n} {bs : Vector α m} :
(as ++ bs).extract n (n + i) = (bs.extract 0 i).cast (by omega) := by
rcases as with as, rfl
rcases bs with bs, rfl
simp only [mk_append_mk, extract_mk, Array.extract_append, Array.extract_size_left, Nat.sub_self,
Array.empty_append, Nat.sub_zero, cast_mk, eq_mk]
congr 1
omega
@[simp] theorem map_extract {as : Vector α n} {i j : Nat} :
(as.extract i j).map f = (as.map f).extract i j := by
ext k h
simp
@[simp] theorem extract_mkVector {a : α} {n i j : Nat} :
(mkVector n a).extract i j = mkVector (min j n - i) a := by
ext i h
simp
theorem extract_add_left {as : Vector α n} {i j k : Nat} :
as.extract (i + j) k = ((as.extract i k).extract j (k - i)).cast (by omega) := by
rcases as with as, rfl
simp only [extract_mk, Array.extract_extract, cast_mk, eq_mk]
rw [Array.extract_add_left]
simp
theorem mem_extract_iff_getElem {as : Vector α n} {a : α} {i j : Nat} :
a as.extract i j (k : Nat) (hm : k < min j n - i), as[i + k] = a := by
rcases as with as
simp [Array.mem_extract_iff_getElem]
constructor <;>
· rintro k, h, rfl
exact k, by omega, rfl
theorem set_eq_push_extract_append_extract {as : Vector α n} {i : Nat} (h : i < n) {a : α} :
as.set i a = ((as.extract 0 i).push a ++ (as.extract (i + 1) n)).cast (by omega) := by
rcases as with as, rfl
simp [Array.set_eq_push_extract_append_extract, h]
theorem extract_reverse {as : Vector α n} {i j : Nat} :
as.reverse.extract i j = (as.extract (n - j) (n - i)).reverse.cast (by omega) := by
ext i h
simp only [getElem_extract, getElem_reverse, getElem_cast]
congr 1
omega
theorem reverse_extract {as : Vector α n} {i j : Nat} :
(as.extract i j).reverse = (as.reverse.extract (n - j) (n - i)).cast (by omega) := by
rcases as with as, rfl
simp [Array.reverse_extract]
end Vector

View File

@@ -10,7 +10,9 @@ import Init.Data.Vector.Range
import Init.Data.Array.Find
/-!
# Lemmas about `Vector.findSome?`, `Vector.find?, `Vector.findIdx?`, `Vector.idxOf?`.
# Lemmas about `Vector.findSome?`, `Vector.find?`, `Vector.findFinIdx?`.
We are still missing results about `idxOf?`, `findIdx`, and `findIdx?`.
-/
namespace Vector

View File

@@ -70,8 +70,8 @@ theorem toArray_mk (a : Array α) (h : a.size = n) : (Vector.mk a h).toArray = a
(Vector.mk a h).back? = a.back? := rfl
@[simp] theorem back_mk [NeZero n] (a : Array α) (h : a.size = n) :
(Vector.mk a h).back =
a[n - 1]'(Nat.lt_of_lt_of_eq (Nat.sub_one_lt (NeZero.ne n)) h.symm) := rfl
(Vector.mk a h).back = a.back (by have : 0 n := NeZero.ne' n; omega) := by
simp [back, Array.back, h]
@[simp] theorem foldlM_mk [Monad m] (f : β α m β) (b : β) (a : Array α) (h : a.size = n) :
(Vector.mk a h).foldlM f b = a.foldlM f b := rfl
@@ -730,6 +730,17 @@ theorem singleton_inj : #v[a] = #v[b] ↔ a = b := by
rcases l with l, rfl
simp
/-- In an equality between two casts, push the casts to the right hand side. -/
@[simp] theorem cast_eq_cast {as : Vector α n} {bs : Vector α m} {wa : n = k} {wb : m = k} :
as.cast wa = bs.cast wb as = bs.cast (by omega) := by
constructor
· intro w
ext i h
replace w := congrArg (fun v => v[i]) w
simpa using w
· rintro rfl
simp
/-! ### mkVector -/
@[simp] theorem mkVector_zero : mkVector 0 a = #v[] := rfl
@@ -2017,6 +2028,10 @@ theorem flatMap_mkArray {β} (f : α → Vector β m) : (mkVector n a).flatMap f
cases as
simp
@[simp] theorem isEmpty_reverse {xs : Vector α n} : xs.reverse.isEmpty = xs.isEmpty := by
rcases xs with xs, rfl
simp
@[simp] theorem getElem_reverse (a : Vector α n) (i : Nat) (hi : i < n) :
(a.reverse)[i] = a[n - 1 - i] := by
rcases a with a, rfl
@@ -2101,7 +2116,7 @@ theorem flatMap_reverse {β} (l : Vector α n) (f : α → Vector β m) :
simp
theorem getElem?_extract {as : Vector α n} {start stop : Nat} :
(as.extract start stop)[i]? = if i < min stop as.size - start then as[start + i]? else none := by
(as.extract start stop)[i]? = if i < min stop n - start then as[start + i]? else none := by
rcases as with as, rfl
simp [Array.getElem?_extract]

View File

@@ -251,6 +251,20 @@ namespace Array
instance : GetElem (Array α) Nat α fun xs i => i < xs.size where
getElem xs i h := xs.get i h
-- We provide a `GetElem?` instance, rather than using the low priority instance,
-- so that we use the `@[extern]` definition of `get!`.
instance : GetElem? (Array α) Nat α fun xs i => i < xs.size where
getElem? xs i := decidableGetElem? xs i
getElem! xs i := xs.get! i
instance : LawfulGetElem (Array α) Nat α fun xs i => i < xs.size where
getElem?_def xs i h := by
simp only [getElem?, decidableGetElem?]
split <;> rfl
getElem!_def xs i := by
simp only [getElem!, getElem?, decidableGetElem?, get!, getD, getElem]
split <;> rfl
@[simp] theorem get_eq_getElem (a : Array α) (i : Nat) (h) : a.get i h = a[i] := rfl
@[simp] theorem get!_eq_getElem! [Inhabited α] (a : Array α) (i : Nat) : a.get! i = a[i]! := by

View File

@@ -8,6 +8,7 @@ import Init.SimpLemmas
import Init.PropLemmas
import Init.Classical
import Init.ByCases
import Init.Data.Int.Linear
namespace Lean.Grind
/-!
@@ -72,6 +73,8 @@ theorem bne_eq_decide_not_eq {_ : BEq α} [LawfulBEq α] [DecidableEq α] (a b :
init_grind_norm
/- Pre theorems -/
not_and not_or not_ite not_forall not_exists
/- Nat relational ops neg -/
Nat.not_ge_eq Nat.not_le_eq
|
/- Post theorems -/
Classical.not_not
@@ -116,9 +119,16 @@ init_grind_norm
Nat.lt_eq
-- Nat.succ
Nat.succ_eq_add_one
-- Nat op folding
Nat.add_eq Nat.sub_eq Nat.mul_eq Nat.zero_eq Nat.le_eq
-- Int
Int.lt_eq
-- GT GE
ge_eq gt_eq
-- Int op folding
Int.add_def Int.mul_def
Int.Linear.sub_fold Int.Linear.neg_fold
-- Int divides
Int.one_dvd Int.zero_dvd
end Lean.Grind

View File

@@ -14,6 +14,12 @@ def nestedProof (p : Prop) {h : p} : p := h
/--
Gadget for marking `match`-expressions that should not be reduced by the `grind` simplifier, but the discriminants should be normalized.
We use it when adding instances of `match`-equations to prevent them from being simplified to true.
Remark: it must not be marked as `[reducible]`. Otherwise, `simp` will reduce
```
simpMatchDiscrsOnly (match 0 with | 0 => true | _ => false) = true
```
using `eq_self`.
-/
def simpMatchDiscrsOnly {α : Sort u} (a : α) : α := a
@@ -28,7 +34,7 @@ Gadget for annotating the equalities in `match`-equations conclusions.
`_origin` is the term used to instantiate the `match`-equation using E-matching.
When `EqMatch a b origin` is `True`, we mark `origin` as a resolved case-split.
-/
def EqMatch (a b : α) {_origin : α} : Prop := a = b
abbrev EqMatch (a b : α) {_origin : α} : Prop := a = b
/--
Gadget for annotating conditions of `match` equational lemmas.
@@ -36,7 +42,13 @@ We use this annotation for two different reasons:
- We don't want to normalize them.
- We have a propagator for them.
-/
def MatchCond (p : Prop) : Prop := p
abbrev MatchCond (p : Prop) : Prop := p
/--
Similar to `MatchCond`, but not reducible. We use it to ensure `simp`
will not eliminate it. After we apply `simp`, we replace it with `MatchCond`.
-/
def PreMatchCond (p : Prop) : Prop := p
theorem nestedProof_congr (p q : Prop) (h : p = q) (hp : p) (hq : q) : HEq (@nestedProof p hp) (@nestedProof q hq) := by
subst h; apply HEq.refl

View File

@@ -735,8 +735,8 @@ def decodeNatLitVal? (s : String) : Option Nat :=
def isLit? (litKind : SyntaxNodeKind) (stx : Syntax) : Option String :=
match stx with
| Syntax.node _ k args =>
if k == litKind && args.size == 1 then
match args.get! 0 with
if h : k == litKind args.size = 1 then
match args[0]'(Nat.lt_of_sub_eq_succ h.2) with
| (Syntax.atom _ val) => some val
| _ => none
else

View File

@@ -756,6 +756,13 @@ This is mostly useful for debugging info trees.
syntax (name := infoTreesCmd)
"#info_trees" " in" ppLine command : command
/--
Specify a premise selection engine.
Note that Lean does not ship a default premise selection engine,
so this is only useful in conjunction with a downstream package which provides one.
-/
syntax (name := setPremiseSelectorCmd)
"set_premise_selector" term : command
namespace Parser

View File

@@ -21,18 +21,18 @@ abbrev IntList := List Int
namespace IntList
/-- Get the `i`-th element (interpreted as `0` if the list is not long enough). -/
def get (xs : IntList) (i : Nat) : Int := (xs.get? i).getD 0
def get (xs : IntList) (i : Nat) : Int := xs[i]?.getD 0
@[simp] theorem get_nil : get ([] : IntList) i = 0 := rfl
@[simp] theorem get_cons_zero : get (x :: xs) 0 = x := rfl
@[simp] theorem get_cons_succ : get (x :: xs) (i+1) = get xs i := rfl
@[simp] theorem get_cons_zero : get (x :: xs) 0 = x := by simp [get]
@[simp] theorem get_cons_succ : get (x :: xs) (i+1) = get xs i := by simp [get]
theorem get_map {xs : IntList} (h : f 0 = 0) : get (xs.map f) i = f (xs.get i) := by
simp only [get, List.get?_eq_getElem?, List.getElem?_map]
simp only [get, List.getElem?_map]
cases xs[i]? <;> simp_all
theorem get_of_length_le {xs : IntList} (h : xs.length i) : xs.get i = 0 := by
rw [get, List.get?_eq_none_iff.mpr h]
rw [get, List.getElem?_eq_none_iff.mpr h]
rfl
/-- Like `List.set`, but right-pad with zeroes as necessary first. -/
@@ -62,7 +62,7 @@ theorem add_def (xs ys : IntList) :
rfl
@[simp] theorem add_get (xs ys : IntList) (i : Nat) : (xs + ys).get i = xs.get i + ys.get i := by
simp only [get, add_def, List.get?_eq_getElem?, List.getElem?_zipWithAll]
simp only [get, add_def, List.getElem?_zipWithAll]
cases xs[i]? <;> cases ys[i]? <;> simp
@[simp] theorem add_nil (xs : IntList) : xs + [] = xs := by simp [add_def]
@@ -79,7 +79,7 @@ theorem mul_def (xs ys : IntList) : xs * ys = List.zipWith (· * ·) xs ys :=
rfl
@[simp] theorem mul_get (xs ys : IntList) (i : Nat) : (xs * ys).get i = xs.get i * ys.get i := by
simp only [get, mul_def, List.get?_eq_getElem?, List.getElem?_zipWith]
simp only [get, mul_def, List.getElem?_zipWith]
cases xs[i]? <;> cases ys[i]? <;> simp
@[simp] theorem mul_nil_left : ([] : IntList) * ys = [] := rfl
@@ -94,7 +94,7 @@ instance : Neg IntList := ⟨neg⟩
theorem neg_def (xs : IntList) : - xs = xs.map fun x => -x := rfl
@[simp] theorem neg_get (xs : IntList) (i : Nat) : (- xs).get i = - xs.get i := by
simp only [get, neg_def, List.get?_eq_getElem?, List.getElem?_map]
simp only [get, neg_def, List.getElem?_map]
cases xs[i]? <;> simp
@[simp] theorem neg_nil : (- ([] : IntList)) = [] := rfl
@@ -120,7 +120,7 @@ instance : HMul Int IntList IntList where
theorem smul_def (xs : IntList) (i : Int) : i * xs = xs.map fun x => i * x := rfl
@[simp] theorem smul_get (xs : IntList) (a : Int) (i : Nat) : (a * xs).get i = a * xs.get i := by
simp only [get, smul_def, List.get?_eq_getElem?, List.getElem?_map]
simp only [get, smul_def, List.getElem?_map]
cases xs[i]? <;> simp
@[simp] theorem smul_nil {i : Int} : i * ([] : IntList) = [] := rfl
@@ -303,7 +303,7 @@ theorem dvd_gcd (xs : IntList) (c : Nat) (w : ∀ {a : Int}, a ∈ xs → (c : I
c xs.gcd := by
simp only [Int.ofNat_dvd_left] at w
induction xs with
| nil => have := Nat.dvd_zero c; simp at this; exact this
| nil => have := Nat.dvd_zero c; simp
| cons x xs ih =>
simp
apply Nat.dvd_gcd

View File

@@ -1904,7 +1904,7 @@ instance : DecidableEq (BitVec n) := BitVec.decEq
/-- The `BitVec` with value `i`, given a proof that `i < 2^n`. -/
@[match_pattern]
protected def BitVec.ofNatLt {n : Nat} (i : Nat) (p : LT.lt i (hPow 2 n)) : BitVec n where
protected def BitVec.ofNatLT {n : Nat} (i : Nat) (p : LT.lt i (hPow 2 n)) : BitVec n where
toFin := i, p
/-- Given a bitvector `x`, return the underlying `Nat`. This is O(1) because `BitVec` is a
@@ -1939,21 +1939,13 @@ structure UInt8 where
attribute [extern "lean_uint8_of_nat_mk"] UInt8.ofBitVec
attribute [extern "lean_uint8_to_nat"] UInt8.toBitVec
/--
Pack a `Nat` less than `2^8` into a `UInt8`.
This function is overridden with a native implementation.
-/
@[extern "lean_uint8_of_nat"]
def UInt8.ofNatCore (n : @& Nat) (h : LT.lt n UInt8.size) : UInt8 where
toBitVec := BitVec.ofNatLt n h
/--
Pack a `Nat` less than `2^8` into a `UInt8`.
This function is overridden with a native implementation.
-/
@[extern "lean_uint8_of_nat"]
def UInt8.ofNatLT (n : @& Nat) (h : LT.lt n UInt8.size) : UInt8 where
toBitVec := BitVec.ofNatLt n h
toBitVec := BitVec.ofNatLT n h
set_option bootstrap.genMatcherCode false in
/--
@@ -1971,7 +1963,7 @@ def UInt8.decEq (a b : UInt8) : Decidable (Eq a b) :=
instance : DecidableEq UInt8 := UInt8.decEq
instance : Inhabited UInt8 where
default := UInt8.ofNatCore 0 (of_decide_eq_true rfl)
default := UInt8.ofNatLT 0 (of_decide_eq_true rfl)
/-- The size of type `UInt16`, that is, `2^16 = 65536`. -/
abbrev UInt16.size : Nat := 65536
@@ -1993,21 +1985,13 @@ structure UInt16 where
attribute [extern "lean_uint16_of_nat_mk"] UInt16.ofBitVec
attribute [extern "lean_uint16_to_nat"] UInt16.toBitVec
/--
Pack a `Nat` less than `2^16` into a `UInt16`.
This function is overridden with a native implementation.
-/
@[extern "lean_uint16_of_nat"]
def UInt16.ofNatCore (n : @& Nat) (h : LT.lt n UInt16.size) : UInt16 where
toBitVec := BitVec.ofNatLt n h
/--
Pack a `Nat` less than `2^16` into a `UInt16`.
This function is overridden with a native implementation.
-/
@[extern "lean_uint16_of_nat"]
def UInt16.ofNatLT (n : @& Nat) (h : LT.lt n UInt16.size) : UInt16 where
toBitVec := BitVec.ofNatLt n h
toBitVec := BitVec.ofNatLT n h
set_option bootstrap.genMatcherCode false in
/--
@@ -2025,7 +2009,7 @@ def UInt16.decEq (a b : UInt16) : Decidable (Eq a b) :=
instance : DecidableEq UInt16 := UInt16.decEq
instance : Inhabited UInt16 where
default := UInt16.ofNatCore 0 (of_decide_eq_true rfl)
default := UInt16.ofNatLT 0 (of_decide_eq_true rfl)
/-- The size of type `UInt32`, that is, `2^32 = 4294967296`. -/
abbrev UInt32.size : Nat := 4294967296
@@ -2047,21 +2031,13 @@ structure UInt32 where
attribute [extern "lean_uint32_of_nat_mk"] UInt32.ofBitVec
attribute [extern "lean_uint32_to_nat"] UInt32.toBitVec
/--
Pack a `Nat` less than `2^32` into a `UInt32`.
This function is overridden with a native implementation.
-/
@[extern "lean_uint32_of_nat"]
def UInt32.ofNatCore (n : @& Nat) (h : LT.lt n UInt32.size) : UInt32 where
toBitVec := BitVec.ofNatLt n h
/--
Pack a `Nat` less than `2^32` into a `UInt32`.
This function is overridden with a native implementation.
-/
@[extern "lean_uint32_of_nat"]
def UInt32.ofNatLT (n : @& Nat) (h : LT.lt n UInt32.size) : UInt32 where
toBitVec := BitVec.ofNatLt n h
toBitVec := BitVec.ofNatLT n h
/--
Unpack a `UInt32` as a `Nat`.
@@ -2084,7 +2060,7 @@ def UInt32.decEq (a b : UInt32) : Decidable (Eq a b) :=
instance : DecidableEq UInt32 := UInt32.decEq
instance : Inhabited UInt32 where
default := UInt32.ofNatCore 0 (of_decide_eq_true rfl)
default := UInt32.ofNatLT 0 (of_decide_eq_true rfl)
instance : LT UInt32 where
lt a b := LT.lt a.toBitVec b.toBitVec
@@ -2132,21 +2108,13 @@ structure UInt64 where
attribute [extern "lean_uint64_of_nat_mk"] UInt64.ofBitVec
attribute [extern "lean_uint64_to_nat"] UInt64.toBitVec
/--
Pack a `Nat` less than `2^64` into a `UInt64`.
This function is overridden with a native implementation.
-/
@[extern "lean_uint64_of_nat"]
def UInt64.ofNatCore (n : @& Nat) (h : LT.lt n UInt64.size) : UInt64 where
toBitVec := BitVec.ofNatLt n h
/--
Pack a `Nat` less than `2^64` into a `UInt64`.
This function is overridden with a native implementation.
-/
@[extern "lean_uint64_of_nat"]
def UInt64.ofNatLT (n : @& Nat) (h : LT.lt n UInt64.size) : UInt64 where
toBitVec := BitVec.ofNatLt n h
toBitVec := BitVec.ofNatLT n h
set_option bootstrap.genMatcherCode false in
/--
@@ -2164,7 +2132,7 @@ def UInt64.decEq (a b : UInt64) : Decidable (Eq a b) :=
instance : DecidableEq UInt64 := UInt64.decEq
instance : Inhabited UInt64 where
default := UInt64.ofNatCore 0 (of_decide_eq_true rfl)
default := UInt64.ofNatLT 0 (of_decide_eq_true rfl)
/-- The size of type `USize`, that is, `2^System.Platform.numBits`. -/
abbrev USize.size : Nat := (hPow 2 System.Platform.numBits)
@@ -2202,21 +2170,13 @@ structure USize where
attribute [extern "lean_usize_of_nat_mk"] USize.ofBitVec
attribute [extern "lean_usize_to_nat"] USize.toBitVec
/--
Pack a `Nat` less than `USize.size` into a `USize`.
This function is overridden with a native implementation.
-/
@[extern "lean_usize_of_nat"]
def USize.ofNatCore (n : @& Nat) (h : LT.lt n USize.size) : USize where
toBitVec := BitVec.ofNatLt n h
/--
Pack a `Nat` less than `USize.size` into a `USize`.
This function is overridden with a native implementation.
-/
@[extern "lean_usize_of_nat"]
def USize.ofNatLT (n : @& Nat) (h : LT.lt n USize.size) : USize where
toBitVec := BitVec.ofNatLt n h
toBitVec := BitVec.ofNatLT n h
set_option bootstrap.genMatcherCode false in
/--
@@ -2234,7 +2194,7 @@ def USize.decEq (a b : USize) : Decidable (Eq a b) :=
instance : DecidableEq USize := USize.decEq
instance : Inhabited USize where
default := USize.ofNatCore 0 usize_size_pos
default := USize.ofNatLT 0 usize_size_pos
/--
A `Nat` denotes a valid unicode codepoint if it is less than `0x110000`, and
@@ -2269,7 +2229,7 @@ This function is overridden with a native implementation.
-/
@[extern "lean_uint32_of_nat"]
def Char.ofNatAux (n : @& Nat) (h : n.isValidChar) : Char :=
{ val := BitVec.ofNatLt n (isValidChar_UInt32 h), valid := h }
{ val := BitVec.ofNatLT n (isValidChar_UInt32 h), valid := h }
/--
Convert a `Nat` into a `Char`. If the `Nat` does not encode a valid unicode scalar value,
@@ -2279,7 +2239,7 @@ Convert a `Nat` into a `Char`. If the `Nat` does not encode a valid unicode scal
def Char.ofNat (n : Nat) : Char :=
dite (n.isValidChar)
(fun h => Char.ofNatAux n h)
(fun _ => { val := BitVec.ofNatLt 0 (of_decide_eq_true rfl), valid := Or.inl (of_decide_eq_true rfl) })
(fun _ => { val := BitVec.ofNatLT 0 (of_decide_eq_true rfl), valid := Or.inl (of_decide_eq_true rfl) })
theorem Char.eq_of_val_eq : {c d : Char}, Eq c.val d.val Eq c d
| _, _, _, _, rfl => rfl
@@ -2302,9 +2262,9 @@ instance : DecidableEq Char :=
/-- Returns the number of bytes required to encode this `Char` in UTF-8. -/
def Char.utf8Size (c : Char) : Nat :=
let v := c.val
ite (LE.le v (UInt32.ofNatCore 0x7F (of_decide_eq_true rfl))) 1
(ite (LE.le v (UInt32.ofNatCore 0x7FF (of_decide_eq_true rfl))) 2
(ite (LE.le v (UInt32.ofNatCore 0xFFFF (of_decide_eq_true rfl))) 3 4))
ite (LE.le v (UInt32.ofNatLT 0x7F (of_decide_eq_true rfl))) 1
(ite (LE.le v (UInt32.ofNatLT 0x7FF (of_decide_eq_true rfl))) 2
(ite (LE.le v (UInt32.ofNatLT 0xFFFF (of_decide_eq_true rfl))) 3 4))
/--
`Option α` is the type of values which are either `some a` for some `a : α`,
@@ -2703,12 +2663,14 @@ def Array.size {α : Type u} (a : @& Array α) : Nat :=
a.toList.length
/--
Use the indexing notation `a[i]` instead.
Access an element from an array without needing a runtime bounds checks,
using a `Nat` index and a proof that it is in bounds.
This function does not use `get_elem_tactic` to automatically find the proof that
the index is in bounds. This is because the tactic itself needs to look up values in
arrays. Use the indexing notation `a[i]` instead.
arrays.
-/
@[extern "lean_array_fget"]
def Array.get {α : Type u} (a : @& Array α) (i : @& Nat) (h : LT.lt i a.size) : α :=
@@ -2718,7 +2680,11 @@ def Array.get {α : Type u} (a : @& Array α) (i : @& Nat) (h : LT.lt i a.size)
@[inline] abbrev Array.getD (a : Array α) (i : Nat) (v₀ : α) : α :=
dite (LT.lt i a.size) (fun h => a.get i h) (fun _ => v₀)
/-- Access an element from an array, or panic if the index is out of bounds. -/
/--
Use the indexing notation `a[i]!` instead.
Access an element from an array, or panic if the index is out of bounds.
-/
@[extern "lean_array_get"]
def Array.get! {α : Type u} [Inhabited α] (a : @& Array α) (i : @& Nat) : α :=
Array.getD a i default
@@ -3569,9 +3535,9 @@ with
/-- A hash function for names, which is stored inside the name itself as a
computed field. -/
@[computed_field] hash : Name UInt64
| .anonymous => .ofNatCore 1723 (of_decide_eq_true rfl)
| .anonymous => .ofNatLT 1723 (of_decide_eq_true rfl)
| .str p s => mixHash p.hash s.hash
| .num p v => mixHash p.hash (dite (LT.lt v UInt64.size) (fun h => UInt64.ofNatCore v h) (fun _ => UInt64.ofNatCore 17 (of_decide_eq_true rfl)))
| .num p v => mixHash p.hash (dite (LT.lt v UInt64.size) (fun h => UInt64.ofNatLT v h) (fun _ => UInt64.ofNatLT 17 (of_decide_eq_true rfl)))
instance : Inhabited Name where
default := Name.anonymous

View File

@@ -450,7 +450,7 @@ if h : p then
decidable_of_decidable_of_iff fun h2 _ => h2, fun al => al h
else isTrue fun h2 => absurd h2 h
theorem decide_eq_true_iff {p : Prop} [Decidable p] : (decide p = true) p := by simp
@[bool_to_prop] theorem decide_eq_true_iff {p : Prop} [Decidable p] : (decide p = true) p := by simp
@[simp, bool_to_prop] theorem decide_eq_decide {p q : Prop} {_ : Decidable p} {_ : Decidable q} :
decide p = decide q (p q) :=

View File

@@ -899,6 +899,46 @@ You can use `with` to provide the variables names for each constructor.
-/
syntax (name := cases) "cases " casesTarget,+ (" using " term)? (inductionAlts)? : tactic
/--
The `fun_induction` tactic is a convenience wrapper of the `induction` tactic when using a functional
induction principle.
The tactic invocation
```
fun_induction f x₁ ... xₙ y₁ ... yₘ
```
where `f` is a function defined by non-mutual structural or well-founded recursion, is equivalent to
```
induction y₁, ... yₘ using f.induct x₁ ... xₙ
```
where the arguments of `f` are used as arguments to `f.induct` or targets of the induction, as
appropriate.
The forms `fun_induction f x y generalizing z₁ ... zₙ` and
`fun_induction f x y with | case1 => tac₁ | case2 x' ih => tac₂` work like with `induction.`
-/
syntax (name := funInduction) "fun_induction " term
(" generalizing" (ppSpace colGt term:max)+)? (inductionAlts)? : tactic
/--
The `fun_cass` tactic is a convenience wrapper of the `cases` tactic when using a functional
cases principle.
The tactic invocation
```
fun_cases f x ... y ...`
```
is equivalent to
```
cases y, ... using f.fun_cases x ...
```
where the arguments of `f` are used as arguments to `f.fun_cases` or targets of the case analysis, as
appropriate.
The form `fun_cases f x y with | case1 => tac₁ | case2 x' ih => tac₂` works like with `cases`.
-/
syntax (name := funCases) "fun_cases " term (inductionAlts)? : tactic
/-- `rename_i x_1 ... x_n` renames the last `n` inaccessible names using the given names. -/
syntax (name := renameI) "rename_i" (ppSpace colGt binderIdent)+ : tactic
@@ -1589,6 +1629,13 @@ as well as tactics such as `next`, `case`, and `rename_i`.
-/
syntax (name := exposeNames) "expose_names" : tactic
/--
`#suggest_premises` will suggest premises for the current goal, using the currently registered premise selector.
The suggestions are printed in the order of their confidence, from highest to lowest.
-/
syntax (name := suggestPremises) "suggest_premises" : tactic
/--
Close fixed-width `BitVec` and `Bool` goals by obtaining a proof from an external SAT solver and
verifying it inside Lean. The solvable goals are currently limited to
@@ -1791,8 +1838,10 @@ users are encouraged to extend `get_elem_tactic_trivial` instead of this tactic.
macro "get_elem_tactic" : tactic =>
`(tactic| first
/-
Recall that `macro_rules` are tried in reverse order.
We want `assumption` to be tried first.
Recall that `macro_rules` (namely, for `get_elem_tactic_trivial`) are tried in reverse order.
We first, however, try `done`, since the necessary proof may already have been
found during unification, in which case there is no goal to solve (see #6999).
If a goal is present, we want `assumption` to be tried first.
This is important for theorems such as
```
[simp] theorem getElem_pop (a : Array α) (i : Nat) (hi : i < a.pop.size) :
@@ -1805,8 +1854,10 @@ macro "get_elem_tactic" : tactic =>
they add new `macro_rules` for `get_elem_tactic_trivial`.
TODO: Implement priorities for `macro_rules`.
TODO: Ensure we have a **high-priority** macro_rules for `get_elem_tactic_trivial` which is just `assumption`.
TODO: Ensure we have **high-priority** macro_rules for `get_elem_tactic_trivial` which are
just `done` and `assumption`.
-/
| done
| assumption
| get_elem_tactic_trivial
| fail "failed to prove index is valid, possible solutions:

View File

@@ -38,3 +38,4 @@ import Lean.LabelAttribute
import Lean.AddDecl
import Lean.Replay
import Lean.PrivateName
import Lean.PremiseSelection

View File

@@ -82,7 +82,7 @@ def addDecl (decl : Declaration) : CoreM Unit := do
async.commitCheckEnv ( getEnv)
let t BaseIO.mapTask (fun _ => checkAct) env.checked
let endRange? := ( getRef).getTailPos?.map fun pos => pos, pos
Core.logSnapshotTask { range? := endRange?, task := t }
Core.logSnapshotTask { stx? := none, reportingRange? := endRange?, task := t }
where doAdd := do
profileitM Exception "type checking" ( getOptions) do
withTraceNode `Kernel (fun _ => return m!"typechecking declarations {decl.getNames}") do

View File

@@ -20,7 +20,7 @@ partial def pushProjs (bs : Array FnBody) (alts : Array Alt) (altsF : Array Inde
let push (x : VarId) :=
if !ctxF.contains x.idx then
let alts := alts.mapIdx fun i alt => alt.modifyBody fun b' =>
if (altsF.get! i).contains x.idx then b.setBody b'
if altsF[i]!.contains x.idx then b.setBody b'
else b'
let altsF := altsF.map fun s => if s.contains x.idx then b.collectFreeIndices s else s
pushProjs bs alts altsF ctx ctxF

View File

@@ -18,7 +18,7 @@ def ensureHasDefault (alts : Array Alt) : Array Alt :=
alts.push (Alt.default last.body)
private def getOccsOf (alts : Array Alt) (i : Nat) : Nat := Id.run do
let aBody := (alts.get! i).body
let aBody := alts[i]!.body
let mut n := 1
for h : j in [i+1:alts.size] do
if alts[j].body == aBody then
@@ -52,8 +52,8 @@ private def mkSimpCase (tid : Name) (x : VarId) (xType : IRType) (alts : Array A
let alts := addDefault alts;
if alts.size == 0 then
FnBody.unreachable
else if alts.size == 1 then
(alts.get! 0).body
else if _ : alts.size = 1 then
alts[0].body
else
FnBody.case tid x xType alts

View File

@@ -537,7 +537,7 @@ partial def compileDecls (decls : List Name) (ref? : Option Declaration := none)
res.commitChecked ( getEnv)
let t BaseIO.mapTask (fun _ => checkAct) env.checked
let endRange? := ( getRef).getTailPos?.map fun pos => pos, pos
Core.logSnapshotTask { range? := endRange?, task := t }
Core.logSnapshotTask { stx? := none, reportingRange? := endRange?, task := t }
where doCompile := do
-- don't compile if kernel errored; should be converted into a task dependency when compilation
-- is made async as well

View File

@@ -109,13 +109,13 @@ private def fuzzyMatchCore (pattern word : String) (patternRoles wordRoles : Arr
let mut penaltyNs : Int := 0
let mut penaltySkip : Int := 0
for wordIdx in [:word.length] do
if (wordIdx != 0) && (wordRoles.get! wordIdx) matches .separator then
if (wordIdx != 0) && wordRoles[wordIdx]! matches .separator then
-- reset skip penalty at namespace separator
penaltySkip := 0
-- add constant penalty for each namespace to prefer shorter namespace nestings
penaltyNs := penaltyNs + 1
lastSepIdx := wordIdx
penaltySkip := penaltySkip + skipPenalty (wordRoles.get! wordIdx) (wordIdx == 0)
penaltySkip := penaltySkip + skipPenalty wordRoles[wordIdx]! (wordIdx == 0)
startPenalties := startPenalties.set! wordIdx $ penaltySkip + penaltyNs
-- TODO: the following code is assuming all characters are ASCII
@@ -124,8 +124,8 @@ private def fuzzyMatchCore (pattern word : String) (patternRoles wordRoles : Arr
`word.length - pattern.length` at each index (because at the very end, we can only consider fuzzy matches
of `pattern` with a longer substring of `word`). -/
for wordIdx in [patternIdx:word.length-(pattern.length - patternIdx - 1)] do
let missScore? :=
if wordIdx >= 1 then
let missScore? :=
if wordIdx >= 1 then
selectBest
(getMiss result patternIdx (wordIdx - 1))
(getMatch result patternIdx (wordIdx - 1))
@@ -133,29 +133,29 @@ private def fuzzyMatchCore (pattern word : String) (patternRoles wordRoles : Arr
let mut matchScore? := none
if allowMatch (pattern.get patternIdx) (word.get wordIdx) (patternRoles.get! patternIdx) (wordRoles.get! wordIdx) then
if patternIdx >= 1 then
let runLength := runLengths.get! (getIdx (patternIdx - 1) (wordIdx - 1)) + 1
if allowMatch (pattern.get patternIdx) (word.get wordIdx) patternRoles[patternIdx]! wordRoles[wordIdx]! then
if patternIdx >= 1 then
let runLength := runLengths[getIdx (patternIdx - 1) (wordIdx - 1)]! + 1
runLengths := runLengths.set! (getIdx patternIdx wordIdx) runLength
matchScore? := selectBest
(getMiss result (patternIdx - 1) (wordIdx - 1) |>.map (· + matchResult
patternIdx wordIdx
(patternRoles.get! patternIdx) (wordRoles.get! wordIdx)
patternRoles[patternIdx]! wordRoles[wordIdx]!
none
- startPenalties.get! wordIdx))
- startPenalties[wordIdx]!))
(getMatch result (patternIdx - 1) (wordIdx - 1) |>.map (· + matchResult
patternIdx wordIdx
(patternRoles.get! patternIdx) (wordRoles.get! wordIdx)
patternRoles[patternIdx]! wordRoles[wordIdx]!
(.some runLength)
)) |>.map fun score => if wordIdx >= lastSepIdx then score + 1 else score -- main identifier bonus
else
runLengths := runLengths.set! (getIdx patternIdx wordIdx) 1
matchScore? := .some $ matchResult
patternIdx wordIdx
(patternRoles.get! patternIdx) (wordRoles.get! wordIdx)
patternRoles[patternIdx]! wordRoles[wordIdx]!
none
- startPenalties.get! wordIdx
- startPenalties[wordIdx]!
result := set result patternIdx wordIdx missScore? matchScore?
@@ -167,10 +167,10 @@ private def fuzzyMatchCore (pattern word : String) (patternRoles wordRoles : Arr
getIdx (patternIdx wordIdx : Nat) := patternIdx * word.length + wordIdx
getMiss (result : Array (Option Int)) (patternIdx wordIdx : Nat) : Option Int :=
result.get! $ getDoubleIdx patternIdx wordIdx
result[getDoubleIdx patternIdx wordIdx]!
getMatch (result : Array (Option Int)) (patternIdx wordIdx : Nat) : Option Int :=
result.get! $ getDoubleIdx patternIdx wordIdx + 1
result[getDoubleIdx patternIdx wordIdx + 1]!
set (result : Array (Option Int)) (patternIdx wordIdx : Nat) (missValue matchValue : Option Int) : Array (Option Int) :=
let idx := getDoubleIdx patternIdx wordIdx
@@ -213,7 +213,7 @@ private def fuzzyMatchCore (pattern word : String) (patternRoles wordRoles : Arr
/- Consecutive character match. -/
if let some bonus := consecutive then
/- consecutive run bonus -/
score := score + bonus
score := score + bonus
return score
/-- Match the given pattern with the given word using a fuzzy matching

View File

@@ -275,7 +275,7 @@ def getObjVal? : Json → String → Except String Json
def getArrVal? : Json Nat Except String Json
| arr a, i =>
match a.get? i with
match a[i]? with
| some v => return v
| none => throw s!"index out of bounds: {i}"
| _ , _ => throw "array expected"

View File

@@ -66,7 +66,7 @@ partial def getAux [Inhabited α] : PersistentArrayNode α → USize → USize
def get! [Inhabited α] (t : PersistentArray α) (i : Nat) : α :=
if i >= t.tailOff then
t.tail.get! (i - t.tailOff)
t.tail[i - t.tailOff]!
else
getAux t.root (USize.ofNat i) t.shift
@@ -175,8 +175,8 @@ def pop (t : PersistentArray α) : PersistentArray α :=
let last := last.pop
let newSize := t.size - 1
let newTailOff := newSize - last.size
if newRoots.size == 1 && (newRoots.get! 0).isNode then
{ root := newRoots.get! 0,
if h : _ : newRoots.size = 1, newRoots[0].isNode then
{ root := newRoots[0]'(have := h.1; by omega),
shift := t.shift - initShift,
size := newSize,
tail := last,
@@ -199,7 +199,7 @@ variable {β : Type v}
@[specialize] private partial def foldlFromMAux (f : β α m β) : PersistentArrayNode α USize USize β m β
| node cs, i, shift, b => do
let j := (div2Shift i shift).toNat
let b foldlFromMAux f (cs.get! j) (mod2Shift i shift) (shift - initShift) b
let b foldlFromMAux f cs[j]! (mod2Shift i shift) (shift - initShift) b
cs.foldlM (init := b) (start := j+1) fun b c => foldlMAux f c b
| leaf vs, i, _, b => vs.foldlM (init := b) (start := i.toNat) f

View File

@@ -149,7 +149,7 @@ partial def findAtAux [BEq α] (keys : Array α) (vals : Array β) (heq : keys.s
partial def findAux [BEq α] : Node α β USize α Option β
| Node.entries entries, h, k =>
let j := (mod2Shift h shift).toNat
match entries.get! j with
match entries[j]! with
| Entry.null => none
| Entry.ref node => findAux node (div2Shift h shift) k
| Entry.entry k' v => if k == k' then some v else none
@@ -180,7 +180,7 @@ partial def findEntryAtAux [BEq α] (keys : Array α) (vals : Array β) (heq : k
partial def findEntryAux [BEq α] : Node α β USize α Option (α × β)
| Node.entries entries, h, k =>
let j := (mod2Shift h shift).toNat
match entries.get! j with
match entries[j]! with
| Entry.null => none
| Entry.ref node => findEntryAux node (div2Shift h shift) k
| Entry.entry k' v => if k == k' then some (k', v) else none
@@ -199,7 +199,7 @@ partial def containsAtAux [BEq α] (keys : Array α) (vals : Array β) (heq : ke
partial def containsAux [BEq α] : Node α β USize α Bool
| Node.entries entries, h, k =>
let j := (mod2Shift h shift).toNat
match entries.get! j with
match entries[j]! with
| Entry.null => false
| Entry.ref node => containsAux node (div2Shift h shift) k
| Entry.entry k' _ => k == k'
@@ -242,7 +242,7 @@ partial def eraseAux [BEq α] : Node α β → USize → α → Node α β
| none => n
| n@(Node.entries entries), h, k =>
let j := (mod2Shift h shift).toNat
let entry := entries.get! j
let entry := entries[j]!
match entry with
| Entry.null => n
| Entry.entry k' _ =>

View File

@@ -80,7 +80,7 @@ partial def toPosition (fmap : FileMap) (pos : String.Pos) : Position :=
if e == b + 1 then { line := fmap.getLine b, column := toColumn posB 0 }
else
let m := (b + e) / 2;
let posM := ps.get! m;
let posM := ps[m]!
if pos == posM then { line := fmap.getLine m, column := 0 }
else if pos > posM then loop m e
else loop b m

View File

@@ -119,7 +119,7 @@ partial def find? (t : Trie α) (s : String) : Option α :=
let c := s.getUtf8Byte i h
match cs.findIdx? (· == c) with
| none => none
| some idx => loop (i + 1) (ts.get! idx)
| some idx => loop (i + 1) ts[idx]!
else
val
loop 0 t
@@ -155,7 +155,7 @@ partial def findPrefix (t : Trie α) (pre : String) : Array α := go t 0
| node _val cs ts =>
match cs.findIdx? (· == c) with
| none => .empty
| some idx => go (ts.get! idx) (i + 1)
| some idx => go ts[idx]! (i + 1)
else
t.values
@@ -180,7 +180,7 @@ partial def matchPrefix (s : String) (t : Trie α) (i : String.Pos) : Option α
let c := s.getUtf8Byte i h
match cs.findIdx? (· == c) with
| none => res
| some idx => loop (ts.get! idx) (i + 1) res
| some idx => loop ts[idx]! (i + 1) res
else
res
loop t i.byteIdx none

View File

@@ -358,7 +358,7 @@ def runLintersAsync (stx : Syntax) : CommandElabM Unit := do
-- We only start one task for all linters for now as most linters are fast and we simply want
-- to unblock elaboration of the next command
let lintAct wrapAsyncAsSnapshot fun _ => runLinters stx
logSnapshotTask { range? := none, task := ( BaseIO.asTask lintAct) }
logSnapshotTask { stx? := none, task := ( BaseIO.asTask lintAct) }
protected def getCurrMacroScope : CommandElabM Nat := do pure ( read).currMacroScope
protected def getMainModule : CommandElabM Name := do pure ( getEnv).mainModule
@@ -496,7 +496,7 @@ partial def elabCommand (stx : Syntax) : CommandElabM Unit := do
newNextMacroScope := nextMacroScope
hasTraces
next := Array.zipWith (fun cmdPromise cmd =>
{ range? := cmd.getRange?, task := cmdPromise.resultD default }) cmdPromises cmds
{ stx? := some cmd, task := cmdPromise.resultD default }) cmdPromises cmds
: MacroExpandedSnapshot
}
-- After the first command whose syntax tree changed, we must disable

View File

@@ -215,14 +215,17 @@ private def elabHeaders (views : Array DefView)
return newHeader
if let some snap := view.headerSnap? then
let (tacStx?, newTacTask?) mkTacTask view.value tacPromise
let bodySnap :=
-- Only use first line of body as range when we have incremental tactics as otherwise we
-- would cover their progress
{ range? := if newTacTask?.isSome then
let bodySnap := {
stx? := view.value
reportingRange? :=
if newTacTask?.isSome then
-- Only use first line of body as range when we have incremental tactics as otherwise we
-- would cover their progress
view.ref.getPos?.map fun pos => pos, pos
else
getBodyTerm? view.value |>.getD view.value |>.getRange?
task := bodyPromise.resultD default }
task := bodyPromise.resultD default
}
snap.new.resolve <| some {
diagnostics :=
( Language.Snapshot.Diagnostics.ofMessageLog ( Core.getAndEmptyMessageLog))
@@ -263,7 +266,7 @@ where
:= do
if let some e := getBodyTerm? body then
if let `(by $tacs*) := e then
return (e, some { range? := mkNullNode tacs |>.getRange?, task := tacPromise.resultD default })
return (e, some { stx? := mkNullNode tacs, task := tacPromise.resultD default })
tacPromise.resolve default
return (none, none)
@@ -1093,7 +1096,7 @@ def elabMutualDef (ds : Array Syntax) : CommandElabM Unit := do
} }
defs := defs.push {
fullHeaderRef
headerProcessedSnap := { range? := d.getRange?, task := headerPromise.resultD default }
headerProcessedSnap := { stx? := d, task := headerPromise.resultD default }
}
reusedAllHeaders := reusedAllHeaders && view.headerSnap?.any (·.old?.isSome)
views := views.push view

View File

@@ -66,6 +66,16 @@ The number of indices in the array.
def Positions.numIndices (positions : Positions) : Nat :=
positions.foldl (fun s poss => s + poss.size) 0
/--
`positions.inverse[k] = i` means that function `i` has type k
-/
def Positions.inverse (positions : Positions) : Array Nat := Id.run do
let mut r := mkArray positions.numIndices 0
for _h : i in [:positions.size] do
for k in positions[i] do
r := r.set! k i
return r
/--
Groups the `xs` by their `f` value, and puts these groups into the order given by `ys`.
-/

View File

@@ -333,7 +333,7 @@ private def getFieldType (infos : Array StructFieldInfo) (parentType : Expr) (fi
let Name.str _ subFieldName .. := subProjName
| throwError "invalid projection name {subProjName}"
let args := e.getAppArgs
if let some major := args.get? numParams then
if let some major := args[numParams]? then
if ( getNestedProjectionArg major) == parent then
if let some existingFieldInfo := findFieldInfo? infos (.mkSimple subFieldName) then
return TransformStep.done <| mkAppN existingFieldInfo.fvar args[numParams+1:args.size]

View File

@@ -39,7 +39,7 @@ def reconstructCounterExample (var2Cnf : Std.HashMap BVBit Nat) (assignment : Ar
Array (Expr × BVExpr.PackedBitVec) := Id.run do
let mut sparseMap : Std.HashMap Nat (RBMap Nat Bool Ord.compare) := {}
let filter bvBit _ :=
let (_, _, synthetic) := atomsAssignment.get! bvBit.var
let (_, _, synthetic) := atomsAssignment[bvBit.var]!
!synthetic
let var2Cnf := var2Cnf.filter filter
for (bitVar, cnfVar) in var2Cnf.toArray do
@@ -74,7 +74,7 @@ def reconstructCounterExample (var2Cnf : Std.HashMap BVBit Nat) (assignment : Ar
if bitValue then
value := value ||| (1 <<< currentBit)
currentBit := currentBit + 1
let (_, atomExpr, _) := atomsAssignment.get! bitVecVar
let (_, atomExpr, _) := atomsAssignment[bitVecVar]!
finalMap := finalMap.push (atomExpr, BitVec.ofNat currentBit value)
return finalMap

View File

@@ -230,18 +230,18 @@ where
stx := stx'
diagnostics := .empty
inner? := none
finished := .pure {
finished := .finished stx' {
diagnostics := .empty
state? := ( Tactic.saveState)
}
next := #[{ range? := stx'.getRange?, task := promise.resultD default }]
next := #[{ stx? := stx', task := promise.resultD default }]
}
-- Update `tacSnap?` to old unfolding
withTheReader Term.Context ({ · with tacSnap? := some {
new := promise
old? := do
let old old?
return old.stx, ( old.next.get? 0)
return old.stx, ( old.next[0]?)
} }) do
evalTactic stx'
return
@@ -269,6 +269,10 @@ def done : TacticM Unit := do
Term.reportUnsolvedGoals gs
throwAbortTactic
/--
Runs `x` with only the first unsolved goal as the goal.
Fails if there are no goal to be solved.
-/
def focus (x : TacticM α) : TacticM α := do
let mvarId :: mvarIds getUnsolvedGoals | throwNoGoalsToBeSolved
setGoals [mvarId]
@@ -277,6 +281,10 @@ def focus (x : TacticM α) : TacticM α := do
setGoals (mvarIds' ++ mvarIds)
pure a
/--
Runs `tactic` with only the first unsolved goal as the goal, and expects it leave no goals.
Fails if there are no goal to be solved.
-/
def focusAndDone (tactic : TacticM α) : TacticM α :=
focus do
let a tactic

View File

@@ -73,19 +73,8 @@ where
if let some state := oldParsed.finished.get.state? then
reusableResult? := some ((), state)
-- only allow `next` reuse in this case
oldNext? := oldParsed.next.get? 0 |>.map (old.stx, ·)
oldNext? := oldParsed.next[0]?.map (old.stx, ·)
-- For `tac`'s snapshot task range, disregard synthetic info as otherwise
-- `SnapshotTree.findInfoTreeAtPos` might choose the wrong snapshot: for example, when
-- hovering over a `show` tactic, we should choose the info tree in `finished` over that in
-- `inner`, which points to execution of the synthesized `refine` step and does not contain
-- the full info. In most other places, siblings in the snapshot tree have disjoint ranges and
-- so this issue does not occur.
let mut range? := tac.getRange? (canonicalOnly := true)
-- Include trailing whitespace in the range so that `goalsAs?` does not have to wait for more
-- snapshots than necessary.
if let some range := range? then
range? := some { range with stop := range.stop.byteIdx + tac.getTrailingSize }
let next IO.Promise.new
let finished IO.Promise.new
let inner IO.Promise.new
@@ -93,9 +82,9 @@ where
desc := tac.getKind.toString
diagnostics := .empty
stx := tac
inner? := some { range?, task := inner.resultD default }
finished := { range?, task := finished.resultD default }
next := #[{ range? := stxs.getRange?, task := next.resultD default }]
inner? := some { stx? := tac, task := inner.resultD default }
finished := { stx? := tac, task := finished.resultD default }
next := #[{ stx? := stxs, task := next.resultD default }]
}
-- Run `tac` in a fresh info tree state and store resulting state in snapshot for
-- incremental reporting, then add back saved trees. Here we rely on `evalTactic`

View File

@@ -10,6 +10,7 @@ import Lean.Parser.Term
import Lean.Meta.RecursorInfo
import Lean.Meta.CollectMVars
import Lean.Meta.Tactic.ElimInfo
import Lean.Meta.Tactic.FunIndInfo
import Lean.Meta.Tactic.Induction
import Lean.Meta.Tactic.Cases
import Lean.Meta.GeneralizeVars
@@ -285,9 +286,9 @@ where
stx := mkNullNode altStxs
diagnostics := .empty
inner? := none
finished := { range? := none, task := finished.resultD default }
finished := { stx? := mkNullNode altStxs, reportingRange? := none, task := finished.resultD default }
next := Array.zipWith
(fun stx prom => { range? := stx.getRange?, task := prom.resultD default })
(fun stx prom => { stx? := some stx, task := prom.resultD default })
altStxs altPromises
}
goWithIncremental <| altPromises.mapIdx fun i prom => {
@@ -547,31 +548,32 @@ private def expandInductionAlts? (inductionAlts : Syntax) : Option Syntax := Id.
else
none
private def inductionAltsPos (stx : Syntax) : Nat :=
if stx.getKind == ``Lean.Parser.Tactic.induction then
4
else if stx.getKind == ``Lean.Parser.Tactic.cases then
3
else if stx.getKind == ``Lean.Parser.Tactic.funInduction then
3
else if stx.getKind == ``Lean.Parser.Tactic.funCases then
2
else
panic! "inductionAltsSyntaxPos: Unexpected syntax kind {stx.getKind}"
/--
Expand
```
syntax "induction " term,+ (" using " ident)? ("generalizing " (colGt term:max)+)? (inductionAlts)? : tactic
```
if `inductionAlts` has an alternative with multiple LHSs.
if `inductionAlts` has an alternative with multiple LHSs, and likewise for
`cases`, `fun_induction`, `fun_cases`.
-/
private def expandInduction? (induction : Syntax) : Option Syntax := do
let optInductionAlts := induction[4]
let inductionAltsPos := inductionAltsPos induction
let optInductionAlts := induction[inductionAltsPos]
guard <| !optInductionAlts.isNone
let inductionAlts' expandInductionAlts? optInductionAlts[0]
return induction.setArg 4 (mkNullNode #[inductionAlts'])
/--
Expand
```
syntax "cases " casesTarget,+ (" using " ident)? (inductionAlts)? : tactic
```
if `inductionAlts` has an alternative with multiple LHSs.
-/
private def expandCases? (induction : Syntax) : Option Syntax := do
let optInductionAlts := induction[3]
guard <| !optInductionAlts.isNone
let inductionAlts' expandInductionAlts? optInductionAlts[0]
return induction.setArg 3 (mkNullNode #[inductionAlts'])
return induction.setArg inductionAltsPos (mkNullNode #[inductionAlts'])
/--
We may have at most one `| _ => ...` (wildcard alternative), and it must not set variable names.
@@ -683,6 +685,43 @@ private def generalizeTargets (exprs : Array Expr) : TacticM (Array Expr) := do
else
return exprs
def checkInductionTargets (targets : Array Expr) : MetaM Unit := do
let mut foundFVars : FVarIdSet := {}
for target in targets do
unless target.isFVar do
throwError "index in target's type is not a variable (consider using the `cases` tactic instead){indentExpr target}"
if foundFVars.contains target.fvarId! then
throwError "target (or one of its indices) occurs more than once{indentExpr target}"
foundFVars := foundFVars.insert target.fvarId!
/--
The code path shared between `induction` and `fun_induct`; when we already have an `elimInfo`
and the `targets` contains the implicit targets
-/
private def evalInductionCore (stx : Syntax) (elimInfo : ElimInfo) (targets : Array Expr) : TacticM Unit := do
let mvarId getMainGoal
-- save initial info before main goal is reassigned
let initInfo mkTacticInfo ( getMCtx) ( getUnsolvedGoals) ( getRef)
let tag mvarId.getTag
mvarId.withContext do
checkInductionTargets targets
let targetFVarIds := targets.map (·.fvarId!)
let (n, mvarId) generalizeVars mvarId stx targets
mvarId.withContext do
let result withRef stx[1] do -- use target position as reference
ElimApp.mkElimApp elimInfo targets tag
trace[Elab.induction] "elimApp: {result.elimApp}"
ElimApp.setMotiveArg mvarId result.motive targetFVarIds
-- drill down into old and new syntax: allow reuse of an rhs only if everything before it is
-- unchanged
-- everything up to the alternatives must be unchanged for reuse
Term.withNarrowedArgTacticReuse (stx := stx) (argIdx := inductionAltsPos stx) fun optInductionAlts => do
withAltsOfOptInductionAlts optInductionAlts fun alts? => do
let optPreTac := getOptPreTacOfOptInductionAlts optInductionAlts
mvarId.assign result.elimApp
ElimApp.evalAlts elimInfo result.alts optPreTac alts? initInfo (numGeneralized := n) (toClear := targetFVarIds)
appendGoals result.others.toList
@[builtin_tactic Lean.Parser.Tactic.induction, builtin_incremental]
def evalInduction : Tactic := fun stx =>
match expandInduction? stx with
@@ -691,38 +730,57 @@ def evalInduction : Tactic := fun stx =>
let targets withMainContext <| stx[1].getSepArgs.mapM (elabTerm · none)
let targets generalizeTargets targets
let elimInfo withMainContext <| getElimNameInfo stx[2] targets (induction := true)
let mvarId getMainGoal
-- save initial info before main goal is reassigned
let initInfo mkTacticInfo ( getMCtx) ( getUnsolvedGoals) ( getRef)
let tag mvarId.getTag
mvarId.withContext do
let targets addImplicitTargets elimInfo targets
checkTargets targets
let targetFVarIds := targets.map (·.fvarId!)
let (n, mvarId) generalizeVars mvarId stx targets
mvarId.withContext do
let result withRef stx[1] do -- use target position as reference
ElimApp.mkElimApp elimInfo targets tag
trace[Elab.induction] "elimApp: {result.elimApp}"
ElimApp.setMotiveArg mvarId result.motive targetFVarIds
-- drill down into old and new syntax: allow reuse of an rhs only if everything before it is
-- unchanged
-- everything up to the alternatives must be unchanged for reuse
Term.withNarrowedArgTacticReuse (stx := stx) (argIdx := 4) fun optInductionAlts => do
withAltsOfOptInductionAlts optInductionAlts fun alts? => do
let optPreTac := getOptPreTacOfOptInductionAlts optInductionAlts
mvarId.assign result.elimApp
ElimApp.evalAlts elimInfo result.alts optPreTac alts? initInfo (numGeneralized := n) (toClear := targetFVarIds)
appendGoals result.others.toList
where
checkTargets (targets : Array Expr) : MetaM Unit := do
let mut foundFVars : FVarIdSet := {}
for target in targets do
unless target.isFVar do
throwError "index in target's type is not a variable (consider using the `cases` tactic instead){indentExpr target}"
if foundFVars.contains target.fvarId! then
throwError "target (or one of its indices) occurs more than once{indentExpr target}"
foundFVars := foundFVars.insert target.fvarId!
let targets withMainContext <| addImplicitTargets elimInfo targets
evalInductionCore stx elimInfo targets
/--
Elaborates the `foo args` of `fun_induction` or `fun_cases`, returning the `ElabInfo` and targets.
-/
private def elabFunTarget (cases : Bool) (stx : Syntax) : TacticM (ElimInfo × Array Expr) := do
withRef stx <| withMainContext do
let funCall elabTerm stx none
funCall.withApp fun fn funArgs => do
let .const fnName fnUs := fn |
throwError "expected application headed by a function constant"
let some funIndInfo getFunIndInfo? cases fnName |
let theoremKind := if cases then "induction" else "cases"
throwError "no functional {theoremKind} theorem for '{.ofConstName fnName}', or function is mutually recursive "
if funArgs.size != funIndInfo.params.size then
throwError "Expected fully applied application of '{.ofConstName fnName}' with \
{funIndInfo.params.size} arguments, but found {funArgs.size} arguments"
let mut params := #[]
let mut targets := #[]
let mut us := #[]
for u in fnUs, b in funIndInfo.levelMask do
if b then
us := us.push u
for a in funArgs, kind in funIndInfo.params do
match kind with
| .dropped => pure ()
| .param => params := params.push a
| .target => targets := targets.push a
if cases then
trace[Elab.cases] "us: {us}\nparams: {params}\ntargets: {targets}"
else
trace[Elab.induction] "us: {us}\nparams: {params}\ntargets: {targets}"
let elimExpr := mkAppN (.const funIndInfo.funIndName us.toList) params
let elimInfo getElimExprInfo elimExpr
unless targets.size = elimInfo.targetsPos.size do
let tacName := if cases then "fun_cases" else "fun_induction"
throwError "{tacName} got confused trying to use \
{.ofConstName funIndInfo.funIndName}. Does it take {targets.size} or \
{elimInfo.targetsPos.size} targets?"
return (elimInfo, targets)
@[builtin_tactic Lean.Parser.Tactic.funInduction, builtin_incremental]
def evalFunInduction : Tactic := fun stx =>
match expandInduction? stx with
| some stxNew => withMacroExpansion stx stxNew <| evalTactic stxNew
| _ => focus do
let (elimInfo, targets) elabFunTarget (cases := false) stx[1]
let targets generalizeTargets targets
evalInductionCore stx elimInfo targets
def elabCasesTargets (targets : Array Syntax) : TacticM (Array Expr × Array (Ident × FVarId)) :=
withMainContext do
@@ -736,7 +794,7 @@ def elabCasesTargets (targets : Array Syntax) : TacticM (Array Expr × Array (Id
pure (some target[0][0].getId)
let expr elabTerm target[1] none
args := args.push { expr, hName? : GeneralizeArg }
if ( withMainContext <| args.anyM fun arg => shouldGeneralizeTarget arg.expr <||> pure arg.hName?.isSome) then
if ( args.anyM fun arg => shouldGeneralizeTarget arg.expr <||> pure arg.hName?.isSome) then
liftMetaTacticAux fun mvarId => do
let argsToGeneralize args.filterM fun arg => shouldGeneralizeTarget arg.expr <||> pure arg.hName?.isSome
let (fvarIdsNew, mvarId) mvarId.generalize argsToGeneralize
@@ -755,38 +813,55 @@ def elabCasesTargets (targets : Array Syntax) : TacticM (Array Expr × Array (Id
else
return (args.map (·.expr), #[])
/--
The code path shared between `cases` and `fun_cases`; when we already have an `elimInfo`
and the `targets` contains the implicit targets
-/
def evalCasesCore (stx : Syntax) (elimInfo : ElimInfo) (targets : Array Expr)
(toTag : Array (Ident × FVarId) := #[]) : TacticM Unit := do
let targetRef := stx[1]
let mvarId getMainGoal
-- save initial info before main goal is reassigned
let initInfo mkTacticInfo ( getMCtx) ( getUnsolvedGoals) ( getRef)
let tag mvarId.getTag
mvarId.withContext do
let result withRef targetRef <| ElimApp.mkElimApp elimInfo targets tag
let elimArgs := result.elimApp.getAppArgs
let targets elimInfo.targetsPos.mapM fun i => instantiateMVars elimArgs[i]!
let motiveType inferType elimArgs[elimInfo.motivePos]!
let mvarId generalizeTargetsEq mvarId motiveType targets
let (targetsNew, mvarId) mvarId.introN targets.size
mvarId.withContext do
ElimApp.setMotiveArg mvarId elimArgs[elimInfo.motivePos]!.mvarId! targetsNew
mvarId.assign result.elimApp
-- drill down into old and new syntax: allow reuse of an rhs only if everything before it is
-- unchanged
-- everything up to the alternatives must be unchanged for reuse
Term.withNarrowedArgTacticReuse (stx := stx) (argIdx := inductionAltsPos stx) fun optInductionAlts => do
withAltsOfOptInductionAlts optInductionAlts fun alts => do
let optPreTac := getOptPreTacOfOptInductionAlts optInductionAlts
ElimApp.evalAlts elimInfo result.alts optPreTac alts initInfo
(numEqs := targets.size) (toClear := targetsNew) (toTag := toTag)
@[builtin_tactic Lean.Parser.Tactic.cases, builtin_incremental]
def evalCases : Tactic := fun stx =>
match expandCases? stx with
match expandInduction? stx with
| some stxNew => withMacroExpansion stx stxNew <| evalTactic stxNew
| _ => focus do
-- leading_parser nonReservedSymbol "cases " >> sepBy1 (group majorPremise) ", " >> usingRec >> optInductionAlts
let (targets, toTag) elabCasesTargets stx[1].getSepArgs
let targetRef := stx[1]
let elimInfo withMainContext <| getElimNameInfo stx[2] targets (induction := false)
let mvarId getMainGoal
-- save initial info before main goal is reassigned
let initInfo mkTacticInfo ( getMCtx) ( getUnsolvedGoals) ( getRef)
let tag mvarId.getTag
mvarId.withContext do
let targets addImplicitTargets elimInfo targets
let result withRef targetRef <| ElimApp.mkElimApp elimInfo targets tag
let elimArgs := result.elimApp.getAppArgs
let targets elimInfo.targetsPos.mapM fun i => instantiateMVars elimArgs[i]!
let motiveType inferType elimArgs[elimInfo.motivePos]!
let mvarId generalizeTargetsEq mvarId motiveType targets
let (targetsNew, mvarId) mvarId.introN targets.size
mvarId.withContext do
ElimApp.setMotiveArg mvarId elimArgs[elimInfo.motivePos]!.mvarId! targetsNew
mvarId.assign result.elimApp
-- drill down into old and new syntax: allow reuse of an rhs only if everything before it is
-- unchanged
-- everything up to the alternatives must be unchanged for reuse
Term.withNarrowedArgTacticReuse (stx := stx) (argIdx := 3) fun optInductionAlts => do
withAltsOfOptInductionAlts optInductionAlts fun alts => do
let optPreTac := getOptPreTacOfOptInductionAlts optInductionAlts
ElimApp.evalAlts elimInfo result.alts optPreTac alts initInfo
(numEqs := targets.size) (toClear := targetsNew) (toTag := toTag)
let targets withMainContext <| addImplicitTargets elimInfo targets
evalCasesCore stx elimInfo targets toTag
@[builtin_tactic Lean.Parser.Tactic.funCases, builtin_incremental]
def evalFunCases : Tactic := fun stx =>
match expandInduction? stx with
| some stxNew => withMacroExpansion stx stxNew <| evalTactic stxNew
| _ => focus do
let (elimInfo, targets) elabFunTarget (cases := true) stx[1]
let targets generalizeTargets targets
evalCasesCore stx elimInfo targets
builtin_initialize
registerTraceClass `Elab.cases

View File

@@ -77,6 +77,13 @@ abbrev ModuleIdx.toNat (midx : ModuleIdx) : Nat := midx
instance : Inhabited ModuleIdx where default := (0 : Nat)
instance : GetElem (Array α) ModuleIdx α (fun a i => i.toNat < a.size) where
getElem a i h := a[i.toNat]
instance : GetElem? (Array α) ModuleIdx α (fun a i => i.toNat < a.size) where
getElem? a i := a[i.toNat]?
getElem! a i := a[i.toNat]!
abbrev ConstMap := SMap Name ConstantInfo
structure Import where
@@ -1102,7 +1109,7 @@ namespace PersistentEnvExtension
def getModuleEntries {α β σ : Type} [Inhabited σ] (ext : PersistentEnvExtension α β σ) (env : Environment) (m : ModuleIdx) : Array α :=
-- `importedEntries` is identical on all environment branches, so `local` is always sufficient
(ext.toEnvExtension.getState (asyncMode := .local) env).importedEntries.get! m
(ext.toEnvExtension.getState (asyncMode := .local) env).importedEntries[m]!
def addEntry {α β σ : Type} (ext : PersistentEnvExtension α β σ) (env : Environment) (b : β) : Environment :=
ext.toEnvExtension.modifyState env fun s =>

View File

@@ -751,7 +751,7 @@ def mkAppN (f : Expr) (args : Array Expr) : Expr :=
args.foldl mkApp f
private partial def mkAppRangeAux (n : Nat) (args : Array Expr) (i : Nat) (e : Expr) : Expr :=
if i < n then mkAppRangeAux n args (i+1) (mkApp e (args.get! i)) else e
if i < n then mkAppRangeAux n args (i+1) (mkApp e args[i]!) else e
/-- `mkAppRange f i j #[a_1, ..., a_i, ..., a_j, ... ]` ==> the expression `f a_i ... a_{j-1}` -/
def mkAppRange (f : Expr) (i j : Nat) (args : Array Expr) : Expr :=
@@ -1467,7 +1467,7 @@ private partial def mkAppRevRangeAux (revArgs : Array Expr) (start : Nat) (b : E
if i == start then b
else
let i := i - 1
mkAppRevRangeAux revArgs start (mkApp b (revArgs.get! i)) i
mkAppRevRangeAux revArgs start (mkApp b revArgs[i]!) i
/-- `mkAppRevRange f b e args == mkAppRev f (revArgs.extract b e)` -/
def mkAppRevRange (f : Expr) (beginIdx endIdx : Nat) (revArgs : Array Expr) : Expr :=
@@ -2245,20 +2245,28 @@ def mkIntMul (a b : Expr) : Expr :=
private def intLEPred : Expr :=
mkApp2 (mkConst ``LE.le [0]) Int.mkType Int.mkInstLE
/-- Given `a b : Int`, return `a ≤ b` -/
/-- Given `a b : Int`, returns `a ≤ b` -/
def mkIntLE (a b : Expr) : Expr :=
mkApp2 intLEPred a b
private def intEqPred : Expr :=
mkApp (mkConst ``Eq [1]) Int.mkType
/-- Given `a b : Int`, return `a = b` -/
/-- Given `a b : Int`, returns `a = b` -/
def mkIntEq (a b : Expr) : Expr :=
mkApp2 intEqPred a b
def mkIntLit (n : Nat) : Expr :=
let r := mkRawNatLit n
mkApp3 (mkConst ``OfNat.ofNat [levelZero]) Int.mkType r (mkApp (mkConst ``instOfNat) r)
/-- Given `a b : Int`, returns `a b` -/
def mkIntDvd (a b : Expr) : Expr :=
mkApp4 (mkConst ``Dvd.dvd [0]) Int.mkType (mkConst ``Int.instDvd) a b
def mkIntLit (n : Int) : Expr :=
let r := mkRawNatLit n.natAbs
let r := mkApp3 (mkConst ``OfNat.ofNat [levelZero]) Int.mkType r (mkApp (mkConst ``instOfNat) r)
if n < 0 then
mkIntNeg r
else
r
def reflBoolTrue : Expr :=
mkApp2 (mkConst ``Eq.refl [levelOne]) (mkConst ``Bool) (mkConst ``Bool.true)

View File

@@ -66,31 +66,48 @@ structure Snapshot where
isFatal := false
deriving Inhabited
/--
Yields the default reporting range of a `Syntax`, which is just the `canonicalOnly` range
of the syntax.
-/
def SnapshotTask.defaultReportingRange? (stx? : Option Syntax) : Option String.Range :=
stx?.bind (·.getRange? (canonicalOnly := true))
/-- A task producing some snapshot type (usually a subclass of `Snapshot`). -/
-- Longer-term TODO: Give the server more control over the priority of tasks, depending on e.g. the
-- cursor position. This may require starting the tasks suspended (e.g. in `Thunk`). The server may
-- also need more dependency information for this in order to avoid priority inversion.
structure SnapshotTask (α : Type) where
/--
`Syntax` processed by this `SnapshotTask`.
The `Syntax` is used by the language server to determine whether to force this `SnapshotTask`
when a request is made.
-/
stx? : Option Syntax
/--
Range that is marked as being processed by the server while the task is running. If `none`,
the range of the outer task if some or else the entire file is reported.
-/
range? : Option String.Range
reportingRange? : Option String.Range := SnapshotTask.defaultReportingRange? stx?
/-- Underlying task producing the snapshot. -/
task : Task α
deriving Nonempty, Inhabited
/-- Creates a snapshot task from a reporting range and a `BaseIO` action. -/
def SnapshotTask.ofIO (range? : Option String.Range) (act : BaseIO α) : BaseIO (SnapshotTask α) := do
/-- Creates a snapshot task from the syntax processed by the task and a `BaseIO` action. -/
def SnapshotTask.ofIO (stx? : Option Syntax)
(reportingRange? : Option String.Range := defaultReportingRange? stx?) (act : BaseIO α) :
BaseIO (SnapshotTask α) := do
return {
range?
stx?
reportingRange?
task := ( BaseIO.asTask act)
}
/-- Creates a finished snapshot task. -/
def SnapshotTask.pure (a : α) : SnapshotTask α where
def SnapshotTask.finished (stx? : Option Syntax) (a : α) : SnapshotTask α where
stx?
-- irrelevant when already finished
range? := none
reportingRange? := none
task := .pure a
/--
@@ -99,25 +116,30 @@ def SnapshotTask.pure (a : α) : SnapshotTask α where
def SnapshotTask.cancel (t : SnapshotTask α) : BaseIO Unit :=
IO.cancel t.task
/-- Transforms a task's output without changing the reporting range. -/
def SnapshotTask.map (t : SnapshotTask α) (f : α β) (range? : Option String.Range := t.range?)
(sync := false) : SnapshotTask β :=
{ range?, task := t.task.map (sync := sync) f }
/-- Transforms a task's output without changing the processed syntax. -/
def SnapshotTask.map (t : SnapshotTask α) (f : α β) (stx? : Option Syntax := t.stx?)
(reportingRange? : Option String.Range := t.reportingRange?) (sync := false) : SnapshotTask β :=
{ stx?, reportingRange?, task := t.task.map (sync := sync) f }
/--
Chains two snapshot tasks. The range is taken from the first task if not specified; the range of
the second task is discarded. -/
Chains two snapshot tasks. The processed syntax and the reporting range are taken from the first
task if not specified; the processed syntax and the reporting range of the second task are
discarded. -/
def SnapshotTask.bind (t : SnapshotTask α) (act : α SnapshotTask β)
(range? : Option String.Range := t.range?) (sync := false) : SnapshotTask β :=
{ range?, task := t.task.bind (sync := sync) (act · |>.task) }
(stx? : Option Syntax := t.stx?) (reportingRange? : Option String.Range := t.reportingRange?)
(sync := false) : SnapshotTask β :=
{ stx?, reportingRange?, task := t.task.bind (sync := sync) (act · |>.task) }
/--
Chains two snapshot tasks. The range is taken from the first task if not specified; the range of
the second task is discarded. -/
Chains two snapshot tasks. The processed syntax and the reporting range are taken from the first
task if not specified; the processed syntax and the reporting range of the second task are
discarded. -/
def SnapshotTask.bindIO (t : SnapshotTask α) (act : α BaseIO (SnapshotTask β))
(range? : Option String.Range := t.range?) (sync := false) : BaseIO (SnapshotTask β) :=
(stx? : Option Syntax := t.stx?) (reportingRange? : Option String.Range := t.reportingRange?)
(sync := false) : BaseIO (SnapshotTask β) :=
return {
range?
stx?
reportingRange?
task := ( BaseIO.bindTask (sync := sync) t.task fun a => (·.task) <$> (act a))
}

View File

@@ -347,21 +347,22 @@ where
cancelTk? := ctx.newCancelTk
result? := some {
parserState := newParserState
processedSnap := ( oldSuccess.processedSnap.bindIO (range? := progressRange?)
(sync := true) fun oldProcessed => do
processedSnap := ( oldSuccess.processedSnap.bindIO (stx? := newStx)
(reportingRange? := progressRange?) (sync := true) fun oldProcessed => do
if let some oldProcSuccess := oldProcessed.result? then
-- also wait on old command parse snapshot as parsing is cheap and may allow for
-- elaboration reuse
oldProcSuccess.firstCmdSnap.bindIO (sync := true) (range? := progressRange?) fun oldCmd => do
oldProcSuccess.firstCmdSnap.bindIO (sync := true) (stx? := newStx)
(reportingRange? := progressRange?) fun oldCmd => do
let prom IO.Promise.new
parseCmd oldCmd newParserState oldProcSuccess.cmdState prom (sync := true) ctx
return .pure {
return .finished newStx {
diagnostics := oldProcessed.diagnostics
result? := some {
cmdState := oldProcSuccess.cmdState
firstCmdSnap := { range? := none, task := prom.result! } } }
firstCmdSnap := { stx? := none, task := prom.result! } } }
else
return .pure oldProcessed) } }
return .finished newStx oldProcessed) } }
else return old
-- fast path: if we have parsed the header successfully...
@@ -416,7 +417,7 @@ where
processHeader (stx : Syntax) (parserState : Parser.ModuleParserState) :
LeanProcessingM (SnapshotTask HeaderProcessedSnapshot) := do
let ctx read
SnapshotTask.ofIO (some 0, ctx.input.endPos) <|
SnapshotTask.ofIO stx (some 0, ctx.input.endPos) <|
ReaderT.run (r := ctx) <| -- re-enter reader in new task
withHeaderExceptions (α := HeaderProcessedSnapshot) ({ · with result? := none }) do
let setup match ( setupImports stx) with
@@ -472,7 +473,7 @@ where
infoTree? := cmdState.infoState.trees[0]!
result? := some {
cmdState
firstCmdSnap := { range? := none, task := prom.result! }
firstCmdSnap := { stx? := none, task := prom.result! }
}
}
@@ -491,13 +492,13 @@ where
let progressRange? := some newParserState.pos, ctx.input.endPos
let newProm IO.Promise.new
-- can reuse range, syntax unchanged
let _ old.finishedSnap.bindIO (sync := true) (range? := progressRange?) fun oldFinished =>
let _ old.finishedSnap.bindIO (sync := true) (reportingRange? := progressRange?) fun oldFinished =>
-- also wait on old command parse snapshot as parsing is cheap and may allow for
-- elaboration reuse
oldNext.bindIO (sync := true) (range? := progressRange?) fun oldNext => do
oldNext.bindIO (sync := true) (reportingRange? := progressRange?) fun oldNext => do
parseCmd oldNext newParserState oldFinished.cmdState newProm sync ctx
return .pure ()
prom.resolve <| { old with nextCmdSnap? := some { range? := none, task := newProm.result! } }
return .finished none ()
prom.resolve <| { old with nextCmdSnap? := some { stx? := none, task := newProm.result! } }
else prom.resolve old -- terminal command, we're done!
-- fast path, do not even start new task for this snapshot (see [Incremental Parsing])
@@ -540,7 +541,7 @@ where
prom.resolve <| {
diagnostics := .empty, stx := .missing, parserState
elabSnap := default
finishedSnap := .pure { diagnostics := .empty, cmdState }
finishedSnap := .finished none { diagnostics := .empty, cmdState }
reportSnap := default
nextCmdSnap? := none
}
@@ -552,27 +553,34 @@ where
let elabPromise IO.Promise.new
let finishedPromise IO.Promise.new
let reportPromise IO.Promise.new
-- report terminal tasks on first line of decl such as not to hide incremental tactics'
-- progress
let initRange? := getNiceCommandStartPos? stx |>.map fun pos => pos, pos
let finishedSnap := { range? := initRange?, task := finishedPromise.result! }
let minimalSnapshots := internal.cmdlineSnapshots.get cmdState.scopes.head!.opts
let next? if Parser.isTerminalCommand stx then pure none
-- for now, wait on "command finished" snapshot before parsing next command
else some <$> IO.Promise.new
let nextCmdSnap? := next?.map
({ range? := some parserState.pos, ctx.input.endPos, task := ·.result! })
let diagnostics Snapshot.Diagnostics.ofMessageLog msgLog
let (stx', parserState') := if minimalSnapshots && !Parser.isTerminalCommand stx then
(default, default)
else
(stx, parserState)
-- report terminal tasks on first line of decl such as not to hide incremental tactics'
-- progress
let initRange? := getNiceCommandStartPos? stx |>.map fun pos => pos, pos
let finishedSnap := {
stx? := stx'
reportingRange? := initRange?
task := finishedPromise.result!
}
let next? if Parser.isTerminalCommand stx then pure none
-- for now, wait on "command finished" snapshot before parsing next command
else some <$> IO.Promise.new
let nextCmdSnap? := next?.map ({
stx? := none
reportingRange? := some parserState.pos, ctx.input.endPos
task := ·.result!
})
let diagnostics Snapshot.Diagnostics.ofMessageLog msgLog
prom.resolve {
diagnostics, finishedSnap, nextCmdSnap?
stx := stx', parserState := parserState'
elabSnap := { range? := stx.getRange?, task := elabPromise.result! }
reportSnap := { range? := initRange?, task := reportPromise.result! }
elabSnap := { stx? := stx', task := elabPromise.result! }
reportSnap := { stx? := none, reportingRange? := initRange?, task := reportPromise.result! }
}
let cmdState doElab stx cmdState beginPos
{ old? := old?.map fun old => old.stx, old.elabSnap, new := elabPromise }
@@ -582,8 +590,8 @@ where
-- We want to trace all of `CommandParsedSnapshot` but `traceTask` is part of it, so let's
-- create a temporary snapshot tree containing all tasks but it
let snaps := #[
{ range? := none, task := elabPromise.result!.map (sync := true) toSnapshotTree },
{ range? := none, task := finishedPromise.result!.map (sync := true) toSnapshotTree }] ++
{ stx? := stx', task := elabPromise.result!.map (sync := true) toSnapshotTree },
{ stx? := stx', task := finishedPromise.result!.map (sync := true) toSnapshotTree }] ++
cmdState.snapshotTasks
let tree := SnapshotTree.mk { diagnostics := .empty } snaps
BaseIO.bindTask ( tree.waitAll) fun _ => do
@@ -603,7 +611,11 @@ where
pure <| .pure <| .mk { diagnostics := .empty } #[]
reportPromise.resolve <|
.mk { diagnostics := .empty } <|
cmdState.snapshotTasks.push { range? := initRange?, task := traceTask }
cmdState.snapshotTasks.push {
stx? := none
reportingRange? := initRange?
task := traceTask
}
if let some next := next? then
-- We're definitely off the fast-forwarding path now
parseCmd none parserState cmdState next (sync := false) ctx

View File

@@ -101,7 +101,7 @@ instance : ToSnapshotTree HeaderParsedSnapshot where
/-- Shortcut accessor to the final header state, if successful. -/
def HeaderParsedSnapshot.processedResult (snap : HeaderParsedSnapshot) :
SnapshotTask (Option HeaderProcessedState) :=
snap.result?.bind (·.processedSnap.map (sync := true) (·.result?)) |>.getD (.pure none)
snap.result?.bind (·.processedSnap.map (sync := true) (·.result?)) |>.getD (.finished none none)
/-- Initial snapshot of the Lean language processor: a "header parsed" snapshot. -/
abbrev InitialSnapshot := HeaderParsedSnapshot

View File

@@ -23,7 +23,7 @@ where go range? s := do
if let some range := range? then
desc := desc ++ f!"{file.toPosition range.start}-{file.toPosition range.stop} "
desc := desc ++ .prefixJoin "\n" ( s.element.diagnostics.msgLog.toList.mapM (·.toString))
if let some t := s.element.infoTree? then
trace[Elab.info] ( t.format)
withTraceNode `Elab.snapshotTree (fun _ => pure desc) do
s.children.toList.forM fun c => go c.range? c.get
s.children.toList.forM fun c => go c.reportingRange? c.get
if let some t := s.element.infoTree? then
trace[Elab.info] ( t.format)

View File

@@ -359,7 +359,7 @@ private partial def isExplicitSubsumedAux (lvls : Array Level) (maxExplicit : Na
private def isExplicitSubsumed (lvls : Array Level) (firstNonExplicit : Nat) : Bool :=
if firstNonExplicit == 0 then false
else
let max := (lvls.get! (firstNonExplicit - 1)).getOffset;
let max := lvls[firstNonExplicit - 1]!.getOffset
isExplicitSubsumedAux lvls max firstNonExplicit
partial def normalize (l : Level) : Level :=

View File

@@ -196,7 +196,7 @@ builtin_initialize addBuiltinUnusedVariablesIgnoreFn (fun _ stack _ =>
/-- `inductive Foo where | unused : Foo` -/
builtin_initialize addBuiltinUnusedVariablesIgnoreFn (fun _ stack _ =>
stack.matches [`null, none, `null, none, ``Lean.Parser.Command.inductive] &&
(stack.get? 3 |>.any fun (stx, pos) =>
(stack[3]? |>.any fun (stx, pos) =>
pos == 0 &&
[``Lean.Parser.Command.optDeclSig, ``Lean.Parser.Command.declSig].any (stx.isOfKind ·)))
@@ -206,7 +206,7 @@ builtin_initialize addBuiltinUnusedVariablesIgnoreFn (fun _ stack _ =>
-/
builtin_initialize addBuiltinUnusedVariablesIgnoreFn (fun _ stack _ =>
stack.matches [`null, none, `null, ``Lean.Parser.Command.optDeclSig, none] &&
(stack.get? 4 |>.any fun (stx, _) =>
(stack[4]? |>.any fun (stx, _) =>
[``Lean.Parser.Command.ctor, ``Lean.Parser.Command.structSimpleBinder].any (stx.isOfKind ·)))
/--
@@ -215,7 +215,7 @@ builtin_initialize addBuiltinUnusedVariablesIgnoreFn (fun _ stack _ =>
-/
builtin_initialize addBuiltinUnusedVariablesIgnoreFn (fun _ stack _ =>
stack.matches [`null, none, `null, ``Lean.Parser.Command.declSig, none] &&
(stack.get? 4 |>.any fun (stx, _) =>
(stack[4]? |>.any fun (stx, _) =>
[``Lean.Parser.Command.opaque, ``Lean.Parser.Command.axiom].any (stx.isOfKind ·)))
/--
@@ -225,10 +225,10 @@ Definition with foreign definition
-/
builtin_initialize addBuiltinUnusedVariablesIgnoreFn (fun _ stack _ =>
stack.matches [`null, none, `null, none, none, ``Lean.Parser.Command.declaration] &&
(stack.get? 3 |>.any fun (stx, _) =>
(stack[3]? |>.any fun (stx, _) =>
stx.isOfKind ``Lean.Parser.Command.optDeclSig ||
stx.isOfKind ``Lean.Parser.Command.declSig) &&
(stack.get? 5 |>.any fun (stx, _) => match stx[0] with
(stack[5]? |>.any fun (stx, _) => match stx[0] with
| `(Lean.Parser.Command.declModifiersT| $[$_:docComment]? @[$[$attrs:attr],*] $[$vis]? $[noncomputable]?) =>
attrs.any (fun attr => attr.raw.isOfKind ``Parser.Attr.extern || attr matches `(attr| implemented_by $_))
| _ => false))
@@ -247,8 +247,8 @@ Function argument in let declaration (when `linter.unusedVariables.funArgs` is f
builtin_initialize addBuiltinUnusedVariablesIgnoreFn (fun _ stack opts =>
!getLinterUnusedVariablesFunArgs opts &&
stack.matches [`null, none, `null, ``Lean.Parser.Term.letIdDecl, none] &&
(stack.get? 3 |>.any fun (_, pos) => pos == 1) &&
(stack.get? 5 |>.any fun (stx, _) => !stx.isOfKind ``Lean.Parser.Term.structInstField))
(stack[3]? |>.any fun (_, pos) => pos == 1) &&
(stack[5]? |>.any fun (stx, _) => !stx.isOfKind ``Lean.Parser.Term.structInstField))
/--
Function argument in declaration signature (when `linter.unusedVariables.funArgs` is false)
@@ -257,7 +257,7 @@ Function argument in declaration signature (when `linter.unusedVariables.funArgs
builtin_initialize addBuiltinUnusedVariablesIgnoreFn (fun _ stack opts =>
!getLinterUnusedVariablesFunArgs opts &&
stack.matches [`null, none, `null, none] &&
(stack.get? 3 |>.any fun (stx, pos) =>
(stack[3]? |>.any fun (stx, pos) =>
pos == 0 &&
[``Lean.Parser.Command.optDeclSig, ``Lean.Parser.Command.declSig].any (stx.isOfKind ·)))

View File

@@ -252,8 +252,8 @@ def getFVars (lctx : LocalContext) : Array Expr :=
lctx.getFVarIds.map mkFVar
private partial def popTailNoneAux (a : PArray (Option LocalDecl)) : PArray (Option LocalDecl) :=
if a.size == 0 then a
else match a.get! (a.size - 1) with
if h : a.size = 0 then a
else match a[a.size - 1] with
| none => popTailNoneAux a.pop
| some _ => a
@@ -268,8 +268,8 @@ def erase (lctx : LocalContext) (fvarId : FVarId) : LocalContext :=
def pop (lctx : LocalContext): LocalContext :=
match lctx with
| { fvarIdToDecl := map, decls := decls } =>
if decls.size == 0 then lctx
else match decls.get! (decls.size - 1) with
if _ : decls.size = 0 then lctx
else match decls[decls.size - 1] with
| none => lctx -- unreachable
| some decl => { fvarIdToDecl := map.erase decl.fvarId, decls := popTailNoneAux decls.pop }
@@ -293,7 +293,7 @@ def getUnusedName (lctx : LocalContext) (suggestion : Name) : Name :=
else suggestion
def lastDecl (lctx : LocalContext) : Option LocalDecl :=
lctx.decls.get! (lctx.decls.size - 1)
lctx.decls[lctx.decls.size - 1]!
def setUserName (lctx : LocalContext) (fvarId : FVarId) (userName : Name) : LocalContext :=
let decl := lctx.get! fvarId
@@ -340,7 +340,7 @@ def numIndices (lctx : LocalContext) : Nat :=
lctx.decls.size
def getAt? (lctx : LocalContext) (i : Nat) : Option LocalDecl :=
lctx.decls.get! i
lctx.decls[i]!
@[specialize] def foldlM [Monad m] (lctx : LocalContext) (f : β LocalDecl m β) (init : β) (start : Nat := 0) : m β :=
lctx.decls.foldlM (init := init) (start := start) fun b decl => match decl with

View File

@@ -165,12 +165,13 @@ def mkHEqTrans (h₁ h₂ : Expr) : MetaM Expr := do
| _, none => throwAppBuilderException ``HEq.trans ("heterogeneous equality proof expected" ++ hasTypeMsg h₂ hType₂)
/-- Given `h : HEq a b` where `a` and `b` have the same type, returns a proof of `Eq a b`. -/
def mkEqOfHEq (h : Expr) : MetaM Expr := do
def mkEqOfHEq (h : Expr) (check := true) : MetaM Expr := do
let hType infer h
match hType.heq? with
| some (α, a, β, b) =>
unless ( isDefEq α β) do
throwAppBuilderException ``eq_of_heq m!"heterogeneous equality types are not definitionally equal{indentExpr α}\nis not definitionally equal to{indentExpr β}"
if check then
unless ( isDefEq α β) do
throwAppBuilderException ``eq_of_heq m!"heterogeneous equality types are not definitionally equal{indentExpr α}\nis not definitionally equal to{indentExpr β}"
let u getLevel α
return mkApp4 (mkConst ``eq_of_heq [u]) α a b h
| _ =>

View File

@@ -52,7 +52,7 @@ and the innermost binder is at the end. We update the binder names therein when
-/
go (e : Expr) : MonadCacheT ExprStructEq Expr (StateT (Array Name) CoreM) Expr := do
checkCache { val := e : ExprStructEq } fun _ => do
if e.isAppOfArity `binderNameHint 6 then
if e.isAppOfArity ``binderNameHint 6 then
let v := e.appFn!.appFn!.appArg!
let b := e.appFn!.appArg!
let e := e.appArg!

View File

@@ -32,6 +32,9 @@ def isInstDivInt (e : Expr) : MetaM Bool := do
def isInstModInt (e : Expr) : MetaM Bool := do
let_expr Int.instMod e | return false
return true
def isInstDvdInt (e : Expr) : MetaM Bool := do
let_expr Int.instDvd e | return false
return true
def isInstHAddInt (e : Expr) : MetaM Bool := do
let_expr instHAdd _ i e | return false
isInstAddInt i

View File

@@ -486,7 +486,7 @@ private partial def evalLazyEntries
private def evalNode (c : TrieIndex) :
MatchM α (Array α × TrieIndex × Std.HashMap Key TrieIndex) := do
let .node vs star cs pending := (get).get! c
let .node vs star cs pending := (get)[c]!
if pending.size = 0 then
return (vs, star, cs)
else

View File

@@ -74,7 +74,7 @@ def getFinValue? (e : Expr) : MetaM (Option ((n : Nat) × Fin n)) := OptionT.run
Return `some ⟨n, v⟩` if `e` is:
- an `OfNat.ofNat` application
- a `BitVec.ofNat` application
- a `BitVec.ofNatLt` application
- a `BitVec.ofNatLT` application
that encode a `BitVec n` with value `v`.
-/
def getBitVecValue? (e : Expr) : MetaM (Option ((n : Nat) × BitVec n)) := OptionT.run do
@@ -83,7 +83,7 @@ def getBitVecValue? (e : Expr) : MetaM (Option ((n : Nat) × BitVec n)) := Optio
let n getNatValue? nExpr
let v getNatValue? vExpr
return n, BitVec.ofNat n v
| BitVec.ofNatLt nExpr vExpr _ =>
| BitVec.ofNatLT nExpr vExpr _ =>
let n getNatValue? nExpr
let v getNatValue? vExpr
return n, BitVec.ofNat n v

View File

@@ -599,8 +599,8 @@ private def processArrayLit (p : Problem) : MetaM (Array Problem) := do
let sizes := collectArraySizes p
let subgoals caseArraySizes p.mvarId x.fvarId! sizes
subgoals.mapIdxM fun i subgoal => do
if i < sizes.size then
let size := sizes.get! i
if h : i < sizes.size then
let size := sizes[i]
let subst := subgoal.subst
let elems := subgoal.elems.toList
let newVars := elems.map mkFVar ++ xs

View File

@@ -547,7 +547,7 @@ def generate : SynthM Unit := do
else
let key := gNode.key
let idx := gNode.currInstanceIdx - 1
let inst := gNode.instances.get! idx
let inst := gNode.instances[idx]!
let mctx := gNode.mctx
let mvar := gNode.mvar
/- See comment at `typeHasMVars` -/

View File

@@ -27,7 +27,6 @@ import Lean.Meta.Tactic.TryThis
import Lean.Meta.Tactic.Cleanup
import Lean.Meta.Tactic.Unfold
import Lean.Meta.Tactic.Rename
import Lean.Meta.Tactic.LinearArith
import Lean.Meta.Tactic.AC
import Lean.Meta.Tactic.Refl
import Lean.Meta.Tactic.Congr

View File

@@ -18,6 +18,7 @@ import Lean.Elab.PreDefinition.Structural.IndGroupInfo
import Lean.Elab.PreDefinition.Structural.FindRecArg
import Lean.Elab.Command
import Lean.Meta.Tactic.ElimInfo
import Lean.Meta.Tactic.FunIndInfo
/-!
This module contains code to derive, from the definition of a recursive function (structural or
@@ -659,7 +660,7 @@ Given a unary definition `foo` defined via `WellFounded.fixF`, derive a suitable
`foo.induct` for it. See module doc for details.
-/
def deriveUnaryInduction (name : Name) : MetaM Name := do
let inductName := .append name `induct
let inductName := getFunInductName name
if hasConst inductName then return inductName
let info getConstInfoDefn name
@@ -677,7 +678,7 @@ def deriveUnaryInduction (name : Name) : MetaM Name := do
mkLambdaFVars (params ++ xs) (mkAppN body xs)
else
pure e
let e' lambdaTelescope e fun params funBody => MatcherApp.withUserNames params varNames do
let (e', paramMask) lambdaTelescope e fun params funBody => MatcherApp.withUserNames params varNames do
match_expr funBody with
| fix@WellFounded.fix α _motive rel wf body target =>
unless params.back! == target do
@@ -719,8 +720,9 @@ def deriveUnaryInduction (name : Name) : MetaM Name := do
-- induction principle match the type of the function better.
-- But this leads to avoidable parameters that make functional induction strictly less
-- useful (e.g. when the unsued parameter mentions bound variables in the users' goal)
let e' mkLambdaFVars (binderInfoForMVars := .default) (usedOnly := true) fixedParams e'
instantiateMVars e'
let (paramMask, e') mkLambdaFVarsMasked fixedParams e'
let e' instantiateMVars e'
return (e', paramMask)
| _ =>
if funBody.isAppOf ``WellFounded.fix then
throwError "Function {name} defined via WellFounded.fix with unexpected arity {funBody.getAppNumArgs}:{indentExpr funBody}"
@@ -734,12 +736,20 @@ def deriveUnaryInduction (name : Name) : MetaM Name := do
let eTyp inferType e'
let eTyp elimOptParam eTyp
-- logInfo m!"eTyp: {eTyp}"
let params := (collectLevelParams {} eTyp).params
let levelParams := (collectLevelParams {} eTyp).params
-- Prune unused level parameters, preserving the original order
let us := info.levelParams.filter (params.contains ·)
let funUs := info.levelParams.toArray
let usMask := funUs.map (levelParams.contains ·)
let us := maskArray usMask funUs |>.toList
addDecl <| Declaration.thmDecl
{ name := inductName, levelParams := us, type := eTyp, value := e' }
setFunIndInfo {
funIndName := inductName
levelMask := usMask
params := paramMask.map (cond · .param .dropped) ++ #[.target]
}
return inductName
/--
@@ -751,7 +761,7 @@ def projectMutualInduct (names : Array Name) (mutualInduct : Name) : MetaM Unit
let levelParams := ci.levelParams
for name in names, idx in [:names.size] do
let inductName := .append name `induct
let inductName := getFunInductName name
unless hasConst inductName do
let value forallTelescope ci.type fun xs _body => do
let value := .const ci.name (levelParams.map mkLevelParam)
@@ -761,6 +771,21 @@ def projectMutualInduct (names : Array Name) (mutualInduct : Name) : MetaM Unit
let type inferType value
addDecl <| Declaration.thmDecl { name := inductName, levelParams, type, value }
/--
For a (non-mutual!) definition of `name`, uses the `FunIndInfo` associated with the `unaryInduct` and
derives the one for the n-ary function.
-/
def setNaryFunIndInfo (name : Name) (arity : Nat) (unaryInduct : Name) : MetaM Unit := do
let inductName := getFunInductName name
unless inductName = unaryInduct do
let some unaryFunIndInfo getFunIndInfoForInduct? unaryInduct
| throwError "Expected {unaryInduct} to have FunIndInfo"
setFunIndInfo {
unaryFunIndInfo with
funIndName := inductName
params := unaryFunIndInfo.params.filter (· != .target) ++ mkArray arity .target
}
/--
In the type of `value`, reduces
* Beta-redexes
@@ -823,10 +848,10 @@ unpacks it into a n-ary and (possibly) joint induction principle.
-/
def unpackMutualInduction (eqnInfo : WF.EqnInfo) (unaryInductName : Name) : MetaM Name := do
let inductName := if eqnInfo.declNames.size > 1 then
.append eqnInfo.declNames[0]! `mutual_induct
getMutualInductName eqnInfo.declNames[0]!
else
-- If there is no mutual recursion, we generate the `foo.induct` directly.
.append eqnInfo.declNames[0]! `induct
getFunInductName eqnInfo.declNames[0]!
if hasConst inductName then return inductName
let ci getConstInfo unaryInductName
@@ -867,11 +892,6 @@ def unpackMutualInduction (eqnInfo : WF.EqnInfo) (unaryInductName : Name) : Meta
return inductName
/-- Given `foo._unary.induct`, define `foo.mutual_induct` and then `foo.induct`, `bar.induct`, … -/
def deriveUnpackedInduction (eqnInfo : WF.EqnInfo) (unaryInductName : Name): MetaM Unit := do
let unpackedInductName unpackMutualInduction eqnInfo unaryInductName
projectMutualInduct eqnInfo.declNames unpackedInductName
def withLetDecls {α} (name : Name) (ts : Array Expr) (es : Array Expr) (k : Array Expr MetaM α) : MetaM α := do
assert! es.size = ts.size
go 0 #[]
@@ -891,7 +911,7 @@ See module doc for details.
def deriveInductionStructural (names : Array Name) (numFixed : Nat) : MetaM Unit := do
let infos names.mapM getConstInfoDefn
-- First open up the fixed parameters everywhere
let e' lambdaBoundedTelescope infos[0]!.value numFixed fun xs _ => do
let (e', paramMask, motiveArities) lambdaBoundedTelescope infos[0]!.value numFixed fun xs _ => do
-- Now look at the body of an arbitrary of the functions (they are essentially the same
-- up to the final projections)
let body instantiateLambda infos[0]!.value xs
@@ -937,12 +957,13 @@ def deriveInductionStructural (names : Array Name) (numFixed : Nat) : MetaM Unit
-- We also need to know the number of indices of each type former, including the auxiliary
-- type formers that do not have IndInfo. We can read it off the motives types of the recursor.
let numTargetss do
let numTypeFormerTargetss do
let aux := mkAppN (.const recInfo.name (0 :: group.levels)) group.params
let motives inferArgumentTypesN recInfo.numMotives aux
motives.mapM fun motive =>
forallTelescopeReducing motive fun xs _ => pure xs.size
let recArgInfos infos.mapM fun info => do
let some eqnInfo := Structural.eqnInfoExt.find? ( getEnv) info.name | throwError "{info.name} missing eqnInfo"
let value instantiateLambda info.value xs
@@ -972,6 +993,7 @@ def deriveInductionStructural (names : Array Name) (numFixed : Nat) : MetaM Unit
lambdaTelescope ( instantiateLambda info.value xs) fun ys _ => pure ys.size
let motiveNames := Array.ofFn (n := infos.size) fun i, _ =>
if infos.size = 1 then .mkSimple "motive" else .mkSimple s!"motive_{i+1}"
withLocalDeclsDND (motiveNames.zip motiveTypes) fun motives => do
-- Prepare the `isRecCall` that recognizes recursive calls
@@ -1000,7 +1022,7 @@ def deriveInductionStructural (names : Array Name) (numFixed : Nat) : MetaM Unit
-- So that we can transform them
let (minors', mvars) M2.run do
let mut minors' := #[]
for brecOnMinor in brecOnMinors, goal in minorTypes, numTargets in numTargetss do
for brecOnMinor in brecOnMinors, goal in minorTypes, numTargets in numTypeFormerTargetss do
let minor' forallTelescope goal fun xs goal => do
unless xs.size numTargets do
throwError ".brecOn argument has too few parameters, expected at least {numTargets}: {xs}"
@@ -1053,10 +1075,10 @@ def deriveInductionStructural (names : Array Name) (numFixed : Nat) : MetaM Unit
-- induction principle match the type of the function better.
-- But this leads to avoidable parameters that make functional induction strictly less
-- useful (e.g. when the unsued parameter mentions bound variables in the users' goal)
let e' mkLambdaFVars (binderInfoForMVars := .default) (usedOnly := true) xs e'
let (paramMask, e') mkLambdaFVarsMasked xs e'
let e' instantiateMVars e'
trace[Meta.FunInd] "complete body of mutual induction principle:{indentExpr e'}"
pure e'
pure (e', paramMask, motiveArities)
unless ( isTypeCorrect e') do
logError m!"constructed induction principle is not type correct:{indentExpr e'}"
@@ -1065,22 +1087,33 @@ def deriveInductionStructural (names : Array Name) (numFixed : Nat) : MetaM Unit
let eTyp inferType e'
let eTyp elimOptParam eTyp
-- logInfo m!"eTyp: {eTyp}"
let params := (collectLevelParams {} eTyp).params
let levelParams := (collectLevelParams {} eTyp).params
-- Prune unused level parameters, preserving the original order
let us := infos[0]!.levelParams.filter (params.contains ·)
let funUs := infos[0]!.levelParams.toArray
let usMask := funUs.map (levelParams.contains ·)
let us := maskArray usMask funUs |>.toList
let inductName :=
if names.size = 1 then
names[0]! ++ `induct
getFunInductName names[0]!
else
names[0]! ++ `mutual_induct
getMutualInductName names[0]!
addDecl <| Declaration.thmDecl
{ name := inductName, levelParams := us, type := eTyp, value := e' }
if names.size > 1 then
projectMutualInduct names inductName
if names.size = 1 then
setFunIndInfo {
funIndName := inductName
levelMask := usMask
params := paramMask.map (cond · .param .dropped) ++
mkArray motiveArities[0]! .target
}
/--
For non-recursive (and recursive functions) functions we derive a “functional case splitting theorem”. This is very similar
@@ -1110,6 +1143,8 @@ def deriveCases (name : Name) : MetaM Unit := do
throwError "'{name}' does not have an unfold theorem nor a value"
let motiveType lambdaTelescope value fun xs _body => do
mkForallFVars xs (.sort 0)
let motiveArity lambdaTelescope value fun xs _body => do
pure xs.size
let e' withLocalDeclD `motive motiveType fun motive => do
lambdaTelescope value fun xs body => do
let (e',mvars) M2.run do
@@ -1131,12 +1166,22 @@ def deriveCases (name : Name) : MetaM Unit := do
let eTyp inferType e'
let eTyp elimOptParam eTyp
-- logInfo m!"eTyp: {eTyp}"
let params := (collectLevelParams {} eTyp).params
let levelParams := (collectLevelParams {} eTyp).params
-- Prune unused level parameters, preserving the original order
let us := info.levelParams.filter (params.contains ·)
let funUs := info.levelParams.toArray
let usMask := funUs.map (levelParams.contains ·)
let us := maskArray usMask funUs |>.toList
let casesName := getFunCasesName info.name
addDecl <| Declaration.thmDecl
{ name := info.name ++ `fun_cases, levelParams := us, type := eTyp, value := e' }
{ name := casesName, levelParams := us, type := eTyp, value := e' }
setFunIndInfo {
funIndName := casesName
levelMask := usMask
params := mkArray motiveArity .target
}
/--
Given a recursively defined function `foo`, derives `foo.induct`. See the module doc for details.
@@ -1145,8 +1190,10 @@ def deriveInduction (name : Name) : MetaM Unit := do
mapError (f := (m!"Cannot derive functional induction principle (please report this issue)\n{indentD ·}")) do
if let some eqnInfo := WF.eqnInfoExt.find? ( getEnv) name then
let unaryInductName deriveUnaryInduction eqnInfo.declNameNonRec
unless eqnInfo.declNameNonRec = name do
deriveUnpackedInduction eqnInfo unaryInductName
let unpackedInductName unpackMutualInduction eqnInfo unaryInductName
projectMutualInduct eqnInfo.declNames unpackedInductName
if eqnInfo.argsPacker.numFuncs = 1 then
setNaryFunIndInfo eqnInfo.declNames[0]! eqnInfo.argsPacker.arities[0]! unaryInductName
else if let some eqnInfo := Structural.eqnInfoExt.find? ( getEnv) name then
deriveInductionStructural eqnInfo.declNames eqnInfo.numFixed
else

View File

@@ -0,0 +1,76 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Joachim Breitner
-/
prelude
import Lean.Meta.Basic
import Lean.ScopedEnvExtension
import Lean.ReservedNameAction
/-!
This module defines the data structure and environment extension to remember how to map the
function's arguments to the functional induction principle's arguments.
Also used for functional cases.
-/
namespace Lean.Meta
inductive FunIndParamKind where
| dropped
| param
| target
deriving BEq, Repr
/--
A `FunIndInfo` indicates how a function's arguments map to the arguments of the functional induction
(resp. cases) theorem.
The size of `params` also indicates the arity of the function.
-/
structure FunIndInfo where
funIndName : Name
/--
`true` means that the corresponding level parameter of the function is also a level param
of the induction principle.
-/
levelMask : Array Bool
params : Array FunIndParamKind
deriving Inhabited, Repr
builtin_initialize funIndInfoExt : MapDeclarationExtension FunIndInfo mkMapDeclarationExtension
def getFunInductName (declName : Name) : Name :=
declName ++ `induct
def getFunCasesName (declName : Name) : Name :=
declName ++ `fun_cases
def getMutualInductName (declName : Name) : Name :=
declName ++ `mutual_induct
def getFunInduct? (cases : Bool) (declName : Name) : CoreM (Option Name) := do
let .defnInfo _ getConstInfo declName | return none
try
let thmName := if cases then
getFunCasesName declName
else
getFunInductName declName
let result realizeGlobalConstNoOverloadCore thmName
return some result
catch _ =>
return none
def setFunIndInfo (funIndInfo : FunIndInfo) : CoreM Unit := do
assert! !(funIndInfoExt.contains ( getEnv) funIndInfo.funIndName)
modifyEnv fun env => funIndInfoExt.insert env funIndInfo.funIndName funIndInfo
def getFunIndInfoForInduct? (inductName : Name) : CoreM (Option FunIndInfo) := do
return funIndInfoExt.find? ( getEnv) inductName
def getFunIndInfo? (cases : Bool) (funName : Name) : CoreM (Option FunIndInfo) := do
let some inductName getFunInduct? cases funName | return none
getFunIndInfoForInduct? inductName
end Lean.Meta

View File

@@ -48,14 +48,6 @@ builtin_initialize registerTraceClass `grind.simp
builtin_initialize registerTraceClass `grind.split
builtin_initialize registerTraceClass `grind.split.candidate
builtin_initialize registerTraceClass `grind.split.resolved
builtin_initialize registerTraceClass `grind.offset
builtin_initialize registerTraceClass `grind.offset.dist
builtin_initialize registerTraceClass `grind.offset.internalize
builtin_initialize registerTraceClass `grind.offset.internalize.term (inherited := true)
builtin_initialize registerTraceClass `grind.offset.propagate
builtin_initialize registerTraceClass `grind.offset.eq
builtin_initialize registerTraceClass `grind.offset.eq.to (inherited := true)
builtin_initialize registerTraceClass `grind.offset.eq.from (inherited := true)
builtin_initialize registerTraceClass `grind.beta
/-! Trace options for `grind` developers -/
@@ -69,8 +61,6 @@ builtin_initialize registerTraceClass `grind.debug.final
builtin_initialize registerTraceClass `grind.debug.forallPropagator
builtin_initialize registerTraceClass `grind.debug.split
builtin_initialize registerTraceClass `grind.debug.canon
builtin_initialize registerTraceClass `grind.debug.offset
builtin_initialize registerTraceClass `grind.debug.offset.proof
builtin_initialize registerTraceClass `grind.debug.ematch.pattern
builtin_initialize registerTraceClass `grind.debug.beta
builtin_initialize registerTraceClass `grind.debug.internalize

View File

@@ -6,5 +6,6 @@ Authors: Leonardo de Moura
prelude
import Lean.Meta.Tactic.Grind.Arith.Util
import Lean.Meta.Tactic.Grind.Arith.Types
import Lean.Meta.Tactic.Grind.Arith.Offset
import Lean.Meta.Tactic.Grind.Arith.Main
import Lean.Meta.Tactic.Grind.Arith.Offset
import Lean.Meta.Tactic.Grind.Arith.Cutsat

View File

@@ -0,0 +1,18 @@
/-
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.Util.Trace
import Lean.Meta.Tactic.Grind.Arith.Cutsat.Types
namespace Lean
builtin_initialize registerTraceClass `grind.cutsat
builtin_initialize registerTraceClass `grind.cutsat.assert
builtin_initialize registerTraceClass `grind.cutsat.assert.dvd
builtin_initialize registerTraceClass `grind.cutsat.internalize
builtin_initialize registerTraceClass `grind.cutsat.internalize.term (inherited := true)
end Lean

View File

@@ -0,0 +1,22 @@
/-
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.Arith.Cutsat.Var
namespace Lean.Meta.Grind.Arith.Cutsat
def assertDvdCnstr (e : Expr) : GoalM Unit := do
let_expr Dvd.dvd _ inst a b e | return ()
unless ( isInstDvdInt inst) do return ()
let some k getIntValue? a
| reportIssue! "non-linear divisibility constraint found{indentExpr e}"
let p toPoly b
let c : DvdCnstr := { k, p }
trace[grind.cutsat.assert.dvd] "{e}, {repr c}"
-- TODO
return ()
end Lean.Meta.Grind.Arith.Cutsat

View File

@@ -0,0 +1,60 @@
/-
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.Arith.Cutsat.Util
namespace Int.Linear
/--
Returns `true` if the variables in the given polynomial are sorted
in decreasing order.
-/
def Poly.isSorted (p : Poly) : Bool :=
go none p
where
go : Option Var Poly Bool
| _, .num _ => true
| none, .add _ y p => go (some y) p
| some x, .add _ y p => x > y && go (some y) p
/-- Returns `true` if all coefficients are not `0`. -/
def Poly.checkCoeffs : Poly Bool
| .num _ => true
| .add k _ p => k != 0 && checkCoeffs p
end Int.Linear
namespace Lean.Meta.Grind.Arith.Cutsat
def checkDvdCnstrs : GoalM Unit := do
let s get'
assert! s.vars.size == s.dvdCnstrs.size
let mut x := 0
for c? in s.dvdCnstrs do
if let some { c, .. } := c? then
assert! c.p.checkCoeffs
assert! c.p.isSorted
assert! c.k > 1
let .add _ y _ := c.p | unreachable!
assert! x == y
x := x + 1
def checkVars : GoalM Unit := do
let s get'
let mut num := 0
for ({ expr }, var) in s.varMap do
if h : var < s.vars.size then
let expr' := s.vars[var]
assert! isSameExpr expr expr'
else
unreachable!
num := num + 1
assert! s.vars.size == num
def checkInvariants : GoalM Unit := do
checkVars
checkDvdCnstrs
end Lean.Meta.Grind.Arith.Cutsat

View File

@@ -0,0 +1,40 @@
/-
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.Int.Linear
import Lean.Data.PersistentArray
import Lean.Meta.Tactic.Grind.ENodeKey
import Lean.Meta.Tactic.Grind.Arith.Util
namespace Lean.Meta.Grind.Arith.Cutsat
export Int.Linear (Var Poly RelCnstr DvdCnstr)
mutual
/-- A divisibility constraint and its justification/proof. -/
structure DvdCnstrWithProof where
c : DvdCnstr
p : DvdCnstrProof
inductive DvdCnstrProof where
| expr (h : Expr)
| solveCombine (c₁ c₂ : DvdCnstrWithProof) (α β : Int)
| solveElim (c₁ c₂ : DvdCnstrWithProof)
end
/-- State of the cutsat procedure. -/
structure State where
/-- Mapping from variables to their denotations. -/
vars : PArray Expr := {}
/-- Mapping from `Expr` to a variable representing it. -/
varMap : PHashMap ENodeKey Var := {}
/--
Mapping from variables to divisibility constraints. Recall that we keep the divisibility constraint in solved form.
Thus, we have at most one divisibility per variable. -/
dvdCnstrs : PArray (Option DvdCnstrWithProof) := {}
deriving Inhabited
end Lean.Meta.Grind.Arith.Cutsat

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