mirror of
https://github.com/leanprover/lean4.git
synced 2026-04-10 06:04:09 +00:00
Compare commits
67 Commits
grind_deci
...
grind_fix_
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6c6b776d87 | ||
|
|
5e8caa562f | ||
|
|
c6d179143e | ||
|
|
6e3ebdca91 | ||
|
|
f57016ee3a | ||
|
|
0669a04704 | ||
|
|
5cd352588c | ||
|
|
e9cc776f22 | ||
|
|
e79fef15df | ||
|
|
c672934f11 | ||
|
|
582877d2d3 | ||
|
|
39ce3d14f4 | ||
|
|
32758aa712 | ||
|
|
0f6e35dc63 | ||
|
|
2528188dde | ||
|
|
1cdadfd47a | ||
|
|
e07c59c831 | ||
|
|
cbd38ceadd | ||
|
|
c46f1e941c | ||
|
|
cf3b257ccd | ||
|
|
09ab15dc6d | ||
|
|
e631efd817 | ||
|
|
d2f4ce0158 | ||
|
|
69536808ca | ||
|
|
3d5dd15de4 | ||
|
|
91c245663b | ||
|
|
1421b6145e | ||
|
|
bffa642ad6 | ||
|
|
deef1c2739 | ||
|
|
acf42bd30b | ||
|
|
4947215325 | ||
|
|
6e7209dfa3 | ||
|
|
97a00b3881 | ||
|
|
d758b4c862 | ||
|
|
61d7716ad8 | ||
|
|
05f16ed279 | ||
|
|
985cd71f23 | ||
|
|
2ede81fe10 | ||
|
|
4d6ad8b0fb | ||
|
|
07e7a43668 | ||
|
|
388b6f045b | ||
|
|
5a6f45a324 | ||
|
|
e6ce55ffd4 | ||
|
|
1b40c46ab1 | ||
|
|
0b54a76e32 | ||
|
|
4bb8d37e37 | ||
|
|
e76eb6fbd2 | ||
|
|
ca3f43907b | ||
|
|
106b772659 | ||
|
|
e8446c81c8 | ||
|
|
bfed223306 | ||
|
|
a35c62d0ad | ||
|
|
e86644f329 | ||
|
|
d1dad44227 | ||
|
|
ba1c1258d7 | ||
|
|
2d8c642711 | ||
|
|
a3b83f7ca9 | ||
|
|
5a849dee9b | ||
|
|
a6f4802d66 | ||
|
|
acd6b13d76 | ||
|
|
b0acdef433 | ||
|
|
0f2ede45d5 | ||
|
|
ab4febd1df | ||
|
|
f8691bcb62 | ||
|
|
9c7f50a42c | ||
|
|
cd0b54ce5d | ||
|
|
8a373cbebe |
2
.github/workflows/build-template.yml
vendored
2
.github/workflows/build-template.yml
vendored
@@ -46,7 +46,7 @@ jobs:
|
||||
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
||||
CCACHE_COMPRESS: true
|
||||
# current cache limit
|
||||
CCACHE_MAXSIZE: 600M
|
||||
CCACHE_MAXSIZE: 400M
|
||||
# squelch error message about missing nixpkgs channel
|
||||
NIX_BUILD_SHELL: bash
|
||||
LSAN_OPTIONS: max_leaks=10
|
||||
|
||||
2
.github/workflows/pr-release.yml
vendored
2
.github/workflows/pr-release.yml
vendored
@@ -111,7 +111,7 @@ jobs:
|
||||
|
||||
- name: 'Setup jq'
|
||||
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
|
||||
uses: dcarbone/install-jq-action@v3.0.1
|
||||
uses: dcarbone/install-jq-action@v3.1.1
|
||||
|
||||
# Check that the most recently nightly coincides with 'git merge-base HEAD master'
|
||||
- name: Check merge-base and nightly-testing-YYYY-MM-DD
|
||||
|
||||
@@ -159,7 +159,7 @@ with builtins; let
|
||||
dir=$(dirname $relpath)
|
||||
mkdir -p $dir $out/$dir $ilean/$dir $c/$dir
|
||||
if [ -d $src ]; then cp -r $src/. .; else cp $src $leanPath; fi
|
||||
lean -o $out/$oleanPath -i $ilean/$ileanPath -c $c/$cPath $leanPath $leanFlags $leanPluginFlags $leanLoadDynlibFlags
|
||||
lean -o $out/$oleanPath -i $out/$ileanPath -c $c/$cPath $leanPath $leanFlags $leanPluginFlags $leanLoadDynlibFlags
|
||||
'';
|
||||
}) // {
|
||||
inherit deps;
|
||||
|
||||
@@ -59,6 +59,9 @@ instance : Monad (OptionT m) where
|
||||
pure := OptionT.pure
|
||||
bind := OptionT.bind
|
||||
|
||||
instance {m : Type u → Type v} [Pure m] : Inhabited (OptionT m α) where
|
||||
default := pure (f:=m) default
|
||||
|
||||
/--
|
||||
Recovers from failures. Typically used via the `<|>` operator.
|
||||
-/
|
||||
|
||||
@@ -597,7 +597,10 @@ structure Task (α : Type u) : Type u where
|
||||
many tasks have finished.
|
||||
|
||||
`Task.map` and `Task.bind` should be preferred over `Task.get` for setting up task dependencies
|
||||
where possible as they do not require temporarily growing the threadpool in this way.
|
||||
where possible as they do not require temporarily growing the threadpool in this way. In
|
||||
particular, calling `Task.get` in a task continuation with `(sync := true)` will panic as the
|
||||
continuation is decidedly not "cheap" in this case and deadlocks may otherwise occur. The
|
||||
waited-upon task should instead be returned and unwrapped using `Task.bind/IO.bindTask`.
|
||||
-/
|
||||
get : α
|
||||
deriving Inhabited, Nonempty
|
||||
|
||||
@@ -446,11 +446,13 @@ theorem findIdx?_eq_none_iff {xs : Array α} {p : α → Bool} :
|
||||
rcases xs with ⟨xs⟩
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem findIdx?_isSome {xs : Array α} {p : α → Bool} :
|
||||
(xs.findIdx? p).isSome = xs.any p := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.findIdx?_isSome]
|
||||
|
||||
@[simp]
|
||||
theorem findIdx?_isNone {xs : Array α} {p : α → Bool} :
|
||||
(xs.findIdx? p).isNone = xs.all (¬p ·) := by
|
||||
rcases xs with ⟨xs⟩
|
||||
@@ -584,13 +586,25 @@ theorem findFinIdx?_eq_some_iff {xs : Array α} {p : α → Bool} {i : Fin xs.si
|
||||
xs.findFinIdx? p = some i ↔
|
||||
p xs[i] ∧ ∀ j (hji : j < i), ¬p (xs[j]'(Nat.lt_trans hji i.2)) := by
|
||||
simp only [findFinIdx?_eq_pmap_findIdx?, Option.pmap_eq_some_iff, findIdx?_eq_some_iff_getElem,
|
||||
Bool.not_eq_true, Option.mem_def, exists_and_left, and_exists_self, Fin.getElem_fin]
|
||||
Bool.not_eq_true, exists_and_left, and_exists_self, Fin.getElem_fin]
|
||||
constructor
|
||||
· rintro ⟨a, ⟨h, w₁, w₂⟩, rfl⟩
|
||||
exact ⟨w₁, fun j hji => by simpa using w₂ j hji⟩
|
||||
· rintro ⟨h, w⟩
|
||||
exact ⟨i, ⟨i.2, h, fun j hji => w ⟨j, by omega⟩ hji⟩, rfl⟩
|
||||
|
||||
@[simp]
|
||||
theorem isSome_findFinIdx? {xs : Array α} {p : α → Bool} :
|
||||
(xs.findFinIdx? p).isSome = xs.any p := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem isNone_findFinIdx? {xs : Array α} {p : α → Bool} :
|
||||
(xs.findFinIdx? p).isNone = xs.all (fun x => ¬ p x) := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp
|
||||
|
||||
@[simp] theorem findFinIdx?_subtype {p : α → Prop} {xs : Array { x // p x }}
|
||||
{f : { x // p x } → Bool} {g : α → Bool} (hf : ∀ x h, f ⟨x, h⟩ = g x) :
|
||||
xs.findFinIdx? f = (xs.unattach.findFinIdx? g).map (fun i => i.cast (by simp)) := by
|
||||
@@ -636,6 +650,20 @@ The lemmas below should be made consistent with those for `findIdx?` (and proved
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.idxOf?_eq_none_iff]
|
||||
|
||||
@[simp]
|
||||
theorem isSome_idxOf? [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
|
||||
(xs.idxOf? a).isSome ↔ a ∈ xs := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem isNone_idxOf? [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
|
||||
(xs.idxOf? a).isNone = ¬ a ∈ xs := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp
|
||||
|
||||
|
||||
|
||||
/-! ### finIdxOf?
|
||||
|
||||
The verification API for `finIdxOf?` is still incomplete.
|
||||
@@ -658,4 +686,16 @@ theorem idxOf?_eq_map_finIdxOf?_val [BEq α] {xs : Array α} {a : α} :
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.finIdxOf?_eq_some_iff]
|
||||
|
||||
@[simp]
|
||||
theorem isSome_finIdxOf? [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
|
||||
(xs.finIdxOf? a).isSome ↔ a ∈ xs := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem isNone_finIdxOf? [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
|
||||
(xs.finIdxOf? a).isNone = ¬ a ∈ xs := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp
|
||||
|
||||
end Array
|
||||
|
||||
@@ -65,4 +65,20 @@ theorem swap_perm {xs : Array α} {i j : Nat} (h₁ : i < xs.size) (h₂ : j < x
|
||||
simp only [swap, perm_iff_toList_perm, toList_set]
|
||||
apply set_set_perm
|
||||
|
||||
namespace Perm
|
||||
|
||||
set_option linter.indexVariables false in
|
||||
theorem extract {xs ys : Array α} (h : xs ~ ys) (lo hi : Nat)
|
||||
(wlo : ∀ i, i < lo → xs[i]? = ys[i]?) (whi : ∀ i, hi ≤ i → xs[i]? = ys[i]?) :
|
||||
(xs.extract lo (hi + 1)) ~ (ys.extract lo (hi + 1)) := by
|
||||
rcases xs with ⟨xs⟩
|
||||
rcases ys with ⟨ys⟩
|
||||
simp_all only [perm_toArray, List.getElem?_toArray, List.extract_toArray,
|
||||
List.extract_eq_drop_take]
|
||||
apply List.Perm.take' (w := fun i h => by simpa using whi (lo + i) (by omega))
|
||||
apply List.Perm.drop' (w := wlo)
|
||||
exact h
|
||||
|
||||
end Perm
|
||||
|
||||
end Array
|
||||
|
||||
@@ -227,6 +227,20 @@ SMT-LIB name: `bvmul`.
|
||||
protected def mul (x y : BitVec n) : BitVec n := BitVec.ofNat n (x.toNat * y.toNat)
|
||||
instance : Mul (BitVec n) := ⟨.mul⟩
|
||||
|
||||
/--
|
||||
Raises a bitvector to a natural number power. Usually accessed via the `^` operator.
|
||||
|
||||
Note that this is currently an inefficient implementation,
|
||||
and should be replaced via an `@[extern]` with a native implementation.
|
||||
See https://github.com/leanprover/lean4/issues/7887.
|
||||
-/
|
||||
protected def pow (x : BitVec n) (y : Nat) : BitVec n :=
|
||||
match y with
|
||||
| 0 => 1
|
||||
| y + 1 => x.pow y * x
|
||||
instance : Pow (BitVec n) Nat where
|
||||
pow x y := x.pow y
|
||||
|
||||
/--
|
||||
Unsigned division of bitvectors using the Lean convention where division by zero returns zero.
|
||||
Usually accessed via the `/` operator.
|
||||
|
||||
@@ -2660,6 +2660,63 @@ theorem msb_append {x : BitVec w} {y : BitVec v} :
|
||||
rw [getElem_append] -- Why does this not work with `simp [getElem_append]`?
|
||||
simp [show i < w by omega]
|
||||
|
||||
theorem toInt_append {x : BitVec n} {y : BitVec m} :
|
||||
(x ++ y).toInt = if n == 0 then y.toInt else (2 ^ m) * x.toInt + y.toNat := by
|
||||
by_cases n0 : n = 0
|
||||
· subst n0
|
||||
simp [BitVec.eq_nil x, BitVec.toInt]
|
||||
· by_cases m0 : m = 0
|
||||
· subst m0
|
||||
simp [BitVec.eq_nil y, n0]
|
||||
· simp only [beq_iff_eq, n0, ↓reduceIte]
|
||||
by_cases x.msb
|
||||
case pos h =>
|
||||
rw [toInt_eq_msb_cond]
|
||||
simp only [show ((x ++ y).msb = true) by simp [msb_append, n0, h], ↓reduceIte, toNat_append,
|
||||
Nat.pow_add, ← Nat.shiftLeft_eq, toInt_eq_msb_cond, h]
|
||||
rw_mod_cast [← Nat.shiftLeft_add_eq_or_of_lt (by omega), Nat.shiftLeft_eq, Nat.shiftLeft_eq,
|
||||
Nat.mul_comm, Int.mul_sub]
|
||||
norm_cast
|
||||
rw [Int.natCast_add, Nat.mul_comm (n := 2 ^ n)]
|
||||
omega
|
||||
case neg h =>
|
||||
rw [Bool.not_eq_true] at h
|
||||
rw [toInt_eq_toNat_of_msb h, toInt_eq_toNat_of_msb (by simp [msb_append, n0, h])]
|
||||
rw_mod_cast [toNat_append, ← Nat.shiftLeft_add_eq_or_of_lt (by omega), Nat.shiftLeft_eq,
|
||||
Nat.mul_comm]
|
||||
|
||||
@[simp] theorem toInt_append_zero {n m : Nat} {x : BitVec n} :
|
||||
(x ++ 0#m).toInt = (2 ^ m) * x.toInt := by
|
||||
simp only [toInt_append, beq_iff_eq, toInt_zero, toNat_ofNat, Nat.zero_mod, Int.cast_ofNat_Int, Int.add_zero,
|
||||
ite_eq_right_iff]
|
||||
intros h
|
||||
subst h
|
||||
simp [BitVec.eq_nil x]
|
||||
|
||||
@[simp] theorem toInt_zero_append {n m : Nat} {x : BitVec n} :
|
||||
(0#m ++ x).toInt = if m = 0 then x.toInt else x.toNat := by
|
||||
simp [toInt_append]
|
||||
|
||||
/--
|
||||
Show that `(x.toNat <<< n) ||| y.toNat` is within bounds of `BitVec (m + n)`.
|
||||
-/
|
||||
theorem toNat_shiftLeft_or_toNat_lt_two_pow_add {m n : Nat} (x : BitVec m) (y : BitVec n) :
|
||||
x.toNat <<< n ||| y.toNat < 2 ^ (m + n) := by
|
||||
have hnLe : 2^n ≤ 2 ^(m + n) := by
|
||||
rw [Nat.pow_add]
|
||||
exact Nat.le_mul_of_pos_left (2 ^ n) (Nat.two_pow_pos m)
|
||||
apply Nat.or_lt_two_pow
|
||||
· have := Nat.two_pow_pos n
|
||||
rw [Nat.shiftLeft_eq, Nat.pow_add, Nat.mul_lt_mul_right]
|
||||
<;> omega
|
||||
· omega
|
||||
|
||||
@[simp] theorem toFin_append {x : BitVec m} {y : BitVec n} :
|
||||
(x ++ y).toFin =
|
||||
@Fin.mk (2^(m+n)) (x.toNat <<< n ||| y.toNat) (toNat_shiftLeft_or_toNat_lt_two_pow_add x y) := by
|
||||
ext
|
||||
simp
|
||||
|
||||
@[simp] theorem zero_width_append (x : BitVec 0) (y : BitVec v) : x ++ y = y.cast (by omega) := by
|
||||
ext i ih
|
||||
simp [getElem_append, show i < v by omega]
|
||||
@@ -3596,6 +3653,13 @@ theorem mul_def {n} {x y : BitVec n} : x * y = (ofFin <| x.toFin * y.toFin) := b
|
||||
@[simp, bitvec_to_nat] theorem toNat_mul (x y : BitVec n) : (x * y).toNat = (x.toNat * y.toNat) % 2 ^ n := rfl
|
||||
@[simp] theorem toFin_mul (x y : BitVec n) : (x * y).toFin = (x.toFin * y.toFin) := rfl
|
||||
|
||||
theorem ofNat_mul {n} (x y : Nat) : BitVec.ofNat n (x * y) = BitVec.ofNat n x * BitVec.ofNat n y := by
|
||||
apply eq_of_toNat_eq
|
||||
simp [BitVec.ofNat, Fin.ofNat'_mul]
|
||||
|
||||
theorem ofNat_mul_ofNat {n} (x y : Nat) : BitVec.ofNat n x * BitVec.ofNat n y = BitVec.ofNat n (x * y) :=
|
||||
(ofNat_mul x y).symm
|
||||
|
||||
protected theorem mul_comm (x y : BitVec w) : x * y = y * x := by
|
||||
apply eq_of_toFin_eq; simpa using Fin.mul_comm ..
|
||||
instance : Std.Commutative (fun (x y : BitVec w) => x * y) := ⟨BitVec.mul_comm⟩
|
||||
@@ -3689,6 +3753,22 @@ theorem setWidth_mul (x y : BitVec w) (h : i ≤ w) :
|
||||
have dvd : 2^i ∣ 2^w := Nat.pow_dvd_pow _ h
|
||||
simp [bitvec_to_nat, h, Nat.mod_mod_of_dvd _ dvd]
|
||||
|
||||
/-! ### pow -/
|
||||
|
||||
@[simp]
|
||||
protected theorem pow_zero {x : BitVec w} : x ^ 0 = 1#w := rfl
|
||||
|
||||
protected theorem pow_succ {x : BitVec w} : x ^ (n + 1) = x ^ n * x := rfl
|
||||
|
||||
@[simp]
|
||||
protected theorem pow_one {x : BitVec w} : x ^ 1 = x := by simp [BitVec.pow_succ]
|
||||
|
||||
protected theorem pow_add {x : BitVec w} {n m : Nat}: x ^ (n + m) = (x ^ n) * (x ^ m):= by
|
||||
induction m with
|
||||
| zero => simp
|
||||
| succ m ih =>
|
||||
rw [← Nat.add_assoc, BitVec.pow_succ, ih, BitVec.mul_assoc, BitVec.pow_succ]
|
||||
|
||||
/-! ### le and lt -/
|
||||
|
||||
@[bitvec_to_nat] theorem le_def {x y : BitVec n} :
|
||||
|
||||
@@ -976,6 +976,16 @@ theorem coe_sub_iff_lt {a b : Fin n} : (↑(a - b) : Nat) = n + a - b ↔ a < b
|
||||
|
||||
/-! ### mul -/
|
||||
|
||||
theorem ofNat'_mul [NeZero n] (x : Nat) (y : Fin n) :
|
||||
Fin.ofNat' n x * y = Fin.ofNat' n (x * y.val) := by
|
||||
apply Fin.eq_of_val_eq
|
||||
simp [Fin.ofNat', Fin.mul_def]
|
||||
|
||||
theorem mul_ofNat' [NeZero n] (x : Fin n) (y : Nat) :
|
||||
x * Fin.ofNat' n y = Fin.ofNat' n (x.val * y) := by
|
||||
apply Fin.eq_of_val_eq
|
||||
simp [Fin.ofNat', Fin.mul_def]
|
||||
|
||||
theorem val_mul {n : Nat} : ∀ a b : Fin n, (a * b).val = a.val * b.val % n
|
||||
| ⟨_, _⟩, ⟨_, _⟩ => rfl
|
||||
|
||||
|
||||
@@ -420,6 +420,8 @@ instance : IntCast Int where intCast n := n
|
||||
protected def Int.cast {R : Type u} [IntCast R] : Int → R :=
|
||||
IntCast.intCast
|
||||
|
||||
@[simp] theorem Int.cast_eq (x : Int) : Int.cast x = x := rfl
|
||||
|
||||
-- see the notes about coercions into arbitrary types in the module doc-string
|
||||
instance [IntCast R] : CoeTail Int R where coe := Int.cast
|
||||
|
||||
|
||||
@@ -24,6 +24,11 @@ namespace Int
|
||||
protected theorem exists_add_of_le {a b : Int} (h : a ≤ b) : ∃ (c : Nat), b = a + c :=
|
||||
⟨(b - a).toNat, by omega⟩
|
||||
|
||||
theorem toNat_emod {x y : Int} (hx : 0 ≤ x) (hy : 0 ≤ y) :
|
||||
(x % y).toNat = x.toNat % y.toNat :=
|
||||
match x, y, eq_ofNat_of_zero_le hx, eq_ofNat_of_zero_le hy with
|
||||
| _, _, ⟨_, rfl⟩, ⟨_, rfl⟩ => rfl
|
||||
|
||||
/-! ### dvd -/
|
||||
|
||||
theorem dvd_antisymm {a b : Int} (H1 : 0 ≤ a) (H2 : 0 ≤ b) : a ∣ b → b ∣ a → a = b := by
|
||||
@@ -2140,6 +2145,11 @@ theorem bmod_pos (x : Int) (m : Nat) (p : x % m < (m + 1) / 2) : bmod x m = x %
|
||||
theorem bmod_neg (x : Int) (m : Nat) (p : x % m ≥ (m + 1) / 2) : bmod x m = (x % m) - m := by
|
||||
simp [bmod_def, Int.not_lt.mpr p]
|
||||
|
||||
theorem bmod_eq_emod (x : Int) (m : Nat) : bmod x m = x % m - if x % m ≥ (m + 1) / 2 then m else 0 := by
|
||||
split
|
||||
· rwa [bmod_neg]
|
||||
· rw [bmod_pos] <;> simp_all
|
||||
|
||||
@[simp]
|
||||
theorem bmod_one_is_zero (x : Int) : Int.bmod x 1 = 0 := by
|
||||
simp [Int.bmod]
|
||||
@@ -2368,6 +2378,43 @@ 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]
|
||||
|
||||
theorem bmod_neg_iff {m : Nat} {x : Int} (h2 : -m ≤ x) (h1 : x < m) :
|
||||
(x.bmod m) < 0 ↔ (-(m / 2) ≤ x ∧ x < 0) ∨ ((m + 1) / 2 ≤ x) := by
|
||||
simp only [Int.bmod_def]
|
||||
by_cases xpos : 0 ≤ x
|
||||
· rw [Int.emod_eq_of_lt xpos (by omega)]; omega
|
||||
· rw [Int.add_emod_self.symm, Int.emod_eq_of_lt (by omega) (by omega)]; omega
|
||||
|
||||
theorem bmod_eq_self_of_le {n : Int} {m : Nat} (hn' : -(m / 2) ≤ n) (hn : n < (m + 1) / 2) :
|
||||
n.bmod m = n := by
|
||||
rw [← Int.sub_eq_zero]
|
||||
have := le_bmod (x := n) (m := m) (by omega)
|
||||
have := bmod_lt (x := n) (m := m) (by omega)
|
||||
apply eq_zero_of_dvd_of_natAbs_lt_natAbs Int.dvd_bmod_sub_self
|
||||
omega
|
||||
|
||||
theorem bmod_bmod_of_dvd {a : Int} {n m : Nat} (hnm : n ∣ m) :
|
||||
(a.bmod m).bmod n = a.bmod n := by
|
||||
rw [← Int.sub_eq_iff_eq_add.2 (bmod_add_bdiv a m).symm]
|
||||
obtain ⟨k, rfl⟩ := hnm
|
||||
simp [Int.mul_assoc]
|
||||
|
||||
theorem bmod_eq_self_of_le_mul_two {x : Int} {y : Nat} (hle : -y ≤ x * 2) (hlt : x * 2 < y) :
|
||||
x.bmod y = x := by
|
||||
apply bmod_eq_self_of_le (by omega) (by omega)
|
||||
|
||||
theorem dvd_iff_bmod_eq_zero {a : Nat} {b : Int} : (a : Int) ∣ b ↔ b.bmod a = 0 := by
|
||||
rw [dvd_iff_emod_eq_zero, bmod]
|
||||
split <;> rename_i h
|
||||
· rfl
|
||||
· simp only [Int.not_lt] at h
|
||||
match a with
|
||||
| 0 => omega
|
||||
| a + 1 =>
|
||||
have : b % (a+1) < a + 1 := emod_lt b (by omega)
|
||||
simp_all
|
||||
omega
|
||||
|
||||
/-! 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
|
||||
|
||||
@@ -377,6 +377,11 @@ theorem toNat_of_nonpos : ∀ {z : Int}, z ≤ 0 → z.toNat = 0
|
||||
@[simp] theorem negSucc_add_one_eq_neg_ofNat_iff {a b : Nat} : -[a+1] + 1 = - (b : Int) ↔ a = b := by
|
||||
rw [eq_comm, neg_ofNat_eq_negSucc_add_one_iff, eq_comm]
|
||||
|
||||
protected theorem sub_eq_iff_eq_add {b a c : Int} : a - b = c ↔ a = c + b := by
|
||||
refine ⟨fun h => ?_, fun h => ?_⟩ <;> subst h <;> simp
|
||||
protected theorem sub_eq_iff_eq_add' {b a c : Int} : a - b = c ↔ a = b + c := by
|
||||
rw [Int.sub_eq_iff_eq_add, Int.add_comm]
|
||||
|
||||
/- ## add/sub injectivity -/
|
||||
|
||||
@[simp] protected theorem add_left_inj {i j : Int} (k : Int) : (i + k = j + k) ↔ i = j := by
|
||||
|
||||
@@ -19,9 +19,6 @@ namespace Int
|
||||
|
||||
@[simp] theorem natCast_le_zero : {n : Nat} → (n : Int) ≤ 0 ↔ n = 0 := by omega
|
||||
|
||||
protected theorem sub_eq_iff_eq_add {b a c : Int} : a - b = c ↔ a = c + b := by omega
|
||||
protected theorem sub_eq_iff_eq_add' {b a c : Int} : a - b = c ↔ a = b + c := by omega
|
||||
|
||||
@[simp] protected theorem neg_nonpos_iff (i : Int) : -i ≤ 0 ↔ 0 ≤ i := by omega
|
||||
|
||||
@[simp] theorem zero_le_ofNat (n : Nat) : 0 ≤ ((no_index (OfNat.ofNat n)) : Int) :=
|
||||
@@ -81,15 +78,6 @@ theorem eq_ofNat_toNat {a : Int} : a = a.toNat ↔ 0 ≤ a := by omega
|
||||
theorem toNat_le_toNat {n m : Int} (h : n ≤ m) : n.toNat ≤ m.toNat := by omega
|
||||
theorem toNat_lt_toNat {n m : Int} (hn : 0 < m) : n.toNat < m.toNat ↔ n < m := by omega
|
||||
|
||||
/-! ### natAbs -/
|
||||
|
||||
theorem eq_zero_of_dvd_of_natAbs_lt_natAbs {d n : Int} (h : d ∣ n) (h₁ : n.natAbs < d.natAbs) :
|
||||
n = 0 := by
|
||||
obtain ⟨a, rfl⟩ := h
|
||||
rw [natAbs_mul] at h₁
|
||||
suffices ¬ 0 < a.natAbs by simp [Int.natAbs_eq_zero.1 (Nat.eq_zero_of_not_pos this)]
|
||||
exact fun h => Nat.lt_irrefl _ (Nat.lt_of_le_of_lt (Nat.le_mul_of_pos_right d.natAbs h) h₁)
|
||||
|
||||
/-! ### min and max -/
|
||||
|
||||
@[simp] protected theorem min_assoc : ∀ (a b c : Int), min (min a b) c = min a (min b c) := by omega
|
||||
@@ -128,33 +116,6 @@ protected theorem sub_min_sub_left (a b c : Int) : min (a - b) (a - c) = a - max
|
||||
|
||||
protected theorem sub_max_sub_left (a b c : Int) : max (a - b) (a - c) = a - min b c := by omega
|
||||
|
||||
/-! ### bmod -/
|
||||
|
||||
theorem bmod_neg_iff {m : Nat} {x : Int} (h2 : -m ≤ x) (h1 : x < m) :
|
||||
(x.bmod m) < 0 ↔ (-(m / 2) ≤ x ∧ x < 0) ∨ ((m + 1) / 2 ≤ x) := by
|
||||
simp only [Int.bmod_def]
|
||||
by_cases xpos : 0 ≤ x
|
||||
· rw [Int.emod_eq_of_lt xpos (by omega)]; omega
|
||||
· rw [Int.add_emod_self.symm, Int.emod_eq_of_lt (by omega) (by omega)]; omega
|
||||
|
||||
theorem bmod_eq_self_of_le {n : Int} {m : Nat} (hn' : -(m / 2) ≤ n) (hn : n < (m + 1) / 2) :
|
||||
n.bmod m = n := by
|
||||
rw [← Int.sub_eq_zero]
|
||||
have := le_bmod (x := n) (m := m) (by omega)
|
||||
have := bmod_lt (x := n) (m := m) (by omega)
|
||||
apply eq_zero_of_dvd_of_natAbs_lt_natAbs Int.dvd_bmod_sub_self
|
||||
omega
|
||||
|
||||
theorem bmod_bmod_of_dvd {a : Int} {n m : Nat} (hnm : n ∣ m) :
|
||||
(a.bmod m).bmod n = a.bmod n := by
|
||||
rw [← Int.sub_eq_iff_eq_add.2 (bmod_add_bdiv a m).symm]
|
||||
obtain ⟨k, rfl⟩ := hnm
|
||||
simp [Int.mul_assoc]
|
||||
|
||||
theorem bmod_eq_self_of_le_mul_two {x : Int} {y : Nat} (hle : -y ≤ x * 2) (hlt : x * 2 < y) :
|
||||
x.bmod y = x := by
|
||||
apply bmod_eq_self_of_le (by omega) (by omega)
|
||||
|
||||
theorem mul_le_mul_of_natAbs_le {x y : Int} {s t : Nat} (hx : x.natAbs ≤ s) (hy : y.natAbs ≤ t) :
|
||||
x * y ≤ s * t := by
|
||||
by_cases 0 < s ∧ 0 < t
|
||||
|
||||
@@ -1827,6 +1827,14 @@ theorem eq_def (ctx : Context) (x : Var) (xPoly : Poly) (p : Poly)
|
||||
simp [eq_def_cert]; intro _ h; subst p; simp [h]
|
||||
rw [← Int.sub_eq_add_neg, Int.sub_self]
|
||||
|
||||
def eq_def'_cert (x : Var) (e : Expr) (p : Poly) : Bool :=
|
||||
p == .add (-1) x e.norm
|
||||
|
||||
theorem eq_def' (ctx : Context) (x : Var) (e : Expr) (p : Poly)
|
||||
: eq_def'_cert x e p → x.denote ctx = e.denote ctx → p.denote' ctx = 0 := by
|
||||
simp [eq_def'_cert]; intro _ h; subst p; simp [h]
|
||||
rw [← Int.sub_eq_add_neg, Int.sub_self]
|
||||
|
||||
end Int.Linear
|
||||
|
||||
theorem Int.not_le_eq (a b : Int) : (¬a ≤ b) = (b + 1 ≤ a) := by
|
||||
|
||||
@@ -6,6 +6,7 @@ Authors: Leonardo de Moura
|
||||
prelude
|
||||
import Init.Data.Int.Lemmas
|
||||
import Init.Data.Int.DivMod
|
||||
import Init.Data.Int.Linear
|
||||
import Init.Data.RArray
|
||||
|
||||
namespace Int.OfNat
|
||||
@@ -47,6 +48,9 @@ def Expr.denoteAsInt (ctx : Context) : Expr → Int
|
||||
theorem Expr.denoteAsInt_eq (ctx : Context) (e : Expr) : e.denoteAsInt ctx = e.denote ctx := by
|
||||
induction e <;> simp [denote, denoteAsInt, Int.ofNat_ediv, *] <;> rfl
|
||||
|
||||
theorem Expr.eq_denoteAsInt (ctx : Context) (e : Expr) : e.denote ctx = e.denoteAsInt ctx := by
|
||||
apply Eq.symm; apply denoteAsInt_eq
|
||||
|
||||
theorem Expr.eq (ctx : Context) (lhs rhs : Expr)
|
||||
: (lhs.denote ctx = rhs.denote ctx) = (lhs.denoteAsInt ctx = rhs.denoteAsInt ctx) := by
|
||||
simp [denoteAsInt_eq, Int.ofNat_inj]
|
||||
|
||||
@@ -562,6 +562,16 @@ theorem natAbs_sub_of_nonneg_of_le {a b : Int} (h₁ : 0 ≤ b) (h₂ : b ≤ a)
|
||||
· rwa [← Int.ofNat_le, natAbs_of_nonneg h₁, natAbs_of_nonneg (Int.le_trans h₁ h₂)]
|
||||
· exact Int.sub_nonneg_of_le h₂
|
||||
|
||||
theorem eq_zero_of_dvd_of_natAbs_lt_natAbs {d n : Int} (h : d ∣ n) (h₁ : n.natAbs < d.natAbs) :
|
||||
n = 0 := by
|
||||
let ⟨a, ha⟩ := h
|
||||
subst ha
|
||||
rw [natAbs_mul] at h₁
|
||||
suffices ¬ 0 < a.natAbs by simp [Int.natAbs_eq_zero.1 (Nat.eq_zero_of_not_pos this)]
|
||||
refine fun h => Nat.lt_irrefl _ (Nat.lt_of_le_of_lt ?_ h₁)
|
||||
rw (occs := [1]) [← Nat.mul_one d.natAbs]
|
||||
exact Nat.mul_le_mul (Nat.le_refl _) h
|
||||
|
||||
/-! ### toNat -/
|
||||
|
||||
theorem toNat_eq_max : ∀ a : Int, (toNat a : Int) = max a 0
|
||||
@@ -599,6 +609,18 @@ theorem toNat_add {a b : Int} (ha : 0 ≤ a) (hb : 0 ≤ b) : (a + b).toNat = a.
|
||||
match a, b, eq_ofNat_of_zero_le ha, eq_ofNat_of_zero_le hb with
|
||||
| _, _, ⟨_, rfl⟩, ⟨_, rfl⟩ => rfl
|
||||
|
||||
theorem toNat_mul {a b : Int} (ha : 0 ≤ a) (hb : 0 ≤ b) : (a * b).toNat = a.toNat * b.toNat :=
|
||||
match a, b, eq_ofNat_of_zero_le ha, eq_ofNat_of_zero_le hb with
|
||||
| _, _, ⟨_, rfl⟩, ⟨_, rfl⟩ => rfl
|
||||
|
||||
/--
|
||||
Variant of `Int.toNat_sub` taking non-negativity hypotheses,
|
||||
rather than expecting the arguments to be casts of natural numbers.
|
||||
-/
|
||||
theorem toNat_sub'' {a b : Int} (ha : 0 ≤ a) (hb : 0 ≤ b) : (a - b).toNat = a.toNat - b.toNat :=
|
||||
match a, b, eq_ofNat_of_zero_le ha, eq_ofNat_of_zero_le hb with
|
||||
| _, _, ⟨_, rfl⟩, ⟨_, rfl⟩ => toNat_sub _ _
|
||||
|
||||
theorem toNat_add_nat {a : Int} (ha : 0 ≤ a) (n : Nat) : (a + n).toNat = a.toNat + n :=
|
||||
match a, eq_ofNat_of_zero_le ha with | _, ⟨_, rfl⟩ => rfl
|
||||
|
||||
|
||||
@@ -334,7 +334,7 @@ theorem getElem_attach {xs : List α} {i : Nat} (h : i < xs.attach.length) :
|
||||
|
||||
@[simp] theorem head?_attachWith {P : α → Prop} {xs : List α}
|
||||
(H : ∀ (a : α), a ∈ xs → P a) :
|
||||
(xs.attachWith P H).head? = xs.head?.pbind (fun a h => some ⟨a, H _ (mem_of_mem_head? h)⟩) := by
|
||||
(xs.attachWith P H).head? = xs.head?.pbind (fun a h => some ⟨a, H _ (mem_of_head? h)⟩) := by
|
||||
cases xs <;> simp_all
|
||||
|
||||
@[simp] theorem head_attachWith {P : α → Prop} {xs : List α}
|
||||
@@ -345,7 +345,7 @@ theorem getElem_attach {xs : List α} {i : Nat} (h : i < xs.attach.length) :
|
||||
| cons x xs => simp [head_attachWith, h]
|
||||
|
||||
@[simp] theorem head?_attach {xs : List α} :
|
||||
xs.attach.head? = xs.head?.pbind (fun a h => some ⟨a, mem_of_mem_head? h⟩) := by
|
||||
xs.attach.head? = xs.head?.pbind (fun a h => some ⟨a, mem_of_head? h⟩) := by
|
||||
cases xs <;> simp_all
|
||||
|
||||
@[simp] theorem head_attach {xs : List α} (h) :
|
||||
@@ -470,20 +470,19 @@ theorem attach_filterMap {l : List α} {f : α → Option β} :
|
||||
| cons x xs ih =>
|
||||
simp only [filterMap_cons, attach_cons, ih, filterMap_map]
|
||||
split <;> rename_i h
|
||||
· simp only [Option.pbind_eq_none_iff, reduceCtorEq, Option.mem_def, exists_false,
|
||||
· simp only [Option.pbind_eq_none_iff, reduceCtorEq, exists_false,
|
||||
or_false] at h
|
||||
rw [attach_congr]
|
||||
rotate_left
|
||||
· simp only [h]
|
||||
rfl
|
||||
rw [ih]
|
||||
simp only [map_filterMap, Option.map_pbind, Option.map_some']
|
||||
simp only [map_filterMap, Option.map_pbind, Option.map_some]
|
||||
rfl
|
||||
· simp only [Option.pbind_eq_some_iff] at h
|
||||
obtain ⟨a, h, w⟩ := h
|
||||
simp only [Option.some.injEq] at w
|
||||
subst w
|
||||
simp only [Option.mem_def] at h
|
||||
rw [attach_congr]
|
||||
rotate_left
|
||||
· simp only [h]
|
||||
|
||||
@@ -2048,10 +2048,10 @@ def sum {α} [Add α] [Zero α] : List α → α :=
|
||||
protected def _root_.Nat.sum (l : List Nat) : Nat := l.foldr (·+·) 0
|
||||
|
||||
set_option linter.deprecated false in
|
||||
@[simp, deprecated sum_nil (since := "2024-10-17")]
|
||||
@[deprecated sum_nil (since := "2024-10-17")]
|
||||
theorem _root_.Nat.sum_nil : Nat.sum ([] : List Nat) = 0 := rfl
|
||||
set_option linter.deprecated false in
|
||||
@[simp, deprecated sum_cons (since := "2024-10-17")]
|
||||
@[deprecated sum_cons (since := "2024-10-17")]
|
||||
theorem _root_.Nat.sum_cons (a : Nat) (l : List Nat) :
|
||||
Nat.sum (a::l) = a + Nat.sum l := rfl
|
||||
|
||||
|
||||
@@ -95,10 +95,10 @@ theorem findSome?_eq_some_iff {f : α → Option β} {l : List α} {b : β} :
|
||||
| cons x xs ih =>
|
||||
simp [guard, findSome?, find?]
|
||||
split <;> rename_i h
|
||||
· simp only [Option.guard_eq_some] at h
|
||||
· simp only [Option.guard_eq_some_iff] at h
|
||||
obtain ⟨rfl, h⟩ := h
|
||||
simp [h]
|
||||
· simp only [Option.guard_eq_none] at h
|
||||
· simp only [Option.guard_eq_none_iff] at h
|
||||
simp [ih, h]
|
||||
|
||||
theorem find?_eq_findSome?_guard {l : List α} : find? p l = findSome? (Option.guard fun x => p x) l :=
|
||||
@@ -700,6 +700,7 @@ theorem findIdx?_eq_none_iff {xs : List α} {p : α → Bool} :
|
||||
simp only [findIdx?_cons]
|
||||
split <;> simp_all [cond_eq_if]
|
||||
|
||||
@[simp]
|
||||
theorem findIdx?_isSome {xs : List α} {p : α → Bool} :
|
||||
(xs.findIdx? p).isSome = xs.any p := by
|
||||
induction xs with
|
||||
@@ -708,6 +709,7 @@ theorem findIdx?_isSome {xs : List α} {p : α → Bool} :
|
||||
simp only [findIdx?_cons]
|
||||
split <;> simp_all
|
||||
|
||||
@[simp]
|
||||
theorem findIdx?_isNone {xs : List α} {p : α → Bool} :
|
||||
(xs.findIdx? p).isNone = xs.all (¬p ·) := by
|
||||
induction xs with
|
||||
@@ -768,7 +770,7 @@ theorem findIdx?_eq_some_iff_getElem {xs : List α} {p : α → Bool} {i : Nat}
|
||||
not_and, Classical.not_forall, Bool.not_eq_false]
|
||||
intros
|
||||
refine ⟨0, zero_lt_succ i, ‹_›⟩
|
||||
· simp only [Option.map_eq_some', ih, Bool.not_eq_true, length_cons]
|
||||
· simp only [Option.map_eq_some_iff, ih, Bool.not_eq_true, length_cons]
|
||||
constructor
|
||||
· rintro ⟨a, ⟨⟨h, h₁, h₂⟩, rfl⟩⟩
|
||||
refine ⟨Nat.succ_lt_succ_iff.mpr h, by simpa, fun j hj => ?_⟩
|
||||
@@ -824,7 +826,7 @@ abbrev findIdx?_of_eq_none := @of_findIdx?_eq_none
|
||||
(xs ++ ys : List α).findIdx? p =
|
||||
(xs.findIdx? p).or ((ys.findIdx? p).map fun i => i + xs.length) := by
|
||||
induction xs with simp
|
||||
| cons _ _ _ => split <;> simp_all [Option.map_or', Option.map_map]; rfl
|
||||
| cons _ _ _ => split <;> simp_all [Option.map_or, Option.map_map]; rfl
|
||||
|
||||
theorem findIdx?_flatten {l : List (List α)} {p : α → Bool} :
|
||||
l.flatten.findIdx? p =
|
||||
@@ -977,13 +979,31 @@ theorem findFinIdx?_eq_some_iff {xs : List α} {p : α → Bool} {i : Fin xs.len
|
||||
xs.findFinIdx? p = some i ↔
|
||||
p xs[i] ∧ ∀ j (hji : j < i), ¬p (xs[j]'(Nat.lt_trans hji i.2)) := by
|
||||
simp only [findFinIdx?_eq_pmap_findIdx?, Option.pmap_eq_some_iff, findIdx?_eq_some_iff_getElem,
|
||||
Bool.not_eq_true, Option.mem_def, exists_and_left, and_exists_self, Fin.getElem_fin]
|
||||
Bool.not_eq_true, exists_and_left, and_exists_self, Fin.getElem_fin]
|
||||
constructor
|
||||
· rintro ⟨a, ⟨h, w₁, w₂⟩, rfl⟩
|
||||
exact ⟨w₁, fun j hji => by simpa using w₂ j hji⟩
|
||||
· rintro ⟨h, w⟩
|
||||
exact ⟨i, ⟨i.2, h, fun j hji => w ⟨j, by omega⟩ hji⟩, rfl⟩
|
||||
|
||||
@[simp]
|
||||
theorem isSome_findFinIdx? {l : List α} {p : α → Bool} :
|
||||
(l.findFinIdx? p).isSome = l.any p := by
|
||||
induction l with
|
||||
| nil => simp
|
||||
| cons x xs ih =>
|
||||
simp only [findFinIdx?_cons]
|
||||
split <;> simp_all
|
||||
|
||||
@[simp]
|
||||
theorem isNone_findFinIdx? {l : List α} {p : α → Bool} :
|
||||
(l.findFinIdx? p).isNone = l.all (fun x => ¬ p x) := by
|
||||
induction l with
|
||||
| nil => simp
|
||||
| cons x xs ih =>
|
||||
simp only [findFinIdx?_cons]
|
||||
split <;> simp_all
|
||||
|
||||
@[simp] theorem findFinIdx?_subtype {p : α → Prop} {l : List { x // p x }}
|
||||
{f : { x // p x } → Bool} {g : α → Bool} (hf : ∀ x h, f ⟨x, h⟩ = g x) :
|
||||
l.findFinIdx? f = (l.unattach.findFinIdx? g).map (fun i => i.cast (by simp)) := by
|
||||
@@ -1084,6 +1104,24 @@ theorem idxOf?_eq_map_finIdxOf?_val [BEq α] {xs : List α} {a : α} :
|
||||
l.finIdxOf? a = some i ↔ l[i] = a ∧ ∀ j (_ : j < i), ¬l[j] = a := by
|
||||
simp only [finIdxOf?, findFinIdx?_eq_some_iff, beq_iff_eq]
|
||||
|
||||
@[simp]
|
||||
theorem isSome_finIdxOf? [BEq α] [LawfulBEq α] {l : List α} {a : α} :
|
||||
(l.finIdxOf? a).isSome ↔ a ∈ l := by
|
||||
induction l with
|
||||
| nil => simp
|
||||
| cons x xs ih =>
|
||||
simp only [finIdxOf?_cons]
|
||||
split <;> simp_all [@eq_comm _ x a]
|
||||
|
||||
@[simp]
|
||||
theorem isNone_finIdxOf? [BEq α] [LawfulBEq α] {l : List α} {a : α} :
|
||||
(l.finIdxOf? a).isNone = ¬ a ∈ l := by
|
||||
induction l with
|
||||
| nil => simp
|
||||
| cons x xs ih =>
|
||||
simp only [finIdxOf?_cons]
|
||||
split <;> simp_all [@eq_comm _ x a]
|
||||
|
||||
/-! ### idxOf?
|
||||
|
||||
The verification API for `idxOf?` is still incomplete.
|
||||
@@ -1109,6 +1147,25 @@ theorem idxOf?_cons [BEq α] {a : α} {xs : List α} {b : α} :
|
||||
@[deprecated idxOf?_eq_none_iff (since := "2025-01-29")]
|
||||
abbrev indexOf?_eq_none_iff := @idxOf?_eq_none_iff
|
||||
|
||||
@[simp]
|
||||
theorem isSome_idxOf? [BEq α] [LawfulBEq α] {l : List α} {a : α} :
|
||||
(l.idxOf? a).isSome ↔ a ∈ l := by
|
||||
induction l with
|
||||
| nil => simp
|
||||
| cons x xs ih =>
|
||||
simp only [idxOf?_cons]
|
||||
split <;> simp_all [@eq_comm _ x a]
|
||||
|
||||
@[simp]
|
||||
theorem isNone_idxOf? [BEq α] [LawfulBEq α] {l : List α} {a : α} :
|
||||
(l.idxOf? a).isNone = ¬ a ∈ l := by
|
||||
induction l with
|
||||
| nil => simp
|
||||
| cons x xs ih =>
|
||||
simp only [idxOf?_cons]
|
||||
split <;> simp_all [@eq_comm _ x a]
|
||||
|
||||
|
||||
/-! ### lookup -/
|
||||
|
||||
section lookup
|
||||
|
||||
@@ -848,7 +848,9 @@ theorem getLast!_cons_eq_getLastD [Inhabited α] : @getLast! α _ (a::l) = getLa
|
||||
| _::a::l, _ => .tail _ <| getLast_mem (cons_ne_nil a l)
|
||||
|
||||
theorem getLast_mem_getLast? : ∀ {l : List α} (h : l ≠ []), getLast l h ∈ getLast? l
|
||||
| [], h => by contradiction
|
||||
| _ :: _, _ => rfl
|
||||
|
||||
theorem getLast?_eq_some_getLast : ∀ {l : List α} (h : l ≠ []), getLast? l = some (getLast l h)
|
||||
| _ :: _, _ => rfl
|
||||
|
||||
theorem getLastD_mem_cons : ∀ {l : List α} {a : α}, getLastD l a ∈ a::l
|
||||
@@ -964,17 +966,16 @@ abbrev head?_isSome := @isSome_head?
|
||||
| [], h => absurd rfl h
|
||||
| _::_, _ => .head ..
|
||||
|
||||
theorem mem_of_mem_head? : ∀ {l : List α} {a : α}, a ∈ l.head? → a ∈ l := by
|
||||
intro l a h
|
||||
cases l with
|
||||
| nil => simp at h
|
||||
| cons b l =>
|
||||
simp at h
|
||||
cases h
|
||||
exact mem_cons_self
|
||||
theorem mem_of_head? : {l : List α} → {a : α} → l.head? = some a → a ∈ l
|
||||
| _::_, _, h => Option.some.inj h ▸ mem_cons_self
|
||||
|
||||
theorem mem_of_mem_head? : ∀ {l : List α} {a : α}, a ∈ l.head? → a ∈ l :=
|
||||
mem_of_head?
|
||||
|
||||
theorem head_mem_head? : ∀ {l : List α} (h : l ≠ []), head l h ∈ head? l
|
||||
| [], h => by contradiction
|
||||
| _ :: _, _ => rfl
|
||||
|
||||
theorem head?_eq_some_head : ∀ {l : List α} (h : l ≠ []), head? l = some (head l h)
|
||||
| _ :: _, _ => rfl
|
||||
|
||||
theorem head?_concat {a : α} : (l ++ [a]).head? = l.head?.getD a := by
|
||||
@@ -983,11 +984,13 @@ theorem head?_concat {a : α} : (l ++ [a]).head? = l.head?.getD a := by
|
||||
theorem head?_concat_concat : (l ++ [a, b]).head? = (l ++ [a]).head? := by
|
||||
cases l <;> simp
|
||||
|
||||
theorem head_of_head?_eq_some {l : List α} {x} (hx : l.head? = some x) :
|
||||
l.head (ne_nil_of_mem (mem_of_head? hx)) = x := by
|
||||
rw [← Option.some_inj, ← head?_eq_some_head, hx]
|
||||
|
||||
theorem head_of_mem_head? {l : List α} {x} (hx : x ∈ l.head?) :
|
||||
l.head (ne_nil_of_mem (mem_of_mem_head? hx)) = x := by
|
||||
cases l
|
||||
· contradiction
|
||||
· simpa using hx
|
||||
l.head (ne_nil_of_mem (mem_of_mem_head? hx)) = x :=
|
||||
head_of_head?_eq_some hx
|
||||
|
||||
/-! ### headD -/
|
||||
|
||||
@@ -2151,7 +2154,7 @@ theorem replicate_succ' : replicate (n + 1) a = replicate n a ++ [a] := by
|
||||
| 0 => by simp
|
||||
| n+1 => by simp [replicate_succ, mem_replicate, Nat.succ_ne_zero]
|
||||
|
||||
@[simp, deprecated mem_replicate (since := "2024-09-05")]
|
||||
@[deprecated mem_replicate (since := "2024-09-05")]
|
||||
theorem contains_replicate [BEq α] {n : Nat} {a b : α} :
|
||||
(replicate n b).contains a = (a == b && !n == 0) := by
|
||||
induction n with
|
||||
@@ -2160,7 +2163,7 @@ theorem contains_replicate [BEq α] {n : Nat} {a b : α} :
|
||||
simp only [replicate_succ, elem_cons]
|
||||
split <;> simp_all
|
||||
|
||||
@[simp, deprecated mem_replicate (since := "2024-09-05")]
|
||||
@[deprecated mem_replicate (since := "2024-09-05")]
|
||||
theorem decide_mem_replicate [BEq α] [LawfulBEq α] {a b : α} :
|
||||
∀ {n}, decide (b ∈ replicate n a) = ((¬ n == 0) && b == a)
|
||||
| 0 => by simp
|
||||
@@ -2469,18 +2472,19 @@ theorem getLast?_eq_head?_reverse {xs : List α} : xs.getLast? = xs.reverse.head
|
||||
theorem head?_eq_getLast?_reverse {xs : List α} : xs.head? = xs.reverse.getLast? := by
|
||||
simp
|
||||
|
||||
theorem mem_of_mem_getLast? {l : List α} {a : α} (h : a ∈ getLast? l) : a ∈ l := by
|
||||
rw [getLast?_eq_head?_reverse] at h
|
||||
rw [← mem_reverse]
|
||||
exact mem_of_mem_head? h
|
||||
theorem mem_of_getLast? {l : List α} {a : α} (h : getLast? l = some a) : a ∈ l :=
|
||||
mem_reverse.1 (mem_of_head? (getLast?_eq_head?_reverse ▸ h))
|
||||
|
||||
theorem mem_of_mem_getLast? {l : List α} {a : α} (h : a ∈ getLast? l) : a ∈ l :=
|
||||
mem_of_getLast? h
|
||||
|
||||
theorem getLast_of_getLast?_eq_some {l : List α} (hx : l.getLast? = some x) :
|
||||
l.getLast (ne_nil_of_mem (mem_of_getLast? hx)) = x := by
|
||||
rw [← Option.some_inj, ← getLast?_eq_some_getLast, hx]
|
||||
|
||||
theorem getLast_of_mem_getLast? {l : List α} (hx : x ∈ l.getLast?) :
|
||||
l.getLast (ne_nil_of_mem (mem_of_mem_getLast? hx)) = x := by
|
||||
rw [Option.mem_def] at hx
|
||||
cases l
|
||||
· contradiction
|
||||
· rw [← Option.some_inj, ← hx]
|
||||
rfl
|
||||
l.getLast (ne_nil_of_mem (mem_of_mem_getLast? hx)) = x :=
|
||||
getLast_of_getLast?_eq_some hx
|
||||
|
||||
@[simp] theorem map_reverse {f : α → β} {l : List α} : l.reverse.map f = (l.map f).reverse := by
|
||||
induction l <;> simp [*]
|
||||
@@ -2860,10 +2864,6 @@ theorem getLast?_eq_some_iff {xs : List α} {a : α} : xs.getLast? = some a ↔
|
||||
rw [getLast?_eq_head?_reverse, isSome_head?]
|
||||
simp
|
||||
|
||||
theorem mem_of_getLast? {xs : List α} {a : α} (h : xs.getLast? = some a) : a ∈ xs := by
|
||||
obtain ⟨ys, rfl⟩ := getLast?_eq_some_iff.1 h
|
||||
exact mem_concat_self
|
||||
|
||||
@[deprecated mem_of_getLast? (since := "2024-10-21")] abbrev mem_of_getLast?_eq_some := @mem_of_getLast?
|
||||
|
||||
@[simp] theorem getLast_reverse {l : List α} (h : l.reverse ≠ []) :
|
||||
|
||||
@@ -339,7 +339,7 @@ theorem getElem?_mapIdx_go : ∀ {l : List α} {acc : Array β} {i : Nat},
|
||||
if h : i < acc.size then some acc[i] else Option.map (f i) l[i - acc.size]?
|
||||
| [], acc, i => by
|
||||
simp only [mapIdx.go, Array.toListImpl_eq, getElem?_def, Array.length_toList,
|
||||
← Array.getElem_toList, length_nil, Nat.not_lt_zero, ↓reduceDIte, Option.map_none']
|
||||
← Array.getElem_toList, length_nil, Nat.not_lt_zero, ↓reduceDIte, Option.map_none]
|
||||
| a :: l, acc, i => by
|
||||
rw [mapIdx.go, getElem?_mapIdx_go]
|
||||
simp only [Array.size_push]
|
||||
|
||||
@@ -54,4 +54,23 @@ theorem set_set_perm {as : List α} {i j : Nat} (h₁ : i < as.length) (h₂ : j
|
||||
subst t
|
||||
apply set_set_perm' _ _ (by omega)
|
||||
|
||||
namespace Perm
|
||||
|
||||
/-- Variant of `List.Perm.take` specifying the the permutation is constant after `i` elementwise. -/
|
||||
theorem take' {l₁ l₂ : List α} (h : l₁ ~ l₂) {i : Nat} (w : ∀ j, i ≤ j → l₁[j]? = l₂[j]?) :
|
||||
(l₁.take i) ~ (l₂.take i) := by
|
||||
apply h.take
|
||||
ext1 j
|
||||
simpa using w (i + j) (by omega)
|
||||
|
||||
/-- Variant of `List.Perm.drop` specifying the the permutation is constant before `i` elementwise. -/
|
||||
theorem drop' {l₁ l₂ : List α} (h : l₁ ~ l₂) {i : Nat} (w : ∀ j, j < i → l₁[j]? = l₂[j]?) :
|
||||
(l₁.drop i) ~ (l₂.drop i) := by
|
||||
apply h.drop
|
||||
ext1
|
||||
simp only [getElem?_take]
|
||||
split <;> simp_all
|
||||
|
||||
end Perm
|
||||
|
||||
end List
|
||||
|
||||
@@ -239,7 +239,7 @@ dropping the first `i` elements. Version designed to rewrite from the small list
|
||||
@[simp]
|
||||
theorem getElem?_drop {xs : List α} {i j : Nat} : (xs.drop i)[j]? = xs[i + j]? := by
|
||||
ext
|
||||
simp only [getElem?_eq_some_iff, getElem_drop, Option.mem_def]
|
||||
simp only [getElem?_eq_some_iff, getElem_drop]
|
||||
constructor <;> intro ⟨h, ha⟩
|
||||
· exact ⟨_, ha⟩
|
||||
· refine ⟨?_, ha⟩
|
||||
|
||||
@@ -118,9 +118,8 @@ theorem Pairwise.map {S : β → β → Prop} (f : α → β) (H : ∀ a b : α,
|
||||
pairwise_map.2 <| p.imp (H _ _)
|
||||
|
||||
theorem pairwise_filterMap {f : β → Option α} {l : List β} :
|
||||
Pairwise R (filterMap f l) ↔ Pairwise (fun a a' : β => ∀ b ∈ f a, ∀ b' ∈ f a', R b b') l := by
|
||||
let _S (a a' : β) := ∀ b ∈ f a, ∀ b' ∈ f a', R b b'
|
||||
simp only [Option.mem_def]
|
||||
Pairwise R (filterMap f l) ↔ Pairwise (fun a a' : β => ∀ b, f a = some b → ∀ b', f a' = some b' → R b b') l := by
|
||||
let _S (a a' : β) := ∀ b, f a = some b → ∀ b', f a' = some b' → R b b'
|
||||
induction l with
|
||||
| nil => simp only [filterMap, Pairwise.nil]
|
||||
| cons a l IH => ?_
|
||||
@@ -134,7 +133,7 @@ theorem pairwise_filterMap {f : β → Option α} {l : List β} :
|
||||
⟨fun h a ha b hab => h _ _ ha hab, fun h a b ha hab => h _ ha _ hab⟩
|
||||
|
||||
theorem Pairwise.filterMap {S : β → β → Prop} (f : α → Option β)
|
||||
(H : ∀ a a' : α, R a a' → ∀ b ∈ f a, ∀ b' ∈ f a', S b b') {l : List α} (p : Pairwise R l) :
|
||||
(H : ∀ a a' : α, R a a' → ∀ b, f a = some b → ∀ b', f a' = some b' → S b b') {l : List α} (p : Pairwise R l) :
|
||||
Pairwise S (filterMap f l) :=
|
||||
pairwise_filterMap.2 <| p.imp (H _ _)
|
||||
|
||||
|
||||
@@ -536,4 +536,16 @@ theorem perm_insertIdx {α} (x : α) (l : List α) {i} (h : i ≤ l.length) :
|
||||
simp only [insertIdx, modifyTailIdx]
|
||||
refine .trans (.cons _ (ih (Nat.le_of_succ_le_succ h))) (.swap ..)
|
||||
|
||||
namespace Perm
|
||||
|
||||
theorem take {l₁ l₂ : List α} (h : l₁ ~ l₂) {n : Nat} (w : l₁.drop n = l₂.drop n) :
|
||||
(l₁.take n) ~ (l₂.take n) := by
|
||||
rwa [← List.take_append_drop n l₁, ← List.take_append_drop n l₂, w, perm_append_right_iff] at h
|
||||
|
||||
theorem drop {l₁ l₂ : List α} (h : l₁ ~ l₂) {n : Nat} (w : l₂.take n = l₁.take n) :
|
||||
(l₁.drop n) ~ (l₂.drop n) := by
|
||||
rwa [← List.take_append_drop n l₁, ← List.take_append_drop n l₂, w, perm_append_left_iff] at h
|
||||
|
||||
end Perm
|
||||
|
||||
end List
|
||||
|
||||
@@ -816,6 +816,11 @@ theorem le_shiftLeft {a b : Nat} : a ≤ a <<< b :=
|
||||
theorem lt_of_shiftLeft_lt {a b c : Nat} (h : a <<< b < c) : a < c :=
|
||||
Nat.lt_of_le_of_lt le_shiftLeft h
|
||||
|
||||
theorem shiftLeft_add_eq_or_of_lt {b : Nat} (b_lt : b < 2^i) (a : Nat) :
|
||||
a <<< i + b = a <<< i ||| b := by
|
||||
rw [shiftLeft_eq, Nat.mul_comm]
|
||||
rw [two_pow_add_eq_or_of_lt b_lt]
|
||||
|
||||
/-! ### le -/
|
||||
|
||||
theorem le_of_testBit {n m : Nat} (h : ∀ i, n.testBit i = true → m.testBit i = true) : n ≤ m := by
|
||||
|
||||
@@ -16,7 +16,7 @@ Unsafe implementation of `attachWith`, taking advantage of the fact that the rep
|
||||
`Option {x // P x}` is the same as the input `Option α`.
|
||||
-/
|
||||
@[inline] private unsafe def attachWithImpl
|
||||
(o : Option α) (P : α → Prop) (_ : ∀ x ∈ o, P x) : Option {x // P x} := unsafeCast o
|
||||
(o : Option α) (P : α → Prop) (_ : ∀ x, o = some x → P x) : Option {x // P x} := unsafeCast o
|
||||
|
||||
/--
|
||||
“Attaches” a proof that some predicate holds for an optional value, if present, returning a subtype
|
||||
@@ -28,10 +28,10 @@ value drawn from a parameter is smaller than the parameter. This allows the well
|
||||
mechanism to prove that the function terminates.
|
||||
-/
|
||||
@[implemented_by attachWithImpl] def attachWith
|
||||
(xs : Option α) (P : α → Prop) (H : ∀ x ∈ xs, P x) : Option {x // P x} :=
|
||||
(xs : Option α) (P : α → Prop) (H : ∀ x, xs = some x → P x) : Option {x // P x} :=
|
||||
match xs with
|
||||
| none => none
|
||||
| some x => some ⟨x, H x (mem_some_self x)⟩
|
||||
| some x => some ⟨x, H x rfl⟩
|
||||
|
||||
/--
|
||||
“Attaches” a proof that an optional value, if present, is indeed this value, returning a subtype
|
||||
@@ -42,14 +42,14 @@ operators (such as `Option.map`) to prove that an optional value drawn from a pa
|
||||
than the parameter. This allows the well-founded recursion mechanism to prove that the function
|
||||
terminates.
|
||||
-/
|
||||
@[inline] def attach (xs : Option α) : Option {x // x ∈ xs} := xs.attachWith _ fun _ => id
|
||||
@[inline] def attach (xs : Option α) : Option {x // xs = some x} := xs.attachWith _ fun _ => id
|
||||
|
||||
@[simp] theorem attach_none : (none : Option α).attach = none := rfl
|
||||
@[simp] theorem attachWith_none : (none : Option α).attachWith P H = none := rfl
|
||||
|
||||
@[simp] theorem attach_some {x : α} :
|
||||
(some x).attach = some ⟨x, rfl⟩ := rfl
|
||||
@[simp] theorem attachWith_some {x : α} {P : α → Prop} (h : ∀ (b : α), b ∈ some x → P b) :
|
||||
@[simp] theorem attachWith_some {x : α} {P : α → Prop} (h : ∀ (b : α), some x = some b → P b) :
|
||||
(some x).attachWith P h = some ⟨x, by simpa using h⟩ := rfl
|
||||
|
||||
theorem attach_congr {o₁ o₂ : Option α} (h : o₁ = o₂) :
|
||||
@@ -57,13 +57,13 @@ theorem attach_congr {o₁ o₂ : Option α} (h : o₁ = o₂) :
|
||||
subst h
|
||||
simp
|
||||
|
||||
theorem attachWith_congr {o₁ o₂ : Option α} (w : o₁ = o₂) {P : α → Prop} {H : ∀ x ∈ o₁, P x} :
|
||||
theorem attachWith_congr {o₁ o₂ : Option α} (w : o₁ = o₂) {P : α → Prop} {H : ∀ x, o₁ = some x → P x} :
|
||||
o₁.attachWith P H = o₂.attachWith P fun _ h => H _ (w ▸ h) := by
|
||||
subst w
|
||||
simp
|
||||
|
||||
theorem attach_map_val (o : Option α) (f : α → β) :
|
||||
(o.attach.map fun (i : {i // i ∈ o}) => f i) = o.map f := by
|
||||
(o.attach.map fun (i : {i // o = some i}) => f i) = o.map f := by
|
||||
cases o <;> simp
|
||||
|
||||
@[deprecated attach_map_val (since := "2025-02-17")]
|
||||
@@ -73,47 +73,50 @@ theorem attach_map_subtype_val (o : Option α) :
|
||||
o.attach.map Subtype.val = o :=
|
||||
(attach_map_val _ _).trans (congrFun Option.map_id _)
|
||||
|
||||
theorem attachWith_map_val {p : α → Prop} (f : α → β) (o : Option α) (H : ∀ a ∈ o, p a) :
|
||||
theorem attachWith_map_val {p : α → Prop} (f : α → β) (o : Option α) (H : ∀ a, o = some a → p a) :
|
||||
((o.attachWith p H).map fun (i : { i // p i}) => f i.val) = o.map f := by
|
||||
cases o <;> simp [H]
|
||||
|
||||
@[deprecated attachWith_map_val (since := "2025-02-17")]
|
||||
abbrev attachWith_map_coe := @attachWith_map_val
|
||||
|
||||
theorem attachWith_map_subtype_val {p : α → Prop} (o : Option α) (H : ∀ a ∈ o, p a) :
|
||||
theorem attachWith_map_subtype_val {p : α → Prop} (o : Option α) (H : ∀ a, o = some a → p a) :
|
||||
(o.attachWith p H).map Subtype.val = o :=
|
||||
(attachWith_map_val _ _ _).trans (congrFun Option.map_id _)
|
||||
|
||||
theorem mem_attach : ∀ (o : Option α) (x : {x // x ∈ o}), x ∈ o.attach
|
||||
theorem attach_eq_some : ∀ (o : Option a) (x : {x // o = some x}), o.attach = some x
|
||||
| none, ⟨x, h⟩ => by simp at h
|
||||
| some a, ⟨x, h⟩ => by simpa using h
|
||||
|
||||
theorem mem_attach : ∀ (o : Option α) (x : {x // o = some x}), x ∈ o.attach :=
|
||||
attach_eq_some
|
||||
|
||||
@[simp] theorem isNone_attach (o : Option α) : o.attach.isNone = o.isNone := by
|
||||
cases o <;> simp
|
||||
|
||||
@[simp] theorem isNone_attachWith {p : α → Prop} (o : Option α) (H : ∀ a ∈ o, p a) :
|
||||
@[simp] theorem isNone_attachWith {p : α → Prop} (o : Option α) (H : ∀ a, o = some a → p a) :
|
||||
(o.attachWith p H).isNone = o.isNone := by
|
||||
cases o <;> simp
|
||||
|
||||
@[simp] theorem isSome_attach (o : Option α) : o.attach.isSome = o.isSome := by
|
||||
cases o <;> simp
|
||||
|
||||
@[simp] theorem isSome_attachWith {p : α → Prop} (o : Option α) (H : ∀ a ∈ o, p a) :
|
||||
@[simp] theorem isSome_attachWith {p : α → Prop} (o : Option α) (H : ∀ a, o = some a → p a) :
|
||||
(o.attachWith p H).isSome = o.isSome := by
|
||||
cases o <;> simp
|
||||
|
||||
@[simp] theorem attach_eq_none_iff {o : Option α} : o.attach = none ↔ o = none := by
|
||||
cases o <;> simp
|
||||
|
||||
@[simp] theorem attach_eq_some_iff {o : Option α} {x : {x // x ∈ o}} :
|
||||
@[simp] theorem attach_eq_some_iff {o : Option α} {x : {x // o = some x}} :
|
||||
o.attach = some x ↔ o = some x.val := by
|
||||
cases o <;> cases x <;> simp
|
||||
|
||||
@[simp] theorem attachWith_eq_none_iff {p : α → Prop} {o : Option α} (H : ∀ a ∈ o, p a) :
|
||||
@[simp] theorem attachWith_eq_none_iff {p : α → Prop} {o : Option α} (H : ∀ a, o = some a → p a) :
|
||||
o.attachWith p H = none ↔ o = none := by
|
||||
cases o <;> simp
|
||||
|
||||
@[simp] theorem attachWith_eq_some_iff {p : α → Prop} {o : Option α} (H : ∀ a ∈ o, p a) {x : {x // p x}} :
|
||||
@[simp] theorem attachWith_eq_some_iff {p : α → Prop} {o : Option α} (H : ∀ a, o = some a → p a) {x : {x // p x}} :
|
||||
o.attachWith p H = some x ↔ o = some x.val := by
|
||||
cases o <;> cases x <;> simp
|
||||
|
||||
@@ -123,7 +126,7 @@ theorem mem_attach : ∀ (o : Option α) (x : {x // x ∈ o}), x ∈ o.attach
|
||||
· simp at h
|
||||
· simp [get_some]
|
||||
|
||||
@[simp] theorem get_attachWith {p : α → Prop} {o : Option α} (H : ∀ a ∈ o, p a) (h : (o.attachWith p H).isSome) :
|
||||
@[simp] theorem get_attachWith {p : α → Prop} {o : Option α} (H : ∀ a, o = some a → p a) (h : (o.attachWith p H).isSome) :
|
||||
(o.attachWith p H).get h = ⟨o.get (by simpa using h), H _ (by simp)⟩ := by
|
||||
cases o
|
||||
· simp at h
|
||||
@@ -138,47 +141,47 @@ theorem toList_attach (o : Option α) :
|
||||
cases o <;> simp
|
||||
|
||||
theorem attach_map {o : Option α} (f : α → β) :
|
||||
(o.map f).attach = o.attach.map (fun ⟨x, h⟩ => ⟨f x, mem_map_of_mem f h⟩) := by
|
||||
(o.map f).attach = o.attach.map (fun ⟨x, h⟩ => ⟨f x, map_eq_some_iff.2 ⟨_, h, rfl⟩⟩) := by
|
||||
cases o <;> simp
|
||||
|
||||
theorem attachWith_map {o : Option α} (f : α → β) {P : β → Prop} {H : ∀ (b : β), b ∈ o.map f → P b} :
|
||||
(o.map f).attachWith P H = (o.attachWith (P ∘ f) (fun _ h => H _ (mem_map_of_mem f h))).map
|
||||
theorem attachWith_map {o : Option α} (f : α → β) {P : β → Prop} {H : ∀ (b : β), o.map f = some b → P b} :
|
||||
(o.map f).attachWith P H = (o.attachWith (P ∘ f) (fun _ h => H _ (map_eq_some_iff.2 ⟨_, h, rfl⟩))).map
|
||||
fun ⟨x, h⟩ => ⟨f x, h⟩ := by
|
||||
cases o <;> simp
|
||||
|
||||
theorem map_attach_eq_pmap {o : Option α} (f : { x // x ∈ o } → β) :
|
||||
o.attach.map f = o.pmap (fun a (h : a ∈ o) => f ⟨a, h⟩) (fun _ h => h) := by
|
||||
theorem map_attach_eq_pmap {o : Option α} (f : { x // o = some x } → β) :
|
||||
o.attach.map f = o.pmap (fun a (h : o = some a) => f ⟨a, h⟩) (fun _ h => h) := by
|
||||
cases o <;> simp
|
||||
|
||||
@[deprecated map_attach_eq_pmap (since := "2025-02-09")]
|
||||
abbrev map_attach := @map_attach_eq_pmap
|
||||
|
||||
@[simp] theorem map_attachWith {l : Option α} {P : α → Prop} {H : ∀ (a : α), a ∈ l → P a}
|
||||
@[simp] theorem map_attachWith {l : Option α} {P : α → Prop} {H : ∀ (a : α), l = some a → P a}
|
||||
(f : { x // P x } → β) :
|
||||
(l.attachWith P H).map f = l.attach.map fun ⟨x, h⟩ => f ⟨x, H _ h⟩ := by
|
||||
cases l <;> simp_all
|
||||
|
||||
theorem map_attachWith_eq_pmap {o : Option α} {P : α → Prop} {H : ∀ (a : α), a ∈ o → P a}
|
||||
theorem map_attachWith_eq_pmap {o : Option α} {P : α → Prop} {H : ∀ (a : α), o = some a → P a}
|
||||
(f : { x // P x } → β) :
|
||||
(o.attachWith P H).map f =
|
||||
o.pmap (fun a (h : a ∈ o ∧ P a) => f ⟨a, h.2⟩) (fun a h => ⟨h, H a h⟩) := by
|
||||
o.pmap (fun a (h : o = some a ∧ P a) => f ⟨a, h.2⟩) (fun a h => ⟨h, H a h⟩) := by
|
||||
cases o <;> simp
|
||||
|
||||
@[simp]
|
||||
theorem map_attach_eq_attachWith {o : Option α} {p : α → Prop} (f : ∀ a, a ∈ o → p a) :
|
||||
theorem map_attach_eq_attachWith {o : Option α} {p : α → Prop} (f : ∀ a, o = some a → p a) :
|
||||
o.attach.map (fun x => ⟨x.1, f x.1 x.2⟩) = o.attachWith p f := by
|
||||
cases o <;> simp_all [Function.comp_def]
|
||||
|
||||
theorem attach_bind {o : Option α} {f : α → Option β} :
|
||||
(o.bind f).attach =
|
||||
o.attach.bind fun ⟨x, h⟩ => (f x).attach.map fun ⟨y, h'⟩ => ⟨y, mem_bind_iff.mpr ⟨x, h, h'⟩⟩ := by
|
||||
o.attach.bind fun ⟨x, h⟩ => (f x).attach.map fun ⟨y, h'⟩ => ⟨y, bind_eq_some_iff.2 ⟨_, h, h'⟩⟩ := by
|
||||
cases o <;> simp
|
||||
|
||||
theorem bind_attach {o : Option α} {f : {x // x ∈ o} → Option β} :
|
||||
theorem bind_attach {o : Option α} {f : {x // o = some x} → Option β} :
|
||||
o.attach.bind f = o.pbind fun a h => f ⟨a, h⟩ := by
|
||||
cases o <;> simp
|
||||
|
||||
theorem pbind_eq_bind_attach {o : Option α} {f : (a : α) → a ∈ o → Option β} :
|
||||
theorem pbind_eq_bind_attach {o : Option α} {f : (a : α) → o = some a → Option β} :
|
||||
o.pbind f = o.attach.bind fun ⟨x, h⟩ => f x h := by
|
||||
cases o <;> simp
|
||||
|
||||
@@ -190,7 +193,7 @@ theorem attach_filter {o : Option α} {p : α → Bool} :
|
||||
| some a =>
|
||||
simp only [filter_some, attach_some]
|
||||
ext
|
||||
simp only [mem_def, attach_eq_some_iff, ite_none_right_eq_some, some.injEq, some_bind,
|
||||
simp only [attach_eq_some_iff, ite_none_right_eq_some, some.injEq, some_bind,
|
||||
dite_none_right_eq_some]
|
||||
constructor
|
||||
· rintro ⟨h, w⟩
|
||||
@@ -198,7 +201,7 @@ theorem attach_filter {o : Option α} {p : α → Bool} :
|
||||
· rintro ⟨h, rfl⟩
|
||||
simp [h]
|
||||
|
||||
theorem filter_attach {o : Option α} {p : {x // x ∈ o} → Bool} :
|
||||
theorem filter_attach {o : Option α} {p : {x // o = some x} → Bool} :
|
||||
o.attach.filter p = o.pbind fun a h => if p ⟨a, h⟩ then some ⟨a, h⟩ else none := by
|
||||
cases o <;> simp [filter_some]
|
||||
|
||||
@@ -242,7 +245,7 @@ def unattach {α : Type _} {p : α → Prop} (o : Option { x // p x }) := o.map
|
||||
cases o <;> simp
|
||||
|
||||
@[simp] theorem unattach_attachWith {p : α → Prop} {o : Option α}
|
||||
{H : ∀ a ∈ o, p a} :
|
||||
{H : ∀ a, o = some a → p a} :
|
||||
(o.attachWith p H).unattach = o := by
|
||||
cases o <;> simp
|
||||
|
||||
|
||||
@@ -94,13 +94,20 @@ Runs a monadic function `f` on an optional value, returning the result. If the o
|
||||
From the perspective of `Option` as a container with at most one element, this is analogous to
|
||||
`List.mapM`, returning the result of running the monadic function on all elements of the container.
|
||||
|
||||
`Option.mapA` is the corresponding operation for applicative functors.
|
||||
This function only requires `m` to be an applicative functor. An alias `Option.mapA` is provided.
|
||||
-/
|
||||
@[inline] protected def mapM [Monad m] (f : α → m β) (o : Option α) : m (Option β) := do
|
||||
if let some a := o then
|
||||
return some (← f a)
|
||||
else
|
||||
return none
|
||||
@[inline] protected def mapM [Applicative m] (f : α → m β) : Option α → m (Option β)
|
||||
| none => pure none
|
||||
| some x => some <$> f x
|
||||
|
||||
/--
|
||||
Applies a function in some applicative functor to an optional value, returning `none` with no
|
||||
effects if the value is missing.
|
||||
|
||||
This is an alias for `Option.mapM`, which already works for applicative functors.
|
||||
-/
|
||||
@[inline] protected def mapA [Applicative m] (f : α → m β) : Option α → m (Option β) :=
|
||||
Option.mapM f
|
||||
|
||||
theorem map_id : (Option.map id : Option α → Option α) = id :=
|
||||
funext (fun o => match o with | none => rfl | some _ => rfl)
|
||||
@@ -209,33 +216,12 @@ The value is `some (fn a b)` if the inputs are `some a` and `some b`. Otherwise,
|
||||
equivalent to `Option.orElse`: if only one input is `some x`, then the value is `some x`, and if
|
||||
both are `none`, then the value is `none`.
|
||||
|
||||
Examples:
|
||||
* `Option.zipWith (· + ·) none (some 3) = some 3`
|
||||
* `Option.zipWith (· + ·) (some 2) (some 3) = some 5`
|
||||
* `Option.zipWith (· + ·) (some 2) none = some 2`
|
||||
* `Option.zipWith (· + ·) none none = none`
|
||||
-/
|
||||
def zipWith (fn : α → α → α) : Option α → Option α → Option α
|
||||
| none , none => none
|
||||
| some x, none => some x
|
||||
| none , some y => some y
|
||||
| some x, some y => some <| fn x y
|
||||
|
||||
/--
|
||||
Applies a function to a two optional values if both are present. Otherwise, if one value is present,
|
||||
it is returned and the function is not used.
|
||||
|
||||
The value is `some (fn a b)` if the inputs are `some a` and `some b`. Otherwise, the behavior is
|
||||
equivalent to `Option.orElse`: if only one input is `some x`, then the value is `some x`, and if
|
||||
both are `none`, then the value is `none`.
|
||||
|
||||
Examples:
|
||||
* `Option.merge (· + ·) none (some 3) = some 3`
|
||||
* `Option.merge (· + ·) (some 2) (some 3) = some 5`
|
||||
* `Option.merge (· + ·) (some 2) none = some 2`
|
||||
* `Option.merge (· + ·) none none = none`
|
||||
-/
|
||||
@[deprecated zipWith (since := "2025-04-04")]
|
||||
def merge (fn : α → α → α) : Option α → Option α → Option α
|
||||
| none , none => none
|
||||
| some x, none => some x
|
||||
@@ -245,13 +231,12 @@ def merge (fn : α → α → α) : Option α → Option α → Option α
|
||||
@[simp] theorem getD_none : getD none a = a := rfl
|
||||
@[simp] theorem getD_some : getD (some a) b = a := rfl
|
||||
|
||||
@[simp] theorem map_none' (f : α → β) : none.map f = none := rfl
|
||||
@[simp] theorem map_some' (a) (f : α → β) : (some a).map f = some (f a) := rfl
|
||||
@[simp] theorem map_none (f : α → β) : none.map f = none := rfl
|
||||
@[simp] theorem map_some (a) (f : α → β) : (some a).map f = some (f a) := rfl
|
||||
|
||||
@[simp] theorem none_bind (f : α → Option β) : none.bind f = none := rfl
|
||||
@[simp] theorem some_bind (a) (f : α → Option β) : (some a).bind f = f a := rfl
|
||||
|
||||
|
||||
/--
|
||||
A case analysis function for `Option`.
|
||||
|
||||
@@ -328,7 +313,7 @@ Examples:
|
||||
* `Option.liftOrGet (· + ·) (some 2) none = some 2`
|
||||
* `Option.liftOrGet (· + ·) none none = none`
|
||||
-/
|
||||
@[deprecated zipWith (since := "2025-04-04")]
|
||||
@[deprecated merge (since := "2025-04-04")]
|
||||
def liftOrGet (f : α → α → α) : Option α → Option α → Option α
|
||||
| none, none => none
|
||||
| some a, none => some a
|
||||
@@ -355,19 +340,11 @@ Examples:
|
||||
-/
|
||||
@[simp, inline] def join (x : Option (Option α)) : Option α := x.bind id
|
||||
|
||||
/--
|
||||
Applies a function in some applicative functor to an optional value, returning `none` with no
|
||||
effects if the value is missing.
|
||||
|
||||
This is analogous to `Option.mapM` for monads.
|
||||
-/
|
||||
@[inline] protected def mapA [Applicative m] {α β} (f : α → m β) : Option α → m (Option β)
|
||||
| none => pure none
|
||||
| some x => some <$> f x
|
||||
|
||||
/--
|
||||
Converts an optional monadic computation into a monadic computation of an optional value.
|
||||
|
||||
This function only requires `m` to be an applicative functor.
|
||||
|
||||
Example:
|
||||
```lean example
|
||||
#eval show IO (Option String) from
|
||||
@@ -382,11 +359,10 @@ hello
|
||||
some "world"
|
||||
```
|
||||
-/
|
||||
@[inline] def sequence [Monad m] {α : Type u} : Option (m α) → m (Option α)
|
||||
@[inline] def sequence [Applicative m] {α : Type u} : Option (m α) → m (Option α)
|
||||
| none => pure none
|
||||
| some fn => some <$> fn
|
||||
|
||||
|
||||
/--
|
||||
A monadic case analysis function for `Option`.
|
||||
|
||||
@@ -405,8 +381,7 @@ Gets the value in an option, monadically computing a default value on `none`.
|
||||
|
||||
This is the monadic analogue of `Option.getD`.
|
||||
-/
|
||||
|
||||
@[inline] def getDM [Monad m] (x : Option α) (y : m α) : m α :=
|
||||
@[inline] def getDM [Pure m] (x : Option α) (y : m α) : m α :=
|
||||
match x with
|
||||
| some a => pure a
|
||||
| none => y
|
||||
|
||||
@@ -39,31 +39,37 @@ This is not an instance because it is not definitionally equal to the standard i
|
||||
|
||||
Try to use the Boolean comparisons `Option.isNone` or `Option.isSome` instead.
|
||||
-/
|
||||
@[inline] def decidable_eq_none {o : Option α} : Decidable (o = none) :=
|
||||
@[inline] def decidableEqNone {o : Option α} : Decidable (o = none) :=
|
||||
decidable_of_decidable_of_iff isNone_iff_eq_none
|
||||
|
||||
instance {p : α → Prop} [DecidablePred p] : ∀ o : Option α, Decidable (∀ a, a ∈ o → p a)
|
||||
| none => isTrue nofun
|
||||
| some a =>
|
||||
if h : p a then isTrue fun _ e => some_inj.1 e ▸ h
|
||||
else isFalse <| mt (· _ rfl) h
|
||||
@[deprecated decidableEqNone (since := "2025-04-10"), inline]
|
||||
def decidable_eq_none {o : Option α} : Decidable (o = none) :=
|
||||
decidableEqNone
|
||||
|
||||
instance {p : α → Prop} [DecidablePred p] : ∀ o : Option α, Decidable (Exists fun a => a ∈ o ∧ p a)
|
||||
| none => isFalse nofun
|
||||
| some a => if h : p a then isTrue ⟨_, rfl, h⟩ else isFalse fun ⟨_, ⟨rfl, hn⟩⟩ => h hn
|
||||
instance decidableForallMem {p : α → Prop} [DecidablePred p] :
|
||||
∀ o : Option α, Decidable (∀ a, a ∈ o → p a)
|
||||
| none => isTrue nofun
|
||||
| some a =>
|
||||
if h : p a then isTrue fun _ e => some_inj.1 e ▸ h
|
||||
else isFalse <| mt (· _ rfl) h
|
||||
|
||||
instance decidableExistsMem {p : α → Prop} [DecidablePred p] :
|
||||
∀ o : Option α, Decidable (Exists fun a => a ∈ o ∧ p a)
|
||||
| none => isFalse nofun
|
||||
| some a => if h : p a then isTrue ⟨_, rfl, h⟩ else isFalse fun ⟨_, ⟨rfl, hn⟩⟩ => h hn
|
||||
|
||||
/--
|
||||
Given an optional value and a function that can be applied when the value is `some`, returns the
|
||||
result of applying the function if this is possible.
|
||||
|
||||
The function `f` is _partial_ because it is only defined for the values `a : α` such `a ∈ o`, which
|
||||
is equivalent to `o = some a`. This restriction allows the function to use the fact that it can only
|
||||
be called when `o` is not `none`: it can relate its argument to the optional value `o`. Its runtime
|
||||
behavior is equivalent to that of `Option.bind`.
|
||||
The function `f` is _partial_ because it is only defined for the values `a : α` such that
|
||||
`o = some a`. This restriction allows the function to use the fact that it can only be called when
|
||||
`o` is not `none`: it can relate its argument to the optional value `o`. Its runtime behavior is
|
||||
equivalent to that of `Option.bind`.
|
||||
|
||||
Examples:
|
||||
```lean example
|
||||
def attach (v : Option α) : Option { y : α // y ∈ v } :=
|
||||
def attach (v : Option α) : Option { y : α // v = some y } :=
|
||||
v.pbind fun x h => some ⟨x, h⟩
|
||||
```
|
||||
```lean example
|
||||
@@ -80,7 +86,7 @@ none
|
||||
```
|
||||
-/
|
||||
@[inline]
|
||||
def pbind : (o : Option α) → (f : (a : α) → a ∈ o → Option β) → Option β
|
||||
def pbind : (o : Option α) → (f : (a : α) → o = some a → Option β) → Option β
|
||||
| none, _ => none
|
||||
| some a, f => f a rfl
|
||||
|
||||
@@ -90,7 +96,7 @@ satisfies `p` if it's present, applies the function to the value.
|
||||
|
||||
Examples:
|
||||
```lean example
|
||||
def attach (v : Option α) : Option { y : α // y ∈ v } :=
|
||||
def attach (v : Option α) : Option { y : α // v = some y } :=
|
||||
v.pmap (fun a (h : a ∈ v) => ⟨_, h⟩) (fun _ h => h)
|
||||
```
|
||||
```lean example
|
||||
@@ -108,7 +114,7 @@ none
|
||||
-/
|
||||
@[inline] def pmap {p : α → Prop}
|
||||
(f : ∀ a : α, p a → β) :
|
||||
(o : Option α) → (∀ a, a ∈ o → p a) → Option β
|
||||
(o : Option α) → (∀ a, o = some a → p a) → Option β
|
||||
| none, _ => none
|
||||
| some a, H => f a (H a rfl)
|
||||
|
||||
@@ -116,14 +122,14 @@ none
|
||||
Given an optional value and a function that can be applied when the value is `some`, returns the
|
||||
result of applying the function if this is possible, or a fallback value otherwise.
|
||||
|
||||
The function `f` is _partial_ because it is only defined for the values `a : α` such `a ∈ o`, which
|
||||
is equivalent to `o = some a`. This restriction allows the function to use the fact that it can only
|
||||
be called when `o` is not `none`: it can relate its argument to the optional value `o`. Its runtime
|
||||
behavior is equivalent to that of `Option.elim`.
|
||||
The function `f` is _partial_ because it is only defined for the values `a : α` such that
|
||||
`o = some a`. This restriction allows the function to use the fact that it can only be called when
|
||||
`o` is not `none`: it can relate its argument to the optional value `o`. Its runtime behavior is
|
||||
equivalent to that of `Option.elim`.
|
||||
|
||||
Examples:
|
||||
```lean example
|
||||
def attach (v : Option α) : Option { y : α // y ∈ v } :=
|
||||
def attach (v : Option α) : Option { y : α // v = some y } :=
|
||||
v.pelim none fun x h => some ⟨x, h⟩
|
||||
```
|
||||
```lean example
|
||||
@@ -139,7 +145,7 @@ some ⟨3, ⋯⟩
|
||||
none
|
||||
```
|
||||
-/
|
||||
@[inline] def pelim (o : Option α) (b : β) (f : (a : α) → a ∈ o → β) : β :=
|
||||
@[inline] def pelim (o : Option α) (b : β) (f : (a : α) → o = some a → β) : β :=
|
||||
match o with
|
||||
| none => b
|
||||
| some a => f a rfl
|
||||
|
||||
@@ -12,11 +12,14 @@ import Init.Ext
|
||||
|
||||
namespace Option
|
||||
|
||||
@[deprecated mem_def (since := "2025-04-07")]
|
||||
theorem mem_iff {a : α} {b : Option α} : a ∈ b ↔ b = some a := .rfl
|
||||
|
||||
theorem mem_some {a b : α} : a ∈ some b ↔ b = a := by simp
|
||||
|
||||
theorem mem_some_self (a : α) : a ∈ some a := mem_some.2 rfl
|
||||
theorem mem_some_iff {a b : α} : a ∈ some b ↔ b = a := mem_some
|
||||
|
||||
theorem mem_some_self (a : α) : a ∈ some a := rfl
|
||||
|
||||
theorem some_ne_none (x : α) : some x ≠ none := nofun
|
||||
|
||||
@@ -28,12 +31,18 @@ protected theorem «exists» {p : Option α → Prop} :
|
||||
⟨fun | ⟨none, hx⟩ => .inl hx | ⟨some x, hx⟩ => .inr ⟨x, hx⟩,
|
||||
fun | .inl h => ⟨_, h⟩ | .inr ⟨_, hx⟩ => ⟨_, hx⟩⟩
|
||||
|
||||
theorem eq_none_or_eq_some (a : Option α) : a = none ∨ ∃ x, a = some x :=
|
||||
Option.exists.mp exists_eq'
|
||||
|
||||
theorem get_mem : ∀ {o : Option α} (h : isSome o), o.get h ∈ o
|
||||
| some _, _ => rfl
|
||||
|
||||
theorem get_of_mem : ∀ {o : Option α} (h : isSome o), a ∈ o → o.get h = a
|
||||
| _, _, rfl => rfl
|
||||
|
||||
theorem get_of_eq_some : ∀ {o : Option α} (h : isSome o), o = some a → o.get h = a
|
||||
| _, _, rfl => rfl
|
||||
|
||||
@[simp] theorem not_mem_none (a : α) : a ∉ (none : Option α) := nofun
|
||||
|
||||
theorem getD_of_ne_none {x : Option α} (hx : x ≠ none) (y : α) : some (x.getD y) = x := by
|
||||
@@ -71,13 +80,24 @@ theorem get_inj {o1 o2 : Option α} {h1} {h2} :
|
||||
theorem mem_unique {o : Option α} {a b : α} (ha : a ∈ o) (hb : b ∈ o) : a = b :=
|
||||
some.inj <| ha ▸ hb
|
||||
|
||||
@[ext] theorem ext : ∀ {o₁ o₂ : Option α}, (∀ a, a ∈ o₁ ↔ a ∈ o₂) → o₁ = o₂
|
||||
theorem eq_some_unique {o : Option α} {a b : α} (ha : o = some a) (hb : o = some b) : a = b :=
|
||||
some.inj <| ha ▸ hb
|
||||
|
||||
@[ext] theorem ext : ∀ {o₁ o₂ : Option α}, (∀ a, o₁ = some a ↔ o₂ = some a) → o₁ = o₂
|
||||
| none, none, _ => rfl
|
||||
| some _, _, H => ((H _).1 rfl).symm
|
||||
| _, some _, H => (H _).2 rfl
|
||||
|
||||
set_option Elab.async false
|
||||
|
||||
theorem eq_none_iff_forall_ne_some : o = none ↔ ∀ a, o ≠ some a := by
|
||||
cases o <;> simp
|
||||
|
||||
theorem eq_none_iff_forall_some_ne : o = none ↔ ∀ a, some a ≠ o := by
|
||||
cases o <;> simp
|
||||
|
||||
theorem eq_none_iff_forall_not_mem : o = none ↔ ∀ a, a ∉ o :=
|
||||
⟨fun e a h => by rw [e] at h; (cases h), fun h => ext <| by simp; exact h⟩
|
||||
eq_none_iff_forall_ne_some
|
||||
|
||||
theorem isSome_iff_exists : isSome x ↔ ∃ a, x = some a := by cases x <;> simp [isSome]
|
||||
|
||||
@@ -90,9 +110,30 @@ theorem isSome_of_mem {x : Option α} {y : α} (h : y ∈ x) : x.isSome := by
|
||||
theorem isSome_of_eq_some {x : Option α} {y : α} (h : x = some y) : x.isSome := by
|
||||
cases x <;> trivial
|
||||
|
||||
@[simp] theorem not_isSome : isSome a = false ↔ a.isNone = true := by
|
||||
@[simp] theorem isSome_eq_false_iff : isSome a = false ↔ a.isNone = true := by
|
||||
cases a <;> simp
|
||||
|
||||
@[simp] theorem isNone_eq_false_iff : isNone a = false ↔ a.isSome = true := by
|
||||
cases a <;> simp
|
||||
|
||||
@[simp]
|
||||
theorem not_isSome (a : Option α) : (!a.isSome) = a.isNone := by
|
||||
cases a <;> simp
|
||||
|
||||
@[simp]
|
||||
theorem not_comp_isSome : (! ·) ∘ @Option.isSome α = Option.isNone := by
|
||||
funext
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem not_isNone (a : Option α) : (!a.isNone) = a.isSome := by
|
||||
cases a <;> simp
|
||||
|
||||
@[simp]
|
||||
theorem not_comp_isNone : (!·) ∘ @Option.isNone α = Option.isSome := by
|
||||
funext x
|
||||
simp
|
||||
|
||||
theorem eq_some_iff_get_eq : o = some a ↔ ∃ h : o.isSome, o.get h = a := by
|
||||
cases o <;> simp
|
||||
|
||||
@@ -134,20 +175,28 @@ abbrev ball_ne_none := @forall_ne_none
|
||||
|
||||
@[simp] theorem bind_eq_bind : bind = @Option.bind α β := rfl
|
||||
|
||||
@[simp] theorem orElse_eq_orElse : HOrElse.hOrElse = @Option.orElse α := rfl
|
||||
|
||||
@[simp] theorem bind_some (x : Option α) : x.bind some = x := by cases x <;> rfl
|
||||
|
||||
@[simp] theorem bind_none (x : Option α) : x.bind (fun _ => none (α := β)) = none := by
|
||||
cases x <;> rfl
|
||||
|
||||
theorem bind_eq_some : x.bind f = some b ↔ ∃ a, x = some a ∧ f a = some b := by
|
||||
theorem bind_eq_some_iff : x.bind f = some b ↔ ∃ a, x = some a ∧ f a = some b := by
|
||||
cases x <;> simp
|
||||
|
||||
@[simp] theorem bind_eq_none {o : Option α} {f : α → Option β} :
|
||||
@[deprecated bind_eq_some_iff (since := "2025-04-10")]
|
||||
abbrev bind_eq_some := @bind_eq_some_iff
|
||||
|
||||
@[simp] theorem bind_eq_none_iff {o : Option α} {f : α → Option β} :
|
||||
o.bind f = none ↔ ∀ a, o = some a → f a = none := by cases o <;> simp
|
||||
|
||||
@[deprecated bind_eq_none_iff (since := "2025-04-10")]
|
||||
abbrev bind_eq_none := @bind_eq_none_iff
|
||||
|
||||
theorem bind_eq_none' {o : Option α} {f : α → Option β} :
|
||||
o.bind f = none ↔ ∀ b a, a ∈ o → b ∉ f a := by
|
||||
simp only [eq_none_iff_forall_not_mem, not_exists, not_and, mem_def, bind_eq_some]
|
||||
o.bind f = none ↔ ∀ b a, o = some a → f a ≠ some b := by
|
||||
cases o <;> simp [eq_none_iff_forall_ne_some]
|
||||
|
||||
theorem mem_bind_iff {o : Option α} {f : α → Option β} :
|
||||
b ∈ o.bind f ↔ ∃ a, a ∈ o ∧ b ∈ f a := by
|
||||
@@ -181,49 +230,67 @@ theorem isSome_apply_of_isSome_bind {α β : Type _} {x : Option α} {f : α →
|
||||
(isSome_apply_of_isSome_bind h) := by
|
||||
cases x <;> trivial
|
||||
|
||||
theorem join_eq_some : x.join = some a ↔ x = some (some a) := by
|
||||
simp [bind_eq_some]
|
||||
theorem join_eq_some_iff : x.join = some a ↔ x = some (some a) := by
|
||||
simp [bind_eq_some_iff]
|
||||
|
||||
@[deprecated join_eq_some_iff (since := "2025-04-10")]
|
||||
abbrev join_eq_some := @join_eq_some_iff
|
||||
|
||||
theorem join_ne_none : x.join ≠ none ↔ ∃ z, x = some (some z) := by
|
||||
simp only [ne_none_iff_exists', join_eq_some, iff_self]
|
||||
simp only [ne_none_iff_exists', join_eq_some_iff, iff_self]
|
||||
|
||||
theorem join_ne_none' : ¬x.join = none ↔ ∃ z, x = some (some z) :=
|
||||
join_ne_none
|
||||
|
||||
theorem join_eq_none : o.join = none ↔ o = none ∨ o = some none :=
|
||||
theorem join_eq_none_iff : o.join = none ↔ o = none ∨ o = some none :=
|
||||
match o with | none | some none | some (some _) => by simp
|
||||
|
||||
@[deprecated join_eq_none_iff (since := "2025-04-10")]
|
||||
abbrev join_eq_none := @join_eq_none_iff
|
||||
|
||||
theorem bind_id_eq_join {x : Option (Option α)} : x.bind id = x.join := rfl
|
||||
|
||||
@[simp] theorem map_eq_map : Functor.map f = Option.map f := rfl
|
||||
|
||||
theorem map_none : f <$> none = none := rfl
|
||||
@[deprecated map_none (since := "2025-04-10")]
|
||||
abbrev map_none' := @map_none
|
||||
|
||||
theorem map_some : f <$> some a = some (f a) := rfl
|
||||
@[deprecated map_some (since := "2025-04-10")]
|
||||
abbrev map_some' := @map_some
|
||||
|
||||
@[simp] theorem map_eq_some' : x.map f = some b ↔ ∃ a, x = some a ∧ f a = b := by cases x <;> simp
|
||||
|
||||
theorem map_eq_some : f <$> x = some b ↔ ∃ a, x = some a ∧ f a = b := map_eq_some'
|
||||
|
||||
@[simp] theorem map_eq_none' : x.map f = none ↔ x = none := by
|
||||
cases x <;> simp [map_none', map_some', eq_self_iff_true]
|
||||
|
||||
theorem isSome_map {x : Option α} : (f <$> x).isSome = x.isSome := by
|
||||
@[simp] theorem map_eq_some_iff : x.map f = some b ↔ ∃ a, x = some a ∧ f a = b := by
|
||||
cases x <;> simp
|
||||
|
||||
@[simp] theorem isSome_map' {x : Option α} : (x.map f).isSome = x.isSome := by
|
||||
@[deprecated map_eq_some_iff (since := "2025-04-10")]
|
||||
abbrev map_eq_some := @map_eq_some_iff
|
||||
|
||||
@[deprecated map_eq_some_iff (since := "2025-04-10")]
|
||||
abbrev map_eq_some' := @map_eq_some_iff
|
||||
|
||||
@[simp] theorem map_eq_none_iff : x.map f = none ↔ x = none := by
|
||||
cases x <;> simp [map_none, map_some, eq_self_iff_true]
|
||||
|
||||
@[deprecated map_eq_none_iff (since := "2025-04-10")]
|
||||
abbrev map_eq_none := @map_eq_none_iff
|
||||
|
||||
@[deprecated map_eq_none_iff (since := "2025-04-10")]
|
||||
abbrev map_eq_none' := @map_eq_none_iff
|
||||
|
||||
@[simp] theorem isSome_map {x : Option α} : (x.map f).isSome = x.isSome := by
|
||||
cases x <;> simp
|
||||
|
||||
@[simp] theorem isNone_map' {x : Option α} : (x.map f).isNone = x.isNone := by
|
||||
cases x <;> simp
|
||||
@[deprecated isSome_map (since := "2025-04-10")]
|
||||
abbrev isSome_map' := @isSome_map
|
||||
|
||||
theorem map_eq_none : f <$> x = none ↔ x = none := map_eq_none'
|
||||
@[simp] theorem isNone_map {x : Option α} : (x.map f).isNone = x.isNone := by
|
||||
cases x <;> simp
|
||||
|
||||
theorem map_eq_bind {x : Option α} : x.map f = x.bind (some ∘ f) := by
|
||||
cases x <;> simp [Option.bind]
|
||||
|
||||
theorem map_congr {x : Option α} (h : ∀ a, a ∈ x → f a = g a) : x.map f = x.map g := by
|
||||
cases x <;> simp only [map_none', map_some', h, mem_def]
|
||||
theorem map_congr {x : Option α} (h : ∀ a, x = some a → f a = g a) :
|
||||
x.map f = x.map g := by
|
||||
cases x <;> simp only [map_none, map_some, h]
|
||||
|
||||
@[simp] theorem map_id_fun {α : Type u} : Option.map (id : α → α) = id := by
|
||||
funext; simp [map_id]
|
||||
@@ -241,7 +308,7 @@ theorem get_map {f : α → β} {o : Option α} {h : (o.map f).isSome} :
|
||||
|
||||
@[simp] theorem map_map (h : β → γ) (g : α → β) (x : Option α) :
|
||||
(x.map g).map h = x.map (h ∘ g) := by
|
||||
cases x <;> simp only [map_none', map_some', ·∘·]
|
||||
cases x <;> simp only [map_none, map_some, ·∘·]
|
||||
|
||||
theorem comp_map (h : β → γ) (g : α → β) (x : Option α) : x.map (h ∘ g) = (x.map g).map h :=
|
||||
(map_map ..).symm
|
||||
@@ -249,7 +316,7 @@ theorem comp_map (h : β → γ) (g : α → β) (x : Option α) : x.map (h ∘
|
||||
@[simp] theorem map_comp_map (f : α → β) (g : β → γ) :
|
||||
Option.map g ∘ Option.map f = Option.map (g ∘ f) := by funext x; simp
|
||||
|
||||
theorem mem_map_of_mem (g : α → β) (h : a ∈ x) : g a ∈ Option.map g x := h.symm ▸ map_some' ..
|
||||
theorem mem_map_of_mem (g : α → β) (h : a ∈ x) : g a ∈ Option.map g x := h.symm ▸ map_some ..
|
||||
|
||||
theorem map_inj_right {f : α → β} {o o' : Option α} (w : ∀ x y, f x = f y → x = y) :
|
||||
o.map f = o'.map f ↔ o = o' := by
|
||||
@@ -279,12 +346,15 @@ theorem isSome_of_isSome_filter (p : α → Bool) (o : Option α) (h : (o.filter
|
||||
@[deprecated isSome_of_isSome_filter (since := "2025-03-18")]
|
||||
abbrev isSome_filter_of_isSome := @isSome_of_isSome_filter
|
||||
|
||||
@[simp] theorem filter_eq_none {o : Option α} {p : α → Bool} :
|
||||
o.filter p = none ↔ ∀ a, a ∈ o → ¬ p a := by
|
||||
@[simp] theorem filter_eq_none_iff {o : Option α} {p : α → Bool} :
|
||||
o.filter p = none ↔ ∀ a, o = some a → ¬ p a := by
|
||||
cases o <;> simp [filter_some]
|
||||
|
||||
@[simp] theorem filter_eq_some {o : Option α} {p : α → Bool} :
|
||||
o.filter p = some a ↔ a ∈ o ∧ p a := by
|
||||
@[deprecated filter_eq_none_iff (since := "2025-04-10")]
|
||||
abbrev filter_eq_none := @filter_eq_none_iff
|
||||
|
||||
@[simp] theorem filter_eq_some_iff {o : Option α} {p : α → Bool} :
|
||||
o.filter p = some a ↔ o = some a ∧ p a := by
|
||||
cases o with
|
||||
| none => simp
|
||||
| some a =>
|
||||
@@ -297,6 +367,9 @@ abbrev isSome_filter_of_isSome := @isSome_of_isSome_filter
|
||||
rintro rfl
|
||||
simpa using h
|
||||
|
||||
@[deprecated filter_eq_some_iff (since := "2025-04-10")]
|
||||
abbrev filter_eq_some := @filter_eq_some_iff
|
||||
|
||||
theorem mem_filter_iff {p : α → Bool} {a : α} {o : Option α} :
|
||||
a ∈ o.filter p ↔ a ∈ o ∧ p a := by
|
||||
simp
|
||||
@@ -370,29 +443,43 @@ theorem join_join {x : Option (Option (Option α))} : x.join.join = (x.map join)
|
||||
cases x <;> simp
|
||||
|
||||
theorem mem_of_mem_join {a : α} {x : Option (Option α)} (h : a ∈ x.join) : some a ∈ x :=
|
||||
h.symm ▸ join_eq_some.1 h
|
||||
h.symm ▸ join_eq_some_iff.1 h
|
||||
|
||||
@[simp] theorem some_orElse (a : α) (x : Option α) : (some a <|> x) = some a := rfl
|
||||
@[simp] theorem some_orElse (a : α) (f) : (some a).orElse f = some a := rfl
|
||||
|
||||
@[simp] theorem none_orElse (x : Option α) : (none <|> x) = x := rfl
|
||||
@[simp] theorem none_orElse (f : Unit → Option α) : none.orElse f = f () := rfl
|
||||
|
||||
@[simp] theorem orElse_none (x : Option α) : (x <|> none) = x := by cases x <;> rfl
|
||||
@[simp] theorem orElse_none (x : Option α) : x.orElse (fun _ => none) = x := by cases x <;> rfl
|
||||
|
||||
theorem map_orElse {x y : Option α} : (x <|> y).map f = (x.map f <|> y.map f) := by
|
||||
theorem orElse_eq_some_iff (o : Option α) (f) (x : α) :
|
||||
(o.orElse f) = some x ↔ o = some x ∨ o = none ∧ f () = some x := by
|
||||
cases o <;> simp
|
||||
|
||||
theorem orElse_eq_none_iff (o : Option α) (f) : (o.orElse f) = none ↔ o = none ∧ f () = none := by
|
||||
cases o <;> simp
|
||||
|
||||
theorem map_orElse {x : Option α} {y} :
|
||||
(x.orElse y).map f = (x.map f).orElse (fun _ => (y ()).map f) := by
|
||||
cases x <;> simp
|
||||
|
||||
@[simp] theorem guard_eq_some [DecidablePred p] : guard p a = some b ↔ a = b ∧ p a :=
|
||||
@[simp] theorem guard_eq_some_iff [DecidablePred p] : guard p a = some b ↔ a = b ∧ p a :=
|
||||
if h : p a then by simp [Option.guard, h] else by simp [Option.guard, h]
|
||||
|
||||
@[deprecated guard_eq_some_iff (since := "2025-04-10")]
|
||||
abbrev guard_eq_some := @guard_eq_some_iff
|
||||
|
||||
@[simp] theorem isSome_guard [DecidablePred p] : (Option.guard p a).isSome ↔ p a :=
|
||||
if h : p a then by simp [Option.guard, h] else by simp [Option.guard, h]
|
||||
|
||||
@[deprecated isSome_guard (since := "2025-03-18")]
|
||||
abbrev guard_isSome := @isSome_guard
|
||||
|
||||
@[simp] theorem guard_eq_none [DecidablePred p] : Option.guard p a = none ↔ ¬ p a :=
|
||||
@[simp] theorem guard_eq_none_iff [DecidablePred p] : Option.guard p a = none ↔ ¬ p a :=
|
||||
if h : p a then by simp [Option.guard, h] else by simp [Option.guard, h]
|
||||
|
||||
@[deprecated guard_eq_none_iff (since := "2025-04-10")]
|
||||
abbrev guard_eq_none := @guard_eq_none_iff
|
||||
|
||||
@[simp] theorem guard_pos [DecidablePred p] (h : p a) : Option.guard p a = some a := by
|
||||
simp [Option.guard, h]
|
||||
|
||||
@@ -429,38 +516,54 @@ theorem guard_eq_map (p : α → Prop) [DecidablePred p] :
|
||||
theorem guard_def (p : α → Prop) {_ : DecidablePred p} :
|
||||
Option.guard p = fun x => if p x then some x else none := rfl
|
||||
|
||||
theorem zipWith_eq_or_eq {f : α → α → α} (h : ∀ a b, f a b = a ∨ f a b = b) :
|
||||
∀ o₁ o₂, zipWith f o₁ o₂ = o₁ ∨ zipWith f o₁ o₂ = o₂
|
||||
theorem merge_eq_or_eq {f : α → α → α} (h : ∀ a b, f a b = a ∨ f a b = b) :
|
||||
∀ o₁ o₂, merge f o₁ o₂ = o₁ ∨ merge f o₁ o₂ = o₂
|
||||
| none, none => .inl rfl
|
||||
| some _, none => .inl rfl
|
||||
| none, some _ => .inr rfl
|
||||
| some a, some b => by have := h a b; simp [zipWith] at this ⊢; exact this
|
||||
| some a, some b => by have := h a b; simp [merge] at this ⊢; exact this
|
||||
|
||||
@[simp] theorem zipWith_none_left {f} {b : Option α} : zipWith f none b = b := by
|
||||
@[simp] theorem merge_none_left {f} {b : Option α} : merge f none b = b := by
|
||||
cases b <;> rfl
|
||||
|
||||
@[simp] theorem zipWith_none_right {f} {a : Option α} : zipWith f a none = a := by
|
||||
@[simp] theorem merge_none_right {f} {a : Option α} : merge f a none = a := by
|
||||
cases a <;> rfl
|
||||
|
||||
@[simp] theorem zipWith_some_some {f} {a b : α} :
|
||||
zipWith f (some a) (some b) = f a b := rfl
|
||||
@[simp] theorem merge_some_some {f} {a b : α} :
|
||||
merge f (some a) (some b) = f a b := rfl
|
||||
|
||||
@[deprecated zipWith_eq_or_eq (since := "2025-04-04")]
|
||||
@[deprecated merge_eq_or_eq (since := "2025-04-04")]
|
||||
theorem liftOrGet_eq_or_eq {f : α → α → α} (h : ∀ a b, f a b = a ∨ f a b = b) :
|
||||
∀ o₁ o₂, zipWith f o₁ o₂ = o₁ ∨ zipWith f o₁ o₂ = o₂ :=
|
||||
zipWith_eq_or_eq h
|
||||
∀ o₁ o₂, merge f o₁ o₂ = o₁ ∨ merge f o₁ o₂ = o₂ :=
|
||||
merge_eq_or_eq h
|
||||
|
||||
@[deprecated zipWith_none_left (since := "2025-04-04")]
|
||||
theorem liftOrGet_none_left {f} {b : Option α} : zipWith f none b = b :=
|
||||
zipWith_none_left
|
||||
@[deprecated merge_none_left (since := "2025-04-04")]
|
||||
theorem liftOrGet_none_left {f} {b : Option α} : merge f none b = b :=
|
||||
merge_none_left
|
||||
|
||||
@[deprecated zipWith_none_right (since := "2025-04-04")]
|
||||
theorem liftOrGet_none_right {f} {a : Option α} : zipWith f a none = a :=
|
||||
zipWith_none_right
|
||||
@[deprecated merge_none_right (since := "2025-04-04")]
|
||||
theorem liftOrGet_none_right {f} {a : Option α} : merge f a none = a :=
|
||||
merge_none_right
|
||||
|
||||
@[deprecated zipWith_some_some (since := "2025-04-04")]
|
||||
theorem liftOrGet_some_some {f} {a b : α} : zipWith f (some a) (some b) = f a b :=
|
||||
zipWith_some_some
|
||||
@[deprecated merge_some_some (since := "2025-04-04")]
|
||||
theorem liftOrGet_some_some {f} {a b : α} : merge f (some a) (some b) = f a b :=
|
||||
merge_some_some
|
||||
|
||||
instance commutative_merge (f : α → α → α) [Std.Commutative f] :
|
||||
Std.Commutative (merge f) :=
|
||||
⟨fun a b ↦ by cases a <;> cases b <;> simp [merge, Std.Commutative.comm]⟩
|
||||
|
||||
instance associative_merge (f : α → α → α) [Std.Associative f] :
|
||||
Std.Associative (merge f) :=
|
||||
⟨fun a b c ↦ by cases a <;> cases b <;> cases c <;> simp [merge, Std.Associative.assoc]⟩
|
||||
|
||||
instance idempotentOp_merge (f : α → α → α) [Std.IdempotentOp f] :
|
||||
Std.IdempotentOp (merge f) :=
|
||||
⟨fun a ↦ by cases a <;> simp [merge, Std.IdempotentOp.idempotent]⟩
|
||||
|
||||
instance lawfulIdentity_merge (f : α → α → α) : Std.LawfulIdentity (merge f) none where
|
||||
left_id a := by cases a <;> simp [merge]
|
||||
right_id a := by cases a <;> simp [merge]
|
||||
|
||||
@[simp] theorem elim_none (x : β) (f : α → β) : none.elim x f = x := rfl
|
||||
|
||||
@@ -522,12 +625,18 @@ theorem or_eq_bif : or o o' = bif o.isSome then o else o' := by
|
||||
@[simp] theorem isNone_or : (or o o').isNone = (o.isNone && o'.isNone) := by
|
||||
cases o <;> rfl
|
||||
|
||||
@[simp] theorem or_eq_none : or o o' = none ↔ o = none ∧ o' = none := by
|
||||
@[simp] theorem or_eq_none_iff : or o o' = none ↔ o = none ∧ o' = none := by
|
||||
cases o <;> simp
|
||||
|
||||
@[simp] theorem or_eq_some : or o o' = some a ↔ o = some a ∨ (o = none ∧ o' = some a) := by
|
||||
@[deprecated or_eq_none_iff (since := "2025-04-10")]
|
||||
abbrev or_eq_none := @or_eq_none_iff
|
||||
|
||||
@[simp] theorem or_eq_some_iff : or o o' = some a ↔ o = some a ∨ (o = none ∧ o' = some a) := by
|
||||
cases o <;> simp
|
||||
|
||||
@[deprecated or_eq_some_iff (since := "2025-04-10")]
|
||||
abbrev or_eq_some := @or_eq_some_iff
|
||||
|
||||
theorem or_assoc : or (or o₁ o₂) o₃ = or o₁ (or o₂ o₃) := by
|
||||
cases o₁ <;> cases o₂ <;> rfl
|
||||
instance : Std.Associative (or (α := α)) := ⟨@or_assoc _⟩
|
||||
@@ -551,11 +660,11 @@ instance : Std.IdempotentOp (or (α := α)) := ⟨@or_self _⟩
|
||||
theorem or_eq_orElse : or o o' = o.orElse (fun _ => o') := by
|
||||
cases o <;> rfl
|
||||
|
||||
theorem map_or : f <$> or o o' = (f <$> o).or (f <$> o') := by
|
||||
theorem map_or : (or o o').map f = (o.map f).or (o'.map f) := by
|
||||
cases o <;> rfl
|
||||
|
||||
theorem map_or' : (or o o').map f = (o.map f).or (o'.map f) := by
|
||||
cases o <;> rfl
|
||||
@[deprecated map_or (since := "2025-04-10")]
|
||||
abbrev map_or' := @map_or
|
||||
|
||||
theorem or_of_isSome {o o' : Option α} (h : o.isSome) : o.or o' = o := by
|
||||
match o, h with
|
||||
@@ -713,10 +822,10 @@ theorem isSome_filter {α : Type _} {x : Option α} {f : α → Bool} :
|
||||
/-! ### pbind -/
|
||||
|
||||
@[simp] theorem pbind_none : pbind none f = none := rfl
|
||||
@[simp] theorem pbind_some : pbind (some a) f = f a (mem_some_self a) := rfl
|
||||
@[simp] theorem pbind_some : pbind (some a) f = f a rfl := rfl
|
||||
|
||||
@[simp] theorem map_pbind {o : Option α} {f : (a : α) → a ∈ o → Option β} {g : β → γ} :
|
||||
(o.pbind f).map g = o.pbind (fun a h => (f a h).map g) := by
|
||||
@[simp] theorem map_pbind {o : Option α} {f : (a : α) → o = some a → Option β}
|
||||
{g : β → γ} : (o.pbind f).map g = o.pbind (fun a h => (f a h).map g) := by
|
||||
cases o <;> rfl
|
||||
|
||||
@[simp] theorem pbind_map {α β γ : Type _} (o : Option α)
|
||||
@@ -729,25 +838,25 @@ theorem isSome_filter {α : Type _} {x : Option α} {f : α → Bool} :
|
||||
cases o <;> rfl
|
||||
|
||||
@[congr] theorem pbind_congr {o o' : Option α} (ho : o = o')
|
||||
{f : (a : α) → a ∈ o → Option β} {g : (a : α) → a ∈ o' → Option β}
|
||||
{f : (a : α) → o = some a → Option β} {g : (a : α) → o' = some a → Option β}
|
||||
(hf : ∀ a h, f a (ho ▸ h) = g a h) : o.pbind f = o'.pbind g := by
|
||||
subst ho
|
||||
exact (funext fun a => funext fun h => hf a h) ▸ Eq.refl (o.pbind f)
|
||||
|
||||
theorem pbind_eq_none_iff {o : Option α} {f : (a : α) → a ∈ o → Option β} :
|
||||
theorem pbind_eq_none_iff {o : Option α} {f : (a : α) → o = some a → Option β} :
|
||||
o.pbind f = none ↔ o = none ∨ ∃ a h, f a h = none := by
|
||||
cases o <;> simp
|
||||
|
||||
theorem isSome_pbind_iff {o : Option α} {f : (a : α) → a ∈ o → Option β} :
|
||||
theorem isSome_pbind_iff {o : Option α} {f : (a : α) → o = some a → Option β} :
|
||||
(o.pbind f).isSome ↔ ∃ a h, (f a h).isSome := by
|
||||
cases o <;> simp
|
||||
|
||||
@[deprecated "isSome_pbind_iff" (since := "2025-04-01")]
|
||||
theorem pbind_isSome {o : Option α} {f : (a : α) → a ∈ o → Option β} :
|
||||
theorem pbind_isSome {o : Option α} {f : (a : α) → o = some a → Option β} :
|
||||
(o.pbind f).isSome = ∃ a h, (f a h).isSome := by
|
||||
exact propext isSome_pbind_iff
|
||||
|
||||
theorem pbind_eq_some_iff {o : Option α} {f : (a : α) → a ∈ o → Option β} {b : β} :
|
||||
theorem pbind_eq_some_iff {o : Option α} {f : (a : α) → o = some a → Option β} {b : β} :
|
||||
o.pbind f = some b ↔ ∃ a h, f a h = some b := by
|
||||
cases o <;> simp
|
||||
|
||||
@@ -756,8 +865,8 @@ theorem pbind_eq_some_iff {o : Option α} {f : (a : α) → a ∈ o → Option
|
||||
@[simp] theorem pmap_none {p : α → Prop} {f : ∀ (a : α), p a → β} {h} :
|
||||
pmap f none h = none := rfl
|
||||
|
||||
@[simp] theorem pmap_some {p : α → Prop} {f : ∀ (a : α), p a → β} {h}:
|
||||
pmap f (some a) h = f a (h a (mem_some_self a)) := rfl
|
||||
@[simp] theorem pmap_some {p : α → Prop} {f : ∀ (a : α), p a → β} {h} :
|
||||
pmap f (some a) h = f a (h a rfl) := rfl
|
||||
|
||||
@[simp] theorem pmap_eq_none_iff {p : α → Prop} {f : ∀ (a : α), p a → β} {h} :
|
||||
pmap f o h = none ↔ o = none := by
|
||||
@@ -791,13 +900,13 @@ theorem map_pmap {p : α → Prop} (g : β → γ) (f : ∀ a, p a → β) (o H)
|
||||
|
||||
theorem pmap_map (o : Option α) (f : α → β) {p : β → Prop} (g : ∀ b, p b → γ) (H) :
|
||||
pmap g (o.map f) H =
|
||||
pmap (fun a h => g (f a) h) o (fun a m => H (f a) (mem_map_of_mem f m)) := by
|
||||
pmap (fun a h => g (f a) h) o (fun a m => H (f a) (map_eq_some_iff.2 ⟨_, m, rfl⟩)) := by
|
||||
cases o <;> simp
|
||||
|
||||
theorem pmap_pred_congr {α : Type u}
|
||||
{p p' : α → Prop} (hp : ∀ x, p x ↔ p' x)
|
||||
{o o' : Option α} (ho : o = o')
|
||||
(h : ∀ x, x ∈ o → p x) : ∀ x, x ∈ o' → p' x := by
|
||||
(h : ∀ x, o = some x → p x) : ∀ x, o' = some x → p' x := by
|
||||
intro y hy
|
||||
cases ho
|
||||
exact (hp y).mp (h y hy)
|
||||
@@ -808,7 +917,7 @@ theorem pmap_congr {α : Type u} {β : Type v}
|
||||
{f : (x : α) → p x → β} {f' : (x : α) → p' x → β}
|
||||
(hf : ∀ x h, f x ((hp x).mpr h) = f' x h)
|
||||
{o o' : Option α} (ho : o = o')
|
||||
{h : ∀ x, x ∈ o → p x} :
|
||||
{h : ∀ x, o = some x → p x} :
|
||||
Option.pmap f o h = Option.pmap f' o' (Option.pmap_pred_congr hp ho h) := by
|
||||
cases ho
|
||||
cases o
|
||||
@@ -825,7 +934,7 @@ theorem pmap_congr {α : Type u} {β : Type v}
|
||||
cases o <;> simp
|
||||
|
||||
@[simp] theorem elim_pmap {p : α → Prop} (f : (a : α) → p a → β) (o : Option α)
|
||||
(H : ∀ (a : α), a ∈ o → p a) (g : γ) (g' : β → γ) :
|
||||
(H : ∀ (a : α), o = some a → p a) (g : γ) (g' : β → γ) :
|
||||
(o.pmap f H).elim g g' =
|
||||
o.pelim g (fun a h => g' (f a (H a h))) := by
|
||||
cases o <;> simp
|
||||
@@ -854,7 +963,7 @@ theorem isSome_pfilter_iff {α : Type _} {o : Option α} {p : (a : α) → o = s
|
||||
cases o <;> simp
|
||||
|
||||
theorem isSome_pfilter_iff_get {α : Type _} {o : Option α} {p : (a : α) → o = some a → Bool} :
|
||||
(o.pfilter p).isSome ↔ ∃ (h : o.isSome), p (o.get h) (get_mem h) := by
|
||||
(o.pfilter p).isSome ↔ ∃ (h : o.isSome), p (o.get h) (some_get _).symm := by
|
||||
cases o <;> simp
|
||||
|
||||
theorem isSome_of_isSome_pfilter {α : Type _} {o : Option α} {p : (a : α) → o = some a → Bool}
|
||||
|
||||
@@ -8,7 +8,7 @@ import Init.Data.List.Lemmas
|
||||
|
||||
namespace Option
|
||||
|
||||
@[simp] theorem mem_toList {a : α} {o : Option α} : a ∈ o.toList ↔ a ∈ o := by
|
||||
@[simp] theorem mem_toList {a : α} {o : Option α} : a ∈ o.toList ↔ o = some a := by
|
||||
cases o <;> simp [eq_comm]
|
||||
|
||||
@[simp] theorem forIn'_none [Monad m] (b : β) (f : (a : α) → a ∈ none → β → m (ForInStep β)) :
|
||||
|
||||
@@ -92,4 +92,6 @@ theorem forIn_eq_elim [Monad m] [LawfulMonad m]
|
||||
forIn (o.map g) init f = forIn o init fun a y => f (g a) y := by
|
||||
cases o <;> simp
|
||||
|
||||
@[simp] theorem mapA_eq_mapM : @Option.mapA = @Option.mapM := rfl
|
||||
|
||||
end Option
|
||||
|
||||
@@ -239,6 +239,17 @@ Examples:
|
||||
@[extern "lean_int8_div"]
|
||||
protected def Int8.div (a b : Int8) : Int8 := ⟨⟨BitVec.sdiv a.toBitVec b.toBitVec⟩⟩
|
||||
/--
|
||||
The power operation, raising an 8-bit signed integer to a natural number power,
|
||||
wrapping around on overflow. Usually accessed via the `^` operator.
|
||||
|
||||
This function is currently *not* overridden at runtime with an efficient implementation,
|
||||
and should be used with caution. See https://github.com/leanprover/lean4/issues/7887.
|
||||
-/
|
||||
protected def Int8.pow (x : Int8) (n : Nat) : Int8 :=
|
||||
match n with
|
||||
| 0 => 1
|
||||
| n + 1 => Int8.mul (Int8.pow x n) x
|
||||
/--
|
||||
The modulo operator for 8-bit signed integers, which computes the remainder when dividing one
|
||||
integer by another with the T-rounding convention used by `Int8.div`. Usually accessed via the `%`
|
||||
operator.
|
||||
@@ -366,6 +377,7 @@ instance : Inhabited Int8 where
|
||||
instance : Add Int8 := ⟨Int8.add⟩
|
||||
instance : Sub Int8 := ⟨Int8.sub⟩
|
||||
instance : Mul Int8 := ⟨Int8.mul⟩
|
||||
instance : Pow Int8 Nat := ⟨Int8.pow⟩
|
||||
instance : Mod Int8 := ⟨Int8.mod⟩
|
||||
instance : Div Int8 := ⟨Int8.div⟩
|
||||
instance : LT Int8 := ⟨Int8.lt⟩
|
||||
@@ -598,6 +610,17 @@ Examples:
|
||||
@[extern "lean_int16_div"]
|
||||
protected def Int16.div (a b : Int16) : Int16 := ⟨⟨BitVec.sdiv a.toBitVec b.toBitVec⟩⟩
|
||||
/--
|
||||
The power operation, raising a 16-bit signed integer to a natural number power,
|
||||
wrapping around on overflow. Usually accessed via the `^` operator.
|
||||
|
||||
This function is currently *not* overridden at runtime with an efficient implementation,
|
||||
and should be used with caution. See https://github.com/leanprover/lean4/issues/7887.
|
||||
-/
|
||||
protected def Int16.pow (x : Int16) (n : Nat) : Int16 :=
|
||||
match n with
|
||||
| 0 => 1
|
||||
| n + 1 => Int16.mul (Int16.pow x n) x
|
||||
/--
|
||||
The modulo operator for 16-bit signed integers, which computes the remainder when dividing one
|
||||
integer by another with the T-rounding convention used by `Int16.div`. Usually accessed via the `%`
|
||||
operator.
|
||||
@@ -725,6 +748,7 @@ instance : Inhabited Int16 where
|
||||
instance : Add Int16 := ⟨Int16.add⟩
|
||||
instance : Sub Int16 := ⟨Int16.sub⟩
|
||||
instance : Mul Int16 := ⟨Int16.mul⟩
|
||||
instance : Pow Int16 Nat := ⟨Int16.pow⟩
|
||||
instance : Mod Int16 := ⟨Int16.mod⟩
|
||||
instance : Div Int16 := ⟨Int16.div⟩
|
||||
instance : LT Int16 := ⟨Int16.lt⟩
|
||||
@@ -973,6 +997,17 @@ Examples:
|
||||
@[extern "lean_int32_div"]
|
||||
protected def Int32.div (a b : Int32) : Int32 := ⟨⟨BitVec.sdiv a.toBitVec b.toBitVec⟩⟩
|
||||
/--
|
||||
The power operation, raising a 32-bit signed integer to a natural number power,
|
||||
wrapping around on overflow. Usually accessed via the `^` operator.
|
||||
|
||||
This function is currently *not* overridden at runtime with an efficient implementation,
|
||||
and should be used with caution. See https://github.com/leanprover/lean4/issues/7887.
|
||||
-/
|
||||
protected def Int32.pow (x : Int32) (n : Nat) : Int32 :=
|
||||
match n with
|
||||
| 0 => 1
|
||||
| n + 1 => Int32.mul (Int32.pow x n) x
|
||||
/--
|
||||
The modulo operator for 32-bit signed integers, which computes the remainder when dividing one
|
||||
integer by another with the T-rounding convention used by `Int32.div`. Usually accessed via the `%`
|
||||
operator.
|
||||
@@ -1100,6 +1135,7 @@ instance : Inhabited Int32 where
|
||||
instance : Add Int32 := ⟨Int32.add⟩
|
||||
instance : Sub Int32 := ⟨Int32.sub⟩
|
||||
instance : Mul Int32 := ⟨Int32.mul⟩
|
||||
instance : Pow Int32 Nat := ⟨Int32.pow⟩
|
||||
instance : Mod Int32 := ⟨Int32.mod⟩
|
||||
instance : Div Int32 := ⟨Int32.div⟩
|
||||
instance : LT Int32 := ⟨Int32.lt⟩
|
||||
@@ -1368,6 +1404,17 @@ Examples:
|
||||
@[extern "lean_int64_div"]
|
||||
protected def Int64.div (a b : Int64) : Int64 := ⟨⟨BitVec.sdiv a.toBitVec b.toBitVec⟩⟩
|
||||
/--
|
||||
The power operation, raising a 64-bit signed integer to a natural number power,
|
||||
wrapping around on overflow. Usually accessed via the `^` operator.
|
||||
|
||||
This function is currently *not* overridden at runtime with an efficient implementation,
|
||||
and should be used with caution. See https://github.com/leanprover/lean4/issues/7887.
|
||||
-/
|
||||
protected def Int64.pow (x : Int64) (n : Nat) : Int64 :=
|
||||
match n with
|
||||
| 0 => 1
|
||||
| n + 1 => Int64.mul (Int64.pow x n) x
|
||||
/--
|
||||
The modulo operator for 64-bit signed integers, which computes the remainder when dividing one
|
||||
integer by another with the T-rounding convention used by `Int64.div`. Usually accessed via the `%`
|
||||
operator.
|
||||
@@ -1495,6 +1542,7 @@ instance : Inhabited Int64 where
|
||||
instance : Add Int64 := ⟨Int64.add⟩
|
||||
instance : Sub Int64 := ⟨Int64.sub⟩
|
||||
instance : Mul Int64 := ⟨Int64.mul⟩
|
||||
instance : Pow Int64 Nat := ⟨Int64.pow⟩
|
||||
instance : Mod Int64 := ⟨Int64.mod⟩
|
||||
instance : Div Int64 := ⟨Int64.div⟩
|
||||
instance : LT Int64 := ⟨Int64.lt⟩
|
||||
@@ -1746,6 +1794,17 @@ Examples:
|
||||
@[extern "lean_isize_div"]
|
||||
protected def ISize.div (a b : ISize) : ISize := ⟨⟨BitVec.sdiv a.toBitVec b.toBitVec⟩⟩
|
||||
/--
|
||||
The power operation, raising a word-sized signed integer to a natural number power,
|
||||
wrapping around on overflow. Usually accessed via the `^` operator.
|
||||
|
||||
This function is currently *not* overridden at runtime with an efficient implementation,
|
||||
and should be used with caution. See https://github.com/leanprover/lean4/issues/7887.
|
||||
-/
|
||||
protected def ISize.pow (x : ISize) (n : Nat) : ISize :=
|
||||
match n with
|
||||
| 0 => 1
|
||||
| n + 1 => ISize.mul (ISize.pow x n) x
|
||||
/--
|
||||
The modulo operator for word-sized signed integers, which computes the remainder when dividing one
|
||||
integer by another with the T-rounding convention used by `ISize.div`. Usually accessed via the `%`
|
||||
operator.
|
||||
@@ -1875,6 +1934,7 @@ instance : Inhabited ISize where
|
||||
instance : Add ISize := ⟨ISize.add⟩
|
||||
instance : Sub ISize := ⟨ISize.sub⟩
|
||||
instance : Mul ISize := ⟨ISize.mul⟩
|
||||
instance : Pow ISize Nat := ⟨ISize.pow⟩
|
||||
instance : Mod ISize := ⟨ISize.mod⟩
|
||||
instance : Div ISize := ⟨ISize.div⟩
|
||||
instance : LT ISize := ⟨ISize.lt⟩
|
||||
|
||||
@@ -2625,6 +2625,17 @@ instance : Std.LawfulCommIdentity (α := ISize) (· * ·) 1 where
|
||||
@[simp] theorem Int64.zero_mul {a : Int64} : 0 * a = 0 := Int64.toBitVec_inj.1 BitVec.zero_mul
|
||||
@[simp] theorem ISize.zero_mul {a : ISize} : 0 * a = 0 := ISize.toBitVec_inj.1 BitVec.zero_mul
|
||||
|
||||
@[simp] protected theorem Int8.pow_zero (x : Int8) : x ^ 0 = 1 := rfl
|
||||
protected theorem Int8.pow_succ (x : Int8) (n : Nat) : x ^ (n + 1) = x ^ n * x := rfl
|
||||
@[simp] protected theorem Int16.pow_zero (x : Int16) : x ^ 0 = 1 := rfl
|
||||
protected theorem Int16.pow_succ (x : Int16) (n : Nat) : x ^ (n + 1) = x ^ n * x := rfl
|
||||
@[simp] protected theorem Int32.pow_zero (x : Int32) : x ^ 0 = 1 := rfl
|
||||
protected theorem Int32.pow_succ (x : Int32) (n : Nat) : x ^ (n + 1) = x ^ n * x := rfl
|
||||
@[simp] protected theorem Int64.pow_zero (x : Int64) : x ^ 0 = 1 := rfl
|
||||
protected theorem Int64.pow_succ (x : Int64) (n : Nat) : x ^ (n + 1) = x ^ n * x := rfl
|
||||
@[simp] protected theorem ISize.pow_zero (x : ISize) : x ^ 0 = 1 := rfl
|
||||
protected theorem ISize.pow_succ (x : ISize) (n : Nat) : x ^ (n + 1) = x ^ n * x := rfl
|
||||
|
||||
protected theorem Int8.mul_add {a b c : Int8} : a * (b + c) = a * b + a * c :=
|
||||
Int8.toBitVec_inj.1 BitVec.mul_add
|
||||
protected theorem Int16.mul_add {a b c : Int16} : a * (b + c) = a * b + a * c :=
|
||||
|
||||
@@ -20,6 +20,9 @@ def UInt8.mk (bitVec : BitVec 8) : UInt8 :=
|
||||
def UInt8.ofNatCore (n : Nat) (h : n < UInt8.size) : UInt8 :=
|
||||
UInt8.ofNatLT n h
|
||||
|
||||
/-- Converts an `Int` to a `UInt8` by taking the (non-negative remainder of the division by `2 ^ 8`. -/
|
||||
def UInt8.ofInt (x : Int) : UInt8 := ofNat (x % 2 ^ 8).toNat
|
||||
|
||||
/--
|
||||
Adds two 8-bit unsigned integers, wrapping around on overflow. Usually accessed via the `+`
|
||||
operator.
|
||||
@@ -55,6 +58,17 @@ This function is overridden at runtime with an efficient implementation.
|
||||
@[extern "lean_uint8_div"]
|
||||
protected def UInt8.div (a b : UInt8) : UInt8 := ⟨BitVec.udiv a.toBitVec b.toBitVec⟩
|
||||
/--
|
||||
The power operation, raising an 8-bit unsigned integer to a natural number power,
|
||||
wrapping around on overflow. Usually accessed via the `^` operator.
|
||||
|
||||
This function is currently *not* overridden at runtime with an efficient implementation,
|
||||
and should be used with caution. See https://github.com/leanprover/lean4/issues/7887.
|
||||
-/
|
||||
protected def UInt8.pow (x : UInt8) (n : Nat) : UInt8 :=
|
||||
match n with
|
||||
| 0 => 1
|
||||
| n + 1 => UInt8.mul (UInt8.pow x n) x
|
||||
/--
|
||||
The modulo operator for 8-bit unsigned integers, which computes the remainder when dividing one
|
||||
integer by another. Usually accessed via the `%` operator.
|
||||
|
||||
@@ -129,6 +143,7 @@ protected def UInt8.le (a b : UInt8) : Prop := a.toBitVec ≤ b.toBitVec
|
||||
instance : Add UInt8 := ⟨UInt8.add⟩
|
||||
instance : Sub UInt8 := ⟨UInt8.sub⟩
|
||||
instance : Mul UInt8 := ⟨UInt8.mul⟩
|
||||
instance : Pow UInt8 Nat := ⟨UInt8.pow⟩
|
||||
instance : Mod UInt8 := ⟨UInt8.mod⟩
|
||||
|
||||
set_option linter.deprecated false in
|
||||
@@ -217,6 +232,9 @@ def UInt16.mk (bitVec : BitVec 16) : UInt16 :=
|
||||
def UInt16.ofNatCore (n : Nat) (h : n < UInt16.size) : UInt16 :=
|
||||
UInt16.ofNatLT n h
|
||||
|
||||
/-- Converts an `Int` to a `UInt16` by taking the (non-negative remainder of the division by `2 ^ 16`. -/
|
||||
def UInt16.ofInt (x : Int) : UInt16 := ofNat (x % 2 ^ 16).toNat
|
||||
|
||||
/--
|
||||
Adds two 16-bit unsigned integers, wrapping around on overflow. Usually accessed via the `+`
|
||||
operator.
|
||||
@@ -252,6 +270,17 @@ This function is overridden at runtime with an efficient implementation.
|
||||
@[extern "lean_uint16_div"]
|
||||
protected def UInt16.div (a b : UInt16) : UInt16 := ⟨BitVec.udiv a.toBitVec b.toBitVec⟩
|
||||
/--
|
||||
The power operation, raising a 16-bit unsigned integer to a natural number power,
|
||||
wrapping around on overflow. Usually accessed via the `^` operator.
|
||||
|
||||
This function is currently *not* overridden at runtime with an efficient implementation,
|
||||
and should be used with caution. See https://github.com/leanprover/lean4/issues/7887.
|
||||
-/
|
||||
protected def UInt16.pow (x : UInt16) (n : Nat) : UInt16 :=
|
||||
match n with
|
||||
| 0 => 1
|
||||
| n + 1 => UInt16.mul (UInt16.pow x n) x
|
||||
/--
|
||||
The modulo operator for 16-bit unsigned integers, which computes the remainder when dividing one
|
||||
integer by another. Usually accessed via the `%` operator.
|
||||
|
||||
@@ -289,7 +318,7 @@ This function is overridden at runtime with an efficient implementation.
|
||||
@[extern "lean_uint16_lor"]
|
||||
protected def UInt16.lor (a b : UInt16) : UInt16 := ⟨a.toBitVec ||| b.toBitVec⟩
|
||||
/--
|
||||
Bitwise exclusive or for 8-bit unsigned integers. Usually accessed via the `^^^` operator.
|
||||
Bitwise exclusive or for 16-bit unsigned integers. Usually accessed via the `^^^` operator.
|
||||
|
||||
Each bit of the resulting integer is set if exactly one of the corresponding bits of both input
|
||||
integers are set.
|
||||
@@ -326,6 +355,7 @@ protected def UInt16.le (a b : UInt16) : Prop := a.toBitVec ≤ b.toBitVec
|
||||
instance : Add UInt16 := ⟨UInt16.add⟩
|
||||
instance : Sub UInt16 := ⟨UInt16.sub⟩
|
||||
instance : Mul UInt16 := ⟨UInt16.mul⟩
|
||||
instance : Pow UInt16 Nat := ⟨UInt16.pow⟩
|
||||
instance : Mod UInt16 := ⟨UInt16.mod⟩
|
||||
|
||||
set_option linter.deprecated false in
|
||||
@@ -416,6 +446,9 @@ def UInt32.mk (bitVec : BitVec 32) : UInt32 :=
|
||||
def UInt32.ofNatCore (n : Nat) (h : n < UInt32.size) : UInt32 :=
|
||||
UInt32.ofNatLT n h
|
||||
|
||||
/-- Converts an `Int` to a `UInt32` by taking the (non-negative remainder of the division by `2 ^ 32`. -/
|
||||
def UInt32.ofInt (x : Int) : UInt32 := ofNat (x % 2 ^ 32).toNat
|
||||
|
||||
/--
|
||||
Adds two 32-bit unsigned integers, wrapping around on overflow. Usually accessed via the `+`
|
||||
operator.
|
||||
@@ -451,6 +484,17 @@ This function is overridden at runtime with an efficient implementation.
|
||||
@[extern "lean_uint32_div"]
|
||||
protected def UInt32.div (a b : UInt32) : UInt32 := ⟨BitVec.udiv a.toBitVec b.toBitVec⟩
|
||||
/--
|
||||
The power operation, raising a 32-bit unsigned integer to a natural number power,
|
||||
wrapping around on overflow. Usually accessed via the `^` operator.
|
||||
|
||||
This function is currently *not* overridden at runtime with an efficient implementation,
|
||||
and should be used with caution. See https://github.com/leanprover/lean4/issues/7887.
|
||||
-/
|
||||
protected def UInt32.pow (x : UInt32) (n : Nat) : UInt32 :=
|
||||
match n with
|
||||
| 0 => 1
|
||||
| n + 1 => UInt32.mul (UInt32.pow x n) x
|
||||
/--
|
||||
The modulo operator for 32-bit unsigned integers, which computes the remainder when dividing one
|
||||
integer by another. Usually accessed via the `%` operator.
|
||||
|
||||
@@ -525,6 +569,7 @@ protected def UInt32.le (a b : UInt32) : Prop := a.toBitVec ≤ b.toBitVec
|
||||
instance : Add UInt32 := ⟨UInt32.add⟩
|
||||
instance : Sub UInt32 := ⟨UInt32.sub⟩
|
||||
instance : Mul UInt32 := ⟨UInt32.mul⟩
|
||||
instance : Pow UInt32 Nat := ⟨UInt32.pow⟩
|
||||
instance : Mod UInt32 := ⟨UInt32.mod⟩
|
||||
|
||||
set_option linter.deprecated false in
|
||||
@@ -577,6 +622,9 @@ def UInt64.mk (bitVec : BitVec 64) : UInt64 :=
|
||||
def UInt64.ofNatCore (n : Nat) (h : n < UInt64.size) : UInt64 :=
|
||||
UInt64.ofNatLT n h
|
||||
|
||||
/-- Converts an `Int` to a `UInt64` by taking the (non-negative remainder of the division by `2 ^ 64`. -/
|
||||
def UInt64.ofInt (x : Int) : UInt64 := ofNat (x % 2 ^ 64).toNat
|
||||
|
||||
/--
|
||||
Adds two 64-bit unsigned integers, wrapping around on overflow. Usually accessed via the `+`
|
||||
operator.
|
||||
@@ -612,6 +660,17 @@ This function is overridden at runtime with an efficient implementation.
|
||||
@[extern "lean_uint64_div"]
|
||||
protected def UInt64.div (a b : UInt64) : UInt64 := ⟨BitVec.udiv a.toBitVec b.toBitVec⟩
|
||||
/--
|
||||
The power operation, raising a 64-bit unsigned integer to a natural number power,
|
||||
wrapping around on overflow. Usually accessed via the `^` operator.
|
||||
|
||||
This function is currently *not* overridden at runtime with an efficient implementation,
|
||||
and should be used with caution. See https://github.com/leanprover/lean4/issues/7887.
|
||||
-/
|
||||
protected def UInt64.pow (x : UInt64) (n : Nat) : UInt64 :=
|
||||
match n with
|
||||
| 0 => 1
|
||||
| n + 1 => UInt64.mul (UInt64.pow x n) x
|
||||
/--
|
||||
The modulo operator for 64-bit unsigned integers, which computes the remainder when dividing one
|
||||
integer by another. Usually accessed via the `%` operator.
|
||||
|
||||
@@ -686,6 +745,7 @@ protected def UInt64.le (a b : UInt64) : Prop := a.toBitVec ≤ b.toBitVec
|
||||
instance : Add UInt64 := ⟨UInt64.add⟩
|
||||
instance : Sub UInt64 := ⟨UInt64.sub⟩
|
||||
instance : Mul UInt64 := ⟨UInt64.mul⟩
|
||||
instance : Pow UInt64 Nat := ⟨UInt64.pow⟩
|
||||
instance : Mod UInt64 := ⟨UInt64.mod⟩
|
||||
|
||||
set_option linter.deprecated false in
|
||||
@@ -706,7 +766,7 @@ This function is overridden at runtime with an efficient implementation.
|
||||
@[extern "lean_uint64_complement"]
|
||||
protected def UInt64.complement (a : UInt64) : UInt64 := ⟨~~~a.toBitVec⟩
|
||||
/--
|
||||
Negation of 32-bit unsigned integers, computed modulo `UInt64.size`.
|
||||
Negation of 64-bit unsigned integers, computed modulo `UInt64.size`.
|
||||
|
||||
`UInt64.neg a` is equivalent to `18_446_744_073_709_551_615 - a + 1`.
|
||||
|
||||
@@ -774,6 +834,9 @@ def USize.mk (bitVec : BitVec System.Platform.numBits) : USize :=
|
||||
def USize.ofNatCore (n : Nat) (h : n < USize.size) : USize :=
|
||||
USize.ofNatLT n h
|
||||
|
||||
/-- Converts an `Int` to a `USize` by taking the (non-negative remainder of the division by `2 ^ numBits`. -/
|
||||
def USize.ofInt (x : Int) : USize := ofNat (x % 2 ^ System.Platform.numBits).toNat
|
||||
|
||||
@[simp] theorem USize.le_size : 2 ^ 32 ≤ USize.size := by cases USize.size_eq <;> simp_all
|
||||
@[simp] theorem USize.size_le : USize.size ≤ 2 ^ 64 := by cases USize.size_eq <;> simp_all
|
||||
|
||||
@@ -804,6 +867,17 @@ This function is overridden at runtime with an efficient implementation.
|
||||
@[extern "lean_usize_div"]
|
||||
protected def USize.div (a b : USize) : USize := ⟨a.toBitVec / b.toBitVec⟩
|
||||
/--
|
||||
The power operation, raising a word-sized unsigned integer to a natural number power,
|
||||
wrapping around on overflow. Usually accessed via the `^` operator.
|
||||
|
||||
This function is currently *not* overridden at runtime with an efficient implementation,
|
||||
and should be used with caution. See https://github.com/leanprover/lean4/issues/7887.
|
||||
-/
|
||||
protected def USize.pow (x : USize) (n : Nat) : USize :=
|
||||
match n with
|
||||
| 0 => 1
|
||||
| n + 1 => USize.mul (USize.pow x n) x
|
||||
/--
|
||||
The modulo operator for word-sized unsigned integers, which computes the remainder when dividing one
|
||||
integer by another. Usually accessed via the `%` operator.
|
||||
|
||||
@@ -937,6 +1011,7 @@ def USize.toUInt64 (a : USize) : UInt64 :=
|
||||
UInt64.ofNatLT a.toBitVec.toNat (Nat.lt_of_lt_of_le a.toBitVec.isLt USize.size_le)
|
||||
|
||||
instance : Mul USize := ⟨USize.mul⟩
|
||||
instance : Pow USize Nat := ⟨USize.pow⟩
|
||||
instance : Mod USize := ⟨USize.mod⟩
|
||||
|
||||
set_option linter.deprecated false in
|
||||
|
||||
@@ -286,6 +286,17 @@ declare_uint_theorems USize System.Platform.numBits
|
||||
theorem USize.toNat_ofNat_of_lt_32 {n : Nat} (h : n < 4294967296) : toNat (ofNat n) = n :=
|
||||
toNat_ofNat_of_lt (Nat.lt_of_lt_of_le h USize.le_size)
|
||||
|
||||
theorem UInt8.ofNat_mod_size : ofNat (x % 2 ^ 8) = ofNat x := by
|
||||
simp [ofNat, BitVec.ofNat, Fin.ofNat']
|
||||
theorem UInt16.ofNat_mod_size : ofNat (x % 2 ^ 16) = ofNat x := by
|
||||
simp [ofNat, BitVec.ofNat, Fin.ofNat']
|
||||
theorem UInt32.ofNat_mod_size : ofNat (x % 2 ^ 32) = ofNat x := by
|
||||
simp [ofNat, BitVec.ofNat, Fin.ofNat']
|
||||
theorem UInt64.ofNat_mod_size : ofNat (x % 2 ^ 64) = ofNat x := by
|
||||
simp [ofNat, BitVec.ofNat, Fin.ofNat']
|
||||
theorem USize.ofNat_mod_size : ofNat (x % 2 ^ System.Platform.numBits) = ofNat x := by
|
||||
simp [ofNat, BitVec.ofNat, Fin.ofNat']
|
||||
|
||||
theorem UInt8.lt_ofNat_iff {n : UInt8} {m : Nat} (h : m < size) : n < ofNat m ↔ n.toNat < m := by
|
||||
rw [lt_iff_toNat_lt, toNat_ofNat_of_lt' h]
|
||||
theorem UInt8.ofNat_lt_iff {n : UInt8} {m : Nat} (h : m < size) : ofNat m < n ↔ m < n.toNat := by
|
||||
@@ -2081,6 +2092,23 @@ theorem USize.ofNat_eq_iff_mod_eq_toNat (a : Nat) (b : USize) : USize.ofNat a =
|
||||
USize.ofNatLT (a % b) (Nat.mod_lt_of_lt ha) = USize.ofNatLT a ha % USize.ofNatLT b hb := by
|
||||
simp [USize.ofNatLT_eq_ofNat, USize.ofNat_mod ha hb]
|
||||
|
||||
@[simp] theorem UInt8.ofInt_one : ofInt 1 = 1 := rfl
|
||||
@[simp] theorem UInt8.ofInt_neg_one : ofInt (-1) = -1 := rfl
|
||||
@[simp] theorem UInt16.ofInt_one : ofInt 1 = 1 := rfl
|
||||
@[simp] theorem UInt16.ofInt_neg_one : ofInt (-1) = -1 := rfl
|
||||
@[simp] theorem UInt32.ofInt_one : ofInt 1 = 1 := rfl
|
||||
@[simp] theorem UInt32.ofInt_neg_one : ofInt (-1) = -1 := rfl
|
||||
@[simp] theorem UInt64.ofInt_one : ofInt 1 = 1 := rfl
|
||||
@[simp] theorem UInt64.ofInt_neg_one : ofInt (-1) = -1 := rfl
|
||||
@[simp] theorem USize.ofInt_one : ofInt 1 = 1 := by
|
||||
rcases System.Platform.numBits_eq with h | h <;>
|
||||
· apply USize.toNat_inj.mp
|
||||
simp_all [USize.ofInt, USize.ofNat, size, toNat]
|
||||
@[simp] theorem USize.ofInt_neg_one : ofInt (-1) = -1 := by
|
||||
rcases System.Platform.numBits_eq with h | h <;>
|
||||
· apply USize.toNat_inj.mp
|
||||
simp_all [USize.ofInt, USize.ofNat, size, toNat]
|
||||
|
||||
@[simp] theorem UInt8.ofNat_add (a b : Nat) : UInt8.ofNat (a + b) = UInt8.ofNat a + UInt8.ofNat b := by
|
||||
simp [UInt8.ofNat_eq_iff_mod_eq_toNat]
|
||||
@[simp] theorem UInt16.ofNat_add (a b : Nat) : UInt16.ofNat (a + b) = UInt16.ofNat a + UInt16.ofNat b := by
|
||||
@@ -2092,6 +2120,70 @@ theorem USize.ofNat_eq_iff_mod_eq_toNat (a : Nat) (b : USize) : USize.ofNat a =
|
||||
@[simp] theorem USize.ofNat_add (a b : Nat) : USize.ofNat (a + b) = USize.ofNat a + USize.ofNat b := by
|
||||
simp [USize.ofNat_eq_iff_mod_eq_toNat]
|
||||
|
||||
@[simp] theorem UInt8.ofInt_add (x y : Int) : ofInt (x + y) = ofInt x + ofInt y := by
|
||||
dsimp only [UInt8.ofInt]
|
||||
rw [Int.add_emod]
|
||||
have h₁ : 0 ≤ x % 2 ^ 8 := Int.emod_nonneg _ (by decide)
|
||||
have h₂ : 0 ≤ y % 2 ^ 8 := Int.emod_nonneg _ (by decide)
|
||||
have h₃ : 0 ≤ x % 2 ^ 8 + y % 2 ^ 8 := Int.add_nonneg h₁ h₂
|
||||
rw [Int.toNat_emod h₃ (by decide), Int.toNat_add h₁ h₂]
|
||||
have : (2 ^ 8 : Int).toNat = 2 ^ 8 := rfl
|
||||
rw [this, UInt8.ofNat_mod_size, UInt8.ofNat_add]
|
||||
@[simp] theorem UInt16.ofInt_add (x y : Int) : UInt16.ofInt (x + y) = UInt16.ofInt x + UInt16.ofInt y := by
|
||||
dsimp only [UInt16.ofInt]
|
||||
rw [Int.add_emod]
|
||||
have h₁ : 0 ≤ x % 2 ^ 16 := Int.emod_nonneg _ (by decide)
|
||||
have h₂ : 0 ≤ y % 2 ^ 16 := Int.emod_nonneg _ (by decide)
|
||||
have h₃ : 0 ≤ x % 2 ^ 16 + y % 2 ^ 16 := Int.add_nonneg h₁ h₂
|
||||
rw [Int.toNat_emod h₃ (by decide), Int.toNat_add h₁ h₂]
|
||||
have : (2 ^ 16 : Int).toNat = 2 ^ 16 := rfl
|
||||
rw [this, UInt16.ofNat_mod_size, UInt16.ofNat_add]
|
||||
@[simp] theorem UInt32.ofInt_add (x y : Int) : UInt32.ofInt (x + y) = UInt32.ofInt x + UInt32.ofInt y := by
|
||||
dsimp only [UInt32.ofInt]
|
||||
rw [Int.add_emod]
|
||||
have h₁ : 0 ≤ x % 2 ^ 32 := Int.emod_nonneg _ (by decide)
|
||||
have h₂ : 0 ≤ y % 2 ^ 32 := Int.emod_nonneg _ (by decide)
|
||||
have h₃ : 0 ≤ x % 2 ^ 32 + y % 2 ^ 32 := Int.add_nonneg h₁ h₂
|
||||
rw [Int.toNat_emod h₃ (by decide), Int.toNat_add h₁ h₂]
|
||||
have : (2 ^ 32 : Int).toNat = 2 ^ 32 := rfl
|
||||
rw [this, UInt32.ofNat_mod_size, UInt32.ofNat_add]
|
||||
@[simp] theorem UInt64.ofInt_add (x y : Int) : UInt64.ofInt (x + y) = UInt64.ofInt x + UInt64.ofInt y := by
|
||||
dsimp only [UInt64.ofInt]
|
||||
rw [Int.add_emod]
|
||||
have h₁ : 0 ≤ x % 2 ^ 64 := Int.emod_nonneg _ (by decide)
|
||||
have h₂ : 0 ≤ y % 2 ^ 64 := Int.emod_nonneg _ (by decide)
|
||||
have h₃ : 0 ≤ x % 2 ^ 64 + y % 2 ^ 64 := Int.add_nonneg h₁ h₂
|
||||
rw [Int.toNat_emod h₃ (by decide), Int.toNat_add h₁ h₂]
|
||||
have : (2 ^ 64 : Int).toNat = 2 ^ 64 := rfl
|
||||
rw [this, UInt64.ofNat_mod_size, UInt64.ofNat_add]
|
||||
|
||||
namespace System.Platform
|
||||
|
||||
theorem two_pow_numBits_nonneg : 0 ≤ (2 ^ System.Platform.numBits : Int) := by
|
||||
rcases System.Platform.numBits_eq with h | h <;>
|
||||
· rw [h]
|
||||
decide
|
||||
theorem two_pow_numBits_ne_zero : (2 ^ System.Platform.numBits : Int) ≠ 0 := by
|
||||
rcases System.Platform.numBits_eq with h | h <;>
|
||||
· rw [h]
|
||||
decide
|
||||
|
||||
end System.Platform
|
||||
|
||||
open System.Platform in
|
||||
@[simp] theorem USize.ofInt_add (x y : Int) : USize.ofInt (x + y) = USize.ofInt x + USize.ofInt y := by
|
||||
dsimp only [USize.ofInt]
|
||||
rw [Int.add_emod]
|
||||
have h₁ : 0 ≤ x % 2 ^ numBits := Int.emod_nonneg _ two_pow_numBits_ne_zero
|
||||
have h₂ : 0 ≤ y % 2 ^ numBits := Int.emod_nonneg _ two_pow_numBits_ne_zero
|
||||
have h₃ : 0 ≤ x % 2 ^ numBits + y % 2 ^ numBits := Int.add_nonneg h₁ h₂
|
||||
rw [Int.toNat_emod h₃ two_pow_numBits_nonneg, Int.toNat_add h₁ h₂]
|
||||
have : (2 ^ numBits : Int).toNat = 2 ^ numBits := by
|
||||
rcases System.Platform.numBits_eq with h | h <;>
|
||||
· rw [h]
|
||||
decide
|
||||
rw [this, USize.ofNat_mod_size, USize.ofNat_add]
|
||||
|
||||
@[simp] theorem UInt8.ofNatLT_add {a b : Nat} (hab : a + b < 2 ^ 8) :
|
||||
UInt8.ofNatLT (a + b) hab = UInt8.ofNatLT a (Nat.lt_of_add_right_lt hab) + UInt8.ofNatLT b (Nat.lt_of_add_left_lt hab) := by
|
||||
simp [UInt8.ofNatLT_eq_ofNat]
|
||||
@@ -2176,6 +2268,56 @@ theorem USize.ofNatLT_sub {a b : Nat} (ha : a < 2 ^ System.Platform.numBits) (ha
|
||||
@[simp] theorem USize.ofNat_mul (a b : Nat) : USize.ofNat (a * b) = USize.ofNat a * USize.ofNat b := by
|
||||
simp [USize.ofNat_eq_iff_mod_eq_toNat]
|
||||
|
||||
@[simp] theorem UInt8.ofInt_mul (x y : Int) : ofInt (x * y) = ofInt x * ofInt y := by
|
||||
dsimp only [UInt8.ofInt]
|
||||
rw [Int.mul_emod]
|
||||
have h₁ : 0 ≤ x % 2 ^ 8 := Int.emod_nonneg _ (by decide)
|
||||
have h₂ : 0 ≤ y % 2 ^ 8 := Int.emod_nonneg _ (by decide)
|
||||
have h₃ : 0 ≤ (x % 2 ^ 8) * (y % 2 ^ 8) := Int.mul_nonneg h₁ h₂
|
||||
rw [Int.toNat_emod h₃ (by decide), Int.toNat_mul h₁ h₂]
|
||||
have : (2 ^ 8 : Int).toNat = 2 ^ 8 := rfl
|
||||
rw [this, UInt8.ofNat_mod_size, UInt8.ofNat_mul]
|
||||
@[simp] theorem UInt16.ofInt_mul (x y : Int) : ofInt (x * y) = ofInt x * ofInt y := by
|
||||
dsimp only [UInt16.ofInt]
|
||||
rw [Int.mul_emod]
|
||||
have h₁ : 0 ≤ x % 2 ^ 16 := Int.emod_nonneg _ (by decide)
|
||||
have h₂ : 0 ≤ y % 2 ^ 16 := Int.emod_nonneg _ (by decide)
|
||||
have h₃ : 0 ≤ (x % 2 ^ 16) * (y % 2 ^ 16) := Int.mul_nonneg h₁ h₂
|
||||
rw [Int.toNat_emod h₃ (by decide), Int.toNat_mul h₁ h₂]
|
||||
have : (2 ^ 16 : Int).toNat = 2 ^ 16 := rfl
|
||||
rw [this, UInt16.ofNat_mod_size, UInt16.ofNat_mul]
|
||||
@[simp] theorem UInt32.ofInt_mul (x y : Int) : ofInt (x * y) = ofInt x * ofInt y := by
|
||||
dsimp only [UInt32.ofInt]
|
||||
rw [Int.mul_emod]
|
||||
have h₁ : 0 ≤ x % 2 ^ 32 := Int.emod_nonneg _ (by decide)
|
||||
have h₂ : 0 ≤ y % 2 ^ 32 := Int.emod_nonneg _ (by decide)
|
||||
have h₃ : 0 ≤ (x % 2 ^ 32) * (y % 2 ^ 32) := Int.mul_nonneg h₁ h₂
|
||||
rw [Int.toNat_emod h₃ (by decide), Int.toNat_mul h₁ h₂]
|
||||
have : (2 ^ 32 : Int).toNat = 2 ^ 32 := rfl
|
||||
rw [this, UInt32.ofNat_mod_size, UInt32.ofNat_mul]
|
||||
@[simp] theorem UInt64.ofInt_mul (x y : Int) : ofInt (x * y) = ofInt x * ofInt y := by
|
||||
dsimp only [UInt64.ofInt]
|
||||
rw [Int.mul_emod]
|
||||
have h₁ : 0 ≤ x % 2 ^ 64 := Int.emod_nonneg _ (by decide)
|
||||
have h₂ : 0 ≤ y % 2 ^ 64 := Int.emod_nonneg _ (by decide)
|
||||
have h₃ : 0 ≤ (x % 2 ^ 64) * (y % 2 ^ 64) := Int.mul_nonneg h₁ h₂
|
||||
rw [Int.toNat_emod h₃ (by decide), Int.toNat_mul h₁ h₂]
|
||||
have : (2 ^ 64 : Int).toNat = 2 ^ 64 := rfl
|
||||
rw [this, UInt64.ofNat_mod_size, UInt64.ofNat_mul]
|
||||
open System.Platform in
|
||||
@[simp] theorem USize.ofInt_mul (x y : Int) : ofInt (x * y) = ofInt x * ofInt y := by
|
||||
dsimp only [USize.ofInt]
|
||||
rw [Int.mul_emod]
|
||||
have h₁ : 0 ≤ x % 2 ^ numBits := Int.emod_nonneg _ two_pow_numBits_ne_zero
|
||||
have h₂ : 0 ≤ y % 2 ^ numBits := Int.emod_nonneg _ two_pow_numBits_ne_zero
|
||||
have h₃ : 0 ≤ (x % 2 ^ numBits) * (y % 2 ^ numBits) := Int.mul_nonneg h₁ h₂
|
||||
rw [Int.toNat_emod h₃ two_pow_numBits_nonneg, Int.toNat_mul h₁ h₂]
|
||||
have : (2 ^ numBits : Int).toNat = 2 ^ numBits := by
|
||||
rcases System.Platform.numBits_eq with h | h <;>
|
||||
· rw [h]
|
||||
decide
|
||||
rw [this, USize.ofNat_mod_size, USize.ofNat_mul]
|
||||
|
||||
@[simp] theorem UInt8.ofNatLT_mul {a b : Nat} (ha : a < 2 ^ 8) (hb : b < 2 ^ 8) (hab : a * b < 2 ^ 8) :
|
||||
UInt8.ofNatLT (a * b) hab = UInt8.ofNatLT a ha * UInt8.ofNatLT b hb := by
|
||||
simp [UInt8.ofNatLT_eq_ofNat]
|
||||
@@ -2467,6 +2609,17 @@ protected theorem USize.neg_add {a b : USize} : - (a + b) = -a - b := USize.toBi
|
||||
@[simp] protected theorem USize.neg_sub {a b : USize} : -(a - b) = b - a := by
|
||||
rw [USize.sub_eq_add_neg, USize.neg_add, USize.sub_neg, USize.add_comm, ← USize.sub_eq_add_neg]
|
||||
|
||||
@[simp] protected theorem UInt8.ofInt_neg (x : Int) : ofInt (-x) = -ofInt x := by
|
||||
rw [Int.neg_eq_neg_one_mul, ofInt_mul, ofInt_neg_one, ← UInt8.neg_eq_neg_one_mul]
|
||||
@[simp] protected theorem UInt16.ofInt_neg (x : Int) : ofInt (-x) = -ofInt x := by
|
||||
rw [Int.neg_eq_neg_one_mul, ofInt_mul, ofInt_neg_one, ← UInt16.neg_eq_neg_one_mul]
|
||||
@[simp] protected theorem UInt32.ofInt_neg (x : Int) : ofInt (-x) = -ofInt x := by
|
||||
rw [Int.neg_eq_neg_one_mul, ofInt_mul, ofInt_neg_one, ← UInt32.neg_eq_neg_one_mul]
|
||||
@[simp] protected theorem UInt64.ofInt_neg (x : Int) : ofInt (-x) = -ofInt x := by
|
||||
rw [Int.neg_eq_neg_one_mul, ofInt_mul, ofInt_neg_one, ← UInt64.neg_eq_neg_one_mul]
|
||||
@[simp] protected theorem USize.ofInt_neg (x : Int) : ofInt (-x) = -ofInt x := by
|
||||
rw [Int.neg_eq_neg_one_mul, ofInt_mul, ofInt_neg_one, ← USize.neg_eq_neg_one_mul]
|
||||
|
||||
@[simp] protected theorem UInt8.add_left_inj {a b : UInt8} (c : UInt8) : (a + c = b + c) ↔ a = b := by
|
||||
simp [← UInt8.toBitVec_inj]
|
||||
@[simp] protected theorem UInt16.add_left_inj {a b : UInt16} (c : UInt16) : (a + c = b + c) ↔ a = b := by
|
||||
@@ -2614,6 +2767,17 @@ instance : Std.LawfulCommIdentity (α := USize) (· * ·) 1 where
|
||||
@[simp] theorem UInt64.zero_mul {a : UInt64} : 0 * a = 0 := UInt64.toBitVec_inj.1 BitVec.zero_mul
|
||||
@[simp] theorem USize.zero_mul {a : USize} : 0 * a = 0 := USize.toBitVec_inj.1 BitVec.zero_mul
|
||||
|
||||
@[simp] protected theorem UInt8.pow_zero (x : UInt8) : x ^ 0 = 1 := rfl
|
||||
protected theorem UInt8.pow_succ (x : UInt8) (n : Nat) : x ^ (n + 1) = x ^ n * x := rfl
|
||||
@[simp] protected theorem UInt16.pow_zero (x : UInt16) : x ^ 0 = 1 := rfl
|
||||
protected theorem UInt16.pow_succ (x : UInt16) (n : Nat) : x ^ (n + 1) = x ^ n * x := rfl
|
||||
@[simp] protected theorem UInt32.pow_zero (x : UInt32) : x ^ 0 = 1 := rfl
|
||||
protected theorem UInt32.pow_succ (x : UInt32) (n : Nat) : x ^ (n + 1) = x ^ n * x := rfl
|
||||
@[simp] protected theorem UInt64.pow_zero (x : UInt64) : x ^ 0 = 1 := rfl
|
||||
protected theorem UInt64.pow_succ (x : UInt64) (n : Nat) : x ^ (n + 1) = x ^ n * x := rfl
|
||||
@[simp] protected theorem USize.pow_zero (x : USize) : x ^ 0 = 1 := rfl
|
||||
protected theorem USize.pow_succ (x : USize) (n : Nat) : x ^ (n + 1) = x ^ n * x := rfl
|
||||
|
||||
protected theorem UInt8.mul_add {a b c : UInt8} : a * (b + c) = a * b + a * c :=
|
||||
UInt8.toBitVec_inj.1 BitVec.mul_add
|
||||
protected theorem UInt16.mul_add {a b c : UInt16} : a * (b + c) = a * b + a * c :=
|
||||
|
||||
@@ -294,6 +294,18 @@ theorem find?_eq_some_iff_getElem {xs : Vector α n} {p : α → Bool} {b : α}
|
||||
subst w
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem isSome_findFinIdx? {xs : Vector α n} {p : α → Bool} :
|
||||
(xs.findFinIdx? p).isSome = xs.any p := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem isNone_findFinIdx? {xs : Vector α n} {p : α → Bool} :
|
||||
(xs.findFinIdx? p).isNone = xs.all (fun x => ¬ p x) := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp
|
||||
|
||||
@[simp] theorem findFinIdx?_subtype {p : α → Prop} {xs : Vector { x // p x } n}
|
||||
{f : { x // p x } → Bool} {g : α → Bool} (hf : ∀ x h, f ⟨x, h⟩ = g x) :
|
||||
xs.findFinIdx? f = xs.unattach.findFinIdx? g := by
|
||||
|
||||
@@ -1511,7 +1511,7 @@ theorem map_eq_iff {f : α → β} {as : Vector α n} {bs : Vector β n} :
|
||||
if h : i < as.size then
|
||||
simpa [h, h'] using w i h
|
||||
else
|
||||
rw [getElem?_neg, getElem?_neg, Option.map_none'] <;> omega
|
||||
rw [getElem?_neg, getElem?_neg, Option.map_none] <;> omega
|
||||
|
||||
@[simp] theorem map_set {f : α → β} {xs : Vector α n} {i : Nat} {h : i < n} {a : α} :
|
||||
(xs.set i a).map f = (xs.map f).set i (f a) (by simpa using h) := by
|
||||
|
||||
@@ -5,14 +5,30 @@ Authors: Kim Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Zero
|
||||
import Init.Data.Int.DivMod.Lemmas
|
||||
import Init.TacticsExtra
|
||||
|
||||
/-!
|
||||
# A monolithic commutative ring typeclass for internal use in `grind`.
|
||||
|
||||
The `Lean.Grind.CommRing` class will be used to convert expressions into the internal representation via polynomials,
|
||||
with coefficients expressed via `OfNat` and `Neg`.
|
||||
|
||||
The `IsCharP α p` typeclass expresses that the ring has characteristic `p`,
|
||||
i.e. that a coefficient `OfNat.ofNat x : α` is zero if and only if `x % p = 0` (in `Nat`).
|
||||
See
|
||||
```
|
||||
theorem ofNat_ext_iff {x y : Nat} : OfNat.ofNat (α := α) x = OfNat.ofNat (α := α) y ↔ x % p = y % p
|
||||
theorem ofNat_emod (x : Nat) : OfNat.ofNat (α := α) (x % p) = OfNat.ofNat x
|
||||
theorem ofNat_eq_iff_of_lt {x y : Nat} (h₁ : x < p) (h₂ : y < p) :
|
||||
OfNat.ofNat (α := α) x = OfNat.ofNat (α := α) y ↔ x = y
|
||||
```
|
||||
-/
|
||||
|
||||
namespace Lean.Grind
|
||||
|
||||
class CommRing (α : Type u) extends Add α, Zero α, Mul α, One α, Neg α where
|
||||
class CommRing (α : Type u) extends Add α, Mul α, Neg α, Sub α, HPow α Nat α where
|
||||
[ofNat : ∀ n, OfNat α n]
|
||||
add_assoc : ∀ a b c : α, a + b + c = a + (b + c)
|
||||
add_comm : ∀ a b : α, a + b = b + a
|
||||
add_zero : ∀ a : α, a + 0 = a
|
||||
@@ -22,11 +38,31 @@ class CommRing (α : Type u) extends Add α, Zero α, Mul α, One α, Neg α whe
|
||||
mul_one : ∀ a : α, a * 1 = a
|
||||
left_distrib : ∀ a b c : α, a * (b + c) = a * b + a * c
|
||||
zero_mul : ∀ a : α, 0 * a = 0
|
||||
sub_eq_add_neg : ∀ a b : α, a - b = a + -b
|
||||
pow_zero : ∀ a : α, a ^ 0 = 1
|
||||
pow_succ : ∀ a : α, ∀ n : Nat, a ^ (n + 1) = (a ^ n) * a
|
||||
ofNat_succ : ∀ a : Nat, OfNat.ofNat (α := α) (a + 1) = OfNat.ofNat a + 1 := by intros; rfl
|
||||
|
||||
-- This is a low-priority instance, to avoid conflicts with existing `OfNat` instances.
|
||||
attribute [instance 100] CommRing.ofNat
|
||||
|
||||
namespace CommRing
|
||||
|
||||
variable {α : Type u} [CommRing α]
|
||||
|
||||
instance : NatCast α where
|
||||
natCast n := OfNat.ofNat n
|
||||
|
||||
theorem natCast_zero : ((0 : Nat) : α) = 0 := rfl
|
||||
theorem ofNat_eq_natCast (n : Nat) : OfNat.ofNat n = (n : α) := rfl
|
||||
|
||||
theorem ofNat_add (a b : Nat) : OfNat.ofNat (α := α) (a + b) = OfNat.ofNat a + OfNat.ofNat b := by
|
||||
induction b with
|
||||
| zero => simp [Nat.add_zero, add_zero]
|
||||
| succ b ih => rw [Nat.add_succ, ofNat_succ, ih, ofNat_succ b, add_assoc]
|
||||
|
||||
theorem natCast_succ (n : Nat) : ((n + 1 : Nat) : α) = ((n : α) + 1) := ofNat_add _ _
|
||||
|
||||
theorem zero_add (a : α) : 0 + a = a := by
|
||||
rw [add_comm, add_zero]
|
||||
|
||||
@@ -42,6 +78,204 @@ theorem right_distrib (a b c : α) : (a + b) * c = a * c + b * c := by
|
||||
theorem mul_zero (a : α) : a * 0 = 0 := by
|
||||
rw [mul_comm, zero_mul]
|
||||
|
||||
theorem ofNat_mul (a b : Nat) : OfNat.ofNat (α := α) (a * b) = OfNat.ofNat a * OfNat.ofNat b := by
|
||||
induction b with
|
||||
| zero => simp [Nat.mul_zero, mul_zero]
|
||||
| succ a ih => rw [Nat.mul_succ, ofNat_add, ih, ofNat_add, left_distrib, mul_one]
|
||||
|
||||
theorem add_left_inj {a b : α} (c : α) : a + c = b + c ↔ a = b :=
|
||||
⟨fun h => by simpa [add_assoc, add_neg_cancel, add_zero] using (congrArg (· + -c) h),
|
||||
fun g => congrArg (· + c) g⟩
|
||||
|
||||
theorem add_right_inj (a b c : α) : a + b = a + c ↔ b = c := by
|
||||
rw [add_comm a b, add_comm a c, add_left_inj]
|
||||
|
||||
theorem neg_zero : (-0 : α) = 0 := by
|
||||
rw [← add_left_inj 0, neg_add_cancel, add_zero]
|
||||
|
||||
theorem neg_neg (a : α) : -(-a) = a := by
|
||||
rw [← add_left_inj (-a), neg_add_cancel, add_neg_cancel]
|
||||
|
||||
theorem neg_eq_zero (a : α) : -a = 0 ↔ a = 0 :=
|
||||
⟨fun h => by
|
||||
replace h := congrArg (-·) h
|
||||
simpa [neg_neg, neg_zero] using h,
|
||||
fun h => by rw [h, neg_zero]⟩
|
||||
|
||||
theorem neg_add (a b : α) : -(a + b) = -a + -b := by
|
||||
rw [← add_left_inj (a + b), neg_add_cancel, add_assoc (-a), add_comm a b, ← add_assoc (-b),
|
||||
neg_add_cancel, zero_add, neg_add_cancel]
|
||||
|
||||
theorem neg_sub (a b : α) : -(a - b) = b - a := by
|
||||
rw [sub_eq_add_neg, neg_add, neg_neg, sub_eq_add_neg, add_comm]
|
||||
|
||||
theorem sub_self (a : α) : a - a = 0 := by
|
||||
rw [sub_eq_add_neg, add_neg_cancel]
|
||||
|
||||
instance : IntCast α where
|
||||
intCast n := match n with
|
||||
| Int.ofNat n => OfNat.ofNat n
|
||||
| Int.negSucc n => -OfNat.ofNat (n + 1)
|
||||
|
||||
theorem intCast_zero : ((0 : Int) : α) = 0 := rfl
|
||||
theorem intCast_one : ((1 : Int) : α) = 1 := rfl
|
||||
theorem intCast_neg_one : ((-1 : Int) : α) = -1 := rfl
|
||||
theorem intCast_ofNat (n : Nat) : ((n : Int) : α) = (n : α) := rfl
|
||||
theorem intCast_ofNat_add_one (n : Nat) : ((n + 1 : Int) : α) = (n : α) + 1 := ofNat_add _ _
|
||||
theorem intCast_negSucc (n : Nat) : ((-(n + 1) : Int) : α) = -((n : α) + 1) := congrArg (- ·) (ofNat_add _ _)
|
||||
theorem intCast_neg (x : Int) : ((-x : Int) : α) = - (x : α) :=
|
||||
match x with
|
||||
| (0 : Nat) => neg_zero.symm
|
||||
| (n + 1 : Nat) => by
|
||||
rw [Int.natCast_add, Int.cast_ofNat_Int, intCast_negSucc, intCast_ofNat_add_one]
|
||||
| -((n : Nat) + 1) => by
|
||||
rw [Int.neg_neg, intCast_ofNat_add_one, intCast_negSucc, neg_neg]
|
||||
theorem intCast_nat_add {x y : Nat} : ((x + y : Int) : α) = ((x : α) + (y : α)) := ofNat_add _ _
|
||||
theorem intCast_nat_sub {x y : Nat} (h : x ≥ y) : (((x - y : Nat) : Int) : α) = ((x : α) - (y : α)) := by
|
||||
induction x with
|
||||
| zero =>
|
||||
have : y = 0 := by omega
|
||||
simp [this, intCast_zero, natCast_zero, sub_eq_add_neg, zero_add, neg_zero]
|
||||
| succ x ih =>
|
||||
by_cases h : x + 1 = y
|
||||
· simp [h, intCast_zero, sub_self]
|
||||
· have : ((x + 1 - y : Nat) : Int) = (x - y : Nat) + 1 := by omega
|
||||
rw [this, intCast_ofNat_add_one]
|
||||
specialize ih (by omega)
|
||||
rw [intCast_ofNat] at ih
|
||||
rw [ih, natCast_succ, sub_eq_add_neg, sub_eq_add_neg, add_assoc, add_comm _ 1, ← add_assoc]
|
||||
theorem intCast_add (x y : Int) : ((x + y : Int) : α) = ((x : α) + (y : α)) :=
|
||||
match x, y with
|
||||
| (x : Nat), (y : Nat) => ofNat_add _ _
|
||||
| (x : Nat), (-(y + 1 : Nat)) => by
|
||||
by_cases h : x ≥ y + 1
|
||||
· have : (x + -(y+1 : Nat) : Int) = ((x - (y + 1) : Nat) : Int) := by omega
|
||||
rw [this, intCast_neg, intCast_nat_sub h, intCast_ofNat, intCast_ofNat, sub_eq_add_neg]
|
||||
· have : (x + -(y+1 : Nat) : Int) = (-(y + 1 - x : Nat) : Int) := by omega
|
||||
rw [this, intCast_neg, intCast_nat_sub (by omega), intCast_ofNat, intCast_neg, intCast_ofNat,
|
||||
neg_sub, sub_eq_add_neg]
|
||||
| (-(x + 1 : Nat)), (y : Nat) => by
|
||||
by_cases h : y ≥ x+ 1
|
||||
· have : (-(x+1 : Nat) + y : Int) = ((y - (x + 1) : Nat) : Int) := by omega
|
||||
rw [this, intCast_neg, intCast_nat_sub h, intCast_ofNat, intCast_ofNat, sub_eq_add_neg, add_comm]
|
||||
· have : (-(x+1 : Nat) + y : Int) = (-(x + 1 - y : Nat) : Int) := by omega
|
||||
rw [this, intCast_neg, intCast_nat_sub (by omega), intCast_ofNat, intCast_neg, intCast_ofNat,
|
||||
neg_sub, sub_eq_add_neg, add_comm]
|
||||
| (-(x + 1 : Nat)), (-(y + 1 : Nat)) => by
|
||||
rw [← Int.neg_add, intCast_neg, intCast_nat_add, neg_add, intCast_neg, intCast_neg, intCast_ofNat, intCast_ofNat]
|
||||
theorem intCast_sub (x y : Int) : ((x - y : Int) : α) = ((x : α) - (y : α)) := by
|
||||
rw [Int.sub_eq_add_neg, intCast_add, intCast_neg, sub_eq_add_neg]
|
||||
|
||||
theorem neg_eq_neg_one_mul (a : α) : -a = (-1) * a := by
|
||||
rw [← add_left_inj a, neg_add_cancel]
|
||||
conv => rhs; arg 2; rw [← one_mul a]
|
||||
rw [← right_distrib, ← intCast_neg_one, ← intCast_one (α := α)]
|
||||
simp [← intCast_add, intCast_zero, zero_mul]
|
||||
|
||||
theorem neg_mul (a b : α) : (-a) * b = -(a * b) := by
|
||||
rw [neg_eq_neg_one_mul a, neg_eq_neg_one_mul (a * b), mul_assoc]
|
||||
|
||||
theorem mul_neg (a b : α) : a * (-b) = -(a * b) := by
|
||||
rw [mul_comm, neg_mul, mul_comm]
|
||||
|
||||
theorem intCast_nat_mul (x y : Nat) : ((x * y : Int) : α) = ((x : α) * (y : α)) := ofNat_mul _ _
|
||||
theorem intCast_mul (x y : Int) : ((x * y : Int) : α) = ((x : α) * (y : α)) :=
|
||||
match x, y with
|
||||
| (x : Nat), (y : Nat) => ofNat_mul _ _
|
||||
| (x : Nat), (-(y + 1 : Nat)) => by
|
||||
rw [Int.mul_neg, intCast_neg, intCast_nat_mul, intCast_neg, mul_neg, intCast_ofNat, intCast_ofNat]
|
||||
| (-(x + 1 : Nat)), (y : Nat) => by
|
||||
rw [Int.neg_mul, intCast_neg, intCast_nat_mul, intCast_neg, neg_mul, intCast_ofNat, intCast_ofNat]
|
||||
| (-(x + 1 : Nat)), (-(y + 1 : Nat)) => by
|
||||
rw [Int.neg_mul_neg, intCast_neg, intCast_neg, neg_mul, mul_neg, neg_neg, intCast_nat_mul,
|
||||
intCast_ofNat, intCast_ofNat]
|
||||
|
||||
end CommRing
|
||||
|
||||
open CommRing
|
||||
|
||||
class IsCharP (α : Type u) [CommRing α] (p : Nat) where
|
||||
ofNat_eq_zero_iff (p) : ∀ (x : Nat), OfNat.ofNat (α := α) x = 0 ↔ x % p = 0
|
||||
|
||||
namespace IsCharP
|
||||
|
||||
variable (p) {α : Type u} [CommRing α] [IsCharP α p]
|
||||
|
||||
theorem natCast_eq_zero_iff (x : Nat) : (x : α) = 0 ↔ x % p = 0 :=
|
||||
ofNat_eq_zero_iff p x
|
||||
|
||||
theorem intCast_eq_zero_iff (x : Int) : (x : α) = 0 ↔ x % p = 0 :=
|
||||
match x with
|
||||
| (x : Nat) => by
|
||||
have := ofNat_eq_zero_iff (α := α) p (x := x)
|
||||
rw [Int.ofNat_mod_ofNat]
|
||||
norm_cast
|
||||
| -(x + 1 : Nat) => by
|
||||
rw [Int.neg_emod, Int.ofNat_mod_ofNat, intCast_neg, intCast_ofNat, neg_eq_zero]
|
||||
have := ofNat_eq_zero_iff (α := α) p (x := x + 1)
|
||||
rw [ofNat_eq_natCast] at this
|
||||
rw [this]
|
||||
simp only [Int.ofNat_dvd]
|
||||
simp only [← Nat.dvd_iff_mod_eq_zero, Int.natAbs_ofNat, Int.natCast_add,
|
||||
Int.cast_ofNat_Int, ite_eq_left_iff]
|
||||
by_cases h : p ∣ x + 1
|
||||
· simp [h]
|
||||
· simp only [h, not_false_eq_true, Int.natCast_add, Int.cast_ofNat_Int,
|
||||
forall_const, false_iff, ne_eq]
|
||||
by_cases w : p = 0
|
||||
· simp [w]
|
||||
omega
|
||||
· have : ((x + 1) % p) < p := Nat.mod_lt _ (by omega)
|
||||
omega
|
||||
|
||||
theorem intCast_ext_iff {x y : Int} : (x : α) = (y : α) ↔ x % p = y % p := by
|
||||
constructor
|
||||
· intro h
|
||||
replace h : ((x - y : Int) : α) = 0 := by rw [intCast_sub, h, sub_self]
|
||||
exact Int.emod_eq_emod_iff_emod_sub_eq_zero.mpr ((intCast_eq_zero_iff p _).mp h)
|
||||
· intro h
|
||||
have : ((x - y : Int) : α) = 0 :=
|
||||
(intCast_eq_zero_iff p _).mpr (by rw [Int.sub_emod, h, Int.sub_self, Int.zero_emod])
|
||||
replace this := congrArg (· + (y : α)) this
|
||||
simpa [intCast_sub, zero_add, sub_eq_add_neg, add_assoc, neg_add_cancel, add_zero] using this
|
||||
|
||||
theorem ofNat_ext_iff {x y : Nat} : OfNat.ofNat (α := α) x = OfNat.ofNat (α := α) y ↔ x % p = y % p := by
|
||||
have := intCast_ext_iff (α := α) p (x := x) (y := y)
|
||||
simp only [intCast_ofNat, ← Int.ofNat_emod] at this
|
||||
simp only [ofNat_eq_natCast]
|
||||
norm_cast at this
|
||||
|
||||
theorem ofNat_ext {x y : Nat} (h : x % p = y % p) : OfNat.ofNat (α := α) x = OfNat.ofNat (α := α) y := (ofNat_ext_iff p).mpr h
|
||||
|
||||
theorem natCast_ext {x y : Nat} (h : x % p = y % p) : (x : α) = (y : α) := ofNat_ext _ h
|
||||
|
||||
theorem natCast_ext_iff {x y : Nat} : (x : α) = (y : α) ↔ x % p = y % p :=
|
||||
ofNat_ext_iff p
|
||||
|
||||
theorem intCast_emod (x : Int) : ((x % p : Int) : α) = (x : α) := by
|
||||
rw [intCast_ext_iff p, Int.emod_emod]
|
||||
|
||||
theorem natCast_emod (x : Nat) : ((x % p : Nat) : α) = (x : α) := by
|
||||
simp only [← intCast_ofNat]
|
||||
rw [Int.ofNat_emod, intCast_emod]
|
||||
|
||||
theorem ofNat_emod (x : Nat) : OfNat.ofNat (α := α) (x % p) = OfNat.ofNat x :=
|
||||
natCast_emod _ _
|
||||
|
||||
theorem ofNat_eq_zero_iff_of_lt {x : Nat} (h : x < p) : OfNat.ofNat (α := α) x = 0 ↔ x = 0 := by
|
||||
rw [ofNat_eq_zero_iff p, Nat.mod_eq_of_lt h]
|
||||
|
||||
theorem ofNat_eq_iff_of_lt {x y : Nat} (h₁ : x < p) (h₂ : y < p) :
|
||||
OfNat.ofNat (α := α) x = OfNat.ofNat (α := α) y ↔ x = y := by
|
||||
rw [ofNat_ext_iff p, Nat.mod_eq_of_lt h₁, Nat.mod_eq_of_lt h₂]
|
||||
|
||||
theorem natCast_eq_zero_iff_of_lt {x : Nat} (h : x < p) : (x : α) = 0 ↔ x = 0 := by
|
||||
rw [natCast_eq_zero_iff p, Nat.mod_eq_of_lt h]
|
||||
|
||||
theorem natCast_eq_iff_of_lt {x y : Nat} (h₁ : x < p) (h₂ : y < p) :
|
||||
(x : α) = (y : α) ↔ x = y := by
|
||||
rw [natCast_ext_iff p, Nat.mod_eq_of_lt h₁, Nat.mod_eq_of_lt h₂]
|
||||
|
||||
end IsCharP
|
||||
|
||||
end Lean.Grind
|
||||
|
||||
@@ -19,5 +19,12 @@ instance : CommRing (BitVec w) where
|
||||
mul_one := BitVec.mul_one
|
||||
left_distrib _ _ _ := BitVec.mul_add
|
||||
zero_mul _ := BitVec.zero_mul
|
||||
sub_eq_add_neg := BitVec.sub_eq_add_neg
|
||||
pow_zero _ := BitVec.pow_zero
|
||||
pow_succ _ _ := BitVec.pow_succ
|
||||
ofNat_succ x := BitVec.ofNat_add x 1
|
||||
|
||||
instance : IsCharP (BitVec w) (2 ^ w) where
|
||||
ofNat_eq_zero_iff {x} := by simp [BitVec.ofInt, BitVec.toNat_eq]
|
||||
|
||||
end Lean.Grind
|
||||
|
||||
@@ -19,5 +19,12 @@ instance : CommRing Int where
|
||||
mul_one := Int.mul_one
|
||||
left_distrib := Int.mul_add
|
||||
zero_mul := Int.zero_mul
|
||||
pow_zero _ := rfl
|
||||
pow_succ _ _ := rfl
|
||||
ofNat_succ _ := rfl
|
||||
sub_eq_add_neg _ _ := Int.sub_eq_add_neg
|
||||
|
||||
instance : IsCharP Int 0 where
|
||||
ofNat_eq_zero_iff {x} := by erw [Int.ofNat_eq_zero]; simp
|
||||
|
||||
end Lean.Grind
|
||||
|
||||
@@ -9,6 +9,9 @@ import Init.Data.SInt.Lemmas
|
||||
|
||||
namespace Lean.Grind
|
||||
|
||||
instance : IntCast Int8 where
|
||||
intCast x := Int8.ofInt x
|
||||
|
||||
instance : CommRing Int8 where
|
||||
add_assoc := Int8.add_assoc
|
||||
add_comm := Int8.add_comm
|
||||
@@ -19,6 +22,20 @@ instance : CommRing Int8 where
|
||||
mul_one := Int8.mul_one
|
||||
left_distrib _ _ _ := Int8.mul_add
|
||||
zero_mul _ := Int8.zero_mul
|
||||
sub_eq_add_neg := Int8.sub_eq_add_neg
|
||||
pow_zero := Int8.pow_zero
|
||||
pow_succ := Int8.pow_succ
|
||||
ofNat_succ x := Int8.ofNat_add x 1
|
||||
|
||||
instance : IsCharP Int8 (2 ^ 8) where
|
||||
ofNat_eq_zero_iff {x} := by
|
||||
have : OfNat.ofNat x = Int8.ofInt x := rfl
|
||||
rw [this]
|
||||
simp [Int8.ofInt_eq_iff_bmod_eq_toInt,
|
||||
← Int.dvd_iff_bmod_eq_zero, ← Nat.dvd_iff_mod_eq_zero, Int.ofNat_dvd_right]
|
||||
|
||||
instance : IntCast Int16 where
|
||||
intCast x := Int16.ofInt x
|
||||
|
||||
instance : CommRing Int16 where
|
||||
add_assoc := Int16.add_assoc
|
||||
@@ -30,6 +47,20 @@ instance : CommRing Int16 where
|
||||
mul_one := Int16.mul_one
|
||||
left_distrib _ _ _ := Int16.mul_add
|
||||
zero_mul _ := Int16.zero_mul
|
||||
sub_eq_add_neg := Int16.sub_eq_add_neg
|
||||
pow_zero := Int16.pow_zero
|
||||
pow_succ := Int16.pow_succ
|
||||
ofNat_succ x := Int16.ofNat_add x 1
|
||||
|
||||
instance : IsCharP Int16 (2 ^ 16) where
|
||||
ofNat_eq_zero_iff {x} := by
|
||||
have : OfNat.ofNat x = Int16.ofInt x := rfl
|
||||
rw [this]
|
||||
simp [Int16.ofInt_eq_iff_bmod_eq_toInt,
|
||||
← Int.dvd_iff_bmod_eq_zero, ← Nat.dvd_iff_mod_eq_zero, Int.ofNat_dvd_right]
|
||||
|
||||
instance : IntCast Int32 where
|
||||
intCast x := Int32.ofInt x
|
||||
|
||||
instance : CommRing Int32 where
|
||||
add_assoc := Int32.add_assoc
|
||||
@@ -41,6 +72,20 @@ instance : CommRing Int32 where
|
||||
mul_one := Int32.mul_one
|
||||
left_distrib _ _ _ := Int32.mul_add
|
||||
zero_mul _ := Int32.zero_mul
|
||||
sub_eq_add_neg := Int32.sub_eq_add_neg
|
||||
pow_zero := Int32.pow_zero
|
||||
pow_succ := Int32.pow_succ
|
||||
ofNat_succ x := Int32.ofNat_add x 1
|
||||
|
||||
instance : IsCharP Int32 (2 ^ 32) where
|
||||
ofNat_eq_zero_iff {x} := by
|
||||
have : OfNat.ofNat x = Int32.ofInt x := rfl
|
||||
rw [this]
|
||||
simp [Int32.ofInt_eq_iff_bmod_eq_toInt,
|
||||
← Int.dvd_iff_bmod_eq_zero, ← Nat.dvd_iff_mod_eq_zero, Int.ofNat_dvd_right]
|
||||
|
||||
instance : IntCast Int64 where
|
||||
intCast x := Int64.ofInt x
|
||||
|
||||
instance : CommRing Int64 where
|
||||
add_assoc := Int64.add_assoc
|
||||
@@ -52,6 +97,20 @@ instance : CommRing Int64 where
|
||||
mul_one := Int64.mul_one
|
||||
left_distrib _ _ _ := Int64.mul_add
|
||||
zero_mul _ := Int64.zero_mul
|
||||
sub_eq_add_neg := Int64.sub_eq_add_neg
|
||||
pow_zero := Int64.pow_zero
|
||||
pow_succ := Int64.pow_succ
|
||||
ofNat_succ x := Int64.ofNat_add x 1
|
||||
|
||||
instance : IsCharP Int64 (2 ^ 64) where
|
||||
ofNat_eq_zero_iff {x} := by
|
||||
have : OfNat.ofNat x = Int64.ofInt x := rfl
|
||||
rw [this]
|
||||
simp [Int64.ofInt_eq_iff_bmod_eq_toInt,
|
||||
← Int.dvd_iff_bmod_eq_zero, ← Nat.dvd_iff_mod_eq_zero, Int.ofNat_dvd_right]
|
||||
|
||||
instance : IntCast ISize where
|
||||
intCast x := ISize.ofInt x
|
||||
|
||||
instance : CommRing ISize where
|
||||
add_assoc := ISize.add_assoc
|
||||
@@ -63,5 +122,18 @@ instance : CommRing ISize where
|
||||
mul_one := ISize.mul_one
|
||||
left_distrib _ _ _ := ISize.mul_add
|
||||
zero_mul _ := ISize.zero_mul
|
||||
sub_eq_add_neg := ISize.sub_eq_add_neg
|
||||
pow_zero := ISize.pow_zero
|
||||
pow_succ := ISize.pow_succ
|
||||
ofNat_succ x := ISize.ofNat_add x 1
|
||||
|
||||
open System.Platform (numBits)
|
||||
|
||||
instance : IsCharP ISize (2 ^ numBits) where
|
||||
ofNat_eq_zero_iff {x} := by
|
||||
have : OfNat.ofNat x = ISize.ofInt x := rfl
|
||||
rw [this]
|
||||
simp [ISize.ofInt_eq_iff_bmod_eq_toInt,
|
||||
← Int.dvd_iff_bmod_eq_zero, ← Nat.dvd_iff_mod_eq_zero, Int.ofNat_dvd_right]
|
||||
|
||||
end Lean.Grind
|
||||
|
||||
@@ -7,6 +7,53 @@ prelude
|
||||
import Init.Grind.CommRing.Basic
|
||||
import Init.Data.UInt.Lemmas
|
||||
|
||||
|
||||
namespace UInt8
|
||||
|
||||
/-- Variant of `UInt8.ofNat_mod_size` replacing `2 ^ 8` with `256`.-/
|
||||
theorem ofNat_mod_size' : ofNat (x % 256) = ofNat x := ofNat_mod_size
|
||||
|
||||
instance : IntCast UInt8 where
|
||||
intCast x := UInt8.ofInt x
|
||||
|
||||
end UInt8
|
||||
|
||||
namespace UInt16
|
||||
|
||||
/-- Variant of `UInt16.ofNat_mod_size` replacing `2 ^ 16` with `65536`.-/
|
||||
theorem ofNat_mod_size' : ofNat (x % 65536) = ofNat x := ofNat_mod_size
|
||||
|
||||
instance : IntCast UInt16 where
|
||||
intCast x := UInt16.ofInt x
|
||||
|
||||
end UInt16
|
||||
|
||||
namespace UInt32
|
||||
|
||||
/-- Variant of `UInt32.ofNat_mod_size` replacing `2 ^ 32` with `4294967296`.-/
|
||||
theorem ofNat_mod_size' : ofNat (x % 4294967296) = ofNat x := ofNat_mod_size
|
||||
|
||||
instance : IntCast UInt32 where
|
||||
intCast x := UInt32.ofInt x
|
||||
|
||||
end UInt32
|
||||
|
||||
namespace UInt64
|
||||
|
||||
/-- Variant of `UInt64.ofNat_mod_size` replacing `2 ^ 64` with `18446744073709551616`.-/
|
||||
theorem ofNat_mod_size' : ofNat (x % 18446744073709551616) = ofNat x := ofNat_mod_size
|
||||
|
||||
instance : IntCast UInt64 where
|
||||
intCast x := UInt64.ofInt x
|
||||
|
||||
end UInt64
|
||||
|
||||
namespace USize
|
||||
|
||||
instance : IntCast USize where
|
||||
intCast x := USize.ofInt x
|
||||
|
||||
end USize
|
||||
namespace Lean.Grind
|
||||
|
||||
instance : CommRing UInt8 where
|
||||
@@ -19,6 +66,15 @@ instance : CommRing UInt8 where
|
||||
mul_one := UInt8.mul_one
|
||||
left_distrib _ _ _ := UInt8.mul_add
|
||||
zero_mul _ := UInt8.zero_mul
|
||||
sub_eq_add_neg := UInt8.sub_eq_add_neg
|
||||
pow_zero := UInt8.pow_zero
|
||||
pow_succ := UInt8.pow_succ
|
||||
ofNat_succ x := UInt8.ofNat_add x 1
|
||||
|
||||
instance : IsCharP UInt8 (2 ^ 8) where
|
||||
ofNat_eq_zero_iff {x} := by
|
||||
have : OfNat.ofNat x = UInt8.ofNat x := rfl
|
||||
simp [this, UInt8.ofNat_eq_iff_mod_eq_toNat]
|
||||
|
||||
instance : CommRing UInt16 where
|
||||
add_assoc := UInt16.add_assoc
|
||||
@@ -30,6 +86,15 @@ instance : CommRing UInt16 where
|
||||
mul_one := UInt16.mul_one
|
||||
left_distrib _ _ _ := UInt16.mul_add
|
||||
zero_mul _ := UInt16.zero_mul
|
||||
sub_eq_add_neg := UInt16.sub_eq_add_neg
|
||||
pow_zero := UInt16.pow_zero
|
||||
pow_succ := UInt16.pow_succ
|
||||
ofNat_succ x := UInt16.ofNat_add x 1
|
||||
|
||||
instance : IsCharP UInt16 (2 ^ 16) where
|
||||
ofNat_eq_zero_iff {x} := by
|
||||
have : OfNat.ofNat x = UInt16.ofNat x := rfl
|
||||
simp [this, UInt16.ofNat_eq_iff_mod_eq_toNat]
|
||||
|
||||
instance : CommRing UInt32 where
|
||||
add_assoc := UInt32.add_assoc
|
||||
@@ -41,6 +106,15 @@ instance : CommRing UInt32 where
|
||||
mul_one := UInt32.mul_one
|
||||
left_distrib _ _ _ := UInt32.mul_add
|
||||
zero_mul _ := UInt32.zero_mul
|
||||
sub_eq_add_neg := UInt32.sub_eq_add_neg
|
||||
pow_zero := UInt32.pow_zero
|
||||
pow_succ := UInt32.pow_succ
|
||||
ofNat_succ x := UInt32.ofNat_add x 1
|
||||
|
||||
instance : IsCharP UInt32 (2 ^ 32) where
|
||||
ofNat_eq_zero_iff {x} := by
|
||||
have : OfNat.ofNat x = UInt32.ofNat x := rfl
|
||||
simp [this, UInt32.ofNat_eq_iff_mod_eq_toNat]
|
||||
|
||||
instance : CommRing UInt64 where
|
||||
add_assoc := UInt64.add_assoc
|
||||
@@ -52,6 +126,15 @@ instance : CommRing UInt64 where
|
||||
mul_one := UInt64.mul_one
|
||||
left_distrib _ _ _ := UInt64.mul_add
|
||||
zero_mul _ := UInt64.zero_mul
|
||||
sub_eq_add_neg := UInt64.sub_eq_add_neg
|
||||
pow_zero := UInt64.pow_zero
|
||||
pow_succ := UInt64.pow_succ
|
||||
ofNat_succ x := UInt64.ofNat_add x 1
|
||||
|
||||
instance : IsCharP UInt64 (2 ^ 64) where
|
||||
ofNat_eq_zero_iff {x} := by
|
||||
have : OfNat.ofNat x = UInt64.ofNat x := rfl
|
||||
simp [this, UInt64.ofNat_eq_iff_mod_eq_toNat]
|
||||
|
||||
instance : CommRing USize where
|
||||
add_assoc := USize.add_assoc
|
||||
@@ -63,5 +146,16 @@ instance : CommRing USize where
|
||||
mul_one := USize.mul_one
|
||||
left_distrib _ _ _ := USize.mul_add
|
||||
zero_mul _ := USize.zero_mul
|
||||
sub_eq_add_neg := USize.sub_eq_add_neg
|
||||
pow_zero := USize.pow_zero
|
||||
pow_succ := USize.pow_succ
|
||||
ofNat_succ x := USize.ofNat_add x 1
|
||||
|
||||
open System.Platform
|
||||
|
||||
instance : IsCharP USize (2 ^ numBits) where
|
||||
ofNat_eq_zero_iff {x} := by
|
||||
have : OfNat.ofNat x = USize.ofNat x := rfl
|
||||
simp [this, USize.ofNat_eq_iff_mod_eq_toNat]
|
||||
|
||||
end Lean.Grind
|
||||
|
||||
@@ -49,6 +49,8 @@ theorem eq_false_of_or_eq_false_right {a b : Prop} (h : (a ∨ b) = False) : b =
|
||||
theorem imp_eq_of_eq_false_left {a b : Prop} (h : a = False) : (a → b) = True := by simp [h]
|
||||
theorem imp_eq_of_eq_true_right {a b : Prop} (h : b = True) : (a → b) = True := by simp [h]
|
||||
theorem imp_eq_of_eq_true_left {a b : Prop} (h : a = True) : (a → b) = b := by simp [h]
|
||||
theorem eq_false_of_imp_eq_true {a b : Prop} (h₁ : (a → b) = True) (h₂ : b = False) : a = False := by
|
||||
simp at *; intro h; exact h₂ (h₁ h)
|
||||
|
||||
theorem eq_true_of_imp_eq_false {a b : Prop} (h : (a → b) = False) : a = True := by simp_all
|
||||
theorem eq_false_of_imp_eq_false {a b : Prop} (h : (a → b) = False) : b = False := by simp_all
|
||||
|
||||
@@ -71,11 +71,14 @@ theorem beq_eq_decide_eq {_ : BEq α} [LawfulBEq α] [DecidableEq α] (a b : α)
|
||||
theorem bne_eq_decide_not_eq {_ : BEq α} [LawfulBEq α] [DecidableEq α] (a b : α) : (a != b) = (decide (¬ a = b)) := by
|
||||
by_cases a = b <;> simp [*]
|
||||
|
||||
theorem natCast_div (a b : Nat) : (↑(a / b) : Int) = ↑a / ↑b := by
|
||||
theorem xor_eq (a b : Bool) : (a ^^ b) = (a != b) := by
|
||||
rfl
|
||||
|
||||
theorem natCast_mod (a b : Nat) : (↑(a % b) : Int) = ↑a % ↑b := by
|
||||
rfl
|
||||
theorem natCast_eq [NatCast α] (a : Nat) : (Nat.cast a : α) = (NatCast.natCast a : α) := rfl
|
||||
theorem natCast_div (a b : Nat) : (NatCast.natCast (a / b) : Int) = (NatCast.natCast a) / (NatCast.natCast b) := rfl
|
||||
theorem natCast_mod (a b : Nat) : (NatCast.natCast (a % b) : Int) = (NatCast.natCast a) % (NatCast.natCast b) := rfl
|
||||
theorem natCast_add (a b : Nat) : (NatCast.natCast (a + b : Nat) : Int) = (NatCast.natCast a : Int) + (NatCast.natCast b : Int) := rfl
|
||||
theorem natCast_mul (a b : Nat) : (NatCast.natCast (a * b : Nat) : Int) = (NatCast.natCast a : Int) * (NatCast.natCast b : Int) := rfl
|
||||
|
||||
theorem Nat.pow_one (a : Nat) : a ^ 1 = a := by
|
||||
simp
|
||||
@@ -86,6 +89,14 @@ theorem Int.pow_one (a : Int) : a ^ 1 = a := by
|
||||
theorem forall_true (p : True → Prop) : (∀ h : True, p h) = p True.intro :=
|
||||
propext <| Iff.intro (fun h => h True.intro) (fun h _ => h)
|
||||
|
||||
-- Helper theorem used by the simproc `simpBoolEq`
|
||||
theorem flip_bool_eq (a b : Bool) : (a = b) = (b = a) := by
|
||||
rw [@Eq.comm _ a b]
|
||||
|
||||
-- Helper theorem used by the simproc `simpBoolEq`
|
||||
theorem bool_eq_to_prop (a b : Bool) : (a = b) = ((a = true) = (b = true)) := by
|
||||
simp
|
||||
|
||||
init_grind_norm
|
||||
/- Pre theorems -/
|
||||
not_and not_or not_ite not_forall not_exists
|
||||
@@ -122,6 +133,8 @@ init_grind_norm
|
||||
Bool.and_false Bool.and_true Bool.false_and Bool.true_and Bool.and_eq_true Bool.and_assoc
|
||||
-- Bool not
|
||||
Bool.not_not
|
||||
-- Bool xor
|
||||
xor_eq
|
||||
-- beq
|
||||
beq_iff_eq beq_eq_decide_eq beq_self_eq_true
|
||||
-- bne
|
||||
@@ -140,8 +153,10 @@ init_grind_norm
|
||||
Int.emod_neg Int.ediv_neg
|
||||
Int.ediv_zero Int.emod_zero
|
||||
Int.ediv_one Int.emod_one
|
||||
Int.natCast_add Int.natCast_mul Int.natCast_pow
|
||||
Int.natCast_zero natCast_div natCast_mod
|
||||
|
||||
natCast_eq natCast_div natCast_mod
|
||||
natCast_add natCast_mul
|
||||
|
||||
Int.pow_zero Int.pow_one
|
||||
-- GT GE
|
||||
ge_eq gt_eq
|
||||
|
||||
@@ -59,12 +59,22 @@ structure Config where
|
||||
If `splitIndPred` is `true`, `grind` performs case-splitting on inductive predicates.
|
||||
Otherwise, it performs case-splitting only on types marked with `[grind cases]` attribute. -/
|
||||
splitIndPred : Bool := false
|
||||
/--
|
||||
If `splitImp` is `true`, then given an implication `p → q` or `(h : p) → q h`, `grind` splits on `p`
|
||||
it the implication is true. Otherwise, it will split only if `p` is an arithmetic predicate.
|
||||
-/
|
||||
splitImp : Bool := false
|
||||
/-- By default, `grind` halts as soon as it encounters a sub-goal where no further progress can be made. -/
|
||||
failures : Nat := 1
|
||||
/-- Maximum number of heartbeats (in thousands) the canonicalizer can spend per definitional equality test. -/
|
||||
canonHeartbeats : Nat := 1000
|
||||
/-- If `ext` is `true`, `grind` uses extensionality theorems available in the environment. -/
|
||||
ext : Bool := true
|
||||
/--
|
||||
If `funext` is `true`, `grind` creates new opportunities for applying function extensionality by case-splitting
|
||||
on equalities between lambda expressions.
|
||||
-/
|
||||
funext : Bool := true
|
||||
/-- If `verbose` is `false`, additional diagnostics information is not collected. -/
|
||||
verbose : Bool := true
|
||||
/-- If `clean` is `true`, `grind` uses `expose_names` and only generates accessible names. -/
|
||||
|
||||
@@ -91,15 +91,14 @@ theorem monotone_bindM (f : γ → α → m (Option β)) (xs : Option α) (hmono
|
||||
· apply monotone_const
|
||||
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem monotone_mapM (f : γ → α → m β) (xs : Option α) (hmono : monotone f) :
|
||||
theorem monotone_mapM [LawfulMonad m] (f : γ → α → m β) (xs : Option α) (hmono : monotone f) :
|
||||
monotone (fun x => xs.mapM (f x)) := by
|
||||
cases xs with
|
||||
| none => apply monotone_const
|
||||
| some x =>
|
||||
apply monotone_bind
|
||||
· apply monotone_apply
|
||||
apply hmono
|
||||
· apply monotone_const
|
||||
apply Functor.monotone_map
|
||||
apply monotone_apply
|
||||
apply hmono
|
||||
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem monotone_elimM (a : γ → m (Option α)) (n : γ → m β) (s : γ → α → m β)
|
||||
|
||||
@@ -166,13 +166,13 @@ theorem combo_sat (a) (w₁ : c₁.sat x₁) (b) (w₂ : c₂.sat x₂) :
|
||||
|
||||
/-- The conjunction of two constraints. -/
|
||||
def combine (x y : Constraint) : Constraint where
|
||||
lowerBound := Option.zipWith max x.lowerBound y.lowerBound
|
||||
upperBound := Option.zipWith min x.upperBound y.upperBound
|
||||
lowerBound := Option.merge max x.lowerBound y.lowerBound
|
||||
upperBound := Option.merge min x.upperBound y.upperBound
|
||||
|
||||
theorem combine_sat : (c : Constraint) → (c' : Constraint) → (t : Int) →
|
||||
(c.combine c').sat t = (c.sat t ∧ c'.sat t) := by
|
||||
rintro ⟨_ | l₁, _ | u₁⟩ <;> rintro ⟨_ | l₂, _ | u₂⟩ t
|
||||
<;> simp [sat, LowerBound.sat, UpperBound.sat, combine, Int.le_min, Int.max_le, Option.zipWith] at *
|
||||
<;> simp [sat, LowerBound.sat, UpperBound.sat, combine, Int.le_min, Int.max_le, Option.merge] at *
|
||||
· rw [And.comm]
|
||||
· rw [← and_assoc, And.comm (a := l₂ ≤ t), and_assoc]
|
||||
· rw [and_assoc]
|
||||
|
||||
@@ -2932,11 +2932,10 @@ This will be deprecated in favor of `Array.emptyWithCapacity` in the future.
|
||||
def Array.mkEmpty {α : Type u} (c : @& Nat) : Array α where
|
||||
toList := List.nil
|
||||
|
||||
|
||||
set_option linter.unusedVariables false in
|
||||
/--
|
||||
Constructs a new empty array with initial capacity `c`.
|
||||
-/
|
||||
@[extern "lean_mk_empty_array_with_capacity"]
|
||||
def Array.emptyWithCapacity {α : Type u} (c : @& Nat) : Array α where
|
||||
toList := List.nil
|
||||
|
||||
|
||||
@@ -44,6 +44,13 @@ register_builtin_option Elab.async : Bool := {
|
||||
`Lean.Command.State.snapshotTasks`."
|
||||
}
|
||||
|
||||
register_builtin_option Elab.inServer : Bool := {
|
||||
defValue := false
|
||||
descr := "true if elaboration is being run inside the Lean language server\
|
||||
\n\
|
||||
\nThis option is set by the file worker and should not be modified otherwise."
|
||||
}
|
||||
|
||||
/-- Performance option used by cmdline driver. -/
|
||||
register_builtin_option internal.cmdlineSnapshots : Bool := {
|
||||
defValue := false
|
||||
|
||||
@@ -52,6 +52,21 @@ instance : FromJson WaitForDiagnostics :=
|
||||
instance : ToJson WaitForDiagnostics :=
|
||||
⟨fun _ => mkObj []⟩
|
||||
|
||||
/--
|
||||
Internal `$/lean/waitForILeans` client->server request.
|
||||
|
||||
Yields a response once the watchdog process has loaded all .ilean files and has received
|
||||
an ILean finalization notification for the worker and the document version designated in the request.
|
||||
Used for test stability in tests that use the .ileans.
|
||||
-/
|
||||
structure WaitForILeansParams where
|
||||
uri : DocumentUri
|
||||
version : Nat
|
||||
deriving FromJson, ToJson
|
||||
|
||||
structure WaitForILeans where
|
||||
deriving FromJson, ToJson
|
||||
|
||||
inductive LeanFileProgressKind
|
||||
| processing | fatalError
|
||||
deriving Inhabited, BEq
|
||||
|
||||
@@ -113,6 +113,19 @@ where
|
||||
| Except.error inner => throw $ userError s!"Cannot decode publishDiagnostics parameters\n{inner}"
|
||||
| _ => loop
|
||||
|
||||
partial def waitForILeans (waitForILeansId : RequestID := 0) (target : DocumentUri) (version : Nat) : IpcM Unit := do
|
||||
writeRequest ⟨waitForILeansId, "$/lean/waitForILeans", WaitForILeansParams.mk target version⟩
|
||||
while true do
|
||||
match (← readMessage) with
|
||||
| .response id _ =>
|
||||
if id == waitForILeansId then
|
||||
return
|
||||
| .responseError id _ msg _ =>
|
||||
if id == waitForILeansId then
|
||||
throw $ userError s!"Waiting for ILeans failed: {msg}"
|
||||
| _ =>
|
||||
pure ()
|
||||
|
||||
/--
|
||||
Waits for a diagnostic notification with a specific message to be emitted. Discards all received
|
||||
messages, so should not be combined with `collectDiagnostics`.
|
||||
|
||||
@@ -510,6 +510,7 @@ where go := do
|
||||
let oldCmds? := oldSnap?.map fun old =>
|
||||
if old.newStx.isOfKind nullKind then old.newStx.getArgs else #[old.newStx]
|
||||
let cmdPromises ← cmds.mapM fun _ => IO.Promise.new
|
||||
let cancelTk? := (← read).cancelTk?
|
||||
snap.new.resolve <| .ofTyped {
|
||||
diagnostics := .empty
|
||||
macroDecl := decl
|
||||
@@ -517,7 +518,7 @@ where go := do
|
||||
newNextMacroScope := nextMacroScope
|
||||
hasTraces
|
||||
next := Array.zipWith (fun cmdPromise cmd =>
|
||||
{ stx? := some cmd, task := cmdPromise.resultD default }) cmdPromises cmds
|
||||
{ stx? := some cmd, task := cmdPromise.resultD default, cancelTk? }) cmdPromises cmds
|
||||
: MacroExpandedSnapshot
|
||||
}
|
||||
-- After the first command whose syntax tree changed, we must disable
|
||||
|
||||
@@ -6,6 +6,7 @@ Authors: Leonardo de Moura, Sebastian Ullrich
|
||||
prelude
|
||||
import Lean.Parser.Module
|
||||
import Lean.Util.Paths
|
||||
import Lean.CoreM
|
||||
|
||||
namespace Lean.Elab
|
||||
|
||||
@@ -21,9 +22,16 @@ def processHeader (header : Syntax) (opts : Options) (messages : MessageLog)
|
||||
(inputCtx : Parser.InputContext) (trustLevel : UInt32 := 0)
|
||||
(plugins : Array System.FilePath := #[]) (leakEnv := false)
|
||||
: IO (Environment × MessageLog) := do
|
||||
let level := if experimental.module.get opts then
|
||||
if Elab.inServer.get opts then
|
||||
.server
|
||||
else
|
||||
.exported
|
||||
else
|
||||
.private
|
||||
try
|
||||
let env ←
|
||||
importModules (leakEnv := leakEnv) (loadExts := true) (headerToImports header) opts trustLevel plugins
|
||||
importModules (leakEnv := leakEnv) (loadExts := true) (level := level) (headerToImports header) opts trustLevel plugins
|
||||
pure (env, messages)
|
||||
catch e =>
|
||||
let env ← mkEmptyEnvironment
|
||||
@@ -46,7 +54,7 @@ def printImports (input : String) (fileName : Option String) : IO Unit := do
|
||||
|
||||
@[export lean_print_import_srcs]
|
||||
def printImportSrcs (input : String) (fileName : Option String) : IO Unit := do
|
||||
let sp ← initSrcSearchPath
|
||||
let sp ← getSrcSearchPath
|
||||
let (deps, _, _) ← parseImports input fileName
|
||||
for dep in deps do
|
||||
let fname ← findLean sp dep.module
|
||||
|
||||
@@ -165,6 +165,8 @@ private def elabHeaders (views : Array DefView) (expandedDeclIds : Array ExpandD
|
||||
-- no syntax guard to store, we already did the necessary checks
|
||||
oldBodySnap? := guard reuseBody *> pure ⟨.missing, old.bodySnap⟩
|
||||
if oldBodySnap?.isNone then
|
||||
-- NOTE: this will eagerly cancel async tasks not associated with an inner snapshot, most
|
||||
-- importantly kernel checking and compilation of the top-level declaration
|
||||
old.bodySnap.cancelRec
|
||||
oldTacSnap? := do
|
||||
guard reuseTac
|
||||
@@ -217,6 +219,7 @@ private def elabHeaders (views : Array DefView) (expandedDeclIds : Array ExpandD
|
||||
return newHeader
|
||||
if let some snap := view.headerSnap? then
|
||||
let (tacStx?, newTacTask?) ← mkTacTask view.value tacPromise
|
||||
let cancelTk? := (← readThe Core.Context).cancelTk?
|
||||
let bodySnap := {
|
||||
stx? := view.value
|
||||
reportingRange? :=
|
||||
@@ -227,6 +230,8 @@ private def elabHeaders (views : Array DefView) (expandedDeclIds : Array ExpandD
|
||||
else
|
||||
getBodyTerm? view.value |>.getD view.value |>.getRange?
|
||||
task := bodyPromise.resultD default
|
||||
-- We should not cancel the entire body early if we have tactics
|
||||
cancelTk? := guard newTacTask?.isNone *> cancelTk?
|
||||
}
|
||||
snap.new.resolve <| some {
|
||||
diagnostics :=
|
||||
@@ -269,7 +274,8 @@ where
|
||||
:= do
|
||||
if let some e := getBodyTerm? body then
|
||||
if let `(by $tacs*) := e then
|
||||
return (e, some { stx? := mkNullNode tacs, task := tacPromise.resultD default })
|
||||
let cancelTk? := (← readThe Core.Context).cancelTk?
|
||||
return (e, some { stx? := mkNullNode tacs, task := tacPromise.resultD default, cancelTk? })
|
||||
tacPromise.resolve default
|
||||
return (none, none)
|
||||
|
||||
@@ -432,8 +438,7 @@ private def elabFunValues (headers : Array DefViewElabHeader) (vars : Array Expr
|
||||
snap.new.resolve <| some old
|
||||
reusableResult? := some (old.value, old.state)
|
||||
else
|
||||
-- NOTE: this will eagerly cancel async tasks not associated with an inner snapshot, most
|
||||
-- importantly kernel checking and compilation of the top-level declaration
|
||||
-- make sure to cancel any async tasks that may still be running (e.g. kernel and codegen)
|
||||
old.val.cancelRec
|
||||
|
||||
let (val, state) ← withRestoreOrSaveFull reusableResult? header.tacSnap? do
|
||||
@@ -1158,7 +1163,7 @@ is error-free and contains no syntactical `sorry`s.
|
||||
-/
|
||||
private def logGoalsAccomplishedSnapshotTask (views : Array DefView)
|
||||
(defsParsedSnap : DefsParsedSnapshot) : TermElabM Unit := do
|
||||
if Lean.internal.cmdlineSnapshots.get (← getOptions) then
|
||||
if Lean.Elab.inServer.get (← getOptions) then
|
||||
-- Skip 'goals accomplished' task if we are on the command line.
|
||||
-- These messages are only used in the language server.
|
||||
return
|
||||
@@ -1197,6 +1202,7 @@ private def logGoalsAccomplishedSnapshotTask (views : Array DefView)
|
||||
-- Use first line of the mutual block to avoid covering the progress of the whole mutual block
|
||||
reportingRange? := (← getRef).getPos?.map fun pos => ⟨pos, pos⟩
|
||||
task := logGoalsAccomplishedTask
|
||||
cancelTk? := none
|
||||
}
|
||||
|
||||
end Term
|
||||
@@ -1235,9 +1241,10 @@ def elabMutualDef (ds : Array Syntax) : CommandElabM Unit := do
|
||||
} }
|
||||
if snap.old?.isSome && (view.headerSnap?.bind (·.old?)).isNone then
|
||||
snap.old?.forM (·.val.cancelRec)
|
||||
let cancelTk? := (← read).cancelTk?
|
||||
defs := defs.push {
|
||||
fullHeaderRef
|
||||
headerProcessedSnap := { stx? := d, task := headerPromise.resultD default }
|
||||
headerProcessedSnap := { stx? := d, task := headerPromise.resultD default, cancelTk? }
|
||||
}
|
||||
reusedAllHeaders := reusedAllHeaders && view.headerSnap?.any (·.old?.isSome)
|
||||
views := views.push view
|
||||
|
||||
@@ -109,11 +109,11 @@ structure InductiveView where
|
||||
/-- Elaborated header for an inductive type before fvars for each inductive are added to the local context. -/
|
||||
structure PreElabHeaderResult where
|
||||
view : InductiveView
|
||||
lctx : LocalContext
|
||||
localInsts : LocalInstances
|
||||
levelNames : List Name
|
||||
params : Array Expr
|
||||
numParams : Nat
|
||||
type : Expr
|
||||
/-- The parameters in the header's initial local context. Used for adding fvar alias terminfo. -/
|
||||
origParams : Array Expr
|
||||
deriving Inhabited
|
||||
|
||||
/-- The elaborated header with the `indFVar` registered for this inductive type. -/
|
||||
@@ -228,16 +228,12 @@ private def checkClass (rs : Array PreElabHeaderResult) : TermElabM Unit := do
|
||||
throwErrorAt r.view.ref "invalid inductive type, mutual classes are not supported"
|
||||
|
||||
private def checkNumParams (rs : Array PreElabHeaderResult) : TermElabM Nat := do
|
||||
let numParams := rs[0]!.params.size
|
||||
let numParams := rs[0]!.numParams
|
||||
for r in rs do
|
||||
unless r.params.size == numParams do
|
||||
unless r.numParams == numParams do
|
||||
throwErrorAt r.view.ref "invalid inductive type, number of parameters mismatch in mutually inductive datatypes"
|
||||
return numParams
|
||||
|
||||
private def mkTypeFor (r : PreElabHeaderResult) : TermElabM Expr := do
|
||||
withLCtx r.lctx r.localInsts do
|
||||
mkForallFVars r.params r.type
|
||||
|
||||
/--
|
||||
Execute `k` with updated binder information for `xs`. Any `x` that is explicit becomes implicit.
|
||||
-/
|
||||
@@ -276,7 +272,7 @@ private def checkHeaders (rs : Array PreElabHeaderResult) (numParams : Nat) (i :
|
||||
checkHeaders rs numParams (i+1) type
|
||||
where
|
||||
checkHeader (r : PreElabHeaderResult) (numParams : Nat) (firstType? : Option Expr) : TermElabM Expr := do
|
||||
let type ← mkTypeFor r
|
||||
let type := r.type
|
||||
match firstType? with
|
||||
| none => return type
|
||||
| some firstType =>
|
||||
@@ -306,7 +302,8 @@ private def elabHeadersAux (views : Array InductiveView) (i : Nat) (acc : Array
|
||||
let params ← Term.addAutoBoundImplicits params (view.declId.getTailPos? (canonicalOnly := true))
|
||||
trace[Elab.inductive] "header params: {params}, type: {type}"
|
||||
let levelNames ← Term.getLevelNames
|
||||
return acc.push { lctx := (← getLCtx), localInsts := (← getLocalInstances), levelNames, params, type, view }
|
||||
let type ← mkForallFVars params type
|
||||
return acc.push { levelNames, numParams := params.size, type, view, origParams := params }
|
||||
elabHeadersAux views (i+1) acc
|
||||
else
|
||||
return acc
|
||||
@@ -326,21 +323,21 @@ private def elabHeaders (views : Array InductiveView) : TermElabM (Array PreElab
|
||||
/--
|
||||
Create a local declaration for each inductive type in `rs`, and execute `x params indFVars`, where `params` are the inductive type parameters and
|
||||
`indFVars` are the new local declarations.
|
||||
We use the local context/instances and parameters of rs[0].
|
||||
We use the parameters of rs[0].
|
||||
Note that this method is executed after we executed `checkHeaders` and established all
|
||||
parameters are compatible.
|
||||
-/
|
||||
private def withInductiveLocalDecls (rs : Array PreElabHeaderResult) (x : Array Expr → Array Expr → TermElabM α) : TermElabM α := do
|
||||
let namesAndTypes ← rs.mapM fun r => do
|
||||
let type ← mkTypeFor r
|
||||
pure (r.view.declName, r.view.shortDeclName, type)
|
||||
let r0 := rs[0]!
|
||||
let params := r0.params
|
||||
withLCtx r0.lctx r0.localInsts <| withRef r0.view.ref do
|
||||
let r0 := rs[0]!
|
||||
forallBoundedTelescope r0.type r0.numParams fun params _ => withRef r0.view.ref do
|
||||
let rec loop (i : Nat) (indFVars : Array Expr) := do
|
||||
if h : i < namesAndTypes.size then
|
||||
let (declName, shortDeclName, type) := namesAndTypes[i]
|
||||
withAuxDecl shortDeclName type declName fun indFVar => loop (i+1) (indFVars.push indFVar)
|
||||
if h : i < rs.size then
|
||||
let r := rs[i]
|
||||
for param in params, origParam in r.origParams do
|
||||
if let .fvar origFVar := origParam then
|
||||
Elab.pushInfoLeaf <| .ofFVarAliasInfo { id := param.fvarId!, baseId := origFVar, userName := ← param.fvarId!.getUserName }
|
||||
withAuxDecl r.view.shortDeclName r.type r.view.declName fun indFVar =>
|
||||
loop (i+1) (indFVars.push indFVar)
|
||||
else
|
||||
x params indFVars
|
||||
loop 0 #[]
|
||||
@@ -359,26 +356,6 @@ private def ElabHeaderResult.checkLevelNames (rs : Array PreElabHeaderResult) :
|
||||
unless r.levelNames == levelNames do
|
||||
throwErrorAt r.view.ref "invalid inductive type, universe parameters mismatch in mutually inductive datatypes"
|
||||
|
||||
/--
|
||||
We need to work inside a single local context across all the inductive types, so we need to update the `ElabHeaderResult`s
|
||||
so that resultant types refer to the fvars in `params`, the parameters for `rs[0]!` specifically.
|
||||
Also updates the local contexts and local instances in each header.
|
||||
-/
|
||||
private def updateElabHeaderTypes (params : Array Expr) (rs : Array PreElabHeaderResult) (indFVars : Array Expr) : TermElabM (Array ElabHeaderResult) := do
|
||||
rs.mapIdxM fun i r => do
|
||||
/-
|
||||
At this point, because of `withInductiveLocalDecls`, the only fvars that are in context are the ones related to the first inductive type.
|
||||
Because of this, we need to replace the fvars present in each inductive type's header of the mutual block with those of the first inductive.
|
||||
However, some mvars may still be uninstantiated there, and might hide some of the old fvars.
|
||||
As such we first need to synthesize all possible mvars at this stage, instantiate them in the header types and only
|
||||
then replace the parameters' fvars in the header type.
|
||||
|
||||
See issue #3242 (`https://github.com/leanprover/lean4/issues/3242`)
|
||||
-/
|
||||
let type ← instantiateMVars r.type
|
||||
let type := type.replaceFVars r.params params
|
||||
pure { r with lctx := ← getLCtx, localInsts := ← getLocalInstances, type := type, indFVar := indFVars[i]! }
|
||||
|
||||
private def getArity (indType : InductiveType) : MetaM Nat :=
|
||||
forallTelescopeReducing indType.type fun xs _ => return xs.size
|
||||
|
||||
@@ -878,7 +855,7 @@ private def mkInductiveDecl (vars : Array Expr) (elabs : Array InductiveElabStep
|
||||
trace[Elab.inductive] "level names: {allUserLevelNames}"
|
||||
let res ← withInductiveLocalDecls rs fun params indFVars => do
|
||||
trace[Elab.inductive] "indFVars: {indFVars}"
|
||||
let rs ← updateElabHeaderTypes params rs indFVars
|
||||
let rs := Array.zipWith (fun r indFVar => { r with indFVar : ElabHeaderResult }) rs indFVars
|
||||
let mut indTypesArray : Array InductiveType := #[]
|
||||
let mut elabs' := #[]
|
||||
for h : i in [:views.size] do
|
||||
@@ -886,8 +863,7 @@ private def mkInductiveDecl (vars : Array Expr) (elabs : Array InductiveElabStep
|
||||
let r := rs[i]!
|
||||
let elab' ← elabs[i]!.elabCtors rs r params
|
||||
elabs' := elabs'.push elab'
|
||||
let type ← mkForallFVars params r.type
|
||||
indTypesArray := indTypesArray.push { name := r.view.declName, type, ctors := elab'.ctors }
|
||||
indTypesArray := indTypesArray.push { name := r.view.declName, type := r.type, ctors := elab'.ctors }
|
||||
Term.synthesizeSyntheticMVarsNoPostponing
|
||||
let numExplicitParams ← fixedIndicesToParams params.size indTypesArray indFVars
|
||||
trace[Elab.inductive] "numExplicitParams: {numExplicitParams}"
|
||||
|
||||
@@ -1260,12 +1260,8 @@ private def addDefaults (levelParams : List Name) (params : Array Expr) (replace
|
||||
let fieldInfos := (← get).fields
|
||||
let lctx ← instantiateLCtxMVars (← getLCtx)
|
||||
/- The parameters `params` for the auxiliary "default value" definitions must be marked as implicit, and all others as explicit. -/
|
||||
let lctx :=
|
||||
params.foldl (init := lctx) fun (lctx : LocalContext) (p : Expr) =>
|
||||
if p.isFVar then
|
||||
lctx.setBinderInfo p.fvarId! BinderInfo.implicit
|
||||
else
|
||||
lctx
|
||||
let lctx := params.foldl (init := lctx) fun (lctx : LocalContext) (p : Expr) =>
|
||||
lctx.setBinderInfo p.fvarId! BinderInfo.implicit
|
||||
let parentFVarIds := fieldInfos |>.filter (·.kind.isParent) |>.map (·.fvar.fvarId!)
|
||||
let fields := fieldInfos |>.filter (!·.kind.isParent)
|
||||
withLCtx lctx (← getLocalInstances) do
|
||||
|
||||
@@ -290,8 +290,8 @@ def canonicalizeWithSharing (P : Expr) (lhs rhs : Expr) : SimpM Simp.Step := do
|
||||
-- of `lCoeff_{old}` are zero iff `lExpr` contains only neutral elements,
|
||||
-- we default to `lNew` being some canonical neutral element if both
|
||||
-- `commonExpr?` and `lNew?` are `none`.
|
||||
let lNew := Option.zipWith (mkApp2 op.toExpr) commonExpr? lNew? |>.getD op.neutralElement
|
||||
let rNew := Option.zipWith (mkApp2 op.toExpr) commonExpr? rNew? |>.getD op.neutralElement
|
||||
let lNew := Option.merge (mkApp2 op.toExpr) commonExpr? lNew? |>.getD op.neutralElement
|
||||
let rNew := Option.merge (mkApp2 op.toExpr) commonExpr? rNew? |>.getD op.neutralElement
|
||||
|
||||
let oldExpr := mkApp2 P lhs rhs
|
||||
let expr := mkApp2 P lNew rNew
|
||||
|
||||
@@ -63,13 +63,10 @@ partial def findInitialId (proof : Array IntAction) (curr : Nat := 0) : Except S
|
||||
else
|
||||
throw "LRAT proof doesn't contain a proper first proof step."
|
||||
|
||||
def findEmptyId (proof : Array IntAction) : Except String Nat := do
|
||||
if h : 0 < proof.size then
|
||||
match proof[proof.size - 1] with
|
||||
| .addEmpty id .. => pure id
|
||||
| _ => throw "Last proof step is not the empty clause"
|
||||
else
|
||||
throw "The LRAT proof contains no steps."
|
||||
def findEmptyId (proof : Array IntAction) : Except String Nat :=
|
||||
match proof.findSomeRev? (if let .addEmpty id .. := . then some id else none) with
|
||||
| some id => pure id
|
||||
| none => throw "LRAT proof doesn't contain the empty clause."
|
||||
|
||||
def run (proof : Array IntAction) (x : M α) : Except String α := do
|
||||
let initialId ← findInitialId proof
|
||||
|
||||
@@ -165,6 +165,7 @@ structure EvalTacticFailure where
|
||||
state : SavedState
|
||||
|
||||
partial def evalTactic (stx : Syntax) : TacticM Unit := do
|
||||
checkSystem "tactic execution"
|
||||
profileitM Exception "tactic execution" (decl := stx.getKind) (← getOptions) <|
|
||||
withRef stx <| withIncRecDepth <| withFreshMacroScope <| match stx with
|
||||
| .node _ k _ =>
|
||||
@@ -240,6 +241,7 @@ where
|
||||
snap.old?.forM (·.val.cancelRec)
|
||||
let promise ← IO.Promise.new
|
||||
-- Store new unfolding in the snapshot tree
|
||||
let cancelTk? := (← readThe Core.Context).cancelTk?
|
||||
snap.new.resolve {
|
||||
stx := stx'
|
||||
diagnostics := .empty
|
||||
@@ -249,7 +251,7 @@ where
|
||||
state? := (← Tactic.saveState)
|
||||
moreSnaps := #[]
|
||||
}
|
||||
next := #[{ stx? := stx', task := promise.resultD default }]
|
||||
next := #[{ stx? := stx', task := promise.resultD default, cancelTk? }]
|
||||
}
|
||||
-- Update `tacSnap?` to old unfolding
|
||||
withTheReader Term.Context ({ · with tacSnap? := some {
|
||||
|
||||
@@ -78,13 +78,14 @@ where
|
||||
let next ← IO.Promise.new
|
||||
let finished ← IO.Promise.new
|
||||
let inner ← IO.Promise.new
|
||||
let cancelTk? := (← readThe Core.Context).cancelTk?
|
||||
snap.new.resolve {
|
||||
desc := tac.getKind.toString
|
||||
diagnostics := .empty
|
||||
stx := tac
|
||||
inner? := some { stx? := tac, task := inner.resultD default }
|
||||
finished := { stx? := tac, task := finished.resultD default }
|
||||
next := #[{ stx? := stxs, task := next.resultD default }]
|
||||
inner? := some { stx? := tac, task := inner.resultD default, cancelTk? }
|
||||
finished := { stx? := tac, task := finished.resultD default, cancelTk? }
|
||||
next := #[{ stx? := stxs, task := next.resultD default, cancelTk? }]
|
||||
}
|
||||
-- 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`
|
||||
|
||||
@@ -286,14 +286,15 @@ where
|
||||
-- them, eventually put each of them back in `Context.tacSnap?` in `applyAltStx`
|
||||
let finished ← IO.Promise.new
|
||||
let altPromises ← altStxs.mapM fun _ => IO.Promise.new
|
||||
let cancelTk? := (← readThe Core.Context).cancelTk?
|
||||
tacSnap.new.resolve {
|
||||
-- save all relevant syntax here for comparison with next document version
|
||||
stx := mkNullNode altStxs
|
||||
diagnostics := .empty
|
||||
inner? := none
|
||||
finished := { stx? := mkNullNode altStxs, reportingRange? := none, task := finished.resultD default }
|
||||
finished := { stx? := mkNullNode altStxs, reportingRange? := none, task := finished.resultD default, cancelTk? }
|
||||
next := Array.zipWith
|
||||
(fun stx prom => { stx? := some stx, task := prom.resultD default })
|
||||
(fun stx prom => { stx? := some stx, task := prom.resultD default, cancelTk? })
|
||||
altStxs altPromises
|
||||
}
|
||||
goWithIncremental <| altPromises.mapIdx fun i prom => {
|
||||
|
||||
@@ -1878,13 +1878,14 @@ where
|
||||
go todo (autos.push auto)
|
||||
|
||||
/--
|
||||
Similar to `autoBoundImplicits`, but immediately if the resulting array of expressions contains metavariables,
|
||||
it immediately uses `mkForallFVars` + `forallBoundedTelescope` to convert them into free variables.
|
||||
Similar to `addAutoBoundImplicits`, but converts all metavariables into free variables.
|
||||
|
||||
It uses `mkForallFVars` + `forallBoundedTelescope` to convert metavariables into free variables.
|
||||
The type `type` is modified during the process if type depends on `xs`.
|
||||
We use this method to simplify the conversion of code using `autoBoundImplicitsOld` to `autoBoundImplicits`.
|
||||
-/
|
||||
def addAutoBoundImplicits' (xs : Array Expr) (type : Expr) (k : Array Expr → Expr → TermElabM α) : TermElabM α := do
|
||||
let xs ← addAutoBoundImplicits xs none
|
||||
def addAutoBoundImplicits' (xs : Array Expr) (type : Expr) (k : Array Expr → Expr → TermElabM α) (inlayHintPos? : Option String.Pos := none) : TermElabM α := do
|
||||
let xs ← addAutoBoundImplicits xs inlayHintPos?
|
||||
if xs.all (·.isFVar) then
|
||||
k xs type
|
||||
else
|
||||
|
||||
@@ -122,13 +122,15 @@ end TagDeclarationExtension
|
||||
structure MapDeclarationExtension (α : Type) extends PersistentEnvExtension (Name × α) (Name × α) (NameMap α)
|
||||
deriving Inhabited
|
||||
|
||||
def mkMapDeclarationExtension (name : Name := by exact decl_name%) : IO (MapDeclarationExtension α) :=
|
||||
def mkMapDeclarationExtension (name : Name := by exact decl_name%)
|
||||
(exportEntriesFn : NameMap α → Array (Name × α) := (·.toArray)) : IO (MapDeclarationExtension α) :=
|
||||
.mk <$> registerPersistentEnvExtension {
|
||||
name := name,
|
||||
mkInitial := pure {}
|
||||
addImportedFn := fun _ => pure {}
|
||||
addEntryFn := fun s (n, v) => s.insert n v
|
||||
exportEntriesFn := fun s => s.toArray
|
||||
saveEntriesFn := fun s => s.toArray
|
||||
exportEntriesFn
|
||||
asyncMode := .async
|
||||
replay? := some fun _ newState newConsts s =>
|
||||
newConsts.foldl (init := s) fun s c =>
|
||||
@@ -145,10 +147,11 @@ def insert (ext : MapDeclarationExtension α) (env : Environment) (declName : Na
|
||||
assert! env.asyncMayContain declName
|
||||
ext.addEntry env (declName, val)
|
||||
|
||||
def find? [Inhabited α] (ext : MapDeclarationExtension α) (env : Environment) (declName : Name) : Option α :=
|
||||
def find? [Inhabited α] (ext : MapDeclarationExtension α) (env : Environment) (declName : Name)
|
||||
(includeServer := false) : Option α :=
|
||||
match env.getModuleIdxFor? declName with
|
||||
| some modIdx =>
|
||||
match (ext.getModuleEntries env modIdx).binSearch (declName, default) (fun a b => Name.quickLt a.1 b.1) with
|
||||
match (ext.getModuleEntries (includeServer := includeServer) env modIdx).binSearch (declName, default) (fun a b => Name.quickLt a.1 b.1) with
|
||||
| some e => some e.2
|
||||
| none => none
|
||||
| none => (ext.findStateAsync env declName).find? declName
|
||||
|
||||
@@ -506,6 +506,12 @@ structure Environment where
|
||||
-/
|
||||
base : Kernel.Environment
|
||||
/--
|
||||
Additional imported environment extension state for use in the language server. This field is
|
||||
identical to `base.extensions` in other contexts. Access via
|
||||
`getModuleEntries (includeServer := true)`.
|
||||
-/
|
||||
private serverBaseExts : Array EnvExtensionState := base.extensions
|
||||
/--
|
||||
Kernel environment task that is fulfilled when all asynchronously elaborated declarations are
|
||||
finished, containing the resulting environment. Also collects the environment extension state of
|
||||
all environment branches that contributed contained declarations.
|
||||
@@ -536,6 +542,12 @@ structure Environment where
|
||||
`findAsyncCore?`/`findStateAsync`; see there.
|
||||
-/
|
||||
private allRealizations : Task (NameMap AsyncConst) := .pure {}
|
||||
/--
|
||||
Indicates whether the environment is being used in an exported context, i.e. whether it should
|
||||
provide access to only the data to be imported by other modules participating in the module
|
||||
system.
|
||||
-/
|
||||
isExporting : Bool := false
|
||||
deriving Nonempty
|
||||
|
||||
namespace Environment
|
||||
@@ -549,6 +561,10 @@ def ofKernelEnv (env : Kernel.Environment) : Environment :=
|
||||
def toKernelEnv (env : Environment) : Kernel.Environment :=
|
||||
env.checked.get
|
||||
|
||||
/-- Updates `Environment.isExporting`. -/
|
||||
def setExporting (env : Environment) (isExporting : Bool) : Environment :=
|
||||
{ env with isExporting }
|
||||
|
||||
/-- Consistently updates synchronous and asynchronous parts of the environment without blocking. -/
|
||||
private def modifyCheckedAsync (env : Environment) (f : Kernel.Environment → Kernel.Environment) : Environment :=
|
||||
{ env with checked := env.checked.map (sync := true) f, base := f env.base }
|
||||
@@ -905,7 +921,8 @@ def addConstAsync (env : Environment) (constName : Name) (kind : ConstantKind) (
|
||||
}
|
||||
exts? := guard reportExts *> some (constPromise.result?.map (sync := true) fun
|
||||
| some (_, exts, _) => exts
|
||||
| none => env.toKernelEnv.extensions)
|
||||
-- any value should work here, `base` does not block
|
||||
| none => env.base.extensions)
|
||||
consts := constPromise.result?.map (sync := true) fun
|
||||
| some (_, _, consts) => .mk consts
|
||||
| none => .mk (α := AsyncConsts) default
|
||||
@@ -1378,7 +1395,15 @@ structure PersistentEnvExtension (α : Type) (β : Type) (σ : Type) where
|
||||
name : Name
|
||||
addImportedFn : Array (Array α) → ImportM σ
|
||||
addEntryFn : σ → β → σ
|
||||
/-- Function to transform state into data that should always be imported into other modules. -/
|
||||
exportEntriesFn : σ → Array α
|
||||
/--
|
||||
Function to transform state into data that should be imported into other modules when the module
|
||||
system is disabled. When it is enabled, the data is loaded only in the language server and
|
||||
accessible via `getModuleEntries (includeServer := true)`. Conventionally, this is a superset of
|
||||
the data returned by `exportEntriesFn`.
|
||||
-/
|
||||
saveEntriesFn : σ → Array α
|
||||
statsFn : σ → Format
|
||||
|
||||
instance {α σ} [Inhabited σ] : Inhabited (PersistentEnvExtensionState α σ) :=
|
||||
@@ -1391,14 +1416,21 @@ instance {α β σ} [Inhabited σ] : Inhabited (PersistentEnvExtension α β σ)
|
||||
addImportedFn := fun _ => default,
|
||||
addEntryFn := fun s _ => s,
|
||||
exportEntriesFn := fun _ => #[],
|
||||
saveEntriesFn := fun _ => #[],
|
||||
statsFn := fun _ => Format.nil
|
||||
}
|
||||
|
||||
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[m]!
|
||||
/--
|
||||
Returns the data saved by `ext.exportEntriesFn/saveEntriesFn` when `m` was elaborated. See docs on
|
||||
the functions for details.
|
||||
-/
|
||||
def getModuleEntries {α β σ : Type} [Inhabited σ] (ext : PersistentEnvExtension α β σ)
|
||||
(env : Environment) (m : ModuleIdx) (includeServer := false) : Array α :=
|
||||
let exts := if includeServer then env.serverBaseExts else env.base.extensions
|
||||
-- safety: as in `getStateUnsafe`
|
||||
unsafe (ext.toEnvExtension.getStateImpl exts).importedEntries[m]!
|
||||
|
||||
def addEntry {α β σ : Type} (ext : PersistentEnvExtension α β σ) (env : Environment) (b : β) : Environment :=
|
||||
ext.toEnvExtension.modifyState env fun s =>
|
||||
@@ -1435,10 +1467,14 @@ structure PersistentEnvExtensionDescr (α β σ : Type) where
|
||||
addImportedFn : Array (Array α) → ImportM σ
|
||||
addEntryFn : σ → β → σ
|
||||
exportEntriesFn : σ → Array α
|
||||
saveEntriesFn : σ → Array α := exportEntriesFn
|
||||
statsFn : σ → Format := fun _ => Format.nil
|
||||
asyncMode : EnvExtension.AsyncMode := .mainOnly
|
||||
replay? : Option (ReplayFn σ) := none
|
||||
|
||||
attribute [inherit_doc PersistentEnvExtension.exportEntriesFn] PersistentEnvExtensionDescr.exportEntriesFn
|
||||
attribute [inherit_doc PersistentEnvExtension.saveEntriesFn] PersistentEnvExtensionDescr.saveEntriesFn
|
||||
|
||||
unsafe def registerPersistentEnvExtensionUnsafe {α β σ : Type} [Inhabited σ] (descr : PersistentEnvExtensionDescr α β σ) : IO (PersistentEnvExtension α β σ) := do
|
||||
let pExts ← persistentEnvExtensionsRef.get
|
||||
if pExts.any (fun ext => ext.name == descr.name) then throw (IO.userError s!"invalid environment extension, '{descr.name}' has already been used")
|
||||
@@ -1457,6 +1493,7 @@ unsafe def registerPersistentEnvExtensionUnsafe {α β σ : Type} [Inhabited σ]
|
||||
addImportedFn := descr.addImportedFn,
|
||||
addEntryFn := descr.addEntryFn,
|
||||
exportEntriesFn := descr.exportEntriesFn,
|
||||
saveEntriesFn := descr.saveEntriesFn,
|
||||
statsFn := descr.statsFn
|
||||
}
|
||||
persistentEnvExtensionsRef.modify fun pExts => pExts.push (unsafeCast pExt)
|
||||
@@ -1465,10 +1502,30 @@ unsafe def registerPersistentEnvExtensionUnsafe {α β σ : Type} [Inhabited σ]
|
||||
@[implemented_by registerPersistentEnvExtensionUnsafe]
|
||||
opaque registerPersistentEnvExtension {α β σ : Type} [Inhabited σ] (descr : PersistentEnvExtensionDescr α β σ) : IO (PersistentEnvExtension α β σ)
|
||||
|
||||
@[extern "lean_save_module_data"]
|
||||
opaque saveModuleData (fname : @& System.FilePath) (mod : @& Name) (data : @& ModuleData) : IO Unit
|
||||
@[extern "lean_read_module_data"]
|
||||
opaque readModuleData (fname : @& System.FilePath) : IO (ModuleData × CompactedRegion)
|
||||
/--
|
||||
Stores each given module data in the respective file name. Objects shared with prior parts are not
|
||||
duplicated. Thus the data cannot be loaded with individual `readModuleData` calls but must loaded by
|
||||
passing (a prefix of) the file names to `readModuleDataParts`. `mod` is used to determine an
|
||||
arbitrary but deterministic base address for `mmap`.
|
||||
-/
|
||||
@[extern "lean_save_module_data_parts"]
|
||||
opaque saveModuleDataParts (mod : @& Name) (parts : Array (System.FilePath × ModuleData)) : IO Unit
|
||||
|
||||
/--
|
||||
Loads the module data from the given file names. The files must be (a prefix of) the result of a
|
||||
`saveModuleDataParts` call.
|
||||
-/
|
||||
@[extern "lean_read_module_data_parts"]
|
||||
opaque readModuleDataParts (fnames : @& Array System.FilePath) : IO (Array (ModuleData × CompactedRegion))
|
||||
|
||||
def saveModuleData (fname : System.FilePath) (mod : Name) (data : ModuleData) : IO Unit :=
|
||||
saveModuleDataParts mod #[(fname, data)]
|
||||
|
||||
def readModuleData (fname : @& System.FilePath) : IO (ModuleData × CompactedRegion) := do
|
||||
let parts ← readModuleDataParts #[fname]
|
||||
assert! parts.size == 1
|
||||
let some part := parts[0]? | unreachable!
|
||||
return part
|
||||
|
||||
/--
|
||||
Free compacted regions of imports. No live references to imported objects may exist at the time of invocation; in
|
||||
@@ -1492,7 +1549,22 @@ unsafe def Environment.freeRegions (env : Environment) : IO Unit :=
|
||||
TODO: statically check for this. -/
|
||||
env.header.regions.forM CompactedRegion.free
|
||||
|
||||
def mkModuleData (env : Environment) : IO ModuleData := do
|
||||
/-- The level of information to save/load. Each level includes all previous ones. -/
|
||||
inductive OLeanLevel where
|
||||
/-- Information from exported contexts. -/
|
||||
| exported
|
||||
/-- Environment extension state for the language server. -/
|
||||
| server
|
||||
/-- Private module data. -/
|
||||
| «private»
|
||||
deriving DecidableEq
|
||||
|
||||
def OLeanLevel.adjustFileName (base : System.FilePath) : OLeanLevel → System.FilePath
|
||||
| .exported => base
|
||||
| .server => base.addExtension "server"
|
||||
| .private => base.addExtension "private"
|
||||
|
||||
def mkModuleData (env : Environment) (level : OLeanLevel := .private) : IO ModuleData := do
|
||||
let pExts ← persistentEnvExtensionsRef.get
|
||||
let entries := pExts.map fun pExt => Id.run do
|
||||
-- get state from `checked` at the end if `async`; it would otherwise panic
|
||||
@@ -1500,19 +1572,37 @@ def mkModuleData (env : Environment) : IO ModuleData := do
|
||||
if asyncMode matches .async then
|
||||
asyncMode := .sync
|
||||
let state := pExt.getState (asyncMode := asyncMode) env
|
||||
(pExt.name, pExt.exportEntriesFn state)
|
||||
(pExt.name, if level = .exported then pExt.exportEntriesFn state else pExt.saveEntriesFn state)
|
||||
let kenv := env.toKernelEnv
|
||||
let constNames := kenv.constants.foldStage2 (fun names name _ => names.push name) #[]
|
||||
let constants := kenv.constants.foldStage2 (fun cs _ c => cs.push c) #[]
|
||||
let env := env.setExporting (level != .private)
|
||||
let constants := kenv.constants.foldStage2 (fun cs _ c => cs.push c) #[]
|
||||
--let constNames := kenv.constants.foldStage2 (fun names name _ => names.push name) #[]
|
||||
-- not all kernel constants may be exported
|
||||
-- TODO: does not include cstage* constants from the old codegen
|
||||
--let constants := constNames.filterMap env.find?
|
||||
let constNames := constants.map (·.name)
|
||||
return {
|
||||
imports := env.header.imports
|
||||
extraConstNames := env.checked.get.extraConstNames.toArray
|
||||
constNames, constants, entries
|
||||
}
|
||||
|
||||
register_builtin_option experimental.module : Bool := {
|
||||
defValue := false
|
||||
descr := "Enable module system (experimental)"
|
||||
}
|
||||
|
||||
@[export lean_write_module]
|
||||
def writeModule (env : Environment) (fname : System.FilePath) : IO Unit := do
|
||||
saveModuleData fname env.mainModule (← mkModuleData env)
|
||||
def writeModule (env : Environment) (fname : System.FilePath) (split := false) : IO Unit := do
|
||||
if split then
|
||||
let mkPart (level : OLeanLevel) :=
|
||||
return (level.adjustFileName fname, (← mkModuleData env level))
|
||||
saveModuleDataParts env.mainModule #[
|
||||
(← mkPart .exported),
|
||||
(← mkPart .server),
|
||||
(← mkPart .private)]
|
||||
else
|
||||
saveModuleData fname env.mainModule (← mkModuleData env)
|
||||
|
||||
/--
|
||||
Construct a mapping from persistent extension name to extension index at the array of persistent extensions.
|
||||
@@ -1526,10 +1616,9 @@ def mkExtNameMap (startingAt : Nat) : IO (Std.HashMap Name Nat) := do
|
||||
result := result.insert descr.name i
|
||||
return result
|
||||
|
||||
private def setImportedEntries (env : Environment) (mods : Array ModuleData) (startingAt : Nat := 0) : IO Environment := do
|
||||
-- We work directly on the states array instead of `env` as `Environment.modifyState` introduces
|
||||
-- significant overhead on such frequent calls
|
||||
let mut states := env.base.extensions
|
||||
private def setImportedEntries (states : Array EnvExtensionState) (mods : Array ModuleData)
|
||||
(startingAt : Nat := 0) : IO (Array EnvExtensionState) := do
|
||||
let mut states := states
|
||||
let extDescrs ← persistentEnvExtensionsRef.get
|
||||
/- For extensions starting at `startingAt`, ensure their `importedEntries` array have size `mods.size`. -/
|
||||
for extDescr in extDescrs[startingAt:] do
|
||||
@@ -1545,7 +1634,7 @@ private def setImportedEntries (env : Environment) (mods : Array ModuleData) (st
|
||||
-- safety: as in `modifyState`
|
||||
states := unsafe extDescrs[entryIdx]!.toEnvExtension.modifyStateImpl states fun s =>
|
||||
{ s with importedEntries := s.importedEntries.set! modIdx entries }
|
||||
return env.setCheckedSync { env.base with extensions := states }
|
||||
return states
|
||||
|
||||
/--
|
||||
"Forward declaration" needed for updating the attribute table with user-defined attributes.
|
||||
@@ -1584,7 +1673,7 @@ where
|
||||
-- This branch is executed when `pExtDescrs[i]` is the extension associated with the `init` attribute, and
|
||||
-- a user-defined persistent extension is imported.
|
||||
-- Thus, we invoke `setImportedEntries` to update the array `importedEntries` with the entries for the new extensions.
|
||||
env ← setImportedEntries env mods prevSize
|
||||
env := env.setCheckedSync { env.base with extensions := (← setImportedEntries env.base.extensions mods prevSize) }
|
||||
-- See comment at `updateEnvAttributesRef`
|
||||
env ← updateEnvAttributes env
|
||||
loop (i + 1) env
|
||||
@@ -1595,7 +1684,7 @@ structure ImportState where
|
||||
moduleNameSet : NameHashSet := {}
|
||||
moduleNames : Array Name := #[]
|
||||
moduleData : Array ModuleData := #[]
|
||||
regions : Array CompactedRegion := #[]
|
||||
parts : Array (Array (ModuleData × CompactedRegion)) := #[]
|
||||
|
||||
def throwAlreadyImported (s : ImportState) (const2ModIdx : Std.HashMap Name ModuleIdx) (modIdx : Nat) (cname : Name) : IO α := do
|
||||
let modName := s.moduleNames[modIdx]!
|
||||
@@ -1607,7 +1696,8 @@ abbrev ImportStateM := StateRefT ImportState IO
|
||||
@[inline] nonrec def ImportStateM.run (x : ImportStateM α) (s : ImportState := {}) : IO (α × ImportState) :=
|
||||
x.run s
|
||||
|
||||
partial def importModulesCore (imports : Array Import) : ImportStateM Unit := do
|
||||
partial def importModulesCore (imports : Array Import) (level := OLeanLevel.private) :
|
||||
ImportStateM Unit := do
|
||||
for i in imports do
|
||||
if i.runtimeOnly || (← get).moduleNameSet.contains i.module then
|
||||
continue
|
||||
@@ -1615,12 +1705,22 @@ partial def importModulesCore (imports : Array Import) : ImportStateM Unit := do
|
||||
let mFile ← findOLean i.module
|
||||
unless (← mFile.pathExists) do
|
||||
throw <| IO.userError s!"object file '{mFile}' of module {i.module} does not exist"
|
||||
let (mod, region) ← readModuleData mFile
|
||||
importModulesCore mod.imports
|
||||
let mut fnames := #[mFile]
|
||||
if level != OLeanLevel.exported then
|
||||
let sFile := OLeanLevel.server.adjustFileName mFile
|
||||
if (← sFile.pathExists) then
|
||||
fnames := fnames.push sFile
|
||||
if level == OLeanLevel.private then
|
||||
let pFile := OLeanLevel.private.adjustFileName mFile
|
||||
if (← pFile.pathExists) then
|
||||
fnames := fnames.push pFile
|
||||
let parts ← readModuleDataParts fnames
|
||||
let some (mod, _) := parts[if level = .exported then 0 else parts.size - 1]? | unreachable!
|
||||
importModulesCore (level := level) mod.imports
|
||||
modify fun s => { s with
|
||||
moduleData := s.moduleData.push mod
|
||||
regions := s.regions.push region
|
||||
moduleNames := s.moduleNames.push i.module
|
||||
parts := s.parts.push parts
|
||||
}
|
||||
|
||||
/--
|
||||
@@ -1684,14 +1784,16 @@ def finalizeImport (s : ImportState) (imports : Array Import) (opts : Options) (
|
||||
extensions := exts
|
||||
header := {
|
||||
trustLevel, imports
|
||||
regions := s.regions
|
||||
regions := s.parts.flatMap (·.map (·.2))
|
||||
moduleNames := s.moduleNames
|
||||
moduleData := s.moduleData
|
||||
}
|
||||
}
|
||||
realizedImportedConsts? := none
|
||||
}
|
||||
env ← setImportedEntries env s.moduleData
|
||||
env := env.setCheckedSync { env.base with extensions := (← setImportedEntries env.base.extensions s.moduleData) }
|
||||
let serverData := s.parts.filterMap fun parts => (parts[1]? <|> parts[0]?).map Prod.fst
|
||||
env := { env with serverBaseExts := (← setImportedEntries env.base.extensions serverData) }
|
||||
if leakEnv then
|
||||
/- Mark persistent a first time before `finalizePersistenExtensions`, which
|
||||
avoids costly MT markings when e.g. an interpreter closure (which
|
||||
@@ -1738,13 +1840,13 @@ environment's constant map can be accessed without `loadExts`, many functions th
|
||||
-/
|
||||
def importModules (imports : Array Import) (opts : Options) (trustLevel : UInt32 := 0)
|
||||
(plugins : Array System.FilePath := #[]) (leakEnv := false) (loadExts := false)
|
||||
: IO Environment := profileitIO "import" opts do
|
||||
(level := OLeanLevel.private) : IO Environment := profileitIO "import" opts do
|
||||
for imp in imports do
|
||||
if imp.module matches .anonymous then
|
||||
throw <| IO.userError "import failed, trying to import module with anonymous name"
|
||||
withImporting do
|
||||
plugins.forM Lean.loadPlugin
|
||||
let (_, s) ← importModulesCore imports |>.run
|
||||
let (_, s) ← importModulesCore (level := level) imports |>.run
|
||||
finalizeImport (leakEnv := leakEnv) (loadExts := loadExts) s imports opts trustLevel
|
||||
|
||||
/--
|
||||
|
||||
@@ -93,18 +93,17 @@ structure SnapshotTask (α : Type) where
|
||||
Cancellation token that can be set by the server to cancel the task when it detects the results
|
||||
are not needed anymore.
|
||||
-/
|
||||
cancelTk? : Option IO.CancelToken := none
|
||||
cancelTk? : Option IO.CancelToken
|
||||
/-- Underlying task producing the snapshot. -/
|
||||
task : Task α
|
||||
deriving Nonempty, Inhabited
|
||||
|
||||
/-- Creates a snapshot task from the syntax processed by the task and a `BaseIO` action. -/
|
||||
def SnapshotTask.ofIO (stx? : Option Syntax)
|
||||
def SnapshotTask.ofIO (stx? : Option Syntax) (cancelTk? : Option IO.CancelToken)
|
||||
(reportingRange? : Option String.Range := defaultReportingRange? stx?) (act : BaseIO α) :
|
||||
BaseIO (SnapshotTask α) := do
|
||||
return {
|
||||
stx?
|
||||
reportingRange?
|
||||
stx?, reportingRange?, cancelTk?
|
||||
task := (← BaseIO.asTask act)
|
||||
}
|
||||
|
||||
@@ -114,6 +113,7 @@ def SnapshotTask.finished (stx? : Option Syntax) (a : α) : SnapshotTask α wher
|
||||
-- irrelevant when already finished
|
||||
reportingRange? := none
|
||||
task := .pure a
|
||||
cancelTk? := none
|
||||
|
||||
/-- Transforms a task's output without changing the processed syntax. -/
|
||||
def SnapshotTask.map (t : SnapshotTask α) (f : α → β) (stx? : Option Syntax := t.stx?)
|
||||
|
||||
@@ -397,7 +397,7 @@ where
|
||||
diagnostics := oldProcessed.diagnostics
|
||||
result? := some {
|
||||
cmdState := oldProcSuccess.cmdState
|
||||
firstCmdSnap := { stx? := none, task := prom.result! } } }
|
||||
firstCmdSnap := { stx? := none, task := prom.result!, cancelTk? := cancelTk } } }
|
||||
else
|
||||
return .finished newStx oldProcessed) } }
|
||||
else return old
|
||||
@@ -450,7 +450,7 @@ where
|
||||
processHeader (stx : Syntax) (parserState : Parser.ModuleParserState) :
|
||||
LeanProcessingM (SnapshotTask HeaderProcessedSnapshot) := do
|
||||
let ctx ← read
|
||||
SnapshotTask.ofIO stx (some ⟨0, ctx.input.endPos⟩) <|
|
||||
SnapshotTask.ofIO stx none (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
|
||||
@@ -507,7 +507,7 @@ where
|
||||
infoTree? := cmdState.infoState.trees[0]!
|
||||
result? := some {
|
||||
cmdState
|
||||
firstCmdSnap := { stx? := none, task := prom.result! }
|
||||
firstCmdSnap := { stx? := none, task := prom.result!, cancelTk? := cancelTk }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -523,17 +523,19 @@ where
|
||||
-- from `old`
|
||||
if let some oldNext := old.nextCmdSnap? then do
|
||||
let newProm ← IO.Promise.new
|
||||
let cancelTk ← IO.CancelToken.new
|
||||
-- can reuse range, syntax unchanged
|
||||
BaseIO.chainTask (sync := true) old.resultSnap.task fun oldResult =>
|
||||
-- also wait on old command parse snapshot as parsing is cheap and may allow for
|
||||
-- elaboration reuse
|
||||
BaseIO.chainTask (sync := true) oldNext.task fun oldNext => do
|
||||
let cancelTk ← IO.CancelToken.new
|
||||
parseCmd oldNext newParserState oldResult.cmdState newProm sync cancelTk ctx
|
||||
prom.resolve <| { old with nextCmdSnap? := some {
|
||||
stx? := none
|
||||
reportingRange? := some ⟨newParserState.pos, ctx.input.endPos⟩
|
||||
task := newProm.result! } }
|
||||
task := newProm.result!
|
||||
cancelTk? := cancelTk
|
||||
} }
|
||||
else prom.resolve old -- terminal command, we're done!
|
||||
|
||||
-- fast path, do not even start new task for this snapshot (see [Incremental Parsing])
|
||||
@@ -615,15 +617,16 @@ where
|
||||
})
|
||||
let diagnostics ← Snapshot.Diagnostics.ofMessageLog msgLog
|
||||
|
||||
-- use per-command cancellation token for elaboration so that
|
||||
-- use per-command cancellation token for elaboration so that cancellation of further commands
|
||||
-- does not affect current command
|
||||
let elabCmdCancelTk ← IO.CancelToken.new
|
||||
prom.resolve {
|
||||
diagnostics, nextCmdSnap?
|
||||
stx := stx', parserState := parserState'
|
||||
elabSnap := { stx? := stx', task := elabPromise.result!, cancelTk? := some elabCmdCancelTk }
|
||||
resultSnap := { stx? := stx', reportingRange? := initRange?, task := resultPromise.result! }
|
||||
infoTreeSnap := { stx? := stx', reportingRange? := initRange?, task := finishedPromise.result! }
|
||||
reportSnap := { stx? := none, reportingRange? := initRange?, task := reportPromise.result! }
|
||||
resultSnap := { stx? := stx', reportingRange? := initRange?, task := resultPromise.result!, cancelTk? := none }
|
||||
infoTreeSnap := { stx? := stx', reportingRange? := initRange?, task := finishedPromise.result!, cancelTk? := none }
|
||||
reportSnap := { stx? := none, reportingRange? := initRange?, task := reportPromise.result!, cancelTk? := none }
|
||||
}
|
||||
let cmdState ← doElab stx cmdState beginPos
|
||||
{ old? := old?.map fun old => ⟨old.stx, old.elabSnap⟩, new := elabPromise }
|
||||
@@ -665,8 +668,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 := #[
|
||||
{ stx? := stx', task := elabPromise.result!.map (sync := true) toSnapshotTree },
|
||||
{ stx? := stx', task := resultPromise.result!.map (sync := true) toSnapshotTree }] ++
|
||||
{ stx? := stx', task := elabPromise.result!.map (sync := true) toSnapshotTree, cancelTk? := none },
|
||||
{ stx? := stx', task := resultPromise.result!.map (sync := true) toSnapshotTree, cancelTk? := none }] ++
|
||||
cmdState.snapshotTasks
|
||||
let tree := SnapshotTree.mk { diagnostics := .empty } snaps
|
||||
BaseIO.bindTask (← tree.waitAll) fun _ => do
|
||||
@@ -690,6 +693,7 @@ where
|
||||
stx? := none
|
||||
reportingRange? := initRange?
|
||||
task := traceTask
|
||||
cancelTk? := none
|
||||
}
|
||||
if let some next := next? then
|
||||
-- We're definitely off the fast-forwarding path now
|
||||
|
||||
@@ -66,7 +66,10 @@ inductive MessageData where
|
||||
`alt` may nest any structured message,
|
||||
for example `ofGoal` to approximate a tactic state widget,
|
||||
and, if necessary, even other widget instances
|
||||
(for which approximations are computed recursively). -/
|
||||
(for which approximations are computed recursively).
|
||||
|
||||
Note that unlike with `Widget.savePanelWidgetInfo`,
|
||||
the infoview will not pass any additional props to the widget instance. -/
|
||||
| ofWidget : Widget.WidgetInstance → MessageData → MessageData
|
||||
/-- `withContext ctx d` specifies the pretty printing context `(env, mctx, lctx, opts)` for the nested expressions in `d`. -/
|
||||
| withContext : MessageDataContext → MessageData → MessageData
|
||||
@@ -506,13 +509,6 @@ def indentExpr (e : Expr) : MessageData :=
|
||||
def aquote (msg : MessageData) : MessageData :=
|
||||
"「" ++ msg ++ "」"
|
||||
|
||||
/-- Quote `e` using `「` and `」` if `e` is not a free variable, constant, or literal. -/
|
||||
def quoteIfNotAtom (e : Expr) : MessageData :=
|
||||
if e.isFVar || e.isConst || e.isLit then
|
||||
e
|
||||
else
|
||||
aquote e
|
||||
|
||||
class AddMessageContext (m : Type → Type) where
|
||||
/--
|
||||
Without context, a `MessageData` object may be missing information
|
||||
|
||||
@@ -2279,6 +2279,7 @@ def realizeConst (forConst : Name) (constName : Name) (realize : MetaM Unit) :
|
||||
initHeartbeats := (← IO.getNumHeartbeats)
|
||||
}
|
||||
let (env, exTask, dyn) ← env.realizeConst forConst constName (realizeAndReport coreCtx)
|
||||
-- Realizations cannot be cancelled as their result is shared across elaboration runs
|
||||
let exAct ← Core.wrapAsyncAsSnapshot (cancelTk? := none) fun
|
||||
| none => return
|
||||
| some ex => do
|
||||
@@ -2286,6 +2287,7 @@ def realizeConst (forConst : Name) (constName : Name) (realize : MetaM Unit) :
|
||||
Core.logSnapshotTask {
|
||||
stx? := none
|
||||
task := (← BaseIO.mapTask (t := exTask) exAct)
|
||||
cancelTk? := none
|
||||
}
|
||||
if let some res := dyn.get? RealizeConstantResult then
|
||||
let mut snap := res.snap
|
||||
|
||||
@@ -52,6 +52,8 @@ builtin_initialize registerTraceClass `grind.split.candidate
|
||||
builtin_initialize registerTraceClass `grind.split.resolved
|
||||
builtin_initialize registerTraceClass `grind.beta
|
||||
builtin_initialize registerTraceClass `grind.mbtc
|
||||
builtin_initialize registerTraceClass `grind.ext
|
||||
builtin_initialize registerTraceClass `grind.ext.candidate
|
||||
|
||||
/-! Trace options for `grind` developers -/
|
||||
builtin_initialize registerTraceClass `grind.debug
|
||||
@@ -74,5 +76,8 @@ builtin_initialize registerTraceClass `grind.debug.mbtc
|
||||
builtin_initialize registerTraceClass `grind.debug.ematch
|
||||
builtin_initialize registerTraceClass `grind.debug.proveEq
|
||||
builtin_initialize registerTraceClass `grind.debug.pushNewFact
|
||||
builtin_initialize registerTraceClass `grind.debug.ematch.activate
|
||||
builtin_initialize registerTraceClass `grind.debug.appMap
|
||||
builtin_initialize registerTraceClass `grind.debug.ext
|
||||
|
||||
end Lean
|
||||
|
||||
@@ -22,50 +22,17 @@ namespace Lean
|
||||
|
||||
builtin_initialize registerTraceClass `grind.cutsat
|
||||
builtin_initialize registerTraceClass `grind.cutsat.model
|
||||
builtin_initialize registerTraceClass `grind.cutsat.subst
|
||||
builtin_initialize registerTraceClass `grind.cutsat.eq
|
||||
builtin_initialize registerTraceClass `grind.cutsat.eq.unsat (inherited := true)
|
||||
builtin_initialize registerTraceClass `grind.cutsat.eq.trivial (inherited := true)
|
||||
builtin_initialize registerTraceClass `grind.cutsat.assert
|
||||
builtin_initialize registerTraceClass `grind.cutsat.assert.dvd
|
||||
builtin_initialize registerTraceClass `grind.cutsat.dvd
|
||||
builtin_initialize registerTraceClass `grind.cutsat.dvd.update (inherited := true)
|
||||
builtin_initialize registerTraceClass `grind.cutsat.dvd.unsat (inherited := true)
|
||||
builtin_initialize registerTraceClass `grind.cutsat.dvd.trivial (inherited := true)
|
||||
builtin_initialize registerTraceClass `grind.cutsat.dvd.solve (inherited := true)
|
||||
builtin_initialize registerTraceClass `grind.cutsat.dvd.solve.combine (inherited := true)
|
||||
builtin_initialize registerTraceClass `grind.cutsat.dvd.solve.elim (inherited := true)
|
||||
builtin_initialize registerTraceClass `grind.cutsat.internalize
|
||||
builtin_initialize registerTraceClass `grind.cutsat.internalize.term (inherited := true)
|
||||
builtin_initialize registerTraceClass `grind.cutsat.assert.trivial
|
||||
builtin_initialize registerTraceClass `grind.cutsat.assert.unsat
|
||||
builtin_initialize registerTraceClass `grind.cutsat.assert.store
|
||||
|
||||
builtin_initialize registerTraceClass `grind.cutsat.assert.le
|
||||
builtin_initialize registerTraceClass `grind.cutsat.le
|
||||
builtin_initialize registerTraceClass `grind.cutsat.le.unsat (inherited := true)
|
||||
builtin_initialize registerTraceClass `grind.cutsat.le.trivial (inherited := true)
|
||||
builtin_initialize registerTraceClass `grind.cutsat.le.lower (inherited := true)
|
||||
builtin_initialize registerTraceClass `grind.cutsat.le.upper (inherited := true)
|
||||
builtin_initialize registerTraceClass `grind.cutsat.assign
|
||||
builtin_initialize registerTraceClass `grind.cutsat.conflict
|
||||
|
||||
builtin_initialize registerTraceClass `grind.cutsat.diseq
|
||||
builtin_initialize registerTraceClass `grind.cutsat.diseq.trivial (inherited := true)
|
||||
|
||||
builtin_initialize registerTraceClass `grind.debug.cutsat.eq
|
||||
builtin_initialize registerTraceClass `grind.debug.cutsat.dvd.le
|
||||
builtin_initialize registerTraceClass `grind.debug.cutsat.diseq
|
||||
builtin_initialize registerTraceClass `grind.debug.cutsat.diseq.split
|
||||
builtin_initialize registerTraceClass `grind.debug.cutsat.backtrack
|
||||
builtin_initialize registerTraceClass `grind.debug.cutsat.search
|
||||
builtin_initialize registerTraceClass `grind.debug.cutsat.cooper
|
||||
builtin_initialize registerTraceClass `grind.debug.cutsat.cooper.diseq
|
||||
builtin_initialize registerTraceClass `grind.debug.cutsat.conflict
|
||||
builtin_initialize registerTraceClass `grind.debug.cutsat.assign
|
||||
builtin_initialize registerTraceClass `grind.debug.cutsat.subst
|
||||
builtin_initialize registerTraceClass `grind.debug.cutsat.getBestLower
|
||||
builtin_initialize registerTraceClass `grind.debug.cutsat.nat
|
||||
builtin_initialize registerTraceClass `grind.debug.cutsat.proof
|
||||
builtin_initialize registerTraceClass `grind.debug.cutsat.search
|
||||
builtin_initialize registerTraceClass `grind.debug.cutsat.search.split (inherited := true)
|
||||
builtin_initialize registerTraceClass `grind.debug.cutsat.search.assign (inherited := true)
|
||||
builtin_initialize registerTraceClass `grind.debug.cutsat.search.conflict (inherited := true)
|
||||
builtin_initialize registerTraceClass `grind.debug.cutsat.search.backtrack (inherited := true)
|
||||
builtin_initialize registerTraceClass `grind.debug.cutsat.internalize
|
||||
builtin_initialize registerTraceClass `grind.debug.cutsat.markTerm
|
||||
builtin_initialize registerTraceClass `grind.debug.cutsat.natCast
|
||||
|
||||
end Lean
|
||||
|
||||
@@ -34,7 +34,7 @@ def DvdCnstr.applyEq (a : Int) (x : Var) (c₁ : EqCnstr) (b : Int) (c₂ : DvdC
|
||||
let q := c₂.p
|
||||
let d := Int.ofNat (a * c₂.d).natAbs
|
||||
let p := (q.mul a |>.combine (p.mul (-b)))
|
||||
trace[grind.cutsat.subst] "{← getVar x}, {← c₁.pp}, {← c₂.pp}"
|
||||
trace[grind.debug.cutsat.subst] "{← getVar x}, {← c₁.pp}, {← c₂.pp}"
|
||||
return { d, p, h := .subst x c₁ c₂ }
|
||||
|
||||
partial def DvdCnstr.applySubsts (c : DvdCnstr) : GoalM DvdCnstr := withIncRecDepth do
|
||||
@@ -46,21 +46,20 @@ partial def DvdCnstr.applySubsts (c : DvdCnstr) : GoalM DvdCnstr := withIncRecDe
|
||||
/-- Asserts divisibility constraint. -/
|
||||
partial def DvdCnstr.assert (c : DvdCnstr) : GoalM Unit := withIncRecDepth do
|
||||
if (← inconsistent) then return ()
|
||||
trace[grind.cutsat.dvd] "{← c.pp}"
|
||||
trace[grind.cutsat.assert] "{← c.pp}"
|
||||
let c ← c.norm.applySubsts
|
||||
if c.isUnsat then
|
||||
trace[grind.cutsat.dvd.unsat] "{← c.pp}"
|
||||
trace[grind.cutsat.assert.unsat] "{← c.pp}"
|
||||
setInconsistent (.dvd c)
|
||||
return ()
|
||||
if c.isTrivial then
|
||||
trace[grind.cutsat.dvd.trivial] "{← c.pp}"
|
||||
trace[grind.cutsat.assert.trivial] "{← c.pp}"
|
||||
return ()
|
||||
let d₁ := c.d
|
||||
let .add a₁ x p₁ := c.p | c.throwUnexpected
|
||||
if (← c.satisfied) == .false then
|
||||
resetAssignmentFrom x
|
||||
if let some c' := (← get').dvds[x]! then
|
||||
trace[grind.cutsat.dvd.solve] "{← c.pp}, {← c'.pp}"
|
||||
let d₂ := c'.d
|
||||
let .add a₂ _ p₂ := c'.p | c'.throwUnexpected
|
||||
let (d, α, β) := gcdExt (a₁*d₂) (a₂*d₁)
|
||||
@@ -75,16 +74,14 @@ partial def DvdCnstr.assert (c : DvdCnstr) : GoalM Unit := withIncRecDepth do
|
||||
let α_d₂_p₁ := p₁.mul (α*d₂)
|
||||
let β_d₁_p₂ := p₂.mul (β*d₁)
|
||||
let combine := { d := d₁*d₂, p := .add d x (α_d₂_p₁.combine β_d₁_p₂), h := .solveCombine c c' : DvdCnstr }
|
||||
trace[grind.cutsat.dvd.solve.combine] "{← combine.pp}"
|
||||
modify' fun s => { s with dvds := s.dvds.set x none}
|
||||
combine.assert
|
||||
let a₂_p₁ := p₁.mul a₂
|
||||
let a₁_p₂ := p₂.mul (-a₁)
|
||||
let elim := { d, p := a₂_p₁.combine a₁_p₂, h := .solveElim c c' : DvdCnstr }
|
||||
trace[grind.cutsat.dvd.solve.elim] "{← elim.pp}"
|
||||
elim.assert
|
||||
else
|
||||
trace[grind.cutsat.dvd.update] "{← c.pp}"
|
||||
trace[grind.cutsat.assert.store] "{← c.pp}"
|
||||
c.p.updateOccs
|
||||
modify' fun s => { s with dvds := s.dvds.set x (some c) }
|
||||
|
||||
@@ -97,7 +94,6 @@ def propagateIntDvd (e : Expr) : GoalM Unit := do
|
||||
if (← isEqTrue e) then
|
||||
let p ← toPoly b
|
||||
let c := { d, p, h := .core e : DvdCnstr }
|
||||
trace[grind.cutsat.assert.dvd] "{← c.pp}"
|
||||
c.assert
|
||||
else if (← isEqFalse e) then
|
||||
pushNewFact <| mkApp4 (mkConst ``Int.Linear.of_not_dvd) a b reflBoolTrue (mkOfEqFalseCore e (← mkEqFalseProof e))
|
||||
|
||||
@@ -38,12 +38,12 @@ def DiseqCnstr.applyEq (a : Int) (x : Var) (c₁ : EqCnstr) (b : Int) (c₂ : Di
|
||||
let p := c₁.p
|
||||
let q := c₂.p
|
||||
let p := p.mul b |>.combine (q.mul (-a))
|
||||
trace[grind.cutsat.subst] "{← getVar x}, {← c₁.pp}, {← c₂.pp}"
|
||||
trace[grind.debug.cutsat.subst] "{← getVar x}, {← c₁.pp}, {← c₂.pp}"
|
||||
return { p, h := .subst x c₁ c₂ }
|
||||
|
||||
partial def DiseqCnstr.applySubsts (c : DiseqCnstr) : GoalM DiseqCnstr := withIncRecDepth do
|
||||
let some (x, c₁, p) ← c.p.substVar | return c
|
||||
trace[grind.cutsat.subst] "{← getVar x}, {← c.pp}, {← c₁.pp}"
|
||||
trace[grind.debug.cutsat.subst] "{← getVar x}, {← c.pp}, {← c₁.pp}"
|
||||
applySubsts { p, h := .subst x c₁ c }
|
||||
|
||||
/--
|
||||
@@ -68,10 +68,11 @@ def DiseqCnstr.assert (c : DiseqCnstr) : GoalM Unit := do
|
||||
trace[grind.cutsat.assert] "{← c.pp}"
|
||||
let c ← c.norm.applySubsts
|
||||
if c.p.isUnsatDiseq then
|
||||
trace[grind.cutsat.assert.unsat] "{← c.pp}"
|
||||
setInconsistent (.diseq c)
|
||||
return ()
|
||||
if c.isTrivial then
|
||||
trace[grind.cutsat.diseq.trivial] "{← c.pp}"
|
||||
trace[grind.cutsat.assert.trivial] "{← c.pp}"
|
||||
return ()
|
||||
let k := c.p.gcdCoeffs c.p.getConst
|
||||
let c := if k == 1 then
|
||||
@@ -82,7 +83,7 @@ def DiseqCnstr.assert (c : DiseqCnstr) : GoalM Unit := do
|
||||
return ()
|
||||
let .add _ x _ := c.p | c.throwUnexpected
|
||||
c.p.updateOccs
|
||||
trace[grind.cutsat.diseq] "{← c.pp}"
|
||||
trace[grind.cutsat.assert.store] "{← c.pp}"
|
||||
modify' fun s => { s with diseqs := s.diseqs.modify x (·.push c) }
|
||||
if (← c.satisfied) == .false then
|
||||
resetAssignmentFrom x
|
||||
@@ -108,7 +109,7 @@ where
|
||||
|
||||
partial def EqCnstr.applySubsts (c : EqCnstr) : GoalM EqCnstr := withIncRecDepth do
|
||||
let some (x, c₁, p) ← c.p.substVar | return c
|
||||
trace[grind.cutsat.subst] "{← getVar x}, {← c.pp}, {← c₁.pp}"
|
||||
trace[grind.debug.cutsat.subst] "{← getVar x}, {← c.pp}, {← c₁.pp}"
|
||||
applySubsts { p, h := .subst x c₁ c : EqCnstr }
|
||||
|
||||
private def updateDvdCnstr (a : Int) (x : Var) (c : EqCnstr) (y : Var) : GoalM Unit := do
|
||||
@@ -197,10 +198,11 @@ def EqCnstr.assertImpl (c : EqCnstr) : GoalM Unit := do
|
||||
trace[grind.cutsat.assert] "{← c.pp}"
|
||||
let c ← c.norm.applySubsts
|
||||
if c.p.isUnsatEq then
|
||||
trace[grind.cutsat.assert.unsat] "{← c.pp}"
|
||||
setInconsistent (.eq c)
|
||||
return ()
|
||||
if c.isTrivial then
|
||||
trace[grind.cutsat.eq.trivial] "{← c.pp}"
|
||||
trace[grind.cutsat.assert.trivial] "{← c.pp}"
|
||||
return ()
|
||||
let k := c.p.gcdCoeffs'
|
||||
if c.p.getConst % k > 0 then
|
||||
@@ -210,9 +212,9 @@ def EqCnstr.assertImpl (c : EqCnstr) : GoalM Unit := do
|
||||
c
|
||||
else
|
||||
{ p := c.p.div k, h := .divCoeffs c }
|
||||
trace[grind.cutsat.eq] "{← c.pp}"
|
||||
let some (k, x) := c.p.pickVarToElim? | c.throwUnexpected
|
||||
trace[grind.debug.cutsat.subst] ">> {← getVar x}, {← c.pp}"
|
||||
trace[grind.cutsat.assert.store] "{← c.pp}"
|
||||
modify' fun s => { s with
|
||||
elimEqs := s.elimEqs.set x (some c)
|
||||
elimStack := x :: s.elimStack
|
||||
@@ -252,7 +254,6 @@ private def processNewNatEq (a b : Expr) : GoalM Unit := do
|
||||
|
||||
@[export lean_process_cutsat_eq]
|
||||
def processNewEqImpl (a b : Expr) : GoalM Unit := do
|
||||
trace[grind.debug.cutsat.eq] "{a} = {b}"
|
||||
match (← foreignTerm? a), (← foreignTerm? b) with
|
||||
| none, none => processNewIntEq a b
|
||||
| some .nat, some .nat => processNewNatEq a b
|
||||
@@ -271,7 +272,6 @@ private def processNewIntLitEq (a ke : Expr) : GoalM Unit := do
|
||||
|
||||
@[export lean_process_cutsat_eq_lit]
|
||||
def processNewEqLitImpl (a ke : Expr) : GoalM Unit := do
|
||||
trace[grind.debug.cutsat.eq] "{a} = {ke}"
|
||||
match (← foreignTerm? a) with
|
||||
| none => processNewIntLitEq a ke
|
||||
| some .nat => processNewNatEq a ke
|
||||
@@ -294,12 +294,10 @@ private def processNewNatDiseq (a b : Expr) : GoalM Unit := do
|
||||
let rhs' ← toLinearExpr (← rhs.denoteAsIntExpr ctx) gen
|
||||
let p := lhs'.sub rhs' |>.norm
|
||||
let c := { p, h := .coreNat a b lhs rhs lhs' rhs' : DiseqCnstr }
|
||||
trace[grind.debug.cutsat.nat] "{← c.pp}"
|
||||
c.assert
|
||||
|
||||
@[export lean_process_cutsat_diseq]
|
||||
def processNewDiseqImpl (a b : Expr) : GoalM Unit := do
|
||||
trace[grind.debug.cutsat.diseq] "{a} ≠ {b}"
|
||||
match (← foreignTerm? a), (← foreignTermOrLit? b) with
|
||||
| none, none => processNewIntDiseq a b
|
||||
| some .nat, some .nat => processNewNatDiseq a b
|
||||
@@ -342,7 +340,7 @@ private def isForbiddenParent (parent? : Option Expr) (k : SupportedTermKind) :
|
||||
private def internalizeInt (e : Expr) : GoalM Unit := do
|
||||
if (← hasVar e) then return ()
|
||||
let p ← toPoly e
|
||||
trace[grind.cutsat.internalize] "{aquote e}:= {← p.pp}"
|
||||
trace[grind.debug.cutsat.internalize] "{aquote e}:= {← p.pp}"
|
||||
let x ← mkVar e
|
||||
if p == .add 1 x (.num 0) then
|
||||
-- It is pointless to assert `x = x`
|
||||
@@ -390,6 +388,26 @@ private def propagateToNat (e : Expr) : GoalM Unit := do
|
||||
let_expr Int.toNat a := e | return ()
|
||||
pushNewFact <| mkApp (mkConst ``Int.OfNat.ofNat_toNat) a
|
||||
|
||||
private def internalizeNat (e : Expr) : GoalM Unit := do
|
||||
let e' : Int.OfNat.Expr ← Int.OfNat.toOfNatExpr e
|
||||
let gen ← getGeneration e
|
||||
let ctx ← getForeignVars .nat
|
||||
let e'' : Expr ← e'.denoteAsIntExpr ctx
|
||||
-- If `e''` is of the form `NatCast.natCast e`, then it is wasteful to
|
||||
-- assert an equality
|
||||
match_expr e'' with
|
||||
| NatCast.natCast _ _ a => if e == a then return ()
|
||||
| _ => pure ()
|
||||
let e'' : Int.Linear.Expr ← toLinearExpr e'' gen
|
||||
let p := e''.norm
|
||||
let natCast_e ← shareCommon (mkIntNatCast e)
|
||||
internalize natCast_e gen
|
||||
trace[grind.debug.cutsat.internalize] "{aquote natCast_e}:= {← p.pp}"
|
||||
let x ← mkVar natCast_e
|
||||
modify' fun s => { s with foreignDef := s.foreignDef.insert { expr := e } x }
|
||||
let c := { p := .add (-1) x p, h := .defnNat e' x e'' : EqCnstr }
|
||||
c.assert
|
||||
|
||||
/--
|
||||
Internalizes an integer (and `Nat`) expression. Here are the different cases that are handled.
|
||||
|
||||
@@ -410,11 +428,13 @@ def internalize (e : Expr) (parent? : Option Expr) : GoalM Unit := do
|
||||
| .num => pure ()
|
||||
| _ => internalizeInt e
|
||||
else if type.isConstOf ``Nat then
|
||||
if (← hasForeignVar e) then return ()
|
||||
discard <| mkForeignVar e .nat
|
||||
match k with
|
||||
| .sub => propagateNatSub e
|
||||
| .natAbs => propagateNatAbs e
|
||||
| .toNat => propagateToNat e
|
||||
| _ => pure ()
|
||||
| .num => pure ()
|
||||
| _ => internalizeNat e
|
||||
|
||||
end Lean.Meta.Grind.Arith.Cutsat
|
||||
|
||||
@@ -24,7 +24,6 @@ def mkForeignVar (e : Expr) (t : ForeignType) : GoalM Var := do
|
||||
foreignVars := s.foreignVars.insert t (vars.push e)
|
||||
foreignVarMap := s.foreignVarMap.insert { expr := e} (x, t)
|
||||
}
|
||||
trace[grind.debug.cutsat.markTerm] "mkForeignVar: {e}"
|
||||
markAsCutsatTerm e
|
||||
return x
|
||||
|
||||
|
||||
@@ -100,24 +100,24 @@ where
|
||||
@[export lean_grind_cutsat_assert_le]
|
||||
def LeCnstr.assertImpl (c : LeCnstr) : GoalM Unit := do
|
||||
if (← inconsistent) then return ()
|
||||
trace[grind.cutsat.assert] "{← c.pp}"
|
||||
let c ← c.norm.applySubsts
|
||||
if c.isUnsat then
|
||||
trace[grind.cutsat.le.unsat] "{← c.pp}"
|
||||
trace[grind.cutsat.assert.unsat] "{← c.pp}"
|
||||
setInconsistent (.le c)
|
||||
return ()
|
||||
if c.isTrivial then
|
||||
trace[grind.cutsat.le.trivial] "{← c.pp}"
|
||||
trace[grind.cutsat.assert.trivial] "{← c.pp}"
|
||||
return ()
|
||||
let .add a x _ := c.p | c.throwUnexpected
|
||||
if (← findEq c) then
|
||||
return ()
|
||||
let c ← refineWithDiseq c
|
||||
trace[grind.cutsat.assert.store] "{← c.pp}"
|
||||
if a < 0 then
|
||||
trace[grind.cutsat.le.lower] "{← c.pp}"
|
||||
c.p.updateOccs
|
||||
modify' fun s => { s with lowers := s.lowers.modify x (·.push c) }
|
||||
else
|
||||
trace[grind.cutsat.le.upper] "{← c.pp}"
|
||||
c.p.updateOccs
|
||||
modify' fun s => { s with uppers := s.uppers.modify x (·.push c) }
|
||||
if (← c.satisfied) == .false then
|
||||
@@ -145,7 +145,6 @@ def propagateIntLe (e : Expr) (eqTrue : Bool) : GoalM Unit := do
|
||||
pure { p, h := .core e : LeCnstr }
|
||||
else
|
||||
pure { p := p.mul (-1) |>.addConst 1, h := .coreNeg e p : LeCnstr }
|
||||
trace[grind.cutsat.assert.le] "{← c.pp}"
|
||||
c.assert
|
||||
|
||||
def propagateNatLe (e : Expr) (eqTrue : Bool) : GoalM Unit := do
|
||||
@@ -155,7 +154,6 @@ def propagateNatLe (e : Expr) (eqTrue : Bool) : GoalM Unit := do
|
||||
let lhs' ← toLinearExpr (← lhs.denoteAsIntExpr ctx) gen
|
||||
let rhs' ← toLinearExpr (← rhs.denoteAsIntExpr ctx) gen
|
||||
let p := lhs'.sub rhs' |>.norm
|
||||
trace[grind.debug.cutsat.nat] "{← p.pp}"
|
||||
let c ← if eqTrue then
|
||||
pure { p, h := .coreNat e lhs rhs lhs' rhs' : LeCnstr }
|
||||
else
|
||||
|
||||
@@ -122,7 +122,7 @@ def mkModel (goal : Goal) : MetaM (Array (Expr × Rat)) := do
|
||||
r := r.qsort fun (e₁, _) (e₂, _) => e₁.lt e₂
|
||||
if (← isTracingEnabledFor `grind.cutsat.model) then
|
||||
for (x, v) in r do
|
||||
trace[grind.cutsat.model] "{quoteIfNotAtom x} := {v}"
|
||||
trace[grind.cutsat.model] "{quoteIfArithTerm x} := {v}"
|
||||
return r
|
||||
|
||||
end Lean.Meta.Grind.Arith.Cutsat
|
||||
|
||||
@@ -136,9 +136,20 @@ def assertDenoteAsIntNonneg (e : Expr) : GoalM Unit := withIncRecDepth do
|
||||
let lhs' : Int.Linear.Expr := .num 0
|
||||
let rhs' ← toLinearExpr (← rhs.denoteAsIntExpr ctx) gen
|
||||
let p := lhs'.sub rhs' |>.norm
|
||||
trace[grind.debug.cutsat.nat] "{← p.pp}"
|
||||
let c := { p, h := .denoteAsIntNonneg rhs rhs' : LeCnstr }
|
||||
trace[grind.cutsat.assert.le] "{← c.pp}"
|
||||
c.assert
|
||||
|
||||
/--
|
||||
Given `x` whose denotation is `e`, if `e` is of the form `NatCast.natCast a`,
|
||||
asserts that it is nonnegative.
|
||||
-/
|
||||
def assertNatCast (e : Expr) (x : Var) : GoalM Unit := do
|
||||
let_expr NatCast.natCast _ inst a := e | return ()
|
||||
let_expr instNatCastInt := inst | return ()
|
||||
if (← get').foreignDef.contains { expr := a } then return ()
|
||||
let n ← mkForeignVar a .nat
|
||||
let p := .add (-1) x (.num 0)
|
||||
let c := { p, h := .denoteAsIntNonneg (.var n) (.var x) : LeCnstr}
|
||||
c.assert
|
||||
|
||||
end Lean.Meta.Grind.Arith.Cutsat
|
||||
|
||||
@@ -142,6 +142,9 @@ partial def EqCnstr.toExprProof (c' : EqCnstr) : ProofM Expr := caching c' do
|
||||
| .defn e p =>
|
||||
let some x := (← get').varMap.find? { expr := e } | throwError "`grind` internal error, missing cutsat variable{indentExpr e}"
|
||||
return mkApp6 (mkConst ``Int.Linear.eq_def) (← getContext) (toExpr x) (← mkPolyDecl p) (← mkPolyDecl c'.p) reflBoolTrue (← mkEqRefl e)
|
||||
| .defnNat e x e' =>
|
||||
let h := mkApp2 (mkConst ``Int.OfNat.Expr.eq_denoteAsInt) (← getForeignContext .nat) (← mkNatExprDecl e)
|
||||
return mkApp6 (mkConst ``Int.Linear.eq_def') (← getContext) (toExpr x) (← mkExprDecl e') (← mkPolyDecl c'.p) reflBoolTrue h
|
||||
| .norm c =>
|
||||
return mkApp5 (mkConst ``Int.Linear.eq_norm) (← getContext) (← mkPolyDecl c.p) (← mkPolyDecl c'.p) reflBoolTrue (← c.toExprProof)
|
||||
| .divCoeffs c =>
|
||||
@@ -298,7 +301,6 @@ partial def LeCnstr.toExprProof (c' : LeCnstr) : ProofM Expr := caching c' do
|
||||
(← getContext) (← mkPolyDecl p₁) (← mkPolyDecl p₂) (← mkPolyDecl c₃.p) (toExpr c₃.d) (toExpr s.k) (toExpr coeff) (← mkPolyDecl c'.p) (← s.toExprProof) reflBoolTrue
|
||||
|
||||
partial def DiseqCnstr.toExprProof (c' : DiseqCnstr) : ProofM Expr := caching c' do
|
||||
trace[grind.debug.cutsat.proof] "{← c'.pp}"
|
||||
match c'.h with
|
||||
| .core0 a zero =>
|
||||
mkDiseqProof a zero
|
||||
@@ -349,7 +351,6 @@ partial def CooperSplit.toExprProof (s : CooperSplit) : ProofM Expr := caching s
|
||||
-- `pred` is an expressions of the form `cooper_*_split ...` with type `Nat → Prop`
|
||||
let mut k := n
|
||||
let mut result := base -- `OrOver k (cooper_*_splti)
|
||||
trace[grind.debug.cutsat.proof] "orOver_cases {n}"
|
||||
result := mkApp3 (mkConst ``Int.Linear.orOver_cases) (toExpr (n-1)) pred result
|
||||
for (fvarId, c) in hs do
|
||||
let type := mkApp pred (toExpr (k-1))
|
||||
@@ -362,7 +363,6 @@ partial def CooperSplit.toExprProof (s : CooperSplit) : ProofM Expr := caching s
|
||||
return result
|
||||
|
||||
partial def UnsatProof.toExprProofCore (h : UnsatProof) : ProofM Expr := do
|
||||
trace[grind.debug.cutsat.proof] "{← h.pp}"
|
||||
match h with
|
||||
| .le c =>
|
||||
return mkApp4 (mkConst ``Int.Linear.le_unsat) (← getContext) (← mkPolyDecl c.p) reflBoolTrue (← c.toExprProof)
|
||||
@@ -389,7 +389,6 @@ def UnsatProof.toExprProof (h : UnsatProof) : GoalM Expr := do
|
||||
withProofContext do h.toExprProofCore
|
||||
|
||||
def setInconsistent (h : UnsatProof) : GoalM Unit := do
|
||||
trace[grind.debug.cutsat.conflict] "setInconsistent [{← inconsistent}]: {← h.pp}"
|
||||
if (← get').caseSplits then
|
||||
-- Let the search procedure in `SearchM` resolve the conflict.
|
||||
modify' fun s => { s with conflict? := some h }
|
||||
@@ -419,7 +418,7 @@ private def markAsFound (fvarId : FVarId) : CollectDecVarsM Unit := do
|
||||
mutual
|
||||
partial def EqCnstr.collectDecVars (c' : EqCnstr) : CollectDecVarsM Unit := do unless (← alreadyVisited c') do
|
||||
match c'.h with
|
||||
| .core0 .. | .core .. | .coreNat .. | .defn .. => return () -- Equalities coming from the core never contain cutsat decision variables
|
||||
| .core0 .. | .core .. | .coreNat .. | .defn .. | .defnNat .. => return () -- Equalities coming from the core never contain cutsat decision variables
|
||||
| .norm c | .divCoeffs c => c.collectDecVars
|
||||
| .subst _ c₁ c₂ | .ofLeGe c₁ c₂ => c₁.collectDecVars; c₂.collectDecVars
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@ def CooperSplit.assert (cs : CooperSplit) : GoalM Unit := do
|
||||
let p₁' := p.mul b |>.combine (q.mul (-a))
|
||||
let p₁' := p₁'.addConst <| if left then b*k else (-a)*k
|
||||
let c₁' := { p := p₁', h := .cooper cs : LeCnstr }
|
||||
trace[grind.debug.cutsat.cooper] "{← c₁'.pp}"
|
||||
c₁'.assert
|
||||
if (← inconsistent) then return ()
|
||||
let d := if left then a else b
|
||||
@@ -35,7 +34,6 @@ def CooperSplit.assert (cs : CooperSplit) : GoalM Unit := do
|
||||
let p₂' := if left then p else q
|
||||
let p₂' := p₂'.addConst k
|
||||
let c₂' := { d, p := p₂', h := .cooper₁ cs : DvdCnstr }
|
||||
trace[grind.debug.cutsat.cooper] "dvd₁: {← c₂'.pp}"
|
||||
c₂'.assert
|
||||
if (← inconsistent) then return ()
|
||||
let some c₃ := c₃? | return ()
|
||||
@@ -51,7 +49,6 @@ def CooperSplit.assert (cs : CooperSplit) : GoalM Unit := do
|
||||
let p₃' := q.mul (-c) |>.combine (s.mul b)
|
||||
let p₃' := p₃'.addConst (-c*k)
|
||||
{ d := b*d, p := p₃', h := .cooper₂ cs : DvdCnstr }
|
||||
trace[grind.debug.cutsat.cooper] "dvd₂: {← c₃'.pp}"
|
||||
c₃'.assert
|
||||
|
||||
private def checkIsNextVar (x : Var) : GoalM Unit := do
|
||||
@@ -59,7 +56,7 @@ private def checkIsNextVar (x : Var) : GoalM Unit := do
|
||||
throwError "`grind` internal error, assigning variable out of order"
|
||||
|
||||
private def traceAssignment (x : Var) (v : Rat) : GoalM Unit := do
|
||||
trace[grind.cutsat.assign] "{quoteIfNotAtom (← getVar x)} := {v}"
|
||||
trace[grind.debug.cutsat.search.assign] "{quoteIfArithTerm (← getVar x)} := {v}"
|
||||
|
||||
private def setAssignment (x : Var) (v : Rat) : GoalM Unit := do
|
||||
checkIsNextVar x
|
||||
@@ -88,7 +85,6 @@ where
|
||||
modify' fun s => { s with assignment := s.assignment.set x 0 }
|
||||
let some v ← c.p.eval? | c.throwUnexpected
|
||||
let v := (-v) / a
|
||||
trace[grind.debug.cutsat.assign] "{← getVar x}, {← c.pp}, {v}"
|
||||
traceAssignment x v
|
||||
modify' fun s => { s with assignment := s.assignment.set x v }
|
||||
go xs
|
||||
@@ -108,7 +104,6 @@ def tightUsingDvd (c : LeCnstr) (dvd? : Option DvdCnstr) : GoalM LeCnstr := do
|
||||
let b₂ := c.p.getConst
|
||||
if (b₂ - b₁) % d != 0 then
|
||||
let b₂' := b₁ - d * ((b₁ - b₂) / d)
|
||||
trace[grind.debug.cutsat.dvd.le] "[pos] {← c.pp}, {← dvd.pp}, {b₂'}"
|
||||
let p := c.p.addConst (b₂'-b₂)
|
||||
return { p, h := .dvdTight dvd c }
|
||||
if eqCoeffs dvd.p c.p true then
|
||||
@@ -116,7 +111,6 @@ def tightUsingDvd (c : LeCnstr) (dvd? : Option DvdCnstr) : GoalM LeCnstr := do
|
||||
let b₂ := c.p.getConst
|
||||
if (b₂ - b₁) % d != 0 then
|
||||
let b₂' := b₁ - d * ((b₁ - b₂) / d)
|
||||
trace[grind.debug.cutsat.dvd.le] "[neg] {← c.pp}, {← dvd.pp}, {b₂'}"
|
||||
let p := c.p.addConst (b₂'-b₂)
|
||||
return { p, h := .negDvdTight dvd c }
|
||||
return c
|
||||
@@ -134,7 +128,6 @@ def getBestLower? (x : Var) (dvd? : Option DvdCnstr) : GoalM (Option (Rat × LeC
|
||||
let .add k _ p := c.p | c.throwUnexpected
|
||||
let some v ← p.eval? | c.throwUnexpected
|
||||
let lower' := v / (-k)
|
||||
trace[grind.debug.cutsat.getBestLower] "k: {k}, x: {x}, p: {repr p}, v: {v}, best?: {best?.map (·.1)}, c: {← c.pp}"
|
||||
if let some (lower, _) := best? then
|
||||
if lower' > lower then
|
||||
best? := some (lower', c)
|
||||
@@ -214,7 +207,7 @@ def DvdCnstr.getSolutions? (c : DvdCnstr) : SearchM (Option DvdSolution) := do
|
||||
return some { d, b := -b*a' }
|
||||
|
||||
def resolveDvdConflict (c : DvdCnstr) : GoalM Unit := do
|
||||
trace[grind.cutsat.conflict] "{← c.pp}"
|
||||
trace[grind.debug.cutsat.search.conflict] "{← c.pp}"
|
||||
let d := c.d
|
||||
let .add a _ p := c.p | c.throwUnexpected
|
||||
{ d := a.gcd d, p, h := .elim c : DvdCnstr }.assert
|
||||
@@ -300,7 +293,7 @@ partial def findRatVal (lower upper : Rat) (diseqVals : Array (Rat × DiseqCnstr
|
||||
v
|
||||
|
||||
def resolveRealLowerUpperConflict (c₁ c₂ : LeCnstr) : GoalM Bool := do
|
||||
trace[grind.cutsat.conflict] "{← c₁.pp}, {← c₂.pp}"
|
||||
trace[grind.debug.cutsat.search.conflict] "{← c₁.pp}, {← c₂.pp}"
|
||||
let .add a₁ _ p₁ := c₁.p | c₁.throwUnexpected
|
||||
let .add a₂ _ p₂ := c₂.p | c₂.throwUnexpected
|
||||
let p := p₁.mul a₂.natAbs |>.combine (p₂.mul a₁.natAbs)
|
||||
@@ -313,7 +306,7 @@ def resolveRealLowerUpperConflict (c₁ c₂ : LeCnstr) : GoalM Bool := do
|
||||
{ p, h := .combine c₁ c₂ : LeCnstr }
|
||||
else
|
||||
{ p := p.div k, h := .combineDivCoeffs c₁ c₂ k : LeCnstr }
|
||||
trace[grind.cutsat.conflict] "resolved: {← c.pp}"
|
||||
trace[grind.debug.cutsat.search.conflict] "resolved: {← c.pp}"
|
||||
c.assert
|
||||
return true
|
||||
|
||||
@@ -330,7 +323,7 @@ def resolveCooperUnary (pred : CooperSplitPred) : SearchM Bool := do
|
||||
return true
|
||||
|
||||
def resolveCooperPred (pred : CooperSplitPred) : SearchM Unit := do
|
||||
trace[grind.cutsat.conflict] "[{pred.numCases}]: {← pred.pp}"
|
||||
trace[grind.debug.cutsat.search.conflict] "[{pred.numCases}]: {← pred.pp}"
|
||||
if (← resolveCooperUnary pred) then
|
||||
return
|
||||
let n := pred.numCases
|
||||
@@ -347,11 +340,11 @@ def resolveCooperDvd (c₁ c₂ : LeCnstr) (c₃ : DvdCnstr) : SearchM Unit := d
|
||||
|
||||
def DiseqCnstr.split (c : DiseqCnstr) : SearchM LeCnstr := do
|
||||
let fvarId ← if let some fvarId := (← get').diseqSplits.find? c.p then
|
||||
trace[grind.debug.cutsat.diseq.split] "{← c.pp}, reusing {fvarId.name}"
|
||||
trace[grind.debug.cutsat.search.split] "{← c.pp}, reusing {fvarId.name}"
|
||||
pure fvarId
|
||||
else
|
||||
let fvarId ← mkCase (.diseq c)
|
||||
trace[grind.debug.cutsat.diseq.split] "{← c.pp}, {fvarId.name}"
|
||||
trace[grind.debug.cutsat.search.split] "{← c.pp}, {fvarId.name}"
|
||||
modify' fun s => { s with diseqSplits := s.diseqSplits.insert c.p fvarId }
|
||||
pure fvarId
|
||||
let p₂ := c.p.addConst 1
|
||||
@@ -419,16 +412,15 @@ def processVar (x : Var) : SearchM Unit := do
|
||||
| some (lower, _), none =>
|
||||
let lower := lower.ceil
|
||||
let v := dvdSol.geAvoiding lower diseqVals
|
||||
trace[grind.debug.cutsat.search] "{lower} ≤ {quoteIfNotAtom (← getVar x)} := {v}"
|
||||
trace[grind.debug.cutsat.search] "{lower} ≤ {quoteIfArithTerm (← getVar x)} := {v}"
|
||||
setAssignment x v
|
||||
| none, some (upper, _) =>
|
||||
let upper := upper.floor
|
||||
let v := dvdSol.leAvoiding upper diseqVals
|
||||
trace[grind.debug.cutsat.search] "{quoteIfNotAtom (← getVar x)} := {v} ≤ {upper}"
|
||||
trace[grind.debug.cutsat.search] "{quoteIfArithTerm (← getVar x)} := {v} ≤ {upper}"
|
||||
setAssignment x v
|
||||
| some (lower, c₁), some (upper, c₂) =>
|
||||
trace[grind.debug.cutsat.search] "{lower} ≤ {lower.ceil} ≤ {quoteIfNotAtom (← getVar x)} ≤ {upper.floor} ≤ {upper}"
|
||||
trace[grind.debug.cutsat.getBestLower] "lower: {lower}, c₁: {← c₁.pp}"
|
||||
trace[grind.debug.cutsat.search] "{lower} ≤ {lower.ceil} ≤ {quoteIfArithTerm (← getVar x)} ≤ {upper.floor} ≤ {upper}"
|
||||
if lower > upper then
|
||||
let .true ← resolveRealLowerUpperConflict c₁ c₂
|
||||
| throwError "`grind` internal error, conflict resolution failed"
|
||||
@@ -472,43 +464,42 @@ private def findCase (decVars : FVarIdSet) : SearchM Case := do
|
||||
if decVars.contains case.fvarId then
|
||||
return case
|
||||
-- Conflict does not depend on this case.
|
||||
trace[grind.debug.cutsat.backtrack] "skipping {case.fvarId.name}"
|
||||
trace[grind.debug.cutsat.search.backtrack] "skipping {case.fvarId.name}"
|
||||
unreachable!
|
||||
|
||||
private def union (vs₁ vs₂ : FVarIdSet) : FVarIdSet :=
|
||||
vs₁.fold (init := vs₂) (·.insert ·)
|
||||
|
||||
def resolveConflict (h : UnsatProof) : SearchM Unit := do
|
||||
trace[grind.debug.cutsat.backtrack] "resolve conflict, decision stack: {(← get).cases.toList.map fun c => c.fvarId.name}"
|
||||
trace[grind.debug.cutsat.search.backtrack] "resolve conflict, decision stack: {(← get).cases.toList.map fun c => c.fvarId.name}"
|
||||
let decVars := h.collectDecVars.run (← get).decVars
|
||||
trace[grind.debug.cutsat.backtrack] "dec vars: {decVars.toList.map (·.name)}"
|
||||
trace[grind.debug.cutsat.search.backtrack] "dec vars: {decVars.toList.map (·.name)}"
|
||||
if decVars.isEmpty then
|
||||
trace[grind.debug.cutsat.backtrack] "close goal: {← h.pp}"
|
||||
trace[grind.debug.cutsat.search.backtrack] "close goal: {← h.pp}"
|
||||
closeGoal (← h.toExprProof)
|
||||
return ()
|
||||
let c ← findCase decVars
|
||||
modify' fun _ => c.saved
|
||||
trace[grind.debug.cutsat.backtrack] "backtracking {c.fvarId.name}"
|
||||
trace[grind.debug.cutsat.search.backtrack] "backtracking {c.fvarId.name}"
|
||||
let decVars := decVars.erase c.fvarId
|
||||
match c.kind with
|
||||
| .diseq c₁ =>
|
||||
let decVars := decVars.toArray
|
||||
let p' := c₁.p.mul (-1) |>.addConst 1
|
||||
let c' := { p := p', h := .ofDiseqSplit c₁ c.fvarId h decVars : LeCnstr }
|
||||
trace[grind.debug.cutsat.backtrack] "resolved diseq split: {← c'.pp}"
|
||||
trace[grind.debug.cutsat.search.backtrack] "resolved diseq split: {← c'.pp}"
|
||||
c'.assert
|
||||
| .cooper pred hs decVars' =>
|
||||
let decVars' := union decVars decVars'
|
||||
let n := pred.numCases
|
||||
let hs := hs.push (c.fvarId, h)
|
||||
trace[grind.debug.cutsat.backtrack] "cooper #{hs.size + 1}, {← pred.pp}, {hs.map fun p => p.1.name}"
|
||||
trace[grind.debug.cutsat.search.backtrack] "cooper #{hs.size + 1}, {← pred.pp}, {hs.map fun p => p.1.name}"
|
||||
let s ← if hs.size + 1 < n then
|
||||
let fvarId ← mkCase (.cooper pred hs decVars')
|
||||
pure { pred, k := n - hs.size - 1, h := .dec fvarId : CooperSplit }
|
||||
else
|
||||
let decVars' := decVars'.toArray
|
||||
trace[grind.debug.cutsat.backtrack] "cooper last case, {← pred.pp}, dec vars: {decVars'.map (·.name)}"
|
||||
trace[grind.debug.cutsat.proof] "CooperSplit.last"
|
||||
trace[grind.debug.cutsat.search.backtrack] "cooper last case, {← pred.pp}, dec vars: {decVars'.map (·.name)}"
|
||||
pure { pred, k := 0, h := .last hs decVars' : CooperSplit }
|
||||
s.assert
|
||||
|
||||
|
||||
@@ -74,7 +74,6 @@ def mkCase (kind : CaseKind) : SearchM FVarId := do
|
||||
decVars := s.decVars.insert fvarId
|
||||
}
|
||||
modify' fun s => { s with caseSplits := true }
|
||||
trace[grind.debug.cutsat.backtrack] "mkCase fvarId: {fvarId.name}"
|
||||
return fvarId
|
||||
|
||||
end Lean.Meta.Grind.Arith.Cutsat
|
||||
|
||||
@@ -85,12 +85,13 @@ inductive EqCnstrProof where
|
||||
-/
|
||||
core (a b : Expr) (p₁ p₂ : Poly)
|
||||
| coreNat (a b : Expr) (lhs rhs : Int.OfNat.Expr) (lhs' rhs' : Int.Linear.Expr)
|
||||
| /-- `e` is `p` -/
|
||||
defn (e : Expr) (p : Poly)
|
||||
| defnNat (e : Int.OfNat.Expr) (x : Var) (e' : Int.Linear.Expr)
|
||||
| norm (c : EqCnstr)
|
||||
| divCoeffs (c : EqCnstr)
|
||||
| subst (x : Var) (c₁ : EqCnstr) (c₂ : EqCnstr)
|
||||
| ofLeGe (c₁ : LeCnstr) (c₂ : LeCnstr)
|
||||
| /-- `e` is `p` -/
|
||||
defn (e : Expr) (p : Poly)
|
||||
|
||||
/-- A divisibility constraint and its justification/proof. -/
|
||||
structure DvdCnstr where
|
||||
@@ -237,6 +238,12 @@ structure State where
|
||||
foreignVarMap : PHashMap ENodeKey (Var × ForeignType) := {}
|
||||
foreignVars : PHashMap ForeignType (PArray Expr) := {}
|
||||
/--
|
||||
Some foreign variables encode nested terms such as `b+1`.
|
||||
This is a mapping from this kind of variable to the integer variable
|
||||
representing `natCast (b+1)`.
|
||||
-/
|
||||
foreignDef : 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. -/
|
||||
dvds : PArray (Option DvdCnstr) := {}
|
||||
|
||||
@@ -87,15 +87,15 @@ def resetAssignmentFrom (x : Var) : GoalM Unit := do
|
||||
def _root_.Int.Linear.Poly.pp (p : Poly) : GoalM MessageData := do
|
||||
match p with
|
||||
| .num k => return m!"{k}"
|
||||
| .add 1 x p => go (quoteIfNotAtom (← getVar x)) p
|
||||
| .add k x p => go m!"{k}*{quoteIfNotAtom (← getVar x)}" p
|
||||
| .add 1 x p => go (quoteIfArithTerm (← getVar x)) p
|
||||
| .add k x p => go m!"{k}*{quoteIfArithTerm (← getVar x)}" p
|
||||
where
|
||||
go (r : MessageData) (p : Int.Linear.Poly) : GoalM MessageData := do
|
||||
match p with
|
||||
| .num 0 => return r
|
||||
| .num k => return m!"{r} + {k}"
|
||||
| .add 1 x p => go m!"{r} + {quoteIfNotAtom (← getVar x)}" p
|
||||
| .add k x p => go m!"{r} + {k}*{quoteIfNotAtom (← getVar x)}" p
|
||||
| .add 1 x p => go m!"{r} + {quoteIfArithTerm (← getVar x)}" p
|
||||
| .add k x p => go m!"{r} + {k}*{quoteIfArithTerm (← getVar x)}" p
|
||||
|
||||
def _root_.Int.Linear.Poly.denoteExpr' (p : Poly) : GoalM Expr := do
|
||||
let vars ← getVars
|
||||
|
||||
@@ -11,23 +11,12 @@ import Lean.Meta.Tactic.Grind.Arith.Cutsat.Nat
|
||||
|
||||
namespace Lean.Meta.Grind.Arith.Cutsat
|
||||
|
||||
private def assertNatCast (e : Expr) : GoalM Unit := do
|
||||
let_expr NatCast.natCast _ inst a := e | return ()
|
||||
let_expr instNatCastInt := inst | return ()
|
||||
trace[grind.debug.cutsat.natCast] "{a}"
|
||||
pushNewFact <| mkApp (mkConst ``Int.Linear.natCast_nonneg) a
|
||||
discard <| mkForeignVar a .nat
|
||||
|
||||
private def assertHelpers (e : Expr) : GoalM Unit := do
|
||||
assertNatCast e
|
||||
assertDenoteAsIntNonneg e
|
||||
|
||||
@[export lean_grind_cutsat_mk_var]
|
||||
def mkVarImpl (expr : Expr) : GoalM Var := do
|
||||
if let some var := (← get').varMap.find? { expr } then
|
||||
return var
|
||||
let var : Var := (← get').vars.size
|
||||
trace[grind.cutsat.internalize.term] "{expr} ↦ #{var}"
|
||||
trace[grind.debug.cutsat.internalize] "{expr} ↦ #{var}"
|
||||
modify' fun s => { s with
|
||||
vars := s.vars.push expr
|
||||
varMap := s.varMap.insert { expr } var
|
||||
@@ -38,9 +27,9 @@ def mkVarImpl (expr : Expr) : GoalM Var := do
|
||||
occurs := s.occurs.push {}
|
||||
elimEqs := s.elimEqs.push none
|
||||
}
|
||||
trace[grind.debug.cutsat.markTerm] "mkVar: {expr}"
|
||||
markAsCutsatTerm expr
|
||||
assertHelpers expr
|
||||
assertNatCast expr var
|
||||
assertDenoteAsIntNonneg expr
|
||||
return var
|
||||
|
||||
def isInt (e : Expr) : GoalM Bool := do
|
||||
|
||||
@@ -76,7 +76,7 @@ def mkModel (goal : Goal) : MetaM (Array (Expr × Nat)) := do
|
||||
r := r.qsort fun (e₁, _) (e₂, _) => e₁.lt e₂
|
||||
if (← isTracingEnabledFor `grind.offset.model) then
|
||||
for (x, v) in r do
|
||||
trace[grind.offset.model] "{quoteIfNotAtom x} := {v}"
|
||||
trace[grind.offset.model] "{quoteIfArithTerm x} := {v}"
|
||||
return r
|
||||
|
||||
end Lean.Meta.Grind.Arith.Offset
|
||||
|
||||
@@ -13,6 +13,10 @@ namespace Lean.Meta.Grind.Arith
|
||||
def isNatType (e : Expr) : Bool :=
|
||||
e.isConstOf ``Nat
|
||||
|
||||
/-- Returns `true` if `e` is of the form `Int` -/
|
||||
def isIntType (e : Expr) : Bool :=
|
||||
e.isConstOf ``Int
|
||||
|
||||
/-- Returns `true` if `e` is of the form `@instHAdd Nat instAddNat` -/
|
||||
def isInstAddNat (e : Expr) : Bool :=
|
||||
let_expr instHAdd a b := e | false
|
||||
@@ -49,5 +53,34 @@ def isNatNum? (e : Expr) : Option Nat := Id.run do
|
||||
let .lit (.natVal k) := k | none
|
||||
some k
|
||||
|
||||
def isSupportedType (e : Expr) : Bool :=
|
||||
isNatType e || isIntType e
|
||||
|
||||
partial def isRelevantPred (e : Expr) : Bool :=
|
||||
match_expr e with
|
||||
| Not p => isRelevantPred p
|
||||
| LE.le α _ _ _ => isSupportedType α
|
||||
| Eq α _ _ => isSupportedType α
|
||||
| Dvd.dvd α _ _ _ => isSupportedType α
|
||||
| _ => false
|
||||
|
||||
def isArithTerm (e : Expr) : Bool :=
|
||||
match_expr e with
|
||||
| HAdd.hAdd _ _ _ _ _ _ => true
|
||||
| HSub.hSub _ _ _ _ _ _ => true
|
||||
| HMul.hMul _ _ _ _ _ _ => true
|
||||
| HDiv.hDiv _ _ _ _ _ _ => true
|
||||
| HMod.hMod _ _ _ _ _ _ => true
|
||||
| HPow.hPow _ _ _ _ _ _ => true
|
||||
| Neg.neg _ _ _ => true
|
||||
| OfNat.ofNat _ _ _ => true
|
||||
| _ => false
|
||||
|
||||
/-- Quote `e` using `「` and `」` if `e` is an arithmetic term that is being treated as a variable. -/
|
||||
def quoteIfArithTerm (e : Expr) : MessageData :=
|
||||
if isArithTerm e then
|
||||
aquote e
|
||||
else
|
||||
e
|
||||
|
||||
end Lean.Meta.Grind.Arith
|
||||
|
||||
@@ -35,6 +35,7 @@ def instantiateExtTheorem (thm : Ext.ExtTheorem) (e : Expr) : GoalM Unit := with
|
||||
if proof'.hasMVar || prop'.hasMVar then
|
||||
reportIssue! "failed to apply extensionality theorem `{thm.declName}` for {indentExpr e}\nresulting terms contain metavariables"
|
||||
return ()
|
||||
trace[grind.ext] "{prop'}"
|
||||
addNewRawFact proof' prop' ((← getGeneration e) + 1)
|
||||
|
||||
end Lean.Meta.Grind
|
||||
|
||||
@@ -46,6 +46,9 @@ where
|
||||
else if (← isEqTrue b) then
|
||||
-- b = True → (a → b) = True
|
||||
pushEqTrue e <| mkApp3 (mkConst ``Grind.imp_eq_of_eq_true_right) a b (← mkEqTrueProof b)
|
||||
else if (← isEqFalse b <&&> isEqTrue e <&&> isProp a) then
|
||||
-- (a → b) = True → b = False → a = False
|
||||
pushEqFalse a <| mkApp4 (mkConst ``Grind.eq_false_of_imp_eq_true) a b (← mkEqTrueProof e) (← mkEqFalseProof b)
|
||||
|
||||
private def isEqTrueHyp? (proof : Expr) : Option FVarId := Id.run do
|
||||
let_expr eq_true _ p := proof | return none
|
||||
@@ -105,5 +108,19 @@ def propagateForallPropDown (e : Expr) : GoalM Unit := do
|
||||
else
|
||||
if b.hasLooseBVars then
|
||||
addLocalEMatchTheorems e
|
||||
else
|
||||
unless (← alreadyInternalized b) do return ()
|
||||
if (← isEqFalse b <&&> isProp a) then
|
||||
-- (a → b) = True → b = False → a = False
|
||||
pushEqFalse a <| mkApp4 (mkConst ``Grind.eq_false_of_imp_eq_true) a b (← mkEqTrueProof e) (← mkEqFalseProof b)
|
||||
|
||||
builtin_grind_propagator propagateExistsDown ↓Exists := fun e => do
|
||||
if (← isEqFalse e) then
|
||||
let_expr f@Exists α p := e | return ()
|
||||
let u := f.constLevels!
|
||||
let notP := mkApp (mkConst ``Not) (mkApp p (.bvar 0) |>.headBeta)
|
||||
let prop := mkForall `x .default α notP
|
||||
let proof := mkApp3 (mkConst ``forall_not_of_not_exists u) α p (mkOfEqFalseCore e (← mkEqFalseProof e))
|
||||
addNewRawFact proof prop (← getGeneration e)
|
||||
|
||||
end Lean.Meta.Grind
|
||||
|
||||
@@ -42,6 +42,7 @@ adds entry `f ↦ e` to `appMap`. Recall that `appMap` is a multi-map.
|
||||
-/
|
||||
private def updateAppMap (e : Expr) : GoalM Unit := do
|
||||
let key := e.toHeadIndex
|
||||
trace_goal[grind.debug.appMap] "{e} => {repr key}"
|
||||
modify fun s => { s with
|
||||
appMap := if let some es := s.appMap.find? key then
|
||||
s.appMap.insert key (e :: es)
|
||||
@@ -93,6 +94,9 @@ private def checkAndAddSplitCandidate (e : Expr) : GoalM Unit := do
|
||||
let .const declName _ := (← whnfD (← inferType e)).getAppFn | return ()
|
||||
if (← get).split.casesTypes.isSplit declName then
|
||||
addSplitCandidate e
|
||||
| .forallE _ d _ _ =>
|
||||
if Arith.isRelevantPred d || (← getConfig).splitImp then
|
||||
addSplitCandidate e
|
||||
| _ => pure ()
|
||||
|
||||
/--
|
||||
@@ -168,13 +172,16 @@ private def activateTheoremPatterns (fName : Name) (generation : Nat) : GoalM Un
|
||||
modify fun s => { s with ematch.thmMap := thmMap }
|
||||
let appMap := (← get).appMap
|
||||
for thm in thms do
|
||||
trace[grind.debug.ematch.activate] "`{fName}` => `{thm.origin.key}`"
|
||||
unless (← get).ematch.thmMap.isErased thm.origin do
|
||||
let symbols := thm.symbols.filter fun sym => !appMap.contains sym
|
||||
let thm := { thm with symbols }
|
||||
match symbols with
|
||||
| [] => activateTheorem thm generation
|
||||
| [] =>
|
||||
trace_goal[grind.debug.ematch.activate] "`{thm.origin.key}`"
|
||||
activateTheorem thm generation
|
||||
| _ =>
|
||||
trace_goal[grind.ematch] "reinsert `{thm.origin.key}`"
|
||||
trace_goal[grind.debug.ematch.activate] "reinsert `{thm.origin.key}`"
|
||||
modify fun s => { s with ematch.thmMap := s.ematch.thmMap.insert thm }
|
||||
|
||||
/--
|
||||
@@ -200,6 +207,65 @@ private def propagateUnitLike (a : Expr) (generation : Nat) : GoalM Unit := do
|
||||
internalize unit generation
|
||||
pushEq a unit <| (← mkEqRefl unit)
|
||||
|
||||
/-- Returns `true` if we can ignore `ext` for functions occurring as arguments of a `declName`-application. -/
|
||||
private def extParentsToIgnore (declName : Name) : Bool :=
|
||||
declName == ``Eq || declName == ``HEq || declName == ``dite || declName == ``ite
|
||||
|| declName == ``Exists || declName == ``Subtype
|
||||
|
||||
/--
|
||||
Given a term `e` that occurs as the argument at position `i` of an `f`-application `parent?`,
|
||||
we consider `e` as a candidate for case-splitting. For every other argument `e'` that also appears
|
||||
at position `i` in an `f`-application and has the same type as `e`, we add the case-split candidate `e = e'`.
|
||||
|
||||
When performing the case split, we consider the following two cases:
|
||||
- `e = e'`, which may introduce a new congruence between the corresponding `f`-applications.
|
||||
- `¬(e = e')`, which may trigger extensionality theorems for the type of `e`.
|
||||
|
||||
This feature enables `grind` to solve examples such as:
|
||||
```lean
|
||||
example (f : (Nat → Nat) → Nat) : a = b → f (fun x => a + x) = f (fun x => b + x) := by
|
||||
grind
|
||||
```
|
||||
-/
|
||||
private def addSplitCandidatesForExt (e : Expr) (generation : Nat) (parent? : Option Expr := none) : GoalM Unit := do
|
||||
let some parent := parent? | return ()
|
||||
unless parent.isApp do return ()
|
||||
let f := parent.getAppFn
|
||||
if let .const declName _ := f then
|
||||
if extParentsToIgnore declName then return ()
|
||||
let type ← inferType e
|
||||
-- Remark: we currently do not perform function extensionality on functions that produce a type that is not a proposition.
|
||||
-- We may add an option to enable that in the future.
|
||||
let u? ← typeFormerTypeLevel type
|
||||
if u? != .none && u? != some .zero then return ()
|
||||
let mut i := parent.getAppNumArgs
|
||||
let mut it := parent
|
||||
repeat
|
||||
if !it.isApp then return ()
|
||||
i := i - 1
|
||||
let arg := it.appArg!
|
||||
if isSameExpr arg e then
|
||||
found f i type
|
||||
it := it.appFn!
|
||||
where
|
||||
found (f : Expr) (i : Nat) (type : Expr) : GoalM Unit := do
|
||||
trace[grind.debug.ext] "{f}, {i}, {e}"
|
||||
let others := (← get).termsAt.find? (f, i) |>.getD []
|
||||
for (e', type') in others do
|
||||
if (← withDefault <| isDefEq type type') then
|
||||
let eq := mkApp3 (mkConst ``Eq [← getLevel type]) type e e'
|
||||
let eq ← shareCommon eq
|
||||
internalize eq generation
|
||||
trace_goal[grind.ext.candidate] "{eq}"
|
||||
addSplitCandidate eq
|
||||
modify fun s => { s with termsAt := s.termsAt.insert (f, i) ((e, type) :: others) }
|
||||
return ()
|
||||
|
||||
/-- Applies `addSplitCandidatesForExt` if `funext` is enabled. -/
|
||||
private def addSplitCandidatesForFunext (e : Expr) (generation : Nat) (parent? : Option Expr := none) : GoalM Unit := do
|
||||
unless (← getConfig).funext do return ()
|
||||
addSplitCandidatesForExt e generation parent?
|
||||
|
||||
@[export lean_grind_internalize]
|
||||
private partial def internalizeImpl (e : Expr) (generation : Nat) (parent? : Option Expr := none) : GoalM Unit := withIncRecDepth do
|
||||
if (← alreadyInternalized e) then
|
||||
@@ -222,7 +288,10 @@ private partial def internalizeImpl (e : Expr) (generation : Nat) (parent? : Opt
|
||||
| .fvar .. =>
|
||||
mkENode' e generation
|
||||
checkAndAddSplitCandidate e
|
||||
| .letE .. | .lam .. =>
|
||||
| .letE .. =>
|
||||
mkENode' e generation
|
||||
| .lam .. =>
|
||||
addSplitCandidatesForFunext e generation parent?
|
||||
mkENode' e generation
|
||||
| .forallE _ d b _ =>
|
||||
mkENode' e generation
|
||||
@@ -233,6 +302,7 @@ private partial def internalizeImpl (e : Expr) (generation : Nat) (parent? : Opt
|
||||
internalizeImpl b generation e
|
||||
registerParent e b
|
||||
propagateUp e
|
||||
checkAndAddSplitCandidate e
|
||||
| .lit .. =>
|
||||
mkENode e generation
|
||||
| .const declName _ =>
|
||||
@@ -256,6 +326,7 @@ private partial def internalizeImpl (e : Expr) (generation : Nat) (parent? : Opt
|
||||
internalizeMatchCond e generation
|
||||
else e.withApp fun f args => do
|
||||
mkENode e generation
|
||||
updateAppMap e
|
||||
checkAndAddSplitCandidate e
|
||||
pushCastHEqs e
|
||||
addMatchEqns f generation
|
||||
@@ -280,7 +351,6 @@ private partial def internalizeImpl (e : Expr) (generation : Nat) (parent? : Opt
|
||||
internalize arg generation e
|
||||
registerParent e arg
|
||||
addCongrTable e
|
||||
updateAppMap e
|
||||
Arith.internalize e parent?
|
||||
propagateUp e
|
||||
propagateBetaForNewApp e
|
||||
|
||||
@@ -60,8 +60,9 @@ def mbtc (ctx : MBTC.Context) : GoalM Bool := do
|
||||
unless others.any (isSameExpr arg ·) do
|
||||
for other in others do
|
||||
if (← ctx.eqAssignment arg other) then
|
||||
let k := mkCandidateKey arg other
|
||||
candidates := candidates.insert k
|
||||
if (← hasSameType arg other) then
|
||||
let k := mkCandidateKey arg other
|
||||
candidates := candidates.insert k
|
||||
map := map.insert (f, i) (arg :: others)
|
||||
else
|
||||
map := map.insert (f, i) [arg]
|
||||
|
||||
@@ -148,9 +148,12 @@ def Result.toMessageData (result : Result) : MetaM MessageData := do
|
||||
let mut msgs ← result.failures.mapM (goalToMessageData · result.config)
|
||||
if result.config.verbose then
|
||||
let mut issues := result.issues
|
||||
-- We did not find the following very useful in practice.
|
||||
/-
|
||||
unless result.skipped.isEmpty do
|
||||
let m := m!"#{result.skipped.length} other goal(s) were not fully processed due to previous failures, threshold: `(failures := {result.config.failures})`"
|
||||
issues := .trace { cls := `issue } m #[] :: issues
|
||||
-/
|
||||
unless issues.isEmpty do
|
||||
msgs := msgs ++ [.trace { cls := `grind } "Issues" issues.reverse.toArray]
|
||||
if let some msg ← mkGlobalDiag result.counters result.simp then
|
||||
|
||||
@@ -126,7 +126,7 @@ private def ppOffset : M Unit := do
|
||||
if model.isEmpty then return ()
|
||||
let mut ms := #[]
|
||||
for (e, val) in model do
|
||||
ms := ms.push <| .trace { cls := `assign } m!"{quoteIfNotAtom e} := {val}" #[]
|
||||
ms := ms.push <| .trace { cls := `assign } m!"{Arith.quoteIfArithTerm e} := {val}" #[]
|
||||
pushMsg <| .trace { cls := `offset } "Assignment satisfying offset constraints" ms
|
||||
|
||||
private def ppCutsat : M Unit := do
|
||||
@@ -138,7 +138,7 @@ private def ppCutsat : M Unit := do
|
||||
if model.isEmpty then return ()
|
||||
let mut ms := #[]
|
||||
for (e, val) in model do
|
||||
ms := ms.push <| .trace { cls := `assign } m!"{quoteIfNotAtom e} := {val}" #[]
|
||||
ms := ms.push <| .trace { cls := `assign } m!"{Arith.quoteIfArithTerm e} := {val}" #[]
|
||||
pushMsg <| .trace { cls := `cutsat } "Assignment satisfying linear constraints" ms
|
||||
|
||||
private def ppThresholds (c : Grind.Config) : M Unit := do
|
||||
|
||||
@@ -23,6 +23,30 @@ def registerNormTheorems (preDeclNames : Array Name) (postDeclNames : Array Name
|
||||
for declName in postDeclNames do
|
||||
addSimpTheorem normExt declName (post := true) (inv := false) .global (eval_prio default)
|
||||
|
||||
-- TODO: should we make this extensible?
|
||||
private def isBoolEqTarget (declName : Name) : Bool :=
|
||||
declName == ``Bool.and ||
|
||||
declName == ``Bool.or ||
|
||||
declName == ``Bool.not ||
|
||||
declName == ``BEq.beq ||
|
||||
declName == ``decide
|
||||
|
||||
builtin_simproc_decl simpBoolEq (@Eq Bool _ _) := fun e => do
|
||||
let_expr f@Eq bool lhs rhs ← e | return .continue
|
||||
let .const rhsName _ := rhs.getAppFn | return .continue
|
||||
if rhsName == ``true || rhsName == ``false then return .continue
|
||||
let .const lhsName _ := lhs.getAppFn | return .continue
|
||||
if lhsName == ``true || lhsName == ``false then
|
||||
-- Just apply comm
|
||||
let e' := mkApp3 f bool rhs lhs
|
||||
return .visit { expr := e', proof? := mkApp2 (mkConst ``Grind.flip_bool_eq) lhs rhs }
|
||||
if isBoolEqTarget lhsName || isBoolEqTarget rhsName then
|
||||
-- Convert into `(lhs = true) = (rhs = true)`
|
||||
let tr := mkConst ``true
|
||||
let e' ← mkEq (mkApp3 f bool lhs tr) (mkApp3 f bool rhs tr)
|
||||
return .visit { expr := e', proof? := mkApp2 (mkConst ``Grind.bool_eq_to_prop) lhs rhs }
|
||||
return .continue
|
||||
|
||||
/-- Returns the array of simprocs used by `grind`. -/
|
||||
protected def getSimprocs : MetaM (Array Simprocs) := do
|
||||
let s ← Simp.getSEvalSimprocs
|
||||
@@ -42,6 +66,7 @@ protected def getSimprocs : MetaM (Array Simprocs) := do
|
||||
let s := s.erase ``List.reduceReplicate
|
||||
let s ← addSimpMatchDiscrsOnly s
|
||||
let s ← addPreMatchCondSimproc s
|
||||
let s ← s.add ``simpBoolEq (post := false)
|
||||
return #[s]
|
||||
|
||||
/-- Returns the simplification context used by `grind`. -/
|
||||
|
||||
@@ -15,7 +15,7 @@ inductive CaseSplitStatus where
|
||||
| resolved
|
||||
| notReady
|
||||
| ready (numCases : Nat) (isRec := false)
|
||||
deriving Inhabited, BEq
|
||||
deriving Inhabited, BEq, Repr
|
||||
|
||||
/-- Given `c`, the condition of an `if-then-else`, check whether we need to case-split on the `if-then-else` or not -/
|
||||
private def checkIteCondStatus (c : Expr) : GoalM CaseSplitStatus := do
|
||||
@@ -76,11 +76,26 @@ private def checkIffStatus (e a b : Expr) : GoalM CaseSplitStatus := do
|
||||
|
||||
/-- Returns `true` is `c` is congruent to a case-split that was already performed. -/
|
||||
private def isCongrToPrevSplit (c : Expr) : GoalM Bool := do
|
||||
unless c.isApp do return false
|
||||
(← get).split.resolved.foldM (init := false) fun flag { expr := c' } => do
|
||||
if flag then
|
||||
return true
|
||||
else
|
||||
return isCongruent (← get).enodes c c'
|
||||
return c'.isApp && isCongruent (← get).enodes c c'
|
||||
|
||||
private def checkForallStatus (e : Expr) : GoalM CaseSplitStatus := do
|
||||
if (← isEqTrue e) then
|
||||
let .forallE _ p q _ := e | return .resolved
|
||||
if (← isEqTrue p <||> isEqFalse p) then
|
||||
return .resolved
|
||||
unless q.hasLooseBVars do
|
||||
if (← isEqTrue q <||> isEqFalse q) then
|
||||
return .resolved
|
||||
return .ready 2
|
||||
else if (← isEqFalse e) then
|
||||
return .resolved
|
||||
else
|
||||
return .notReady
|
||||
|
||||
private def checkCaseSplitStatus (e : Expr) : GoalM CaseSplitStatus := do
|
||||
match_expr e with
|
||||
@@ -97,6 +112,10 @@ private def checkCaseSplitStatus (e : Expr) : GoalM CaseSplitStatus := do
|
||||
if (← isResolvedCaseSplit e) then
|
||||
trace_goal[grind.debug.split] "split resolved: {e}"
|
||||
return .resolved
|
||||
if e.isForall then
|
||||
let s ← checkForallStatus e
|
||||
trace_goal[grind.debug.split] "{e}, status: {repr s}"
|
||||
return s
|
||||
if (← isCongrToPrevSplit e) then
|
||||
return .resolved
|
||||
if let some info := isMatcherAppCore? (← getEnv) e then
|
||||
@@ -137,6 +156,7 @@ where
|
||||
modify fun s => { s with split.num := numSplits, ematch.num := 0 }
|
||||
return c?
|
||||
| c::cs =>
|
||||
trace_goal[grind.debug.split] "checking: {c}"
|
||||
match (← checkCaseSplitStatus c) with
|
||||
| .notReady => go cs c? (c::cs')
|
||||
| .resolved => go cs c? cs'
|
||||
@@ -174,7 +194,9 @@ private def mkCasesMajor (c : Expr) : GoalM Expr := do
|
||||
-- model-based theory combination split
|
||||
return mkGrindEM c
|
||||
| _ =>
|
||||
if (← isEqTrue c) then
|
||||
if let .forallE _ p _ _ := c then
|
||||
return mkGrindEM p
|
||||
else if (← isEqTrue c) then
|
||||
return mkOfEqTrueCore c (← mkEqTrueProof c)
|
||||
else
|
||||
return c
|
||||
|
||||
@@ -529,6 +529,13 @@ structure Goal where
|
||||
arith : Arith.State := {}
|
||||
/-- State of the clean name generator. -/
|
||||
clean : Clean.State := {}
|
||||
/--
|
||||
Mapping from pairs `(f, i)` to a list of `(e, type)`.
|
||||
The meaning is: `e : type` is lambda expression that occurs at argument `i` of an `f`-application.
|
||||
We use this information to add case-splits for triggering extensionality theorems.
|
||||
See `addSplitCandidatesForExt`.
|
||||
-/
|
||||
termsAt : PHashMap (Expr × Nat) (List (Expr × Expr)) := {}
|
||||
deriving Inhabited
|
||||
|
||||
def Goal.admit (goal : Goal) : MetaM Unit :=
|
||||
|
||||
@@ -44,9 +44,10 @@ where `<replacement*>` is a link which will perform the replacement.
|
||||
@[builtin_widget_module] def tryThisWidget : Widget.Module where
|
||||
javascript := "
|
||||
import * as React from 'react';
|
||||
import { EditorContext } from '@leanprover/infoview';
|
||||
import { EditorContext, EnvPosContext } from '@leanprover/infoview';
|
||||
const e = React.createElement;
|
||||
export default function ({ pos, suggestions, range, header, isInline, style }) {
|
||||
export default function ({ suggestions, range, header, isInline, style }) {
|
||||
const pos = React.useContext(EnvPosContext)
|
||||
const editorConnection = React.useContext(EditorContext)
|
||||
const defStyle = style || {
|
||||
className: 'link pointer dim',
|
||||
|
||||
@@ -20,10 +20,10 @@ open TSyntax.Compat
|
||||
/--
|
||||
If `cond` is true, wraps the syntax produced by `d` in a type ascription.
|
||||
-/
|
||||
def withTypeAscription (d : Delab) (cond : Bool := true) : DelabM Term := do
|
||||
def withTypeAscription (d : Delab) (cond : Bool := true) : Delab := do
|
||||
let stx ← d
|
||||
if cond then
|
||||
let stx ← annotateCurPos stx
|
||||
let stx ← annotateTermInfoUnlessAnnotated stx
|
||||
let typeStx ← withType delab
|
||||
`(($stx : $typeStx))
|
||||
else
|
||||
@@ -124,6 +124,18 @@ def delabConst : Delab := do
|
||||
else
|
||||
return stx
|
||||
|
||||
/--
|
||||
If `pp.tagAppFns` is set, and if the current expression is a constant application,
|
||||
then `d` is evaluated with the head constant delaborated with `delabConst` as the ref.
|
||||
-/
|
||||
def withFnRefWhenTagAppFns (d : Delab) : Delab := do
|
||||
if (← getExpr).getAppFn.isConst && (← getPPOption getPPTagAppFns) then
|
||||
-- delabConst in `pp.tagAppFns` mode annotates the term.
|
||||
let head ← withNaryFn delabConst
|
||||
withRef head <| d
|
||||
else
|
||||
d
|
||||
|
||||
def withMDataOptions [Inhabited α] (x : DelabM α) : DelabM α := do
|
||||
match ← getExpr with
|
||||
| Expr.mdata m .. =>
|
||||
@@ -637,7 +649,7 @@ def delabStructureInstance : Delab := do
|
||||
else
|
||||
return (i + 1, args))
|
||||
withTypeAscription (cond := (← withType <| getPPOption getPPStructureInstanceType)) do
|
||||
`(⟨$[$args],*⟩)
|
||||
withFnRefWhenTagAppFns `(⟨$[$args],*⟩)
|
||||
else
|
||||
/-
|
||||
Otherwise, we use structure instance notation.
|
||||
@@ -650,7 +662,7 @@ def delabStructureInstance : Delab := do
|
||||
let (_, fields) ← collectStructFields s.induct levels params #[] {} s
|
||||
let tyStx? : Option Term ← withType do
|
||||
if ← getPPOption getPPStructureInstanceType then delab else pure none
|
||||
`({ $fields,* $[: $tyStx?]? })
|
||||
withFnRefWhenTagAppFns `({ $fields,* $[: $tyStx?]? })
|
||||
|
||||
|
||||
/-- State for `delabAppMatch` and helpers. -/
|
||||
|
||||
@@ -108,7 +108,7 @@ def collectAvailableImportsFromLake : IO (Option AvailableImports) := do
|
||||
|
||||
def collectAvailableImportsFromSrcSearchPath : IO AvailableImports :=
|
||||
(·.2) <$> StateT.run (s := #[]) do
|
||||
let srcSearchPath ← initSrcSearchPath
|
||||
let srcSearchPath ← getSrcSearchPath
|
||||
for p in srcSearchPath do
|
||||
if ! (← p.isDir) then
|
||||
continue
|
||||
|
||||
@@ -348,7 +348,6 @@ structure WorkerState where
|
||||
doc : EditableDocument
|
||||
/-- Token flagged for aborting `doc.reporter` when a new document version comes in. -/
|
||||
reporterCancelTk : CancelToken
|
||||
srcSearchPathTask : ServerTask SearchPath
|
||||
importCachingTask? : Option (ServerTask (Except Error AvailableImportsCache))
|
||||
pendingRequests : PendingRequestMap
|
||||
/-- A map of RPC session IDs. We allow asynchronous elab tasks and request handlers
|
||||
@@ -365,9 +364,12 @@ open Language Lean in
|
||||
Callback from Lean language processor after parsing imports that requests necessary information from
|
||||
Lake for processing imports.
|
||||
-/
|
||||
def setupImports (meta : DocumentMeta) (cmdlineOpts : Options) (chanOut : Std.Channel JsonRpc.Message)
|
||||
(srcSearchPathPromise : Promise SearchPath) (stx : Syntax) :
|
||||
Language.ProcessingT IO (Except Language.Lean.HeaderProcessedSnapshot SetupImportsResult) := do
|
||||
def setupImports
|
||||
(meta : DocumentMeta)
|
||||
(cmdlineOpts : Options)
|
||||
(chanOut : Std.Channel JsonRpc.Message)
|
||||
(stx : Syntax)
|
||||
: Language.ProcessingT IO (Except Language.Lean.HeaderProcessedSnapshot SetupImportsResult) := do
|
||||
let importsAlreadyLoaded ← importsLoadedRef.modifyGet ((·, true))
|
||||
if importsAlreadyLoaded then
|
||||
-- As we never unload imports in the server, we should not run the code below twice in the
|
||||
@@ -405,25 +407,16 @@ def setupImports (meta : DocumentMeta) (cmdlineOpts : Options) (chanOut : Std.Ch
|
||||
}
|
||||
| _ => pure ()
|
||||
|
||||
srcSearchPathPromise.resolve fileSetupResult.srcSearchPath
|
||||
|
||||
let mainModuleName ← if let some path := System.Uri.fileUriToPath? meta.uri then
|
||||
EIO.catchExceptions (h := fun _ => pure Name.anonymous) do
|
||||
if let some mod ← searchModuleNameOfFileName path fileSetupResult.srcSearchPath then
|
||||
pure mod
|
||||
else
|
||||
moduleNameOfFileName path none
|
||||
else
|
||||
pure Name.anonymous
|
||||
|
||||
-- override cmdline options with file options
|
||||
let opts := cmdlineOpts.mergeBy (fun _ _ fileOpt => fileOpt) fileSetupResult.fileOptions
|
||||
|
||||
-- default to async elaboration; see also `Elab.async` docs
|
||||
let opts := Elab.async.setIfNotSet opts true
|
||||
|
||||
let opts := Elab.inServer.set opts true
|
||||
|
||||
return .ok {
|
||||
mainModuleName
|
||||
mainModuleName := meta.mod
|
||||
opts
|
||||
plugins := fileSetupResult.plugins
|
||||
}
|
||||
@@ -438,7 +431,6 @@ section Initialization
|
||||
let stickyDiagnosticsRef ← IO.mkRef ∅
|
||||
let pendingServerRequestsRef ← IO.mkRef ∅
|
||||
let chanOut ← mkLspOutputChannel maxDocVersionRef
|
||||
let srcSearchPathPromise ← IO.Promise.new
|
||||
let timestamp ← IO.monoMsNow
|
||||
let partialHandlersRef ← IO.mkRef <| RBMap.fromArray (cmp := compare) <|
|
||||
(← partialLspRequestHandlerMethods).map fun (method, refreshMethod, _) =>
|
||||
@@ -448,12 +440,12 @@ section Initialization
|
||||
-- Emit a refresh request after a file worker restart.
|
||||
pendingRefreshInfo? := some { lastRefreshTimestamp := timestamp, successiveRefreshAttempts := 0 }
|
||||
})
|
||||
let processor := Language.Lean.process (setupImports meta opts chanOut srcSearchPathPromise)
|
||||
let processor := Language.Lean.process (setupImports meta opts chanOut)
|
||||
let processor ← Language.mkIncrementalProcessor processor
|
||||
let initSnap ← processor meta.mkInputContext
|
||||
let _ ← ServerTask.IO.mapTaskCostly (t := srcSearchPathPromise.result!) fun srcSearchPath => do
|
||||
let _ ← ServerTask.IO.asTask do
|
||||
let importClosure := getImportClosure? initSnap
|
||||
let importClosure ← importClosure.filterMapM (documentUriFromModule srcSearchPath ·)
|
||||
let importClosure ← importClosure.filterMapM (documentUriFromModule? ·)
|
||||
chanOut.send <| mkImportClosureNotification importClosure
|
||||
let ctx := {
|
||||
chanOut
|
||||
@@ -477,7 +469,6 @@ section Initialization
|
||||
return (ctx, {
|
||||
doc := { doc with reporter }
|
||||
reporterCancelTk
|
||||
srcSearchPathTask := srcSearchPathPromise.result!
|
||||
pendingRequests := RBMap.empty
|
||||
rpcSessions := RBMap.empty
|
||||
importCachingTask? := none
|
||||
@@ -579,7 +570,6 @@ section NotificationHandling
|
||||
let newVersion := docId.version?.getD 0
|
||||
let rc : RequestContext := {
|
||||
rpcSessions := st.rpcSessions
|
||||
srcSearchPathTask := st.srcSearchPathTask
|
||||
doc := oldDoc
|
||||
cancelTk
|
||||
hLog := ctx.hLog
|
||||
@@ -589,7 +579,10 @@ section NotificationHandling
|
||||
RequestM.runInIO (handleOnDidChange p) rc
|
||||
if ¬ changes.isEmpty then
|
||||
let newDocText := foldDocumentChanges changes oldDoc.meta.text
|
||||
updateDocument ⟨docId.uri, newVersion, newDocText, oldDoc.meta.dependencyBuildMode⟩
|
||||
updateDocument { oldDoc.meta with
|
||||
version := newVersion
|
||||
text := newDocText
|
||||
}
|
||||
for (_, r) in st.pendingRequests do
|
||||
r.cancelTk.cancelByEdit
|
||||
|
||||
@@ -872,7 +865,6 @@ section MessageHandling
|
||||
-- TODO: move into language-specific request handling
|
||||
let rc : RequestContext := {
|
||||
rpcSessions := st.rpcSessions
|
||||
srcSearchPathTask := st.srcSearchPathTask
|
||||
doc := st.doc
|
||||
cancelTk
|
||||
hLog := ctx.hLog
|
||||
@@ -1017,9 +1009,16 @@ def initAndRunWorker (i o e : FS.Stream) (opts : Options) : IO Unit := do
|
||||
let initParams ← i.readLspRequestAs "initialize" InitializeParams
|
||||
let ⟨_, param⟩ ← i.readLspNotificationAs "textDocument/didOpen" LeanDidOpenTextDocumentParams
|
||||
let doc := param.textDocument
|
||||
-- LSP always refers to characters by (line, column),
|
||||
-- so converting CRLF to LF preserves line and column numbers.
|
||||
let meta : DocumentMeta := ⟨doc.uri, doc.version, doc.text.crlfToLf.toFileMap, param.dependencyBuildMode?.getD .always⟩
|
||||
|
||||
let meta : DocumentMeta := {
|
||||
uri := doc.uri
|
||||
mod := ← moduleFromDocumentUri doc.uri
|
||||
version := doc.version
|
||||
-- LSP always refers to characters by (line, column),
|
||||
-- so converting CRLF to LF preserves line and column numbers.
|
||||
text := doc.text.crlfToLf.toFileMap
|
||||
dependencyBuildMode := param.dependencyBuildMode?.getD .always
|
||||
}
|
||||
let e := e.withPrefix s!"[{param.textDocument.uri}] "
|
||||
let _ ← IO.setStderr e
|
||||
let (ctx, st) ← try
|
||||
|
||||
@@ -9,28 +9,28 @@ import Lean.Server.Requests
|
||||
|
||||
namespace Lean.Elab
|
||||
|
||||
def InlayHintLinkLocation.toLspLocation (srcSearchPath : SearchPath) (text : FileMap)
|
||||
def InlayHintLinkLocation.toLspLocation (text : FileMap)
|
||||
(l : InlayHintLinkLocation) : IO (Option Lsp.Location) := do
|
||||
let some uri ← Server.documentUriFromModule srcSearchPath l.module
|
||||
let some uri ← Server.documentUriFromModule? l.module
|
||||
| return none
|
||||
return some {
|
||||
uri
|
||||
range := text.utf8RangeToLspRange l.range
|
||||
}
|
||||
|
||||
def InlayHintLabelPart.toLspInlayHintLabelPart (srcSearchPath : SearchPath) (text : FileMap)
|
||||
def InlayHintLabelPart.toLspInlayHintLabelPart (text : FileMap)
|
||||
(p : InlayHintLabelPart) : IO Lsp.InlayHintLabelPart := do
|
||||
let location? ← p.location?.bindM fun loc => loc.toLspLocation srcSearchPath text
|
||||
let tooltip? := do return .markdown { kind := .markdown, value := ← p.tooltip? }
|
||||
return {
|
||||
value := p.value
|
||||
location?,
|
||||
tooltip?
|
||||
}
|
||||
let location? ← p.location?.bindM fun loc => loc.toLspLocation text
|
||||
let tooltip? := do return .markdown { kind := .markdown, value := ← p.tooltip? }
|
||||
return {
|
||||
value := p.value
|
||||
location?,
|
||||
tooltip?
|
||||
}
|
||||
|
||||
def InlayHintLabel.toLspInlayHintLabel (srcSearchPath : SearchPath) (text : FileMap) : InlayHintLabel → IO Lsp.InlayHintLabel
|
||||
def InlayHintLabel.toLspInlayHintLabel (text : FileMap) : InlayHintLabel → IO Lsp.InlayHintLabel
|
||||
| .name n => do return .name n
|
||||
| .parts p => do return .parts <| ← p.mapM (·.toLspInlayHintLabelPart srcSearchPath text)
|
||||
| .parts p => do return .parts <| ← p.mapM (·.toLspInlayHintLabelPart text)
|
||||
|
||||
def InlayHintKind.toLspInlayHintKind : InlayHintKind → Lsp.InlayHintKind
|
||||
| .type => .type
|
||||
@@ -41,10 +41,10 @@ def InlayHintTextEdit.toLspTextEdit (text : FileMap) (e : InlayHintTextEdit) : L
|
||||
newText := e.newText
|
||||
}
|
||||
|
||||
def InlayHintInfo.toLspInlayHint (srcSearchPath : SearchPath) (text : FileMap) (i : InlayHintInfo) : IO Lsp.InlayHint := do
|
||||
def InlayHintInfo.toLspInlayHint (text : FileMap) (i : InlayHintInfo) : IO Lsp.InlayHint := do
|
||||
return {
|
||||
position := text.utf8PosToLspPos i.position
|
||||
label := ← i.label.toLspInlayHintLabel srcSearchPath text
|
||||
label := ← i.label.toLspInlayHintLabel text
|
||||
kind? := i.kind?.map (·.toLspInlayHintKind)
|
||||
textEdits? := some <| i.textEdits.map (·.toLspTextEdit text)
|
||||
tooltip? := do return .markdown { kind := .markdown, value := ← i.tooltip? }
|
||||
@@ -116,7 +116,6 @@ def handleInlayHints (p : InlayHintParams) (s : InlayHintState) :
|
||||
let ctx ← read
|
||||
let text := ctx.doc.meta.text
|
||||
let range := text.lspRangeToUtf8Range p.range
|
||||
let srcSearchPath := ctx.srcSearchPath
|
||||
if s.isFirstRequestAfterEdit then
|
||||
-- We immediately respond to the first inlay hint request after an edit with the old inlay hints,
|
||||
-- without waiting for the edit delay.
|
||||
@@ -127,7 +126,7 @@ def handleInlayHints (p : InlayHintParams) (s : InlayHintState) :
|
||||
-- To reduce the size of the window for this race condition, we attempt to minimize the delay
|
||||
-- after an edit, providing VS Code with a set of old inlay hints that we have already updated
|
||||
-- correctly for VS Code ASAP.
|
||||
let lspInlayHints ← s.oldInlayHints.mapM (·.toLspInlayHint srcSearchPath text)
|
||||
let lspInlayHints ← s.oldInlayHints.mapM (·.toLspInlayHint text)
|
||||
let r := { response := lspInlayHints, isComplete := false }
|
||||
let s := { s with isFirstRequestAfterEdit := false }
|
||||
return (r, s)
|
||||
@@ -149,7 +148,7 @@ def handleInlayHints (p : InlayHintParams) (s : InlayHintState) :
|
||||
-- In the latter case, we respond with the old inlay hints, since we can't respond with an error.
|
||||
-- This is to prevent cancellation from making us serve updated inlay hints before the
|
||||
-- edit delay has passed.
|
||||
let lspInlayHints ← s.oldInlayHints.mapM (·.toLspInlayHint srcSearchPath text)
|
||||
let lspInlayHints ← s.oldInlayHints.mapM (·.toLspInlayHint text)
|
||||
let r := { response := lspInlayHints, isComplete := false }
|
||||
return (r, s)
|
||||
let snaps := snaps.toArray
|
||||
@@ -179,7 +178,7 @@ def handleInlayHints (p : InlayHintParams) (s : InlayHintState) :
|
||||
modify (·.push ih.toInlayHintInfo))
|
||||
let allInlayHints := newInlayHints ++ oldInlayHints
|
||||
let inlayHintsInRange := allInlayHints.filter (range.contains (includeStop := true) ·.position)
|
||||
let lspInlayHints ← inlayHintsInRange.mapM (·.toLspInlayHint srcSearchPath text)
|
||||
let lspInlayHints ← inlayHintsInRange.mapM (·.toLspInlayHint text)
|
||||
let r := { response := lspInlayHints, isComplete }
|
||||
let s := { s with
|
||||
oldInlayHints := allInlayHints
|
||||
@@ -204,22 +203,6 @@ where
|
||||
updateOldInlayHints (oldInlayHints : Array Elab.InlayHintInfo) : RequestM (Array Elab.InlayHintInfo) := do
|
||||
let meta := (← read).doc.meta
|
||||
let text := meta.text
|
||||
let srcSearchPath := (← read).srcSearchPath
|
||||
let modName? ← EIO.toBaseIO <| do
|
||||
let some path := System.Uri.fileUriToPath? meta.uri
|
||||
| return none
|
||||
let some mod ← searchModuleNameOfFileName path srcSearchPath
|
||||
| return some <| ← moduleNameOfFileName path none
|
||||
return some mod
|
||||
let modName ← match modName? with
|
||||
| .ok (some modName) => pure modName
|
||||
-- `.anonymous` occurs in untitled files (`.ok none` case).
|
||||
-- There is an intentional bug here where the `.error _` case spits out `.anonymous`.
|
||||
-- This means that we don't correctly update inlay hint locations when the file for this
|
||||
-- file worker has been deleted. As of writing this, there are no inlay hints that use this
|
||||
-- field anyways.
|
||||
-- In the future, we should resolve this by caching the module name in `DocumentMeta`.
|
||||
| _ => pure .anonymous
|
||||
let mut updatedOldInlayHints := #[]
|
||||
for ihi in oldInlayHints do
|
||||
let mut ihi := ihi
|
||||
@@ -228,7 +211,7 @@ where
|
||||
let .rangeChange changeRange newText := c
|
||||
| return #[] -- `fullChange` => all old inlay hints invalidated
|
||||
let changeRange := text.lspRangeToUtf8Range changeRange
|
||||
let some ihi' := applyEditToHint? modName ihi changeRange newText
|
||||
let some ihi' := applyEditToHint? meta.mod ihi changeRange newText
|
||||
| -- Change in some position of inlay hint => inlay hint invalidated
|
||||
inlayHintInvalidated := true
|
||||
break
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user