mirror of
https://github.com/leanprover/lean4.git
synced 2026-03-31 01:04:07 +00:00
Compare commits
41 Commits
grind_offs
...
align_extr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb896cabfa | ||
|
|
353eaf7014 | ||
|
|
6a17e62523 | ||
|
|
1ce7047bf5 | ||
|
|
ef759d874f | ||
|
|
6f5bb3e896 | ||
|
|
96c6f9dc96 | ||
|
|
f50b863868 | ||
|
|
dd3652ecdc | ||
|
|
a9efbf04f4 | ||
|
|
3a76ac5620 | ||
|
|
747ea91c3a | ||
|
|
ecdc2d57f2 | ||
|
|
f4afcfc923 | ||
|
|
9cce0ce8d9 | ||
|
|
57aadf8af9 | ||
|
|
1babe9fc67 | ||
|
|
dd1a4188a0 | ||
|
|
ed42d068d4 | ||
|
|
784444c7a9 | ||
|
|
05fb67af90 | ||
|
|
22d1d04059 | ||
|
|
36ac6eb912 | ||
|
|
47548aa171 | ||
|
|
b26b781992 | ||
|
|
c9c3366521 | ||
|
|
2c2a3a65b2 | ||
|
|
8cefb2cf65 | ||
|
|
80c8837f49 | ||
|
|
40c6dfa3ae | ||
|
|
cc76c46244 | ||
|
|
b38da34db2 | ||
|
|
4a900cc65c | ||
|
|
a3fd2eb0fe | ||
|
|
6ac530aa1a | ||
|
|
04fe72fee0 | ||
|
|
a833afa935 | ||
|
|
7c9454edd2 | ||
|
|
1ecb4a43ae | ||
|
|
ae9d12aeaa | ||
|
|
e617ce7e4f |
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 α :=
|
||||
|
||||
427
src/Init/Data/Array/Extract.lean
Normal file
427
src/Init/Data/Array/Extract.lean
Normal 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
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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) :
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
@@ -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) :
|
||||
|
||||
@@ -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] :
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 =>
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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
|
||||
|
||||
68
src/Init/Data/Int/Cutsat.lean
Normal file
68
src/Init/Data/Int/Cutsat.lean
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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⟩ =
|
||||
|
||||
@@ -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 -/
|
||||
|
||||
/--
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 :=
|
||||
|
||||
@@ -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 _
|
||||
|
||||
|
||||
@@ -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. -/
|
||||
|
||||
@@ -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} :
|
||||
|
||||
@@ -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 : α} :
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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⟩
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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⟩⟩
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
166
src/Init/Data/Vector/Extract.lean
Normal file
166
src/Init/Data/Vector/Extract.lean
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) :=
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -38,3 +38,4 @@ import Lean.LabelAttribute
|
||||
import Lean.AddDecl
|
||||
import Lean.Replay
|
||||
import Lean.PrivateName
|
||||
import Lean.PremiseSelection
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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' _ =>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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`.
|
||||
-/
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 =>
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 :=
|
||||
|
||||
@@ -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 ·)))
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
| _ =>
|
||||
|
||||
@@ -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!
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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⟩
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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` -/
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
76
src/Lean/Meta/Tactic/FunIndInfo.lean
Normal file
76
src/Lean/Meta/Tactic/FunIndInfo.lean
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
18
src/Lean/Meta/Tactic/Grind/Arith/Cutsat.lean
Normal file
18
src/Lean/Meta/Tactic/Grind/Arith/Cutsat.lean
Normal 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
|
||||
22
src/Lean/Meta/Tactic/Grind/Arith/Cutsat/DvdCnstr.lean
Normal file
22
src/Lean/Meta/Tactic/Grind/Arith/Cutsat/DvdCnstr.lean
Normal 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
|
||||
60
src/Lean/Meta/Tactic/Grind/Arith/Cutsat/Inv.lean
Normal file
60
src/Lean/Meta/Tactic/Grind/Arith/Cutsat/Inv.lean
Normal 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
|
||||
40
src/Lean/Meta/Tactic/Grind/Arith/Cutsat/Types.lean
Normal file
40
src/Lean/Meta/Tactic/Grind/Arith/Cutsat/Types.lean
Normal 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
Reference in New Issue
Block a user