Compare commits

...

32 Commits

Author SHA1 Message Date
Leonardo de Moura
bdc8c77132 perf: bad filter
`has_fvar(e)` is a bad filter. For example, the free variable may be a
let-variable. The kernel was timining out when checking some proof
terms produced by cutsat. Many thanks to @nomeata for finding the
issue.
2025-03-07 10:14:22 -08:00
Kim Morrison
ca0d822619 chore: protect Int.sub_eq_iff_eq_add (#7359)
Minor problems introduced in #7274.
2025-03-06 05:42:12 +00:00
Kitamado
e2a80875c9 fix: doc in List.removeAll (#7288)
This PR fixes the doc of `List.removeAll`
2025-03-06 05:25:19 +00:00
Leonardo de Moura
061ebe1dca feat: mod and div in cutsat (#7357)
This PR adds support for `/` and `%` to the cutsat procedure.
2025-03-06 04:15:28 +00:00
Leonardo de Moura
7a8c8a4fb3 fix: markNestedProofs (#7355)
This PR fixes a bug in the `markNestedProofs` preprocessor used in the
`grind` tactic.
2025-03-06 00:51:13 +00:00
Leonardo de Moura
3ff10c6cdd test: cutsat cooper resolution (#7354) 2025-03-06 00:40:38 +00:00
Leonardo de Moura
9ae2ac39c9 feat: avoid cooper case analysis for univariate polynomials (#7351)
This PR ensures cutsat does not have to perform case analysis in the
univariate polynomial case. That it, it can close a goal whenever there
is no solution for a divisibility constraint in an interval. Example of
theorem that is now proved in a single step by cutsat:
```lean
example (x : Int) : 100 ≤ x → x ≤ 10000 → 20000 ∣ 3*x → False := by
  grind
```
2025-03-05 20:37:29 +00:00
Wojciech Rozowski
2c8fb9d3fc fix: strip optional parameters when elaborating the termination hints (#7335)
This PR modifies `elabTerminationByHints` in a way that the type of the
recursive function used for elaboration of the termination measure is
striped of from optional parameters. It prevents introducing
dependencies between the default values for arguments, that can cause
the termination checker to fail.

Closes https://github.com/leanprover/lean4/issues/6351.
2025-03-05 18:15:49 +00:00
Henrik Böving
dc7358b4df feat: upgrade cadical to 2.1.2 (#7347)
This PR upgrades the CaDiCal we ship and use for bv_decide to version
2.1.2. Additionally it enables binary LRAT proofs on windows by default
as https://github.com/arminbiere/cadical/issues/112 has been fixed.

Version 2.1.3 is already available but as the Bitwuzla authors [have
pointed out](https://github.com/bitwuzla/bitwuzla/pull/129) one needs to
be careful when upgrading CaDiCal so we just move to a version [they
confirmed](6e93389d86)
is fine for now.
2025-03-05 17:58:58 +00:00
Sebastian Ullrich
44a518b331 fix: never transfer constants from checked environment into elab branches (#7306)
Otherwise we may lose the environment extension state of the constant
2025-03-05 17:12:27 +00:00
Markus Himmel
68f3fc6d5d feat: finite type conversions (Nat/Int/Fin/BitVec -> UIntX -> *) (#7340)
This PR adds lemmas for iterated conversions between finite types which
start with `Nat`/`Int`/`Fin`/`BitVec` and then go through `UIntX`.
2025-03-05 15:35:36 +00:00
Sebastian Ullrich
72c4630aab feat: use realizeConst for all equation and unfold theorems (#7348)
This PR ensures all equation and unfold theorem generators in core are
compatible with parallelism.
2025-03-05 14:56:50 +00:00
Lean stage0 autoupdater
db0abe89cf chore: update stage0 2025-03-05 13:37:40 +00:00
Marc Huisinga
2b44a4f0d9 fix: inlay hint assertion violation when deleting open file (#7346)
This PR fixes an issue where the language server would run into an inlay
hint assertion violation when deleting a file that is still open in the
language server.
2025-03-05 12:40:21 +00:00
Marc Huisinga
72f4098156 feat: combined auto-implicit inlay hint tooltip (#7344)
This PR combines the auto-implicit inlay hint tooltips into a single
tooltip. This works around an issue in VS Code where VS Code fails to
update hovers for tooltips in adjacent inlay hint parts when moving the
mouse.
2025-03-05 12:23:58 +00:00
Marc Huisinga
f0f7c3ff01 fix: inlay hints inserted at wrong position after edit (#7343)
This PR mitigates an issue where inserting an inlay hint in VS Code by
double-clicking would insert the inlay hint at the wrong position right
after an edit.

This bug was originally reported by @plp127 at
https://leanprover.zulipchat.com/#narrow/channel/113488-general/topic/v4.2E18.2E0.20-.20inlay.20hints/near/503362330.

The cause of this bug is that when VS Code hasn't yet received a new set
of inlay hints for a new document state, it will happily move around the
displayed inlay hint, but it won't move around any of the other
position-dependent properties of the inlay hint, like the property
describing where to insert the inlay hint. Since we delay responses
after an edit by an edit delay of 3000ms to prevent inlay hint
flickering while typing, the window for this bug is relatively large.

To work around this bug, we now always immediately respond to the first
inlay hint request after an edit with the old state of the inlay hints,
which we already update correctly on edits on the server-side so that we
can serve old inlay hints for parts of the file that are still
in-progress. Essentially, we are just telling VS Code how it should have
moved all position-dependent properties of each inlay hint.

Even with this mitigation, there is still a small window for this bug to
occur, namely the window from an edit to when VS Code receives the old
inlay hints from the server. In practice, this window should be a couple
of milliseconds at most, so I'd hope it doesn't cause many problems.
There's nothing we can do about this in either vscode-lean4 or the
language server, unfortunately.
2025-03-05 12:23:53 +00:00
Kim Morrison
5536281238 feat: force-mathlib-ci label (#7337)
This PR adds support for a `force-mathlib-ci` label, which attempts full
Mathlib CI even if the PR branch is not based off the
`nightly-with-mathlib` branch, or if the relevant
`nightly-testing-YYYY-MM-DD` branch is not present at Batteries or
Mathlib.
2025-03-05 06:36:38 +00:00
Markus Himmel
8de6233326 feat: IntX conversion lemmas (#7274)
This PR adds lemmas about iterated conversions between finite types,
starting with something of type `IntX`.
2025-03-05 06:27:53 +00:00
Leonardo de Moura
f312170f21 feat: cooper resolution in cutsat (#7339)
This PR implements cooper conflict resolution in the cutsat procedure.
It also fixes several bugs in the proof term construction. We still need
to add more tests, but we can already solve the following example that
`omega` fails to solve:
```lean
example (x y : Int) :
    27 ≤ 11*x + 13*y →
    11*x + 13*y ≤ 45 →
    -10 ≤ 7*x - 9*y →
    7*x - 9*y ≤ 4 → False := by
  grind
```
2025-03-05 03:37:45 +00:00
Kim Morrison
6d1bda6ff2 feat: add @[simp] to Int.neg_inj (#7338)
This PR adds @[simp] to `Int.neg_inj`.
2025-03-05 02:53:41 +00:00
Joachim Breitner
f45c19b428 feat: identify more fixed parameters (#7166)
This PR extends the notion of “fixed parameter” of a recursive function
also to parameters that come after varying function. The main benefit is
that we get nicer induction principles.


Before the definition

```lean
def app (as : List α) (bs : List α) : List α :=
  match as with
  | [] => bs
  | a::as => a :: app as bs
```

produced

```lean
app.induct.{u_1} {α : Type u_1} (motive : List α → List α → Prop) (case1 : ∀ (bs : List α), motive [] bs)
  (case2 : ∀ (bs : List α) (a : α) (as : List α), motive as bs → motive (a :: as) bs) (as bs : List α) : motive as bs
```
and now you get
```lean
app.induct.{u_1} {α : Type u_1} (motive : List α → Prop) (case1 : motive [])
  (case2 : ∀ (a : α) (as : List α), motive as → motive (a :: as)) (as : List α) : motive as
```
because `bs` is fixed throughout the recursion (and can completely be
dropped from the principle).

This is a breaking change when such an induction principle is used
explicitly. Using `fun_induction` makes proof tactics robust against
this change.

The rules for when a parameter is fixed are now:

1. A parameter is fixed if it is reducibly defq to the the corresponding
argument in each recursive call, so we have to look at each such call.
2. With mutual recursion, it is not clear a-priori which arguments of
another function correspond to the parameter. This requires an analysis
with some graph algorithms to determine.
3. A parameter can only be fixed if all parameters occurring in its type
are fixed as well.
This dependency graph on parameters can be different for the different
functions in a recursive group, even leading to cycles.
4. For structural recursion, we kinda want to know the fixed parameters
before investigating which argument to actually recurs on. But once we
have that we may find that we fixed an index of the recursive
parameter’s type, and these cannot be fixed. So we have to un-fix them
5. … and all other fixed parameters that have dependencies on them.

Lean tries to identify the largest set of parameters that satisfies
these criteria.

Note that in a definition like
```lean
def app : List α → List α → List α
  | [], bs => bs
  | a::as, bs => a :: app as bs
```
the `bs` is not considered fixes, as it goes through the matcher
machinery.


Fixes #7027
Fixes #2113
2025-03-04 22:26:20 +00:00
Joachim Breitner
e2ee629022 fix: allow aux decls to be generated by decreasing_by tactics (#7333)
This PR allows aux decls (like generated by `match`) to be generated by
decreasing_by tactics.

Fixes #7332.
2025-03-04 18:42:36 +00:00
Sebastian Ullrich
64731b71aa fix: enable realizations for inductives as late as possible (#7336)
Realizations on them were missing access to e.g. `recOn`
2025-03-04 17:57:51 +00:00
Joachim Breitner
23b5baa5ec feat: WF/Fix.lean: only refine fix’s ih for atomic discriminant onlys (#7324)
This PR changes the internal construction of well-founded recursion, to
not change the type of `fix`’s induction hypothesis in non-defeq ways.

Fixes #7322 and hopefully unblocks #7166.
2025-03-04 13:49:01 +00:00
Sebastian Ullrich
f58e893e63 chore: Mathlib fixes (#7327)
* chore: revert changes to Environment.replay 
* chore: disable realizeConst for now when Elab.async is not set
2025-03-04 13:41:30 +00:00
Rob23oba
a856518265 perf: optimize elaboration of HashMap verification files (#7323)
This PR improves the elaboration time of
`Std.Data.DHashMap.Internal.RawLemmas` and
`Std.Data.DHashMap.RawLemmas`.
2025-03-04 13:30:15 +00:00
Joachim Breitner
45806017e5 feat: allow cond to be used in proofs (#7141)
This PR generalizes `cond` to allow the motive to be in `Sort u`, not
just `Type u`.
2025-03-04 12:10:29 +00:00
Paul Reichert
058e63a3d6 feat: tree map lemmas for foldlM, foldl, foldrM and foldr (#7270)
This PR provides lemmas about the tree map functions `foldlM`, `foldl`,
`foldrM` and `foldr` and their interactions with other functions for
which lemmas already exist. Additionally, it generalizes the
`fold*`/`keys` lemmas to arbitrary tree maps, which were previously
stated only for the `DTreeMap α Unit` case.

A later PR will make the hash map functions `fold` and `revFold`
internal and also update their signature to conform to the tree map and
list API. This is out of scope for this PR.

---------

Co-authored-by: Paul Reichert <6992158+datokrat@users.noreply.github.com>
2025-03-04 11:44:41 +00:00
Kim Morrison
e8e6c4716f chore: copy v4.17.0 release notes from releases/v4.17.0 branch (#7325) 2025-03-04 11:24:51 +00:00
Lean stage0 autoupdater
3ce8c73315 chore: update stage0 2025-03-04 11:40:02 +00:00
Kim Morrison
88edd13642 feat: alignment of Int.ediv/fdiv/tdiv lemmas (#7319)
This PR continues alignment of lemmas about `Int.ediv/fdiv/tdiv`,
including adding notes about "missing" lemmas that do not apply in one
case. Also lemmas about `emod/fmod/tmod`. There's still more to do.
2025-03-04 10:41:01 +00:00
Sebastian Ullrich
c70e614a5b chore: harden use of panics in Lean.Environment (#7321)
* avoid `panic!`s that return `Unit` or some otherwise unused value lest
they get optimized away
* make some fallback values explicit to avoid follow-up errors
* avoid redundant declaration names in panic messages
2025-03-04 10:29:54 +00:00
310 changed files with 6594 additions and 1668 deletions

View File

@@ -155,6 +155,20 @@ jobs:
fi
if [[ -n "$MESSAGE" ]]; then
# Check if force-mathlib-ci label is present
LABELS="$(curl --retry 3 --location --silent \
-H "Authorization: token ${{ secrets.MATHLIB4_COMMENT_BOT }}" \
-H "Accept: application/vnd.github.v3+json" \
"https://api.github.com/repos/leanprover/lean4/issues/${{ steps.workflow-info.outputs.pullRequestNumber }}/labels" \
| jq -r '.[].name')"
if echo "$LABELS" | grep -q "^force-mathlib-ci$"; then
echo "force-mathlib-ci label detected, forcing CI despite issues"
MESSAGE="Forcing Mathlib CI because the \`force-mathlib-ci\` label is present, despite problem: $MESSAGE"
FORCE_CI=true
else
MESSAGE="$MESSAGE You can force Mathlib CI using the \`force-mathlib-ci\` label."
fi
echo "Checking existing messages"
@@ -201,7 +215,12 @@ jobs:
else
echo "The message already exists in the comment body."
fi
echo "mathlib_ready=false" >> "$GITHUB_OUTPUT"
if [[ "$FORCE_CI" == "true" ]]; then
echo "mathlib_ready=true" >> "$GITHUB_OUTPUT"
else
echo "mathlib_ready=false" >> "$GITHUB_OUTPUT"
fi
else
echo "mathlib_ready=true" >> "$GITHUB_OUTPUT"
fi
@@ -252,7 +271,7 @@ jobs:
if git ls-remote --heads --tags --exit-code origin "nightly-testing-${MOST_RECENT_NIGHTLY}" >/dev/null; then
BASE="nightly-testing-${MOST_RECENT_NIGHTLY}"
else
echo "This shouldn't be possible: couldn't find a 'nightly-testing-${MOST_RECENT_NIGHTLY}' tag at Batteries. Falling back to 'nightly-testing'."
echo "Couldn't find a 'nightly-testing-${MOST_RECENT_NIGHTLY}' tag at Batteries. Falling back to 'nightly-testing'."
BASE=nightly-testing
fi
@@ -316,7 +335,7 @@ jobs:
if git ls-remote --heads --tags --exit-code origin "nightly-testing-${MOST_RECENT_NIGHTLY}" >/dev/null; then
BASE="nightly-testing-${MOST_RECENT_NIGHTLY}"
else
echo "This shouldn't be possible: couldn't find a 'nightly-testing-${MOST_RECENT_NIGHTLY}' branch at Mathlib. Falling back to 'nightly-testing'."
echo "Couldn't find a 'nightly-testing-${MOST_RECENT_NIGHTLY}' branch at Mathlib. Falling back to 'nightly-testing'."
BASE=nightly-testing
fi

View File

@@ -47,10 +47,11 @@ if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
string(APPEND CADICAL_CXXFLAGS " -DNUNLOCKED")
endif()
string(APPEND CADICAL_CXXFLAGS " -DNCLOSEFROM")
ExternalProject_add(cadical
PREFIX cadical
GIT_REPOSITORY https://github.com/arminbiere/cadical
GIT_TAG rel-1.9.5
GIT_TAG rel-2.1.2
CONFIGURE_COMMAND ""
# https://github.com/arminbiere/cadical/blob/master/BUILD.md#manual-build
BUILD_COMMAND $(MAKE) -f ${CMAKE_SOURCE_DIR}/src/cadical.mk CMAKE_EXECUTABLE_SUFFIX=${CMAKE_EXECUTABLE_SUFFIX} CXX=${CADICAL_CXX} CXXFLAGS=${CADICAL_CXXFLAGS}

8
flake.lock generated
View File

@@ -36,17 +36,17 @@
},
"nixpkgs-cadical": {
"locked": {
"lastModified": 1722221733,
"narHash": "sha256-sga9SrrPb+pQJxG1ttJfMPheZvDOxApFfwXCFO0H9xw=",
"lastModified": 1740791350,
"narHash": "sha256-igS2Z4tVw5W/x3lCZeeadt0vcU9fxtetZ/RyrqsCRQ0=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "12bf09802d77264e441f48e25459c10c93eada2e",
"rev": "199169a2135e6b864a888e89a2ace345703c025d",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "12bf09802d77264e441f48e25459c10c93eada2e",
"rev": "199169a2135e6b864a888e89a2ace345703c025d",
"type": "github"
}
},

View File

@@ -8,8 +8,8 @@
# old nixpkgs used for portable release with older glibc (2.26)
inputs.nixpkgs-older.url = "github:NixOS/nixpkgs/0b307aa73804bbd7a7172899e59ae0b8c347a62d";
inputs.nixpkgs-older.flake = false;
# for cadical 1.9.5; sync with CMakeLists.txt
inputs.nixpkgs-cadical.url = "github:NixOS/nixpkgs/12bf09802d77264e441f48e25459c10c93eada2e";
# for cadical 2.1.2; sync with CMakeLists.txt by taking commit from https://www.nixhub.io/packages/cadical
inputs.nixpkgs-cadical.url = "github:NixOS/nixpkgs/199169a2135e6b864a888e89a2ace345703c025d";
inputs.flake-utils.url = "github:numtide/flake-utils";
outputs = inputs: inputs.flake-utils.lib.eachDefaultSystem (system:

1110
releases/v4.17.0.md Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -544,6 +544,15 @@ theorem slt_eq_not_carry (x y : BitVec w) :
theorem sle_eq_not_slt (x y : BitVec w) : x.sle y = !y.slt x := by
simp only [BitVec.sle, BitVec.slt, decide_not, decide_eq_decide]; omega
theorem zero_sle_eq_not_msb {w : Nat} {x : BitVec w} : BitVec.sle 0#w x = !x.msb := by
rw [sle_eq_not_slt, BitVec.slt_zero_eq_msb]
theorem zero_sle_iff_msb_eq_false {w : Nat} {x : BitVec w} : BitVec.sle 0#w x x.msb = false := by
simp [zero_sle_eq_not_msb]
theorem toNat_toInt_of_sle {w : Nat} (b : BitVec w) (hb : BitVec.sle 0#w b) : b.toInt.toNat = b.toNat :=
toNat_toInt_of_msb b (zero_sle_iff_msb_eq_false.1 hb)
theorem sle_eq_carry (x y : BitVec w) :
x.sle y = !((x.msb == y.msb).xor (carry w y (~~~x) true)) := by
rw [sle_eq_not_slt, slt_eq_not_carry, beq_comm]

View File

@@ -324,6 +324,9 @@ theorem getMsbD_ofNatLt {n x i : Nat} (h : x < 2^n) :
@[simp, bitvec_to_nat] theorem toNat_ofNat (x w : Nat) : (BitVec.ofNat w x).toNat = x % 2^w := by
simp [BitVec.toNat, BitVec.ofNat, Fin.ofNat']
theorem ofNatLT_eq_ofNat {w : Nat} {n : Nat} (hn) : BitVec.ofNatLT n hn = BitVec.ofNat w n :=
eq_of_toNat_eq (by simp [Nat.mod_eq_of_lt hn])
@[simp] theorem toFin_ofNat (x : Nat) : toFin (BitVec.ofNat w x) = Fin.ofNat' (2^w) x := rfl
-- Remark: we don't use `[simp]` here because simproc` subsumes it for literals.
@@ -529,6 +532,9 @@ theorem toInt_eq_toNat_of_msb {x : BitVec w} (h : x.msb = false) :
x.toInt = x.toNat := by
simp [toInt_eq_msb_cond, h]
theorem toNat_toInt_of_msb {w : Nat} (b : BitVec w) (hb : b.msb = false) : b.toInt.toNat = b.toNat := by
simp [b.toInt_eq_toNat_of_msb hb]
theorem toInt_eq_toNat_bmod (x : BitVec n) : x.toInt = Int.bmod x.toNat (2^n) := by
simp only [toInt_eq_toNat_cond]
split
@@ -651,6 +657,12 @@ theorem slt_zero_iff_msb_cond {x : BitVec w} : x.slt 0#w ↔ x.msb = true := by
simp [BitVec.slt, this]
omega
theorem slt_zero_eq_msb {w : Nat} {x : BitVec w} : x.slt 0#w = x.msb := by
rw [Bool.eq_iff_iff, BitVec.slt_zero_iff_msb_cond]
theorem sle_iff_toInt_le {w : Nat} {b b' : BitVec w} : b.sle b' b.toInt b'.toInt :=
decide_eq_true_iff
/-! ### setWidth, zeroExtend and truncate -/
@[simp]
@@ -2058,7 +2070,7 @@ theorem signExtend_eq_setWidth_of_lt (x : BitVec w) {v : Nat} (hv : v ≤ w):
simp [getElem_signExtend, show i < w by omega]
/-- Sign extending to the same bitwidth is a no op. -/
theorem signExtend_eq (x : BitVec w) : x.signExtend w = x := by
@[simp] theorem signExtend_eq (x : BitVec w) : x.signExtend w = x := by
rw [signExtend_eq_setWidth_of_lt _ (Nat.le_refl _), setWidth_eq]
/-- Sign extending to a larger bitwidth depends on the msb.
@@ -2100,43 +2112,63 @@ theorem toNat_signExtend (x : BitVec w) {v : Nat} :
· have : 2^w 2^v := Nat.pow_le_pow_right Nat.two_pos (by omega)
rw [toNat_signExtend_of_le x (by omega), toNat_setWidth, Nat.mod_eq_of_lt (by omega)]
/-
/--
If the current width `w` is smaller than the extended width `v`,
then the value when interpreted as an integer does not change.
-/
theorem toInt_signExtend_of_lt {x : BitVec w} (hv : w < v):
theorem toInt_signExtend_of_le {x : BitVec w} (h : w v) :
(x.signExtend v).toInt = x.toInt := by
simp only [toInt_eq_msb_cond, toNat_signExtend]
have : (x.signExtend v).msb = x.msb := by
rw [msb_eq_getLsbD_last, getLsbD_eq_getElem (Nat.sub_one_lt_of_lt hv)]
simp [getElem_signExtend, Nat.le_sub_one_of_lt hv]
by_cases hlt : w < v
· rw [toInt_signExtend_of_lt hlt]
· obtain rfl : w = v := by omega
simp
where
toInt_signExtend_of_lt {x : BitVec w} (hv : w < v):
(x.signExtend v).toInt = x.toInt := by
simp only [toInt_eq_msb_cond, toNat_signExtend]
have : (x.signExtend v).msb = x.msb := by
rw [msb_eq_getLsbD_last, getLsbD_eq_getElem (Nat.sub_one_lt_of_lt hv)]
simp [getElem_signExtend, Nat.le_sub_one_of_lt hv]
omega
have H : 2^w 2^v := Nat.pow_le_pow_right (by omega) (by omega)
simp only [this, toNat_setWidth, Int.natCast_add, Int.ofNat_emod, Int.natCast_mul]
by_cases h : x.msb
<;> norm_cast
<;> simp [h, Nat.mod_eq_of_lt (Nat.lt_of_lt_of_le x.isLt H)]
omega
have H : 2^w 2^v := Nat.pow_le_pow_right (by omega) (by omega)
simp only [this, toNat_setWidth, Int.natCast_add, Int.ofNat_emod, Int.natCast_mul]
by_cases h : x.msb
<;> norm_cast
<;> simp [h, Nat.mod_eq_of_lt (Nat.lt_of_lt_of_le x.isLt H)]
omega
/-
/--
If the current width `w` is larger than the extended width `v`,
then the value when interpreted as an integer is truncated,
and we compute a modulo by `2^v`.
-/
theorem toInt_signExtend_of_le {x : BitVec w} (hv : v w) :
theorem toInt_signExtend_eq_toNat_bmod_of_le {x : BitVec w} (hv : v w) :
(x.signExtend v).toInt = Int.bmod x.toNat (2^v) := by
simp [signExtend_eq_setWidth_of_lt _ hv]
/-
/--
Interpreting the sign extension of `(x : BitVec w)` to width `v`
computes `x % 2^v` (where `%` is the balanced mod).
computes `x % 2^v` (where `%` is the balanced mod). See `toInt_signExtend` for a version stated
in terms of `toInt` instead of `toNat`.
-/
theorem toInt_signExtend (x : BitVec w) :
(x.signExtend v).toInt = Int.bmod x.toNat (2^(min v w)) := by
theorem toInt_signExtend_eq_toNat_bmod (x : BitVec w) :
(x.signExtend v).toInt = Int.bmod x.toNat (2 ^ min v w) := by
by_cases hv : v w
· simp [toInt_signExtend_of_le hv, Nat.min_eq_left hv]
· simp [toInt_signExtend_eq_toNat_bmod_of_le hv, Nat.min_eq_left hv]
· simp only [Nat.not_le] at hv
rw [toInt_signExtend_of_lt hv, Nat.min_eq_right (by omega), toInt_eq_toNat_bmod]
rw [toInt_signExtend_of_le (Nat.le_of_lt hv),
Nat.min_eq_right (by omega), toInt_eq_toNat_bmod]
theorem toInt_signExtend (x : BitVec w) :
(x.signExtend v).toInt = x.toInt.bmod (2 ^ min v w) := by
rw [toInt_signExtend_eq_toNat_bmod, BitVec.toInt_eq_toNat_bmod, Int.bmod_bmod_of_dvd]
exact Nat.pow_dvd_pow _ (Nat.min_le_right v w)
theorem toInt_signExtend_eq_toInt_bmod_of_le (x : BitVec w) (h : v w) :
(x.signExtend v).toInt = x.toInt.bmod (2 ^ v) := by
rw [BitVec.toInt_signExtend, Nat.min_eq_left h]
attribute [simp] BitVec.signExtend_eq
/-! ### append -/

View File

@@ -539,8 +539,8 @@ theorem cond_decide {α} (p : Prop) [Decidable p] (t e : α) :
@[simp] theorem cond_eq_false_distrib : (c t f : Bool),
(cond c t f = false) = ite (c = true) (t = false) (f = false) := by decide
protected theorem cond_true {α : Type u} {a b : α} : cond true a b = a := cond_true a b
protected theorem cond_false {α : Type u} {a b : α} : cond false a b = b := cond_false a b
protected theorem cond_true {α : Sort u} {a b : α} : cond true a b = a := cond_true a b
protected theorem cond_false {α : Sort u} {a b : α} : cond false a b = b := cond_false a b
@[simp] theorem cond_true_left : (c f : Bool), cond c true f = ( c || f) := by decide
@[simp] theorem cond_false_left : (c f : Bool), cond c false f = (!c && f) := by decide

View File

@@ -158,6 +158,10 @@ theorem add_mul_ediv_right (a b : Int) {c : Int} (H : c ≠ 0) : (a + b * c) / c
apply congrArg negSucc
rw [Nat.mul_comm, Nat.sub_mul_div]; rwa [Nat.mul_comm]
theorem add_mul_ediv_left (a : Int) {b : Int}
(c : Int) (H : b 0) : (a + b * c) / b = a / b + c :=
Int.mul_comm .. Int.add_mul_ediv_right _ _ H
theorem add_ediv_of_dvd_right {a b c : Int} (H : c b) : (a + b) / c = a / c + b / c :=
if h : c = 0 then by simp [h] else by
let k, hk := H
@@ -196,16 +200,6 @@ theorem emod_lt_of_pos (a : Int) {b : Int} (H : 0 < b) : a % b < b :=
| ofNat _, _, _, rfl => ofNat_lt.2 (Nat.mod_lt _ (Nat.succ_pos _))
| -[_+1], _, _, rfl => Int.sub_lt_self _ (ofNat_lt.2 <| Nat.succ_pos _)
theorem mul_ediv_self_le {x k : Int} (h : k 0) : k * (x / k) x :=
calc k * (x / k)
_ k * (x / k) + x % k := Int.le_add_of_nonneg_right (emod_nonneg x h)
_ = x := ediv_add_emod _ _
theorem lt_mul_ediv_self_add {x k : Int} (h : 0 < k) : x < k * (x / k) + k :=
calc x
_ = k * (x / k) + x % k := (ediv_add_emod _ _).symm
_ < k * (x / k) + k := Int.add_lt_add_left (emod_lt_of_pos x h) _
@[simp] theorem add_mul_emod_self {a b c : Int} : (a + b * c) % c = a % c :=
if cz : c = 0 then by
rw [cz, Int.mul_zero, Int.add_zero]
@@ -313,6 +307,18 @@ theorem emod_pos_of_not_dvd {a b : Int} (h : ¬ a b) : a = 0 0 < b % a :
· simp_all
· exact Or.inr (Int.lt_iff_le_and_ne.mpr emod_nonneg b w, Ne.symm h)
/-! ### `/` and ordering -/
theorem mul_ediv_self_le {x k : Int} (h : k 0) : k * (x / k) x :=
calc k * (x / k)
_ k * (x / k) + x % k := Int.le_add_of_nonneg_right (emod_nonneg x h)
_ = x := ediv_add_emod _ _
theorem lt_mul_ediv_self_add {x k : Int} (h : 0 < k) : x < k * (x / k) + k :=
calc x
_ = k * (x / k) + x % k := (ediv_add_emod _ _).symm
_ < k * (x / k) + k := Int.add_lt_add_left (emod_lt_of_pos x h) _
/-! ### bmod -/
@[simp] theorem bmod_emod : bmod x m % m = x % m := by

View File

@@ -40,6 +40,17 @@ protected theorem dvd_add_left {a b c : Int} (H : a c) : a b + c ↔ a
protected theorem dvd_add_right {a b c : Int} (H : a b) : a b + c a c := by
rw [Int.add_comm, Int.dvd_add_left H]
@[simp] protected theorem dvd_add_mul_self {a b c : Int} : a b + c * a a b := by
rw [Int.dvd_add_left (Int.dvd_mul_left c a)]
@[simp] protected theorem dvd_add_self_mul {a b c : Int} : a b + a * c a b := by
rw [Int.mul_comm, Int.dvd_add_mul_self]
@[simp] protected theorem dvd_mul_self_add {a b c : Int} : a b * a + c a c := by
rw [Int.add_comm, Int.dvd_add_mul_self]
@[simp] protected theorem dvd_self_mul_add {a b c : Int} : a a * b + c a c := by
rw [Int.mul_comm, Int.dvd_mul_self_add]
protected theorem dvd_iff_dvd_of_dvd_sub {a b c : Int} (H : a b - c) : a b a c :=
fun h => Int.sub_sub_self b c Int.dvd_sub h H,
fun h => Int.sub_add_cancel b c Int.dvd_add H h
@@ -390,10 +401,13 @@ theorem tmod_eq_fmod {a b : Int} :
/-! ### `/` ediv -/
theorem ediv_neg' {a b : Int} (Ha : a < 0) (Hb : 0 < b) : a / b < 0 :=
theorem ediv_neg_of_neg_of_pos {a b : Int} (Ha : a < 0) (Hb : 0 < b) : a / b < 0 :=
match a, b, eq_negSucc_of_lt_zero Ha, eq_succ_of_zero_lt Hb with
| _, _, _, rfl, _, rfl => negSucc_lt_zero _
@[deprecated ediv_neg_of_neg_of_pos (since := "2025-03-04")]
abbrev ediv_neg' := @ediv_neg_of_neg_of_pos
theorem negSucc_ediv (m : Nat) {b : Int} (H : 0 < b) : -[m+1] / b = -(ediv m b + 1) :=
match b, eq_succ_of_zero_lt H with
| _, _, rfl => rfl
@@ -423,17 +437,16 @@ theorem ediv_pos_of_neg_of_neg {a b : Int} (ha : a < 0) (hb : b < 0) : 0 < a / b
match a, b, ha, hb with
| .negSucc a, .negSucc b, _, _ => apply ofNat_succ_pos
theorem ediv_nonpos {a b : Int} (Ha : 0 a) (Hb : b 0) : a / b 0 :=
theorem ediv_nonpos_of_nonneg_of_nonpos {a b : Int} (Ha : 0 a) (Hb : b 0) : a / b 0 :=
Int.nonpos_of_neg_nonneg <| Int.ediv_neg .. Int.ediv_nonneg Ha (Int.neg_nonneg_of_nonpos Hb)
@[deprecated ediv_nonpos_of_nonneg_of_nonpos (since := "2025-03-04")]
abbrev ediv_nonpos := @ediv_nonpos_of_nonneg_of_nonpos
theorem ediv_eq_zero_of_lt {a b : Int} (H1 : 0 a) (H2 : a < b) : a / b = 0 :=
match a, b, eq_ofNat_of_zero_le H1, eq_succ_of_zero_lt (Int.lt_of_le_of_lt H1 H2) with
| _, _, _, rfl, _, rfl => congrArg Nat.cast <| Nat.div_eq_of_lt <| ofNat_lt.1 H2
theorem add_mul_ediv_left (a : Int) {b : Int}
(c : Int) (H : b 0) : (a + b * c) / b = a / b + c :=
Int.mul_comm .. Int.add_mul_ediv_right _ _ H
@[simp] theorem mul_ediv_mul_of_pos {a : Int}
(b c : Int) (H : 0 < a) : (a * b) / (a * c) = b / c :=
suffices (m k : Nat) (b : Int), (m.succ * b) / (m.succ * k) = b / k from
@@ -477,6 +490,15 @@ protected theorem eq_ediv_of_mul_eq_left {a b c : Int}
(H1 : b 0) (H2 : a * b = c) : a = c / b :=
(Int.ediv_eq_of_eq_mul_left H1 H2.symm).symm
@[simp] protected theorem ediv_self {a : Int} (H : a 0) : a / a = 1 := by
have := Int.mul_ediv_cancel 1 H; rwa [Int.one_mul] at this
@[simp] protected theorem neg_ediv_self (a : Int) (h : a 0) : (-a) / a = -1 := by
rw [neg_ediv_of_dvd (Int.dvd_refl a), Int.ediv_self h]
-- There are no theorems `neg_ediv : ∀ {a b : Int}, (-a) / b = - (a / b)` or
-- `neg_ediv_neg: ∀ {a b : Int}, (-a) / (-b) = a / b` because these are false.
/-! ### emod -/
theorem mod_def' (m n : Int) : m % n = emod m n := rfl
@@ -607,12 +629,6 @@ theorem dvd_emod_sub_self {x : Int} {m : Nat} : (m : Int) x % m - x := by
@[simp] theorem emod_one (a : Int) : a % 1 = 0 := by
simp [emod_def, Int.one_mul, Int.sub_self]
@[simp] protected theorem ediv_self {a : Int} (H : a 0) : a / a = 1 := by
have := Int.mul_ediv_cancel 1 H; rwa [Int.one_mul] at this
@[simp] protected theorem neg_ediv_self (a : Int) (h : a 0) : (-a) / a = -1 := by
rw [neg_ediv_of_dvd (Int.dvd_refl a), Int.ediv_self h]
@[simp]
theorem emod_sub_cancel (x y : Int): (x - y) % y = x % y := by
by_cases h : y = 0
@@ -746,6 +762,8 @@ theorem ediv_eq_ediv_of_mul_eq_mul {a b c d : Int}
/-! ### tdiv -/
-- `tdiv` analogues of `ediv` lemmas from `Bootstrap.lean`
unseal Nat.div in
@[simp] protected theorem tdiv_neg : a b : Int, a.tdiv (-b) = -(a.tdiv b)
| ofNat m, 0 => show ofNat (m / 0) = -(m / 0) by rw [Nat.div_zero]; rfl
@@ -753,12 +771,12 @@ unseal Nat.div in
| ofNat _, succ _ | -[_+1], 0 | -[_+1], -[_+1] => rfl
/-!
We don't give `tdiv` versions of
* `add_mul_ediv_right : c ≠ 0 → (a + b * c) / c = a / c + b`
* `add_mul_ediv_left : b ≠ 0 → (a + b * c) / b = a / b + c`
* `add_ediv_of_dvd_right : c b → (a + b) / c = a / c + b / c`
* `add_ediv_of_dvd_left : c a → (a + b) / c = a / c + b / c`
because they all involve awkward off-by-one corrections.
There are no lemmas
* `add_mul_tdiv_right : c ≠ 0 → (a + b * c).tdiv c = a.tdiv c + b`
* `add_mul_tdiv_left : b ≠ 0 → (a + b * c).tdiv b = a.tdiv b + c`
* `add_tdiv_of_dvd_right : c b → (a + b).tdiv c = a.tdiv c + b.tdiv c`
* `add_tdiv_of_dvd_left : c a → (a + b).tdiv c = a.tdiv c + b.tdiv c`
because these statements are all incorrect, and require awkward conditional off-by-one corrections.
-/
@[simp] theorem mul_tdiv_cancel (a : Int) {b : Int} (H : b 0) : (a * b).tdiv b = a := by
@@ -767,8 +785,10 @@ because they all involve awkward off-by-one corrections.
@[simp] theorem mul_tdiv_cancel_left (b : Int) (H : a 0) : (a * b).tdiv a = b :=
Int.mul_comm .. Int.mul_tdiv_cancel _ H
-- There's no good analogues of `ediv_nonneg_iff_of_pos`, `ediv_neg'`, or `negSucc_ediv`
-- for `tdiv`.
-- `tdiv` analogues of `ediv` lemmas given above
-- There are no lemmas `tdiv_nonneg_iff_of_pos`, `tdiv_neg_of_neg_of_pos`, or `negSucc_tdiv`
-- corresponding to `ediv_nonneg_iff_of_pos`, `ediv_neg_of_neg_of_pos`, or `negSucc_ediv` as they require awkward corrections.
protected theorem tdiv_nonneg {a b : Int} (Ha : 0 a) (Hb : 0 b) : 0 a.tdiv b :=
match a, b, eq_ofNat_of_zero_le Ha, eq_ofNat_of_zero_le Hb with
@@ -792,9 +812,12 @@ theorem tdiv_nonneg_of_nonpos_of_nonpos {a b : Int} (Ha : a ≤ 0) (Hb : b ≤ 0
exact ediv_pos_of_neg_of_neg h'' h'
omega
protected theorem tdiv_nonpos {a b : Int} (Ha : 0 a) (Hb : b 0) : a.tdiv b 0 :=
protected theorem tdiv_nonpos_of_nonneg_of_nonpos {a b : Int} (Ha : 0 a) (Hb : b 0) : a.tdiv b 0 :=
Int.nonpos_of_neg_nonneg <| Int.tdiv_neg .. Int.tdiv_nonneg Ha (Int.neg_nonneg_of_nonpos Hb)
@[deprecated Int.tdiv_nonpos_of_nonneg_of_nonpos (since := "2025-03-04")]
abbrev tdiv_nonpos := @Int.tdiv_nonpos_of_nonneg_of_nonpos
theorem tdiv_eq_zero_of_lt {a b : Int} (H1 : 0 a) (H2 : a < b) : a.tdiv b = 0 :=
match a, b, eq_ofNat_of_zero_le H1, eq_succ_of_zero_lt (Int.lt_of_le_of_lt H1 H2) with
| _, _, _, rfl, _, rfl => congrArg Nat.cast <| Nat.div_eq_of_lt <| ofNat_lt.1 H2
@@ -840,6 +863,9 @@ protected theorem eq_tdiv_of_mul_eq_left {a b c : Int}
(H1 : b 0) (H2 : a * b = c) : a = c.tdiv b :=
(Int.tdiv_eq_of_eq_mul_left H1 H2.symm).symm
@[simp] protected theorem tdiv_self {a : Int} (H : a 0) : a.tdiv a = 1 := by
have := Int.mul_tdiv_cancel 1 H; rwa [Int.one_mul] at this
unseal Nat.div in
@[simp] protected theorem neg_tdiv : a b : Int, (-a).tdiv b = -(a.tdiv b)
| 0, n => by simp [Int.neg_zero]
@@ -849,34 +875,6 @@ unseal Nat.div in
protected theorem neg_tdiv_neg (a b : Int) : (-a).tdiv (-b) = a.tdiv b := by
simp [Int.tdiv_neg, Int.neg_tdiv, Int.neg_neg]
@[simp] protected theorem tdiv_self {a : Int} (H : a 0) : a.tdiv a = 1 := by
have := Int.mul_tdiv_cancel 1 H; rwa [Int.one_mul] at this
theorem mul_tdiv_cancel_of_tmod_eq_zero {a b : Int} (H : a.tmod b = 0) : b * (a.tdiv b) = a := by
have := tmod_add_tdiv a b; rwa [H, Int.zero_add] at this
theorem tdiv_mul_cancel_of_tmod_eq_zero {a b : Int} (H : a.tmod b = 0) : a.tdiv b * b = a := by
rw [Int.mul_comm, mul_tdiv_cancel_of_tmod_eq_zero H]
theorem dvd_of_tmod_eq_zero {a b : Int} (H : tmod b a = 0) : a b :=
b.tdiv a, (mul_tdiv_cancel_of_tmod_eq_zero H).symm
protected theorem mul_tdiv_assoc (a : Int) : {b c : Int}, c b (a * b).tdiv c = a * (b.tdiv c)
| _, c, d, rfl =>
if cz : c = 0 then by simp [cz, Int.mul_zero] else by
rw [Int.mul_left_comm, Int.mul_tdiv_cancel_left _ cz, Int.mul_tdiv_cancel_left _ cz]
protected theorem mul_tdiv_assoc' (b : Int) {a c : Int} (h : c a) :
(a * b).tdiv c = a.tdiv c * b := by
rw [Int.mul_comm, Int.mul_tdiv_assoc _ h, Int.mul_comm]
theorem tdiv_dvd_tdiv : {a b c : Int}, a b b c b.tdiv a c.tdiv a
| a, _, _, b, rfl, c, rfl => by
by_cases az : a = 0
· simp [az]
· rw [Int.mul_tdiv_cancel_left _ az, Int.mul_assoc, Int.mul_tdiv_cancel_left _ az]
apply Int.dvd_mul_right
@[simp] theorem natAbs_tdiv (a b : Int) : natAbs (a.tdiv b) = (natAbs a).div (natAbs b) :=
match a, b, eq_nat_or_neg a, eq_nat_or_neg b with
| _, _, _, .inl rfl, _, .inl rfl => rfl
@@ -886,13 +884,15 @@ theorem tdiv_dvd_tdiv : ∀ {a b c : Int}, a b → b c → b.tdiv a
/-! ### tmod -/
-- `tmod` analogues of `emod` lemmas from `Bootstrap.lean`
theorem ofNat_tmod (m n : Nat) : ((m % n) : Int) = tmod m n := rfl
@[simp] theorem tmod_one (a : Int) : tmod a 1 = 0 := by
simp [tmod_def, Int.tdiv_one, Int.one_mul, Int.sub_self]
theorem tmod_eq_of_lt {a b : Int} (H1 : 0 a) (H2 : a < b) : tmod a b = a := by
rw [tmod_eq_emod_of_nonneg H1, emod_eq_of_lt H1 H2]
theorem tmod_nonneg : {a : Int} (b : Int), 0 a 0 tmod a b
| ofNat _, -[_+1], _ | ofNat _, ofNat _, _ => ofNat_nonneg _
theorem tmod_lt_of_pos (a : Int) {b : Int} (H : 0 < b) : tmod a b < b :=
match a, b, eq_succ_of_zero_lt H with
@@ -900,12 +900,23 @@ theorem tmod_lt_of_pos (a : Int) {b : Int} (H : 0 < b) : tmod a b < b :=
| -[_+1], _, n, rfl => Int.lt_of_le_of_lt
(Int.neg_nonpos_of_nonneg <| Int.ofNat_nonneg _) (ofNat_pos.2 n.succ_pos)
theorem tmod_nonneg : {a : Int} (b : Int), 0 a 0 tmod a b
| ofNat _, -[_+1], _ | ofNat _, ofNat _, _ => ofNat_nonneg _
@[simp] theorem tmod_neg (a b : Int) : tmod a (-b) = tmod a b := by
rw [tmod_def, tmod_def, Int.tdiv_neg, Int.neg_mul_neg]
@[simp] theorem neg_tmod (a b : Int) : tmod (-a) b = -tmod a b := by
rw [tmod_def, Int.neg_tdiv, Int.mul_neg, tmod_def]
omega
-- The following statements for `tmod` are false:
-- `add_mul_tmod_self {a b c : Int} : (a + b * c).tmod c = a.tmod c`
-- `add_mul_tmod_self_left (a b c : Int) : (a + b * c).tmod b = a.tmod b`
-- `tmod_add_tmod (m n k : Int) : (m.tmod n + k).tmod n = (m + k).tmod n`
-- `add_tmod_tmod (m n k : Int) : (m + n.tmod k).tmod k = (m + n).tmod k`
-- `add_tmod (a b n : Int) : (a + b).tmod n = (a.tmod n + b.tmod n).tmod n`
-- `add_tmod_eq_add_tmod_right {m n k : Int} (i : Int) : (m.tmod n = k.tmod n) → (m + i).tmod n = (k + i).tmod n`
-- `tmod_add_cancel_right {m n k : Int} (i) : (m + i).tmod n = (k + i).tmod n ↔ m.tmod n = k.tmod n`
-- `sub_tmod (a b n : Int) : (a - b).tmod n = (a.tmod n - b.tmod n).tmod n`
@[simp] theorem mul_tmod_left (a b : Int) : (a * b).tmod b = 0 :=
if h : b = 0 then by simp [h, Int.mul_zero] else by
rw [Int.tmod_def, Int.mul_tdiv_cancel _ h, Int.mul_comm, Int.sub_self]
@@ -913,9 +924,78 @@ theorem tmod_nonneg : ∀ {a : Int} (b : Int), 0 ≤ a → 0 ≤ tmod a b
@[simp] theorem mul_tmod_right (a b : Int) : (a * b).tmod a = 0 := by
rw [Int.mul_comm, mul_tmod_left]
/--
If a predicate on the integers is invariant under negation,
then it is sufficient to prove it for the nonnegative integers.
-/
theorem wlog_sign {P : Int Prop} (inv : a, P a P (-a)) (w : n : Nat, P n) (a : Int) : P a := by
cases a with
| ofNat n => exact w n
| negSucc n =>
rw [negSucc_eq, inv, ofNat_succ]
apply w
attribute [local simp] Int.neg_inj
theorem mul_tmod (a b n : Int) : (a * b).tmod n = (a.tmod n * b.tmod n).tmod n := by
induction a using wlog_sign
case inv => simp
induction b using wlog_sign
case inv => simp
induction n using wlog_sign
case inv => simp
simp only [ Int.natCast_mul, ofNat_tmod]
rw [Nat.mul_mod]
@[simp] theorem tmod_self {a : Int} : a.tmod a = 0 := by
have := mul_tmod_left 1 a; rwa [Int.one_mul] at this
@[simp] theorem tmod_tmod_of_dvd (n : Int) {m k : Int}
(h : m k) : (n.tmod k).tmod m = n.tmod m := by
induction n using wlog_sign
case inv => simp
induction k using wlog_sign
case inv => simp [Int.dvd_neg]
induction m using wlog_sign
case inv => simp
simp only [ Int.natCast_mul, ofNat_tmod]
norm_cast at h
rw [Nat.mod_mod_of_dvd _ h]
@[simp] theorem tmod_tmod (a b : Int) : (a.tmod b).tmod b = a.tmod b :=
tmod_tmod_of_dvd a (Int.dvd_refl b)
theorem tmod_eq_zero_of_dvd : {a b : Int}, a b tmod b a = 0
| _, _, _, rfl => mul_tmod_right ..
-- `tmod` analogues of `emod` lemmas from above
theorem tmod_eq_of_lt {a b : Int} (H1 : 0 a) (H2 : a < b) : tmod a b = a := by
rw [tmod_eq_emod_of_nonneg H1, emod_eq_of_lt H1 H2]
-- lemmas about `tmod` without `emod` analogues
theorem tdiv_sign : a b, a.tdiv (sign b) = a * sign b
| _, succ _ => by simp [sign, Int.mul_one]
| _, 0 => by simp [sign, Int.mul_zero]
| _, -[_+1] => by simp [sign, Int.mul_neg, Int.mul_one]
protected theorem sign_eq_tdiv_abs (a : Int) : sign a = a.tdiv (natAbs a) :=
if az : a = 0 then by simp [az] else
(Int.tdiv_eq_of_eq_mul_left (ofNat_ne_zero.2 <| natAbs_ne_zero.2 az)
(sign_mul_natAbs _).symm).symm
/-! properties of `tdiv` and `tmod` -/
theorem mul_tdiv_cancel_of_tmod_eq_zero {a b : Int} (H : a.tmod b = 0) : b * (a.tdiv b) = a := by
have := tmod_add_tdiv a b; rwa [H, Int.zero_add] at this
theorem tdiv_mul_cancel_of_tmod_eq_zero {a b : Int} (H : a.tmod b = 0) : a.tdiv b * b = a := by
rw [Int.mul_comm, mul_tdiv_cancel_of_tmod_eq_zero H]
theorem dvd_of_tmod_eq_zero {a b : Int} (H : tmod b a = 0) : a b :=
b.tdiv a, (mul_tdiv_cancel_of_tmod_eq_zero H).symm
theorem dvd_iff_tmod_eq_zero {a b : Int} : a b tmod b a = 0 :=
tmod_eq_zero_of_dvd, dvd_of_tmod_eq_zero
@@ -936,9 +1016,6 @@ protected theorem mul_tdiv_cancel' {a b : Int} (H : a b) : a * b.tdiv a = b
protected theorem eq_mul_of_tdiv_eq_right {a b c : Int}
(H1 : b a) (H2 : a.tdiv b = c) : a = b * c := by rw [ H2, Int.mul_tdiv_cancel' H1]
@[simp] theorem tmod_self {a : Int} : a.tmod a = 0 := by
have := mul_tmod_left 1 a; rwa [Int.one_mul] at this
@[simp] theorem neg_tmod_self (a : Int) : (-a).tmod a = 0 := by
rw [ dvd_iff_tmod_eq_zero, Int.dvd_neg]
exact Int.dvd_refl a
@@ -959,7 +1036,6 @@ protected theorem eq_mul_of_tdiv_eq_left {a b c : Int}
(H1 : b a) (H2 : a.tdiv b = c) : a = c * b := by
rw [Int.mul_comm, Int.eq_mul_of_tdiv_eq_right H1 H2]
protected theorem eq_zero_of_tdiv_eq_zero {d n : Int} (h : d n) (H : n.tdiv d = 0) : n = 0 := by
rw [ Int.mul_tdiv_cancel' h, H, Int.mul_zero]
@@ -968,33 +1044,50 @@ protected theorem eq_zero_of_tdiv_eq_zero {d n : Int} (h : d n) (H : n.tdiv
refine fun h => ?_, congrArg (tdiv · d)
rw [ Int.mul_tdiv_cancel' hda, Int.mul_tdiv_cancel' hdb, h]
theorem tdiv_sign : a b, a.tdiv (sign b) = a * sign b
| _, succ _ => by simp [sign, Int.mul_one]
| _, 0 => by simp [sign, Int.mul_zero]
| _, -[_+1] => by simp [sign, Int.mul_neg, Int.mul_one]
protected theorem mul_tdiv_assoc (a : Int) : {b c : Int}, c b (a * b).tdiv c = a * (b.tdiv c)
| _, c, d, rfl =>
if cz : c = 0 then by simp [cz, Int.mul_zero] else by
rw [Int.mul_left_comm, Int.mul_tdiv_cancel_left _ cz, Int.mul_tdiv_cancel_left _ cz]
protected theorem sign_eq_tdiv_abs (a : Int) : sign a = a.tdiv (natAbs a) :=
if az : a = 0 then by simp [az] else
(Int.tdiv_eq_of_eq_mul_left (ofNat_ne_zero.2 <| natAbs_ne_zero.2 az)
(sign_mul_natAbs _).symm).symm
protected theorem mul_tdiv_assoc' (b : Int) {a c : Int} (h : c a) :
(a * b).tdiv c = a.tdiv c * b := by
rw [Int.mul_comm, Int.mul_tdiv_assoc _ h, Int.mul_comm]
theorem tdiv_dvd_tdiv : {a b c : Int}, a b b c b.tdiv a c.tdiv a
| a, _, _, b, rfl, c, rfl => by
by_cases az : a = 0
· simp [az]
· rw [Int.mul_tdiv_cancel_left _ az, Int.mul_assoc, Int.mul_tdiv_cancel_left _ az]
apply Int.dvd_mul_right
/-! ### `tdiv` and ordering -/
-- Theorems about `tdiv` and ordering, whose `ediv` analogues are in `Bootstrap.lean`.
theorem mul_tdiv_self_le {x k : Int} (h : 0 x) : k * (x.tdiv k) x := by
by_cases w : k = 0
· simp [w, h]
· rw [tdiv_eq_ediv_of_nonneg h]
apply mul_ediv_self_le w
theorem lt_mul_tdiv_self_add {x k : Int} (h : 0 < k) : x < k * (x.tdiv k) + k := by
rw [tdiv_eq_ediv, sign_eq_one_of_pos h]
have := lt_mul_ediv_self_add (x := x) h
split <;> simp [Int.mul_add] <;> omega
/-! ### fdiv -/
theorem fdiv_nonneg {a b : Int} (Ha : 0 a) (Hb : 0 b) : 0 a.fdiv b :=
match a, b, eq_ofNat_of_zero_le Ha, eq_ofNat_of_zero_le Hb with
| _, _, _, rfl, _, rfl => ofNat_fdiv .. ofNat_zero_le _
-- There is no theorem `fdiv_neg : ∀ a b : Int, a.fdiv (-b) = -(a.fdiv b)`
-- because this is false, for example at `a = 2`, `b = 3`, as `-1 ≠ 0`.
unseal Nat.div in
theorem fdiv_nonpos : {a b : Int}, 0 a b 0 a.fdiv b 0
| 0, 0, _, _ | 0, -[_+1], _, _ | succ _, 0, _, _ | succ _, -[_+1], _, _ => _
theorem add_mul_fdiv_right (a b : Int) {c : Int} (H : c 0) : (a + b * c).fdiv c = a.fdiv c + b := by
rw [fdiv_eq_ediv, add_mul_ediv_right _ _ H, fdiv_eq_ediv]
simp only [Int.dvd_add_left (Int.dvd_mul_left _ _)]
split <;> omega
theorem fdiv_neg' : {a b : Int}, a < 0 0 < b a.fdiv b < 0
| -[_+1], succ _, _, _ => negSucc_lt_zero _
@[simp] theorem fdiv_one : a : Int, a.fdiv 1 = a
| 0 => rfl
| succ _ => congrArg Nat.cast (Nat.div_one _)
| -[_+1] => congrArg negSucc (Nat.div_one _)
theorem add_mul_fdiv_left (a : Int) {b : Int}
(c : Int) (H : b 0) : (a + b * c).fdiv b = a.fdiv b + c := by
rw [Int.mul_comm, Int.add_mul_fdiv_right _ _ H]
@[simp] theorem mul_fdiv_cancel (a : Int) {b : Int} (H : b 0) : fdiv (a * b) b = a :=
if b0 : 0 b then by
@@ -1010,32 +1103,174 @@ theorem fdiv_neg' : ∀ {a b : Int}, a < 0 → 0 < b → a.fdiv b < 0
@[simp] theorem mul_fdiv_cancel_left (b : Int) (H : a 0) : fdiv (a * b) a = b :=
Int.mul_comm .. Int.mul_fdiv_cancel _ H
theorem add_fdiv_of_dvd_right {a b c : Int} (H : c b) : (a + b).fdiv c = a.fdiv c + b.fdiv c := by
by_cases h : c = 0
· simp [h]
· obtain d, rfl := H
rw [add_mul_fdiv_left _ _ h]
simp [h]
theorem add_fdiv_of_dvd_left {a b c : Int} (H : c a) : (a + b).fdiv c = a.fdiv c + b.fdiv c := by
rw [Int.add_comm, Int.add_fdiv_of_dvd_right H, Int.add_comm]
theorem fdiv_nonneg {a b : Int} (Ha : 0 a) (Hb : 0 b) : 0 a.fdiv b :=
match a, b, eq_ofNat_of_zero_le Ha, eq_ofNat_of_zero_le Hb with
| _, _, _, rfl, _, rfl => ofNat_fdiv .. ofNat_zero_le _
theorem fdiv_nonneg_of_nonpos_of_nonpos {a b : Int} (Ha : a 0) (Hb : b 0) : 0 a.fdiv b := by
rw [fdiv_eq_ediv]
by_cases ha : a = 0
· simp [ha]
· by_cases hb : b = 0
· simp [hb]
· have : 0 < a / b := ediv_pos_of_neg_of_neg (by omega) (by omega)
split <;> omega
unseal Nat.div in
theorem fdiv_nonpos_of_nonneg_of_nonpos : {a b : Int}, 0 a b 0 a.fdiv b 0
| 0, 0, _, _ | 0, -[_+1], _, _ | succ _, 0, _, _ | succ _, -[_+1], _, _ => _
@[deprecated fdiv_nonpos_of_nonneg_of_nonpos (since := "2025-03-04")]
abbrev fdiv_nonpos := @fdiv_nonpos_of_nonneg_of_nonpos
theorem fdiv_neg_of_neg_of_pos : {a b : Int}, a < 0 0 < b a.fdiv b < 0
| -[_+1], succ _, _, _ => negSucc_lt_zero _
@[deprecated fdiv_neg_of_neg_of_pos (since := "2025-03-04")]
abbrev fdiv_neg := @fdiv_neg_of_neg_of_pos
theorem fdiv_eq_zero_of_lt {a b : Int} (H1 : 0 a) (H2 : a < b) : a.fdiv b = 0 := by
rw [fdiv_eq_ediv, if_pos, Int.sub_zero]
· apply ediv_eq_zero_of_lt (by omega) (by omega)
· left; omega
@[simp] theorem mul_fdiv_mul_of_pos {a : Int}
(b c : Int) (H : 0 < a) : (a * b).fdiv (a * c) = b.fdiv c := by
rw [fdiv_eq_ediv, mul_ediv_mul_of_pos _ _ H, fdiv_eq_ediv]
congr 2
simp [Int.mul_dvd_mul_iff_left (Int.ne_of_gt H)]
constructor
· rintro (h | h)
· exact .inl (Int.nonneg_of_mul_nonneg_right h H)
· exact .inr h
· rintro (h | h)
· exact .inl (Int.mul_nonneg (by omega) h)
· exact .inr h
@[simp] theorem mul_fdiv_mul_of_pos_left
(a : Int) {b : Int} (c : Int) (H : 0 < b) : (a * b).fdiv (c * b) = a.fdiv c := by
rw [Int.mul_comm a b, Int.mul_comm c b, Int.mul_fdiv_mul_of_pos _ _ H]
@[simp] theorem fdiv_one : a : Int, a.fdiv 1 = a
| 0 => rfl
| succ _ => congrArg Nat.cast (Nat.div_one _)
| -[_+1] => congrArg negSucc (Nat.div_one _)
protected theorem fdiv_eq_of_eq_mul_right {a b c : Int}
(H1 : b 0) (H2 : a = b * c) : a.fdiv b = c := by rw [H2, Int.mul_fdiv_cancel_left _ H1]
protected theorem eq_fdiv_of_mul_eq_right {a b c : Int}
(H1 : a 0) (H2 : a * b = c) : b = c.tdiv a :=
(Int.tdiv_eq_of_eq_mul_right H1 H2.symm).symm
protected theorem fdiv_eq_of_eq_mul_left {a b c : Int}
(H1 : b 0) (H2 : a = c * b) : a.fdiv b = c :=
Int.fdiv_eq_of_eq_mul_right H1 (by rw [Int.mul_comm, H2])
protected theorem eq_fdiv_of_mul_eq_left {a b c : Int}
(H1 : b 0) (H2 : a * b = c) : a = c.fdiv b :=
(Int.fdiv_eq_of_eq_mul_left H1 H2.symm).symm
@[simp] protected theorem fdiv_self {a : Int} (H : a 0) : a.fdiv a = 1 := by
have := Int.mul_fdiv_cancel 1 H; rwa [Int.one_mul] at this
theorem lt_fdiv_add_one_mul_self (a : Int) {b : Int} (H : 0 < b) : a < (a.fdiv b + 1) * b :=
Int.fdiv_eq_ediv_of_nonneg _ (Int.le_of_lt H) lt_ediv_add_one_mul_self a H
-- `neg_fdiv : ∀ a b : Int, (-a).fdiv b = -(a.fdiv b)` is untrue.
protected theorem neg_fdiv_neg (a b : Int) : (-a).fdiv (-b) = a.fdiv b := by
match a, b with
| 0, 0 => rfl
| 0, ofNat b => simp
| 0, -[b+1] => simp
| ofNat (a + 1), 0 => simp
| ofNat (a + 1), ofNat (b + 1) =>
unfold fdiv
simp only [ofNat_eq_coe, natCast_add, Nat.cast_ofNat_Int, Nat.succ_eq_add_one]
rw [ negSucc_eq, negSucc_eq]
| ofNat (a + 1), -[b+1] =>
unfold fdiv
simp only [ofNat_eq_coe, natCast_add, Nat.cast_ofNat_Int, Nat.succ_eq_add_one]
rw [ negSucc_eq, neg_negSucc]
| -[a+1], 0 => simp
| -[a+1], ofNat (b + 1) =>
unfold fdiv
simp only [ofNat_eq_coe, natCast_add, Nat.cast_ofNat_Int, Nat.succ_eq_add_one]
rw [neg_negSucc, negSucc_eq]
| -[a+1], -[b+1] =>
unfold fdiv
simp only [ofNat_eq_coe, ofNat_ediv, Nat.succ_eq_add_one, natCast_add, Nat.cast_ofNat_Int]
rw [neg_negSucc, neg_negSucc]
simp
-- `natAbs_fdiv (a b : Int) : natAbs (a.fdiv b) = (natAbs a).div (natAbs b)` is untrue.
/-! ### fmod -/
-- `fmod` analogues of `emod` lemmas from `Bootstrap.lean`
theorem ofNat_fmod (m n : Nat) : (m % n) = fmod m n := by
cases m <;> simp [fmod, Nat.succ_eq_add_one]
@[simp] theorem fmod_one (a : Int) : a.fmod 1 = 0 := by
simp [fmod_def, Int.one_mul, Int.sub_self]
theorem fmod_eq_of_lt {a b : Int} (H1 : 0 a) (H2 : a < b) : a.fmod b = a := by
rw [fmod_eq_emod_of_nonneg _ (Int.le_trans H1 (Int.le_of_lt H2)), emod_eq_of_lt H1 H2]
theorem fmod_nonneg {a b : Int} (ha : 0 a) (hb : 0 b) : 0 a.fmod b :=
fmod_eq_tmod_of_nonneg ha hb tmod_nonneg _ ha
theorem fmod_nonneg' (a : Int) {b : Int} (hb : 0 < b) : 0 a.fmod b :=
theorem fmod_nonneg_of_pos (a : Int) {b : Int} (hb : 0 < b) : 0 a.fmod b :=
fmod_eq_emod_of_nonneg _ (Int.le_of_lt hb) emod_nonneg _ (Int.ne_of_lt hb).symm
@[deprecated fmod_nonneg_of_pos (since := "2025-03-04")]
abbrev fmod_nonneg' := @fmod_nonneg_of_pos
theorem fmod_lt_of_pos (a : Int) {b : Int} (H : 0 < b) : a.fmod b < b :=
fmod_eq_emod_of_nonneg _ (Int.le_of_lt H) emod_lt_of_pos a H
-- There is no `fmod_neg : ∀ {a b : Int}, a.fmod (-b) = -a.fmod b` as this is false.
@[simp] theorem add_mul_fmod_self {a b c : Int} : (a + b * c).fmod c = a.fmod c := by
rw [fmod_eq_emod, add_mul_emod_self, fmod_eq_emod]
simp
@[simp] theorem add_mul_fmod_self_left (a b c : Int) : (a + b * c).fmod b = a.fmod b := by
rw [Int.mul_comm, Int.add_mul_fmod_self]
@[simp] theorem fmod_add_fmod (m n k : Int) : (m.fmod n + k).fmod n = (m + k).fmod n := by
by_cases h : n = 0
· simp [h]
rw [fmod_def, fmod_def]
conv => rhs; rw [fmod_def]
have : m - n * m.fdiv n + k = m + k + n * (- m.fdiv n) := by simp [Int.mul_neg]; omega
rw [this, add_fdiv_of_dvd_right (Int.dvd_mul_right ..), Int.mul_add, mul_fdiv_cancel_left _ h]
omega
@[simp] theorem add_fmod_fmod (m n k : Int) : (m + n.fmod k).fmod k = (m + n).fmod k := by
rw [Int.add_comm, Int.fmod_add_fmod, Int.add_comm]
theorem add_fmod (a b n : Int) : (a + b).fmod n = (a.fmod n + b.fmod n).fmod n := by
simp
theorem add_fmod_eq_add_fmod_right {m n k : Int} (i : Int)
(H : m.fmod n = k.fmod n) : (m + i).fmod n = (k + i).fmod n := by
rw [add_fmod]
conv => rhs; rw [add_fmod]
rw [H]
theorem fmod_add_cancel_right {m n k : Int} (i) : (m + i).fmod n = (k + i).fmod n m.fmod n = k.fmod n :=
fun H => by
have := add_fmod_eq_add_fmod_right (-i) H
rwa [Int.add_neg_cancel_right, Int.add_neg_cancel_right] at this,
add_fmod_eq_add_fmod_right _
@[simp] theorem mul_fmod_left (a b : Int) : (a * b).fmod b = 0 :=
if h : b = 0 then by simp [h, Int.mul_zero] else by
rw [Int.fmod_def, Int.mul_fdiv_cancel _ h, Int.mul_comm, Int.sub_self]
@@ -1043,9 +1278,56 @@ theorem fmod_lt_of_pos (a : Int) {b : Int} (H : 0 < b) : a.fmod b < b :=
@[simp] theorem mul_fmod_right (a b : Int) : (a * b).fmod a = 0 := by
rw [Int.mul_comm, mul_fmod_left]
theorem mul_fmod (a b n : Int) : (a * b).fmod n = (a.fmod n * b.fmod n).fmod n := by
conv => lhs; rw [
fmod_add_fdiv a n, fmod_add_fdiv' b n, Int.add_mul, Int.mul_add, Int.mul_add,
Int.mul_assoc, Int.mul_assoc, Int.mul_add n _ _, add_mul_fmod_self_left,
Int.mul_assoc, add_mul_fmod_self]
@[simp] theorem fmod_self {a : Int} : a.fmod a = 0 := by
have := mul_fmod_left 1 a; rwa [Int.one_mul] at this
@[simp] theorem fmod_fmod_of_dvd (n : Int) {m k : Int}
(h : m k) : (n.fmod k).fmod m = n.fmod m := by
conv => rhs; rw [ fmod_add_fdiv n k]
match k, h with
| _, t, rfl => rw [Int.mul_assoc, add_mul_fmod_self_left]
@[simp] theorem fmod_fmod (a b : Int) : (a.fmod b).fmod b = a.fmod b :=
fmod_fmod_of_dvd _ (Int.dvd_refl b)
theorem sub_fmod (a b n : Int) : (a - b).fmod n = (a.fmod n - b.fmod n).fmod n := by
apply (fmod_add_cancel_right b).mp
rw [Int.sub_add_cancel, Int.add_fmod_fmod, Int.sub_add_cancel, fmod_fmod]
theorem fmod_eq_zero_of_dvd : {a b : Int}, a b b.fmod a = 0
| _, _, _, rfl => mul_fmod_right ..
-- `fmod` analogues of `emod` lemmas from above
theorem fmod_eq_of_lt {a b : Int} (H1 : 0 a) (H2 : a < b) : a.fmod b = a := by
rw [fmod_eq_emod_of_nonneg _ (Int.le_trans H1 (Int.le_of_lt H2)), emod_eq_of_lt H1 H2]
-- lemmas about `fmod` without `emod` analogues
theorem fdiv_sign {a b : Int} : a.fdiv (sign b) = a * sign b := by
rw [fdiv_eq_ediv]
rcases sign_trichotomy b with h | h | h <;> simp [h]
protected theorem sign_eq_fdiv_abs (a : Int) : sign a = a.fdiv (natAbs a) :=
if az : a = 0 then by simp [az] else
(Int.fdiv_eq_of_eq_mul_left (ofNat_ne_zero.2 <| natAbs_ne_zero.2 az)
(sign_mul_natAbs _).symm).symm
/-! ### properties of `fdiv` and `fmod` -/
/-! ### `fdiv` and ordering -/
-- Theorems about `fdiv` and ordering, whose `ediv` analogues are in `Bootstrap.lean`.
theorem lt_fdiv_add_one_mul_self (a : Int) {b : Int} (H : 0 < b) : a < (a.fdiv b + 1) * b :=
Int.fdiv_eq_ediv_of_nonneg _ (Int.le_of_lt H) lt_ediv_add_one_mul_self a H
/-! ### bmod -/
@[simp]
@@ -1100,6 +1382,9 @@ theorem bmod_add_mul_cancel (x : Int) (n : Nat) (k : Int) : Int.bmod (x + n * k)
theorem bmod_sub_cancel (x : Int) (n : Nat) : Int.bmod (x - n) n = Int.bmod x n := by
simp [bmod_def]
@[simp] theorem Int.bmod_sub_mul_cancel (x : Int) (n : Nat) (k : Int) : (x - n * k).bmod n = x.bmod n := by
rw [Int.sub_eq_add_neg, Int.neg_mul_eq_mul_neg, Int.bmod_add_mul_cancel]
@[simp]
theorem emod_add_bmod_congr (x : Int) (n : Nat) : Int.bmod (x%n + y) n = Int.bmod (x + y) n := by
simp [Int.emod_def, Int.sub_eq_add_neg]

View File

@@ -78,7 +78,7 @@ theorem negSucc_eq (n : Nat) : -[n+1] = -((n : Int) + 1) := rfl
| succ _ => rfl
| -[_+1] => rfl
protected theorem neg_inj {a b : Int} : -a = -b a = b :=
@[simp] protected theorem neg_inj {a b : Int} : -a = -b a = b :=
fun h => by rw [ Int.neg_neg a, Int.neg_neg b, h], congrArg _
@[simp] protected theorem neg_eq_zero : -a = 0 a = 0 := Int.neg_inj (b := 0)

View File

@@ -65,4 +65,16 @@ theorem bmod_eq_self_of_le {n : Int} {m : Nat} (hn' : -(m / 2) ≤ n) (hn : n <
apply eq_zero_of_dvd_of_natAbs_lt_natAbs Int.dvd_bmod_sub_self
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
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]
@[simp] theorem toNat_le {m : Int} {n : Nat} : m.toNat n m n := by omega
@[simp] theorem toNat_lt' {m : Int} {n : Nat} (hn : 0 < n) : m.toNat < n m < n := by omega
end Int

View File

@@ -1213,7 +1213,7 @@ def cooper_dvd_left_split_ineq_cert (p₁ p₂ : Poly) (k : Int) (b : Int) (p' :
p₂.leadCoeff == b && p' == p₁.addConst (b*k)
theorem cooper_dvd_left_split_ineq (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) (b : Int) (p' : Poly)
: cooper_dvd_left_split ctx p₁ p₂ p₃ d k cooper_dvd_left_split_ineq_cert p₁ p₂ k b p' p'.denote ctx 0 := by
: cooper_dvd_left_split ctx p₁ p₂ p₃ d k cooper_dvd_left_split_ineq_cert p₁ p₂ k b p' p'.denote' ctx 0 := by
simp [cooper_dvd_left_split_ineq_cert, cooper_dvd_left_split]
intros; subst p' b; simp [denote'_mul_combine_mul_addConst_eq]; assumption
@@ -1221,7 +1221,7 @@ def cooper_dvd_left_split_dvd1_cert (p₁ p' : Poly) (a : Int) (k : Int) : Bool
a == p₁.leadCoeff && p' == p₁.tail.addConst k
theorem cooper_dvd_left_split_dvd1 (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) (a : Int) (p' : Poly)
: cooper_dvd_left_split ctx p₁ p₂ p₃ d k cooper_dvd_left_split_dvd1_cert p₁ p' a k a p'.denote ctx := by
: cooper_dvd_left_split ctx p₁ p₂ p₃ d k cooper_dvd_left_split_dvd1_cert p₁ p' a k a p'.denote' ctx := by
simp [cooper_dvd_left_split_dvd1_cert, cooper_dvd_left_split]
intros; subst a p'; simp; assumption
@@ -1234,7 +1234,7 @@ def cooper_dvd_left_split_dvd2_cert (p₁ p₃ : Poly) (d : Int) (k : Nat) (d' :
d' == a*d && p' == p₂.addConst (c*k)
theorem cooper_dvd_left_split_dvd2 (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) (d' : Int) (p' : Poly)
: cooper_dvd_left_split ctx p₁ p₂ p₃ d k cooper_dvd_left_split_dvd2_cert p₁ p₃ d k d' p' d' p'.denote ctx := by
: cooper_dvd_left_split ctx p₁ p₂ p₃ d k cooper_dvd_left_split_dvd2_cert p₁ p₃ d k d' p' d' p'.denote' ctx := by
simp [cooper_dvd_left_split_dvd2_cert, cooper_dvd_left_split]
intros; subst d' p'; simp; assumption
@@ -1295,7 +1295,7 @@ def cooper_left_split_ineq_cert (p₁ p₂ : Poly) (k : Int) (b : Int) (p' : Pol
p₂.leadCoeff == b && p' == p₁.addConst (b*k)
theorem cooper_left_split_ineq (ctx : Context) (p₁ p₂ : Poly) (k : Nat) (b : Int) (p' : Poly)
: cooper_left_split ctx p₁ p₂ k cooper_left_split_ineq_cert p₁ p₂ k b p' p'.denote ctx 0 := by
: cooper_left_split ctx p₁ p₂ k cooper_left_split_ineq_cert p₁ p₂ k b p' p'.denote' ctx 0 := by
simp [cooper_left_split_ineq_cert, cooper_left_split]
intros; subst p' b; simp [denote'_mul_combine_mul_addConst_eq]; assumption
@@ -1303,7 +1303,7 @@ def cooper_left_split_dvd_cert (p₁ p' : Poly) (a : Int) (k : Int) : Bool :=
a == p₁.leadCoeff && p' == p₁.tail.addConst k
theorem cooper_left_split_dvd (ctx : Context) (p₁ p₂ : Poly) (k : Nat) (a : Int) (p' : Poly)
: cooper_left_split ctx p₁ p₂ k cooper_left_split_dvd_cert p₁ p' a k a p'.denote ctx := by
: cooper_left_split ctx p₁ p₂ k cooper_left_split_dvd_cert p₁ p' a k a p'.denote' ctx := by
simp [cooper_left_split_dvd_cert, cooper_left_split]
intros; subst a p'; simp; assumption
@@ -1380,7 +1380,7 @@ def cooper_dvd_right_split_ineq_cert (p₁ p₂ : Poly) (k : Int) (a : Int) (p'
p₁.leadCoeff == a && p' == p₂.addConst ((-a)*k)
theorem cooper_dvd_right_split_ineq (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) (a : Int) (p' : Poly)
: cooper_dvd_right_split ctx p₁ p₂ p₃ d k cooper_dvd_right_split_ineq_cert p₁ p₂ k a p' p'.denote ctx 0 := by
: cooper_dvd_right_split ctx p₁ p₂ p₃ d k cooper_dvd_right_split_ineq_cert p₁ p₂ k a p' p'.denote' ctx 0 := by
simp [cooper_dvd_right_split_ineq_cert, cooper_dvd_right_split]
intros; subst a p'; simp [denote'_mul_combine_mul_addConst_eq]; assumption
@@ -1388,7 +1388,7 @@ def cooper_dvd_right_split_dvd1_cert (p₂ p' : Poly) (b : Int) (k : Int) : Bool
b == p₂.leadCoeff && p' == p₂.tail.addConst k
theorem cooper_dvd_right_split_dvd1 (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) (b : Int) (p' : Poly)
: cooper_dvd_right_split ctx p₁ p₂ p₃ d k cooper_dvd_right_split_dvd1_cert p₂ p' b k b p'.denote ctx := by
: cooper_dvd_right_split ctx p₁ p₂ p₃ d k cooper_dvd_right_split_dvd1_cert p₂ p' b k b p'.denote' ctx := by
simp [cooper_dvd_right_split_dvd1_cert, cooper_dvd_right_split]
intros; subst b p'; simp; assumption
@@ -1401,7 +1401,7 @@ def cooper_dvd_right_split_dvd2_cert (p₂ p₃ : Poly) (d : Int) (k : Nat) (d'
d' == b*d && p' == p₂.addConst ((-c)*k)
theorem cooper_dvd_right_split_dvd2 (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) (d' : Int) (p' : Poly)
: cooper_dvd_right_split ctx p₁ p₂ p₃ d k cooper_dvd_right_split_dvd2_cert p₂ p₃ d k d' p' d' p'.denote ctx := by
: cooper_dvd_right_split ctx p₁ p₂ p₃ d k cooper_dvd_right_split_dvd2_cert p₂ p₃ d k d' p' d' p'.denote' ctx := by
simp [cooper_dvd_right_split_dvd2_cert, cooper_dvd_right_split]
intros; subst d' p'; simp; assumption
@@ -1461,7 +1461,7 @@ def cooper_right_split_ineq_cert (p₁ p₂ : Poly) (k : Int) (a : Int) (p' : Po
p₁.leadCoeff == a && p' == p₂.addConst ((-a)*k)
theorem cooper_right_split_ineq (ctx : Context) (p₁ p₂ : Poly) (k : Nat) (a : Int) (p' : Poly)
: cooper_right_split ctx p₁ p₂ k cooper_right_split_ineq_cert p₁ p₂ k a p' p'.denote ctx 0 := by
: cooper_right_split ctx p₁ p₂ k cooper_right_split_ineq_cert p₁ p₂ k a p' p'.denote' ctx 0 := by
simp [cooper_right_split_ineq_cert, cooper_right_split]
intros; subst a p'; simp [denote'_mul_combine_mul_addConst_eq]; assumption
@@ -1469,10 +1469,159 @@ def cooper_right_split_dvd_cert (p₂ p' : Poly) (b : Int) (k : Int) : Bool :=
b == p₂.leadCoeff && p' == p₂.tail.addConst k
theorem cooper_right_split_dvd (ctx : Context) (p₁ p₂ : Poly) (k : Nat) (b : Int) (p' : Poly)
: cooper_right_split ctx p₁ p₂ k cooper_right_split_dvd_cert p₂ p' b k b p'.denote ctx := by
: cooper_right_split ctx p₁ p₂ k cooper_right_split_dvd_cert p₂ p' b k b p'.denote' ctx := by
simp [cooper_right_split_dvd_cert, cooper_right_split]
intros; subst b p'; simp; assumption
private theorem one_emod_eq_one {a : Int} (h : a > 1) : 1 % a = 1 := by
have aux₁ := Int.ediv_add_emod 1 a
have : 1 / a = 0 := Int.ediv_eq_zero_of_lt (by decide) h
simp [this] at aux₁
assumption
private theorem ex_of_dvd {α β a b d x : Int}
(h₀ : d > 1)
(h₁ : d a*x + b)
(h₂ : α * a + β * d = 1)
: k, x = k * d + (- α * b) % d := by
have k, h₁ := h₁
have aux₁ : (α * a) % d = 1 := by
replace h₂ := congrArg (· % d) h₂; simp at h₂
rw [one_emod_eq_one h₀] at h₂
assumption
have : ((α * a) * x) % d = (- α * b) % d := by
replace h₁ := congrArg (α * ·) h₁; simp only at h₁
rw [Int.mul_add] at h₁
replace h₁ := congrArg (· - α * b) h₁; simp only [Int.add_sub_cancel] at h₁
rw [ Int.mul_assoc, Int.mul_left_comm, Int.sub_eq_add_neg] at h₁
replace h₁ := congrArg (· % d) h₁; simp only at h₁
rw [Int.add_emod, Int.mul_emod_right, Int.zero_add, Int.emod_emod, Int.neg_mul] at h₁
assumption
have : x % d = (- α * b) % d := by
rw [Int.mul_emod, aux₁, Int.one_mul, Int.emod_emod] at this
assumption
have : x = (x / d)*d + (- α * b) % d := by
conv => lhs; rw [ Int.ediv_add_emod x d]
rw [Int.mul_comm, this]
exists x / d
private theorem cdiv_le {a d k : Int} : d > 0 a k * d cdiv a d k := by
intro h₁ h₂
simp [cdiv]
replace h₂ := Int.neg_le_neg h₂
rw [ Int.neg_mul] at h₂
replace h₂ := Int.le_ediv_of_mul_le h₁ h₂
replace h₂ := Int.neg_le_neg h₂
simp at h₂
assumption
private theorem cooper_unsat'_helper {a b d c k x : Int}
(d_pos : d > 0)
(h₁ : x = k * d + c)
(h₂ : a x)
(h₃ : x b)
: ¬ b < (cdiv (a - c) d) * d + c := by
intro h₄
have aux₁ : cdiv (a - c) d k := by
rw [h₁] at h₂
replace h₂ := Int.sub_right_le_of_le_add h₂
exact cdiv_le d_pos h₂
have aux₂ : cdiv (a - c) d * d k * d := Int.mul_le_mul_of_nonneg_right aux₁ (Int.le_of_lt d_pos)
have aux₃ : cdiv (a - c) d * d + c k * d + c := Int.add_le_add_right aux₂ _
have aux₄ : cdiv (a - c) d * d + c x := by rw [h₁] at aux₃; assumption
have aux₅ : cdiv (a - c) d * d + c b := Int.le_trans aux₄ h₃
have := Int.lt_of_le_of_lt aux₅ h₄
exact Int.lt_irrefl _ this
private theorem cooper_unsat' {a c b d e α β x : Int}
(h₁ : d > 1)
(h₂ : d c*x + e)
(h₃ : α * c + β * d = 1)
(h₄ : (-1)*x + a 0)
(h₅ : x + b 0)
(h₆ : -b < cdiv (a - -α * e % d) d * d + -α * e % d)
: False := by
have k, h := ex_of_dvd h₁ h₂ h₃
have d_pos : d > 0 := Int.lt_trans (by decide) h₁
replace h₄ := Int.le_neg_add_of_add_le h₄; simp at h₄
replace h₅ := Int.neg_le_neg (Int.le_neg_add_of_add_le h₅); simp at h₅
have := cooper_unsat'_helper d_pos h h₄ h₅
exact this h₆
abbrev Poly.casesOnAdd (p : Poly) (k : Int Var Poly Bool) : Bool :=
p.casesOn (fun _ => false) k
abbrev Poly.casesOnNum (p : Poly) (k : Int Bool) : Bool :=
p.casesOn k (fun _ _ _ => false)
def cooper_unsat_cert (p₁ p₂ p₃ : Poly) (d : Int) (α β : Int) : Bool :=
p₁.casesOnAdd fun k₁ x p₁ =>
p₂.casesOnAdd fun k₂ y p₂ =>
p₃.casesOnAdd fun c z p₃ =>
p₁.casesOnNum fun a =>
p₂.casesOnNum fun b =>
p₃.casesOnNum fun e =>
(k₁ == -1) |>.and (k₂ == 1) |>.and
(x == y) |>.and (x == z) |>.and
(d > 1) |>.and (α * c + β * d == 1) |>.and
(-b < cdiv (a - -α * e % d) d * d + -α * e % d)
theorem cooper_unsat (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (α β : Int)
: cooper_unsat_cert p₁ p₂ p₃ d α β
p₁.denote' ctx 0 p₂.denote' ctx 0 d p₃.denote' ctx False := by
unfold cooper_unsat_cert <;> cases p₁ <;> cases p₂ <;> cases p₃ <;> simp only [Poly.casesOnAdd,
Bool.false_eq_true, Poly.denote'_add, mul_def, add_def, false_implies]
next k₁ x p₁ k₂ y p₂ c z p₃ =>
cases p₁ <;> cases p₂ <;> cases p₃ <;> simp only [Poly.casesOnNum, Int.reduceNeg,
Bool.and_eq_true, beq_iff_eq, decide_eq_true_eq, and_imp, Bool.false_eq_true,
mul_def, add_def, false_implies, Poly.denote]
next a b e =>
intro _ _ _ _; subst k₁ k₂ y z
intro h₁ h₃ h₆; generalize Var.denote ctx x = x'
intro h₄ h₅ h₂
rw [Int.one_mul] at h₅
exact cooper_unsat' h₁ h₂ h₃ h₄ h₅ h₆
theorem ediv_emod (x y : Int) : -1 * x + y * (x / y) + x % y = 0 := by
rw [Int.add_assoc, Int.ediv_add_emod x y, Int.add_comm]
simp
rw [ Int.sub_eq_add_neg, Int.sub_self]
theorem emod_nonneg (x y : Int) : y != 0 -1 * (x % y) 0 := by
simp; intro h
have := Int.neg_le_neg (Int.emod_nonneg x h)
simp at this
assumption
def emod_le_cert (y n : Int) : Bool :=
y != 0 && n == 1 - y.natAbs
theorem emod_le (x y : Int) (n : Int) : emod_le_cert y n x % y + n 0 := by
simp [emod_le_cert]
intro h₁
cases Int.lt_or_gt_of_ne h₁
next h =>
rw [Int.ofNat_natAbs_of_nonpos (Int.le_of_lt h)]
simp only [Int.sub_neg]
intro; subst n
rw [Int.add_assoc, Int.add_left_comm]
apply Int.add_le_of_le_sub_left
rw [Int.zero_sub, Int.add_comm]
have : 0 < -y := by
have := Int.neg_lt_neg h
rw [Int.neg_zero] at this
assumption
have := Int.emod_lt_of_pos x this
rw [Int.emod_neg] at this
exact this
next h =>
rw [Int.natAbs_of_nonneg (Int.le_of_lt h)]
intro; subst n
rw [Int.sub_eq_add_neg, Int.add_assoc, Int.add_left_comm]
apply Int.add_le_of_le_sub_left
simp only [Int.add_comm, Int.sub_neg, Int.add_zero]
exact Int.emod_lt_of_pos x h
end Int.Linear
theorem Int.not_le_eq (a b : Int) : (¬a b) = (b + 1 a) := by

View File

@@ -139,6 +139,9 @@ protected theorem not_le_of_gt {a b : Int} (h : b < a) : ¬a ≤ b :=
@[simp] protected theorem not_lt {a b : Int} : ¬a < b b a :=
by rw [ Int.not_le, Decidable.not_not]
protected theorem le_of_not_gt {a b : Int} (h : ¬ a > b) : a b :=
Int.not_lt.mp h
protected theorem lt_trichotomy (a b : Int) : a < b a = b b < a :=
if eq : a = b then .inr <| .inl eq else
if le : a b then .inl <| Int.lt_iff_le_and_ne.2 le, eq else
@@ -938,6 +941,22 @@ protected theorem mul_self_le_mul_self {a b : Int} (h1 : 0 ≤ a) (h2 : a ≤ b)
protected theorem mul_self_lt_mul_self {a b : Int} (h1 : 0 a) (h2 : a < b) : a * a < b * b :=
Int.mul_lt_mul' (Int.le_of_lt h2) h2 h1 (Int.lt_of_le_of_lt h1 h2)
protected theorem nonneg_of_mul_nonneg_left {a b : Int}
(h : 0 a * b) (hb : 0 < b) : 0 a :=
Int.le_of_not_gt fun ha => Int.not_le_of_gt (Int.mul_neg_of_neg_of_pos ha hb) h
protected theorem nonneg_of_mul_nonneg_right {a b : Int}
(h : 0 a * b) (ha : 0 < a) : 0 b :=
Int.le_of_not_gt fun hb => Int.not_le_of_gt (Int.mul_neg_of_pos_of_neg ha hb) h
protected theorem nonpos_of_mul_nonpos_left {a b : Int}
(h : a * b 0) (hb : 0 < b) : a 0 :=
Int.le_of_not_gt fun ha : a > 0 => Int.not_le_of_gt (Int.mul_pos ha hb) h
protected theorem nonpos_of_mul_nonpos_right {a b : Int}
(h : a * b 0) (ha : 0 < a) : b 0 :=
Int.le_of_not_gt fun hb : b > 0 => Int.not_le_of_gt (Int.mul_pos ha hb) h
/- ## sign -/
@[simp] theorem sign_zero : sign 0 = 0 := rfl
@@ -1021,6 +1040,12 @@ theorem sign_eq_neg_one_iff_neg {a : Int} : sign a = -1 ↔ a < 0 :=
@[simp] theorem sign_mul_self : sign i * i = natAbs i := by
rw [Int.mul_comm, mul_sign_self]
theorem sign_trichotomy (a : Int) : sign a = 1 sign a = 0 sign a = -1 := by
match a with
| 0 => simp
| .ofNat (_ + 1) => simp
| .negSucc _ => simp
/- ## natAbs -/
theorem natAbs_ne_zero {a : Int} : a.natAbs 0 a 0 := not_congr Int.natAbs_eq_zero

View File

@@ -1758,10 +1758,10 @@ where
/-! ### removeAll -/
/-- `O(|xs|)`. Computes the "set difference" of lists,
/-- `O(|xs| * |ys|)`. Computes the "set difference" of lists,
by filtering out all elements of `xs` which are also in `ys`.
* `removeAll [1, 1, 5, 1, 2, 4, 5] [1, 2, 2] = [5, 4, 5]`
-/
-/
def removeAll [BEq α] (xs ys : List α) : List α :=
xs.filter (fun x => !ys.elem x)

View File

@@ -5,7 +5,9 @@ Authors: Markus Himmel
-/
prelude
import Init.Data.SInt.Basic
import Init.Data.BitVec.Lemmas
import Init.Data.BitVec.Bitblast
import Init.Data.Int.LemmasAux
import Init.Data.UInt.Lemmas
open Lean in
set_option hygiene false in
@@ -21,8 +23,8 @@ macro "declare_int_theorems" typeName:ident _bits:term:arg : command => do
toBitVec_inj.symm
@[int_toBitVec] theorem ne_iff_toBitVec_ne {a b : $typeName} : a b a.toBitVec b.toBitVec :=
Decidable.not_iff_not.2 eq_iff_toBitVec_eq
@[simp] theorem toBitVec_ofNat {n : Nat} : toBitVec (ofNat n) = BitVec.ofNat _ n := rfl
@[simp, int_toBitVec] theorem toBitVec_ofNatOfNat {n : Nat} : toBitVec (no_index (OfNat.ofNat n)) = OfNat.ofNat n := rfl
@[simp] theorem toBitVec_ofNat' {n : Nat} : toBitVec (ofNat n) = BitVec.ofNat _ n := rfl
@[simp, int_toBitVec] theorem toBitVec_ofNat {n : Nat} : toBitVec (no_index (OfNat.ofNat n)) = OfNat.ofNat n := rfl
end $typeName
)
@@ -34,6 +36,118 @@ declare_int_theorems Int32 32
declare_int_theorems Int64 64
declare_int_theorems ISize System.Platform.numBits
theorem Int8.toInt.inj {x y : Int8} (h : x.toInt = y.toInt) : x = y := Int8.toBitVec.inj (BitVec.eq_of_toInt_eq h)
theorem Int8.toInt_inj {x y : Int8} : x.toInt = y.toInt x = y := Int8.toInt.inj, fun h => h rfl
theorem Int16.toInt.inj {x y : Int16} (h : x.toInt = y.toInt) : x = y := Int16.toBitVec.inj (BitVec.eq_of_toInt_eq h)
theorem Int16.toInt_inj {x y : Int16} : x.toInt = y.toInt x = y := Int16.toInt.inj, fun h => h rfl
theorem Int32.toInt.inj {x y : Int32} (h : x.toInt = y.toInt) : x = y := Int32.toBitVec.inj (BitVec.eq_of_toInt_eq h)
theorem Int32.toInt_inj {x y : Int32} : x.toInt = y.toInt x = y := Int32.toInt.inj, fun h => h rfl
theorem Int64.toInt.inj {x y : Int64} (h : x.toInt = y.toInt) : x = y := Int64.toBitVec.inj (BitVec.eq_of_toInt_eq h)
theorem Int64.toInt_inj {x y : Int64} : x.toInt = y.toInt x = y := Int64.toInt.inj, fun h => h rfl
theorem ISize.toInt.inj {x y : ISize} (h : x.toInt = y.toInt) : x = y := ISize.toBitVec.inj (BitVec.eq_of_toInt_eq h)
theorem ISize.toInt_inj {x y : ISize} : x.toInt = y.toInt x = y := ISize.toInt.inj, fun h => h rfl
@[simp] theorem Int8.toBitVec_neg (x : Int8) : (-x).toBitVec = -x.toBitVec := rfl
@[simp] theorem Int16.toBitVec_neg (x : Int16) : (-x).toBitVec = -x.toBitVec := rfl
@[simp] theorem Int32.toBitVec_neg (x : Int32) : (-x).toBitVec = -x.toBitVec := rfl
@[simp] theorem Int64.toBitVec_neg (x : Int64) : (-x).toBitVec = -x.toBitVec := rfl
@[simp] theorem ISize.toBitVec_neg (x : ISize) : (-x).toBitVec = -x.toBitVec := rfl
@[simp] theorem ISize.toBitVec_zero : (0 : ISize).toBitVec = 0 := rfl
@[simp] theorem Int8.toBitVec_ofInt (i : Int) : (ofInt i).toBitVec = BitVec.ofInt _ i := rfl
@[simp] theorem Int16.toBitVec_ofInt (i : Int) : (ofInt i).toBitVec = BitVec.ofInt _ i := rfl
@[simp] theorem Int32.toBitVec_ofInt (i : Int) : (ofInt i).toBitVec = BitVec.ofInt _ i := rfl
@[simp] theorem Int64.toBitVec_ofInt (i : Int) : (ofInt i).toBitVec = BitVec.ofInt _ i := rfl
@[simp] theorem ISize.toBitVec_ofInt (i : Int) : (ofInt i).toBitVec = BitVec.ofInt _ i := rfl
@[simp] theorem Int8.neg_zero : -(0 : Int8) = 0 := rfl
@[simp] theorem Int16.neg_zero : -(0 : Int16) = 0 := rfl
@[simp] theorem Int32.neg_zero : -(0 : Int32) = 0 := rfl
@[simp] theorem Int64.neg_zero : -(0 : Int64) = 0 := rfl
@[simp] theorem ISize.neg_zero : -(0 : ISize) = 0 := ISize.toBitVec.inj (by simp)
theorem ISize.toNat_toBitVec_ofNat_of_lt {n : Nat} (h : n < 2^32) :
(ofNat n).toBitVec.toNat = n :=
Nat.mod_eq_of_lt (Nat.lt_of_lt_of_le h (by cases USize.size_eq <;> simp_all +decide))
theorem Int8.toInt_ofInt {n : Int} (hn : -2^7 n) (hn' : n < 2^7) : toInt (ofInt n) = n := by
rw [toInt, toBitVec_ofInt, BitVec.toInt_ofInt_eq_self (by decide) hn hn']
theorem Int16.toInt_ofInt {n : Int} (hn : -2^15 n) (hn' : n < 2^15) : toInt (ofInt n) = n := by
rw [toInt, toBitVec_ofInt, BitVec.toInt_ofInt_eq_self (by decide) hn hn']
theorem Int32.toInt_ofInt {n : Int} (hn : -2^31 n) (hn' : n < 2^31) : toInt (ofInt n) = n := by
rw [toInt, toBitVec_ofInt, BitVec.toInt_ofInt_eq_self (by decide) hn hn']
theorem Int64.toInt_ofInt {n : Int} (hn : -2^63 n) (hn' : n < 2^63) : toInt (ofInt n) = n := by
rw [toInt, toBitVec_ofInt, BitVec.toInt_ofInt_eq_self (by decide) hn hn']
theorem ISize.toInt_ofInt {n : Int} (hn : -2^31 n) (hn' : n < 2^31) : toInt (ofInt n) = n := by
rw [toInt, toBitVec_ofInt, BitVec.toInt_ofInt_eq_self] <;> cases System.Platform.numBits_eq
<;> (simp_all; try omega)
theorem ISize.toInt_ofInt_of_two_pow_numBits_le {n : Int} (hn : -2 ^ (System.Platform.numBits - 1) n)
(hn' : n < 2 ^ (System.Platform.numBits - 1)) : toInt (ofInt n) = n := by
rw [toInt, toBitVec_ofInt, BitVec.toInt_ofInt_eq_self _ hn hn']
cases System.Platform.numBits_eq <;> simp_all
theorem ISize.toNatClampNeg_ofInt_eq_zero {n : Int} (hn : -2^31 n) (hn' : n 0) :
toNatClampNeg (ofInt n) = 0 := by
rwa [toNatClampNeg, toInt_ofInt hn (by omega), Int.toNat_eq_zero]
theorem Int64.neg_ofInt {n : Int} : -ofInt n = ofInt (-n) :=
toBitVec.inj (by simp [BitVec.ofInt_neg])
theorem ISize.neg_ofInt {n : Int} : -ofInt n = ofInt (-n) :=
toBitVec.inj (by simp [BitVec.ofInt_neg])
theorem Int8.ofInt_eq_ofNat {n : Nat} : ofInt n = ofNat n := toBitVec.inj (by simp)
theorem Int16.ofInt_eq_ofNat {n : Nat} : ofInt n = ofNat n := toBitVec.inj (by simp)
theorem Int32.ofInt_eq_ofNat {n : Nat} : ofInt n = ofNat n := toBitVec.inj (by simp)
theorem Int64.ofInt_eq_ofNat {n : Nat} : ofInt n = ofNat n := toBitVec.inj (by simp)
theorem ISize.ofInt_eq_ofNat {n : Nat} : ofInt n = ofNat n := toBitVec.inj (by simp)
theorem ISize.neg_ofNat {n : Nat} : -ofNat n = ofInt (-n) := by
rw [ neg_ofInt, ofInt_eq_ofNat]
theorem Int8.toNatClampNeg_ofNat_of_lt {n : Nat} (h : n < 2 ^ 7) : toNatClampNeg (ofNat n) = n := by
rw [toNatClampNeg, ofInt_eq_ofNat, toInt_ofInt (by omega) (by omega), Int.toNat_ofNat]
theorem ISize.toNatClampNeg_ofNat_of_lt {n : Nat} (h : n < 2 ^ 31) : toNatClampNeg (ofNat n) = n := by
rw [toNatClampNeg, ofInt_eq_ofNat, toInt_ofInt (by omega) (by omega), Int.toNat_ofNat]
theorem ISize.toNatClampNeg_neg_ofNat_of_le {n : Nat} (h : n 2 ^ 31) :
toNatClampNeg (-ofNat n) = 0 := by
rw [neg_ofNat, toNatClampNeg_ofInt_eq_zero (by omega) (by omega)]
theorem Int8.toInt_ofNat_of_lt {n : Nat} (h : n < 2 ^ 7) : toInt (ofNat n) = n := by
rw [ ofInt_eq_ofNat, toInt_ofInt (by omega) (by omega)]
theorem Int16.toInt_ofNat_of_lt {n : Nat} (h : n < 2 ^ 15) : toInt (ofNat n) = n := by
rw [ ofInt_eq_ofNat, toInt_ofInt (by omega) (by omega)]
theorem Int32.toInt_ofNat_of_lt {n : Nat} (h : n < 2 ^ 31) : toInt (ofNat n) = n := by
rw [ ofInt_eq_ofNat, toInt_ofInt (by omega) (by omega)]
theorem Int64.toInt_ofNat_of_lt {n : Nat} (h : n < 2 ^ 63) : toInt (ofNat n) = n := by
rw [ ofInt_eq_ofNat, toInt_ofInt (by omega) (by omega)]
theorem ISize.toInt_ofNat_of_lt {n : Nat} (h : n < 2 ^ 31) : toInt (ofNat n) = n := by
rw [ ofInt_eq_ofNat, toInt_ofInt (by omega) (by omega)]
theorem ISize.toInt_ofNat_of_lt_two_pow_numBits {n : Nat}
(h : n < 2 ^ (System.Platform.numBits - 1)) : toInt (ofNat n) = n := by
rw [ ofInt_eq_ofNat, toInt_ofInt_of_two_pow_numBits_le] <;>
cases System.Platform.numBits_eq <;> simp_all <;> omega
theorem Int64.toInt_neg_ofNat_of_le {n : Nat} (h : n 2^63) : toInt (-ofNat n) = -n := by
rw [ ofInt_eq_ofNat, neg_ofInt, toInt_ofInt (by omega) (by omega)]
theorem ISize.toInt_neg_ofNat_of_le {n : Nat} (h : n 2 ^ 31) : toInt (-ofNat n) = -n := by
rw [ ofInt_eq_ofNat, neg_ofInt, toInt_ofInt (by omega) (by omega)]
theorem Int8.toInt_zero : toInt 0 = 0 := by simp
theorem Int16.toInt_zero : toInt 0 = 0 := by simp
theorem Int32.toInt_zero : toInt 0 = 0 := by simp
theorem Int64.toInt_zero : toInt 0 = 0 := by simp
theorem ISize.toInt_zero : toInt 0 = 0 := by simp
@[simp] theorem ISize.toInt_minValue : ISize.minValue.toInt = -2^(System.Platform.numBits - 1) := by
rw [minValue, toInt_ofInt_of_two_pow_numBits_le] <;> cases System.Platform.numBits_eq
<;> simp_all
@[simp] theorem ISize.toInt_maxValue : ISize.maxValue.toInt = 2^(System.Platform.numBits - 1) - 1:= by
rw [maxValue, toInt_ofInt_of_two_pow_numBits_le] <;> cases System.Platform.numBits_eq
<;> simp_all
@[simp] theorem UInt8.toBitVec_toInt8 (x : UInt8) : x.toInt8.toBitVec = x.toBitVec := rfl
@[simp] theorem UInt16.toBitVec_toInt16 (x : UInt16) : x.toInt16.toBitVec = x.toBitVec := rfl
@[simp] theorem UInt32.toBitVec_toInt32 (x : UInt32) : x.toInt32.toBitVec = x.toBitVec := rfl
@@ -52,47 +166,702 @@ declare_int_theorems ISize System.Platform.numBits
@[simp] theorem UInt64.toUInt64_toInt64 (x : UInt64) : x.toInt64.toUInt64 = x := rfl
@[simp] theorem USize.toUSize_toISize (x : USize) : x.toISize.toUSize = x := rfl
@[simp] theorem ISize.toBitVec_neg (x : ISize) : (-x).toBitVec = -x.toBitVec := rfl
@[simp] theorem ISize.toBitVec_zero : (0 : ISize).toBitVec = 0 := rfl
@[simp] theorem ISize.toBitVec_ofInt (i : Int) : (ofInt i).toBitVec = BitVec.ofInt _ i := rfl
@[simp] theorem Int8.toNat_toInt (x : Int8) : x.toInt.toNat = x.toNatClampNeg := rfl
@[simp] theorem Int16.toNat_toInt (x : Int16) : x.toInt.toNat = x.toNatClampNeg := rfl
@[simp] theorem Int32.toNat_toInt (x : Int32) : x.toInt.toNat = x.toNatClampNeg := rfl
@[simp] theorem Int64.toNat_toInt (x : Int64) : x.toInt.toNat = x.toNatClampNeg := rfl
@[simp] theorem ISize.toNat_toInt (x : ISize) : x.toInt.toNat = x.toNatClampNeg := rfl
@[simp] theorem Int8.neg_zero : -(0 : Int8) = 0 := rfl
@[simp] theorem Int16.neg_zero : -(0 : Int16) = 0 := rfl
@[simp] theorem Int32.neg_zero : -(0 : Int32) = 0 := rfl
@[simp] theorem Int64.neg_zero : -(0 : Int64) = 0 := rfl
@[simp] theorem ISize.neg_zero : -(0 : ISize) = 0 := ISize.toBitVec.inj (by simp)
@[simp] theorem Int8.toInt_toBitVec (x : Int8) : x.toBitVec.toInt = x.toInt := rfl
@[simp] theorem Int16.toInt_toBitVec (x : Int16) : x.toBitVec.toInt = x.toInt := rfl
@[simp] theorem Int32.toInt_toBitVec (x : Int32) : x.toBitVec.toInt = x.toInt := rfl
@[simp] theorem Int64.toInt_toBitVec (x : Int64) : x.toBitVec.toInt = x.toInt := rfl
@[simp] theorem ISize.toInt_toBitVec (x : ISize) : x.toBitVec.toInt = x.toInt := rfl
theorem ISize.toNat_toBitVec_ofNat_of_lt {n : Nat} (h : n < 2^32) :
(ofNat n).toBitVec.toNat = n :=
Nat.mod_eq_of_lt (Nat.lt_of_lt_of_le h (by cases USize.size_eq <;> simp_all +decide))
@[simp] theorem Int8.toBitVec_toInt16 (x : Int8) : x.toInt16.toBitVec = x.toBitVec.signExtend 16 := rfl
@[simp] theorem Int8.toBitVec_toInt32 (x : Int8) : x.toInt32.toBitVec = x.toBitVec.signExtend 32 := rfl
@[simp] theorem Int8.toBitVec_toInt64 (x : Int8) : x.toInt64.toBitVec = x.toBitVec.signExtend 64 := rfl
@[simp] theorem Int8.toBitVec_toISize (x : Int8) : x.toISize.toBitVec = x.toBitVec.signExtend System.Platform.numBits := rfl
theorem ISize.toInt_ofInt {n : Int} (hn : -2^31 n) (hn' : n < 2^31) : toInt (ofInt n) = n := by
rw [toInt, toBitVec_ofInt, BitVec.toInt_ofInt_eq_self] <;> cases System.Platform.numBits_eq
<;> (simp_all; try omega)
@[simp] theorem Int16.toBitVec_toInt8 (x : Int16) : x.toInt8.toBitVec = x.toBitVec.signExtend 8 := rfl
@[simp] theorem Int16.toBitVec_toInt32 (x : Int16) : x.toInt32.toBitVec = x.toBitVec.signExtend 32 := rfl
@[simp] theorem Int16.toBitVec_toInt64 (x : Int16) : x.toInt64.toBitVec = x.toBitVec.signExtend 64 := rfl
@[simp] theorem Int16.toBitVec_toISize (x : Int16) : x.toISize.toBitVec = x.toBitVec.signExtend System.Platform.numBits := rfl
theorem ISize.toNatClampNeg_ofInt_eq_zero {n : Int} (hn : -2^31 n) (hn' : n 0) :
toNatClampNeg (ofInt n) = 0 := by
rwa [toNatClampNeg, toInt_ofInt hn (by omega), Int.toNat_eq_zero]
@[simp] theorem Int32.toBitVec_toInt8 (x : Int32) : x.toInt8.toBitVec = x.toBitVec.signExtend 8 := rfl
@[simp] theorem Int32.toBitVec_toInt16 (x : Int32) : x.toInt16.toBitVec = x.toBitVec.signExtend 16 := rfl
@[simp] theorem Int32.toBitVec_toInt64 (x : Int32) : x.toInt64.toBitVec = x.toBitVec.signExtend 64 := rfl
@[simp] theorem Int32.toBitVec_toISize (x : Int32) : x.toISize.toBitVec = x.toBitVec.signExtend System.Platform.numBits := rfl
theorem ISize.neg_ofInt {n : Int} : -ofInt n = ofInt (-n) :=
toBitVec.inj (by simp [BitVec.ofInt_neg])
@[simp] theorem Int64.toBitVec_toInt8 (x : Int64) : x.toInt8.toBitVec = x.toBitVec.signExtend 8 := rfl
@[simp] theorem Int64.toBitVec_toInt16 (x : Int64) : x.toInt16.toBitVec = x.toBitVec.signExtend 16 := rfl
@[simp] theorem Int64.toBitVec_toInt32 (x : Int64) : x.toInt32.toBitVec = x.toBitVec.signExtend 32 := rfl
@[simp] theorem Int64.toBitVec_toISize (x : Int64) : x.toISize.toBitVec = x.toBitVec.signExtend System.Platform.numBits := rfl
theorem ISize.ofInt_eq_ofNat {n : Nat} : ofInt n = ofNat n :=
toBitVec.inj (by simp)
@[simp] theorem ISize.toBitVec_toInt8 (x : ISize) : x.toInt8.toBitVec = x.toBitVec.signExtend 8 := rfl
@[simp] theorem ISize.toBitVec_toInt16 (x : ISize) : x.toInt16.toBitVec = x.toBitVec.signExtend 16 := rfl
@[simp] theorem ISize.toBitVec_toInt32 (x : ISize) : x.toInt32.toBitVec = x.toBitVec.signExtend 32 := rfl
@[simp] theorem ISize.toBitVec_toInt64 (x : ISize) : x.toInt64.toBitVec = x.toBitVec.signExtend 64 := rfl
theorem ISize.neg_ofNat {n : Nat} : -ofNat n = ofInt (-n) := by
rw [ neg_ofInt, ofInt_eq_ofNat]
theorem Int8.toInt_lt (x : Int8) : x.toInt < 2 ^ 7 := Int.lt_of_mul_lt_mul_left BitVec.toInt_lt (by decide)
theorem Int8.le_toInt (x : Int8) : -2 ^ 7 x.toInt := Int.le_of_mul_le_mul_left BitVec.le_toInt (by decide)
theorem Int16.toInt_lt (x : Int16) : x.toInt < 2 ^ 15 := Int.lt_of_mul_lt_mul_left BitVec.toInt_lt (by decide)
theorem Int16.le_toInt (x : Int16) : -2 ^ 15 x.toInt := Int.le_of_mul_le_mul_left BitVec.le_toInt (by decide)
theorem Int32.toInt_lt (x : Int32) : x.toInt < 2 ^ 31 := Int.lt_of_mul_lt_mul_left BitVec.toInt_lt (by decide)
theorem Int32.le_toInt (x : Int32) : -2 ^ 31 x.toInt := Int.le_of_mul_le_mul_left BitVec.le_toInt (by decide)
theorem Int64.toInt_lt (x : Int64) : x.toInt < 2 ^ 63 := Int.lt_of_mul_lt_mul_left BitVec.toInt_lt (by decide)
theorem Int64.le_toInt (x : Int64) : -2 ^ 63 x.toInt := Int.le_of_mul_le_mul_left BitVec.le_toInt (by decide)
theorem ISize.toInt_lt_two_pow_numBits (x : ISize) : x.toInt < 2 ^ (System.Platform.numBits - 1) := by
have := x.toBitVec.toInt_lt; cases System.Platform.numBits_eq <;> simp_all <;> omega
theorem ISize.two_pow_numBits_le_toInt (x : ISize) : -2 ^ (System.Platform.numBits - 1) x.toInt := by
have := x.toBitVec.le_toInt; cases System.Platform.numBits_eq <;> simp_all <;> omega
theorem ISize.toInt_lt (x : ISize) : x.toInt < 2 ^ 63 := by
have := x.toBitVec.toInt_lt; cases System.Platform.numBits_eq <;> simp_all <;> omega
theorem ISize.le_toInt (x : ISize) : -2 ^ 63 x.toInt := by
have := x.toBitVec.le_toInt; cases System.Platform.numBits_eq <;> simp_all <;> omega
theorem ISize.toNatClampNeg_ofNat_of_lt {n : Nat} (h : n < 2 ^ 31) :
toNatClampNeg (ofNat n) = n := by
rw [toNatClampNeg, ofInt_eq_ofNat, toInt_ofInt (by omega) (by omega), Int.toNat_ofNat]
theorem Int8.toInt_le (x : Int8) : x.toInt Int8.maxValue.toInt := Int.le_of_lt_add_one x.toInt_lt
theorem Int16.toInt_le (x : Int16) : x.toInt Int16.maxValue.toInt := Int.le_of_lt_add_one x.toInt_lt
theorem Int32.toInt_le (x : Int32) : x.toInt Int32.maxValue.toInt := Int.le_of_lt_add_one x.toInt_lt
theorem Int64.toInt_le (x : Int64) : x.toInt Int64.maxValue.toInt := Int.le_of_lt_add_one x.toInt_lt
theorem ISize.toInt_le (x : ISize) : x.toInt ISize.maxValue.toInt := by
rw [toInt_ofInt_of_two_pow_numBits_le]
· exact Int.le_of_lt_add_one (by simpa using x.toInt_lt_two_pow_numBits)
· cases System.Platform.numBits_eq <;> simp_all
· cases System.Platform.numBits_eq <;> simp_all
theorem Int8.minValue_le_toInt (x : Int8) : Int8.minValue.toInt x.toInt := x.le_toInt
theorem Int16.minValue_le_toInt (x : Int16) : Int16.minValue.toInt x.toInt := x.le_toInt
theorem Int32.minValue_le_toInt (x : Int32) : Int32.minValue.toInt x.toInt := x.le_toInt
theorem Int64.minValue_le_toInt (x : Int64) : Int64.minValue.toInt x.toInt := x.le_toInt
theorem ISize.minValue_le_toInt (x : ISize) : ISize.minValue.toInt x.toInt := by
rw [toInt_ofInt_of_two_pow_numBits_le]
· exact x.two_pow_numBits_le_toInt
· cases System.Platform.numBits_eq <;> simp_all
· cases System.Platform.numBits_eq <;> simp_all
theorem ISize.toNatClampNeg_neg_ofNat_of_le {n : Nat} (h : n 2 ^ 31) :
toNatClampNeg (-ofNat n) = 0 := by
rw [neg_ofNat, toNatClampNeg_ofInt_eq_zero (by omega) (by omega)]
theorem ISize.toInt_minValue_le : ISize.minValue.toInt -2^31 := by
rw [minValue, toInt_ofInt_of_two_pow_numBits_le] <;> cases System.Platform.numBits_eq
<;> simp_all
theorem ISize.toInt_ofNat_of_lt {n : Nat} (h : n < 2 ^ 31) : toInt (ofNat n) = n := by
rw [ ofInt_eq_ofNat, toInt_ofInt (by omega) (by omega)]
theorem ISize.le_toInt_maxValue : 2 ^ 31 - 1 ISize.maxValue.toInt := by
rw [maxValue, toInt_ofInt_of_two_pow_numBits_le] <;> cases System.Platform.numBits_eq
<;> simp_all
theorem ISize.toInt_neg_ofNat_of_le {n : Nat} (h : n 2 ^ 31) : toInt (-ofNat n) = -n := by
rw [ ofInt_eq_ofNat, neg_ofInt, toInt_ofInt (by omega) (by omega)]
theorem Int8.iSizeMinValue_le_toInt (x : Int8) : ISize.minValue.toInt x.toInt :=
Int.le_trans (Int.le_trans ISize.toInt_minValue_le (by decide)) x.le_toInt
theorem Int8.toInt_le_iSizeMaxValue (x : Int8) : x.toInt ISize.maxValue.toInt :=
Int.le_trans x.toInt_le (Int.le_trans (by decide) ISize.le_toInt_maxValue)
theorem Int16.iSizeMinValue_le_toInt (x : Int16) : ISize.minValue.toInt x.toInt :=
Int.le_trans (Int.le_trans ISize.toInt_minValue_le (by decide)) x.le_toInt
theorem Int16.toInt_le_iSizeMaxValue (x : Int16) : x.toInt ISize.maxValue.toInt :=
Int.le_trans x.toInt_le (Int.le_trans (by decide) ISize.le_toInt_maxValue)
theorem Int32.iSizeMinValue_le_toInt (x : Int32) : ISize.minValue.toInt x.toInt :=
Int.le_trans (Int.le_trans ISize.toInt_minValue_le (by decide)) x.le_toInt
theorem Int32.toInt_le_iSizeMaxValue (x : Int32) : x.toInt ISize.maxValue.toInt :=
Int.le_trans x.toInt_le (Int.le_trans (by decide) ISize.le_toInt_maxValue)
theorem ISize.int64MinValue_le_toInt (x : ISize) : Int64.minValue.toInt x.toInt :=
Int.le_trans (by decide) x.le_toInt
theorem ISize.toInt_le_int64MaxValue (x : ISize) : x.toInt Int64.maxValue.toInt :=
Int.le_of_lt_add_one x.toInt_lt
theorem Int8.toNatClampNeg_lt (x : Int8) : x.toNatClampNeg < 2 ^ 7 := (Int.toNat_lt' (by decide)).2 x.toInt_lt
theorem Int16.toNatClampNeg_lt (x : Int16) : x.toNatClampNeg < 2 ^ 15 := (Int.toNat_lt' (by decide)).2 x.toInt_lt
theorem Int32.toNatClampNeg_lt (x : Int32) : x.toNatClampNeg < 2 ^ 31 := (Int.toNat_lt' (by decide)).2 x.toInt_lt
theorem Int64.toNatClampNeg_lt (x : Int64) : x.toNatClampNeg < 2 ^ 63 := (Int.toNat_lt' (by decide)).2 x.toInt_lt
theorem ISize.toNatClampNeg_lt_two_pow_numBits (x : ISize) : x.toNatClampNeg < 2 ^ (System.Platform.numBits - 1) := by
rw [toNatClampNeg, Int.toNat_lt', Int.natCast_pow]
· exact x.toInt_lt_two_pow_numBits
· cases System.Platform.numBits_eq <;> simp_all
theorem ISize.toNatClampNeg_lt (x : ISize) : x.toNatClampNeg < 2 ^ 63 := (Int.toNat_lt' (by decide)).2 x.toInt_lt
@[simp] theorem Int8.toInt_toInt16 (x : Int8) : x.toInt16.toInt = x.toInt :=
x.toBitVec.toInt_signExtend_of_le (by decide)
@[simp] theorem Int8.toInt_toInt32 (x : Int8) : x.toInt32.toInt = x.toInt :=
x.toBitVec.toInt_signExtend_of_le (by decide)
@[simp] theorem Int8.toInt_toInt64 (x : Int8) : x.toInt64.toInt = x.toInt :=
x.toBitVec.toInt_signExtend_of_le (by decide)
@[simp] theorem Int8.toInt_toISize (x : Int8) : x.toISize.toInt = x.toInt :=
x.toBitVec.toInt_signExtend_of_le (by cases System.Platform.numBits_eq <;> simp_all)
@[simp] theorem Int16.toInt_toInt8 (x : Int16) : x.toInt8.toInt = x.toInt.bmod (2 ^ 8) :=
x.toBitVec.toInt_signExtend_eq_toInt_bmod_of_le (by decide)
@[simp] theorem Int16.toInt_toInt32 (x : Int16) : x.toInt32.toInt = x.toInt :=
x.toBitVec.toInt_signExtend_of_le (by decide)
@[simp] theorem Int16.toInt_toInt64 (x : Int16) : x.toInt64.toInt = x.toInt :=
x.toBitVec.toInt_signExtend_of_le (by decide)
@[simp] theorem Int16.toInt_toISize (x : Int16) : x.toISize.toInt = x.toInt :=
x.toBitVec.toInt_signExtend_of_le (by cases System.Platform.numBits_eq <;> simp_all)
@[simp] theorem Int32.toInt_toInt8 (x : Int32) : x.toInt8.toInt = x.toInt.bmod (2 ^ 8) :=
x.toBitVec.toInt_signExtend_eq_toInt_bmod_of_le (by decide)
@[simp] theorem Int32.toInt_toInt16 (x : Int32) : x.toInt16.toInt = x.toInt.bmod (2 ^ 16) :=
x.toBitVec.toInt_signExtend_eq_toInt_bmod_of_le (by decide)
@[simp] theorem Int32.toInt_toInt64 (x : Int32) : x.toInt64.toInt = x.toInt :=
x.toBitVec.toInt_signExtend_of_le (by decide)
@[simp] theorem Int32.toInt_toISize (x : Int32) : x.toISize.toInt = x.toInt :=
x.toBitVec.toInt_signExtend_of_le (by cases System.Platform.numBits_eq <;> simp_all)
@[simp] theorem Int64.toInt_toInt8 (x : Int64) : x.toInt8.toInt = x.toInt.bmod (2 ^ 8) :=
x.toBitVec.toInt_signExtend_eq_toInt_bmod_of_le (by decide)
@[simp] theorem Int64.toInt_toInt16 (x : Int64) : x.toInt16.toInt = x.toInt.bmod (2 ^ 16) :=
x.toBitVec.toInt_signExtend_eq_toInt_bmod_of_le (by decide)
@[simp] theorem Int64.toInt_toInt32 (x : Int64) : x.toInt32.toInt = x.toInt.bmod (2 ^ 32) :=
x.toBitVec.toInt_signExtend_eq_toInt_bmod_of_le (by decide)
@[simp] theorem Int64.toInt_toISize (x : Int64) : x.toISize.toInt = x.toInt.bmod (2 ^ System.Platform.numBits) :=
x.toBitVec.toInt_signExtend_eq_toInt_bmod_of_le (by cases System.Platform.numBits_eq <;> simp_all)
@[simp] theorem ISize.toInt_toInt8 (x : ISize) : x.toInt8.toInt = x.toInt.bmod (2 ^ 8) :=
x.toBitVec.toInt_signExtend_eq_toInt_bmod_of_le (by cases System.Platform.numBits_eq <;> simp_all)
@[simp] theorem ISize.toInt_toInt16 (x : ISize) : x.toInt16.toInt = x.toInt.bmod (2 ^ 16) :=
x.toBitVec.toInt_signExtend_eq_toInt_bmod_of_le (by cases System.Platform.numBits_eq <;> simp_all)
@[simp] theorem ISize.toInt_toInt32 (x : ISize) : x.toInt32.toInt = x.toInt.bmod (2 ^ 32) :=
x.toBitVec.toInt_signExtend_eq_toInt_bmod_of_le (by cases System.Platform.numBits_eq <;> simp_all)
@[simp] theorem ISize.toInt_toInt64 (x : ISize) : x.toInt64.toInt = x.toInt :=
x.toBitVec.toInt_signExtend_of_le (by cases System.Platform.numBits_eq <;> simp_all)
@[simp] theorem Int8.toNatClampNeg_toInt16 (x : Int8) : x.toInt16.toNatClampNeg = x.toNatClampNeg :=
congrArg Int.toNat x.toInt_toInt16
@[simp] theorem Int8.toNatClampNeg_toInt32 (x : Int8) : x.toInt32.toNatClampNeg = x.toNatClampNeg :=
congrArg Int.toNat x.toInt_toInt32
@[simp] theorem Int8.toNatClampNeg_toInt64 (x : Int8) : x.toInt64.toNatClampNeg = x.toNatClampNeg :=
congrArg Int.toNat x.toInt_toInt64
@[simp] theorem Int8.toNatClampNeg_toISize (x : Int8) : x.toISize.toNatClampNeg = x.toNatClampNeg :=
congrArg Int.toNat x.toInt_toISize
@[simp] theorem Int16.toNatClampNeg_toInt32 (x : Int16) : x.toInt32.toNatClampNeg = x.toNatClampNeg :=
congrArg Int.toNat x.toInt_toInt32
@[simp] theorem Int16.toNatClampNeg_toInt64 (x : Int16) : x.toInt64.toNatClampNeg = x.toNatClampNeg :=
congrArg Int.toNat x.toInt_toInt64
@[simp] theorem Int16.toNatClampNeg_toISize (x : Int16) : x.toISize.toNatClampNeg = x.toNatClampNeg :=
congrArg Int.toNat x.toInt_toISize
@[simp] theorem Int32.toNatClampNeg_toInt64 (x : Int32) : x.toInt64.toNatClampNeg = x.toNatClampNeg :=
congrArg Int.toNat x.toInt_toInt64
@[simp] theorem Int32.toNatClampNeg_toISize (x : Int32) : x.toISize.toNatClampNeg = x.toNatClampNeg :=
congrArg Int.toNat x.toInt_toISize
@[simp] theorem ISize.toNatClampNeg_toInt64 (x : ISize) : x.toInt64.toNatClampNeg = x.toNatClampNeg :=
congrArg Int.toNat x.toInt_toInt64
@[simp] theorem Int8.toInt8_toUInt8 (x : Int8) : x.toUInt8.toInt8 = x := rfl
@[simp] theorem Int16.toInt16_toUInt16 (x : Int16) : x.toUInt16.toInt16 = x := rfl
@[simp] theorem Int32.toInt32_toUInt32 (x : Int32) : x.toUInt32.toInt32 = x := rfl
@[simp] theorem Int64.toInt64_toUInt64 (x : Int64) : x.toUInt64.toInt64 = x := rfl
@[simp] theorem ISize.toISize_toUSize (x : ISize) : x.toUSize.toISize = x := rfl
theorem Int8.toNat_toBitVec (x : Int8) : x.toBitVec.toNat = x.toUInt8.toNat := rfl
theorem Int16.toNat_toBitVec (x : Int16) : x.toBitVec.toNat = x.toUInt16.toNat := rfl
theorem Int32.toNat_toBitVec (x : Int32) : x.toBitVec.toNat = x.toUInt32.toNat := rfl
theorem Int64.toNat_toBitVec (x : Int64) : x.toBitVec.toNat = x.toUInt64.toNat := rfl
theorem ISize.toNat_toBitVec (x : ISize) : x.toBitVec.toNat = x.toUSize.toNat := rfl
theorem Int8.toNat_toBitVec_of_le {x : Int8} (hx : 0 x) : x.toBitVec.toNat = x.toNatClampNeg :=
(x.toBitVec.toNat_toInt_of_sle hx).symm
theorem Int16.toNat_toBitVec_of_le {x : Int16} (hx : 0 x) : x.toBitVec.toNat = x.toNatClampNeg :=
(x.toBitVec.toNat_toInt_of_sle hx).symm
theorem Int32.toNat_toBitVec_of_le {x : Int32} (hx : 0 x) : x.toBitVec.toNat = x.toNatClampNeg :=
(x.toBitVec.toNat_toInt_of_sle hx).symm
theorem Int64.toNat_toBitVec_of_le {x : Int64} (hx : 0 x) : x.toBitVec.toNat = x.toNatClampNeg :=
(x.toBitVec.toNat_toInt_of_sle hx).symm
theorem ISize.toNat_toBitVec_of_le {x : ISize} (hx : 0 x) : x.toBitVec.toNat = x.toNatClampNeg :=
(x.toBitVec.toNat_toInt_of_sle hx).symm
theorem Int8.toNat_toUInt8_of_le {x : Int8} (hx : 0 x) : x.toUInt8.toNat = x.toNatClampNeg := by
rw [ toNat_toBitVec, toNat_toBitVec_of_le hx]
theorem Int16.toNat_toUInt16_of_le {x : Int16} (hx : 0 x) : x.toUInt16.toNat = x.toNatClampNeg := by
rw [ toNat_toBitVec, toNat_toBitVec_of_le hx]
theorem Int32.toNat_toUInt32_of_le {x : Int32} (hx : 0 x) : x.toUInt32.toNat = x.toNatClampNeg := by
rw [ toNat_toBitVec, toNat_toBitVec_of_le hx]
theorem Int64.toNat_toUInt64_of_le {x : Int64} (hx : 0 x) : x.toUInt64.toNat = x.toNatClampNeg := by
rw [ toNat_toBitVec, toNat_toBitVec_of_le hx]
theorem ISize.toNat_toUISize_of_le {x : ISize} (hx : 0 x) : x.toUSize.toNat = x.toNatClampNeg := by
rw [ toNat_toBitVec, toNat_toBitVec_of_le hx]
theorem Int8.toFin_toBitVec (x : Int8) : x.toBitVec.toFin = x.toUInt8.toFin := rfl
theorem Int16.toFin_toBitVec (x : Int16) : x.toBitVec.toFin = x.toUInt16.toFin := rfl
theorem Int32.toFin_toBitVec (x : Int32) : x.toBitVec.toFin = x.toUInt32.toFin := rfl
theorem Int64.toFin_toBitVec (x : Int64) : x.toBitVec.toFin = x.toUInt64.toFin := rfl
theorem ISize.toFin_toBitVec (x : ISize) : x.toBitVec.toFin = x.toUSize.toFin := rfl
@[simp] theorem Int8.toBitVec_toUInt8 (x : Int8) : x.toUInt8.toBitVec = x.toBitVec := rfl
@[simp] theorem Int16.toBitVec_toUInt16 (x : Int16) : x.toUInt16.toBitVec = x.toBitVec := rfl
@[simp] theorem Int32.toBitVec_toUInt32 (x : Int32) : x.toUInt32.toBitVec = x.toBitVec := rfl
@[simp] theorem Int64.toBitVec_toUInt64 (x : Int64) : x.toUInt64.toBitVec = x.toBitVec := rfl
@[simp] theorem ISize.toBitVec_toUISize (x : ISize) : x.toUSize.toBitVec = x.toBitVec := rfl
@[simp] theorem UInt8.ofBitVec_int8ToBitVec (x : Int8) : UInt8.ofBitVec x.toBitVec = x.toUInt8 := rfl
@[simp] theorem UInt16.ofBitVec_int16ToBitVec (x : Int16) : UInt16.ofBitVec x.toBitVec = x.toUInt16 := rfl
@[simp] theorem UInt32.ofBitVec_int32ToBitVec (x : Int32) : UInt32.ofBitVec x.toBitVec = x.toUInt32 := rfl
@[simp] theorem UInt64.ofBitVec_int64ToBitVec (x : Int64) : UInt64.ofBitVec x.toBitVec = x.toUInt64 := rfl
@[simp] theorem USize.ofBitVec_iSizeToBitVec (x : ISize) : USize.ofBitVec x.toBitVec = x.toUSize := rfl
@[simp] theorem Int8.ofBitVec_toBitVec (x : Int8) : Int8.ofBitVec x.toBitVec = x := rfl
@[simp] theorem Int16.ofBitVec_toBitVec (x : Int16) : Int16.ofBitVec x.toBitVec = x := rfl
@[simp] theorem Int32.ofBitVec_toBitVec (x : Int32) : Int32.ofBitVec x.toBitVec = x := rfl
@[simp] theorem Int64.ofBitVec_toBitVec (x : Int64) : Int64.ofBitVec x.toBitVec = x := rfl
@[simp] theorem ISize.ofBitVec_toBitVec (x : ISize) : ISize.ofBitVec x.toBitVec = x := rfl
@[simp] theorem Int8.ofBitVec_int16ToBitVec (x : Int16) : Int8.ofBitVec (x.toBitVec.signExtend 8) = x.toInt8 := rfl
@[simp] theorem Int8.ofBitVec_int32ToBitVec (x : Int32) : Int8.ofBitVec (x.toBitVec.signExtend 8) = x.toInt8 := rfl
@[simp] theorem Int8.ofBitVec_int64ToBitVec (x : Int64) : Int8.ofBitVec (x.toBitVec.signExtend 8) = x.toInt8 := rfl
@[simp] theorem Int8.ofBitVec_iSizeToBitVec (x : ISize) : Int8.ofBitVec (x.toBitVec.signExtend 8) = x.toInt8 := rfl
@[simp] theorem Int16.ofBitVec_int8toBitVec (x : Int8) : Int16.ofBitVec (x.toBitVec.signExtend 16) = x.toInt16 := rfl
@[simp] theorem Int16.ofBitVec_int32ToBitVec (x : Int32) : Int16.ofBitVec (x.toBitVec.signExtend 16) = x.toInt16 := rfl
@[simp] theorem Int16.ofBitVec_int64ToBitVec (x : Int64) : Int16.ofBitVec (x.toBitVec.signExtend 16) = x.toInt16 := rfl
@[simp] theorem Int16.ofBitVec_iSizeToBitVec (x : ISize) : Int16.ofBitVec (x.toBitVec.signExtend 16) = x.toInt16 := rfl
@[simp] theorem Int32.ofBitVec_int8toBitVec (x : Int8) : Int32.ofBitVec (x.toBitVec.signExtend 32) = x.toInt32 := rfl
@[simp] theorem Int32.ofBitVec_int16ToBitVec (x : Int16) : Int32.ofBitVec (x.toBitVec.signExtend 32) = x.toInt32 := rfl
@[simp] theorem Int32.ofBitVec_int64ToBitVec (x : Int64) : Int32.ofBitVec (x.toBitVec.signExtend 32) = x.toInt32 := rfl
@[simp] theorem Int32.ofBitVec_iSizeToBitVec (x : ISize) : Int32.ofBitVec (x.toBitVec.signExtend 32) = x.toInt32 := rfl
@[simp] theorem Int64.ofBitVec_int8toBitVec (x : Int8) : Int64.ofBitVec (x.toBitVec.signExtend 64) = x.toInt64 := rfl
@[simp] theorem Int64.ofBitVec_int16ToBitVec (x : Int16) : Int64.ofBitVec (x.toBitVec.signExtend 64) = x.toInt64 := rfl
@[simp] theorem Int64.ofBitVec_int32ToBitVec (x : Int32) : Int64.ofBitVec (x.toBitVec.signExtend 64) = x.toInt64 := rfl
@[simp] theorem Int64.ofBitVec_iSizeToBitVec (x : ISize) : Int64.ofBitVec (x.toBitVec.signExtend 64) = x.toInt64 := rfl
@[simp] theorem ISize.ofBitVec_int8toBitVec (x : Int8) : ISize.ofBitVec (x.toBitVec.signExtend System.Platform.numBits) = x.toISize := rfl
@[simp] theorem ISize.ofBitVec_int16ToBitVec (x : Int16) : ISize.ofBitVec (x.toBitVec.signExtend System.Platform.numBits) = x.toISize := rfl
@[simp] theorem ISize.ofBitVec_int32ToBitVec (x : Int32) : ISize.ofBitVec (x.toBitVec.signExtend System.Platform.numBits) = x.toISize := rfl
@[simp] theorem ISize.ofBitVec_int64ToBitVec (x : Int64) : ISize.ofBitVec (x.toBitVec.signExtend System.Platform.numBits) = x.toISize := rfl
@[simp] theorem Int8.toBitVec_ofIntLE (x : Int) (h₁ h₂) : (Int8.ofIntLE x h₁ h₂).toBitVec = BitVec.ofInt 8 x := rfl
@[simp] theorem Int16.toBitVec_ofIntLE (x : Int) (h₁ h₂) : (Int16.ofIntLE x h₁ h₂).toBitVec = BitVec.ofInt 16 x := rfl
@[simp] theorem Int32.toBitVec_ofIntLE (x : Int) (h₁ h₂) : (Int32.ofIntLE x h₁ h₂).toBitVec = BitVec.ofInt 32 x := rfl
@[simp] theorem Int64.toBitVec_ofIntLE (x : Int) (h₁ h₂) : (Int64.ofIntLE x h₁ h₂).toBitVec = BitVec.ofInt 64 x := rfl
@[simp] theorem ISize.toBitVec_ofIntLE (x : Int) (h₁ h₂) : (ISize.ofIntLE x h₁ h₂).toBitVec = BitVec.ofInt System.Platform.numBits x := rfl
@[simp] theorem Int8.toInt_bmod (x : Int8) : x.toInt.bmod 256 = x.toInt := Int.bmod_eq_self_of_le x.le_toInt x.toInt_lt
@[simp] theorem Int16.toInt_bmod (x : Int16) : x.toInt.bmod 65536 = x.toInt := Int.bmod_eq_self_of_le x.le_toInt x.toInt_lt
@[simp] theorem Int32.toInt_bmod (x : Int32) : x.toInt.bmod 4294967296 = x.toInt := Int.bmod_eq_self_of_le x.le_toInt x.toInt_lt
@[simp] theorem Int64.toInt_bmod (x : Int64) : x.toInt.bmod 18446744073709551616 = x.toInt := Int.bmod_eq_self_of_le x.le_toInt x.toInt_lt
@[simp] theorem ISize.toInt_bmod_two_pow_numBits (x : ISize) : x.toInt.bmod (2 ^ System.Platform.numBits) = x.toInt := by
refine Int.bmod_eq_self_of_le ?_ ?_
· have := x.two_pow_numBits_le_toInt
cases System.Platform.numBits_eq <;> simp_all
· have := x.toInt_lt_two_pow_numBits
cases System.Platform.numBits_eq <;> simp_all
@[simp] theorem Int8.toInt_bmod_65536 (x : Int8) : x.toInt.bmod 65536 = x.toInt :=
Int.bmod_eq_self_of_le (Int.le_trans (by decide) x.le_toInt) (Int.lt_of_lt_of_le x.toInt_lt (by decide))
@[simp] theorem Int8.toInt_bmod_4294967296 (x : Int8) : x.toInt.bmod 4294967296 = x.toInt :=
Int.bmod_eq_self_of_le (Int.le_trans (by decide) x.le_toInt) (Int.lt_of_lt_of_le x.toInt_lt (by decide))
@[simp] theorem Int16.toInt_bmod_4294967296 (x : Int16) : x.toInt.bmod 4294967296 = x.toInt :=
Int.bmod_eq_self_of_le (Int.le_trans (by decide) x.le_toInt) (Int.lt_of_lt_of_le x.toInt_lt (by decide))
@[simp] theorem Int8.toInt_bmod_18446744073709551616 (x : Int8) : x.toInt.bmod 18446744073709551616 = x.toInt :=
Int.bmod_eq_self_of_le (Int.le_trans (by decide) x.le_toInt) (Int.lt_of_lt_of_le x.toInt_lt (by decide))
@[simp] theorem Int16.toInt_bmod_18446744073709551616 (x : Int16) : x.toInt.bmod 18446744073709551616 = x.toInt :=
Int.bmod_eq_self_of_le (Int.le_trans (by decide) x.le_toInt) (Int.lt_of_lt_of_le x.toInt_lt (by decide))
@[simp] theorem Int32.toInt_bmod_18446744073709551616 (x : Int32) : x.toInt.bmod 18446744073709551616 = x.toInt :=
Int.bmod_eq_self_of_le (Int.le_trans (by decide) x.le_toInt) (Int.lt_of_lt_of_le x.toInt_lt (by decide))
@[simp] theorem ISize.toInt_bmod_18446744073709551616 (x : ISize) : x.toInt.bmod 18446744073709551616 = x.toInt :=
Int.bmod_eq_self_of_le x.le_toInt x.toInt_lt
@[simp] theorem Int8.toInt_bmod_two_pow_numBits (x : Int8) : x.toInt.bmod (2 ^ System.Platform.numBits) = x.toInt := by
refine Int.bmod_eq_self_of_le (Int.le_trans ?_ x.iSizeMinValue_le_toInt)
(Int.lt_of_le_sub_one (Int.le_trans x.toInt_le_iSizeMaxValue ?_))
all_goals cases System.Platform.numBits_eq <;> simp_all
@[simp] theorem Int16.toInt_bmod_two_pow_numBits (x : Int16) : x.toInt.bmod (2 ^ System.Platform.numBits) = x.toInt := by
refine Int.bmod_eq_self_of_le (Int.le_trans ?_ x.iSizeMinValue_le_toInt)
(Int.lt_of_le_sub_one (Int.le_trans x.toInt_le_iSizeMaxValue ?_))
all_goals cases System.Platform.numBits_eq <;> simp_all
@[simp] theorem Int32.toInt_bmod_two_pow_numBits (x : Int32) : x.toInt.bmod (2 ^ System.Platform.numBits) = x.toInt := by
refine Int.bmod_eq_self_of_le (Int.le_trans ?_ x.iSizeMinValue_le_toInt)
(Int.lt_of_le_sub_one (Int.le_trans x.toInt_le_iSizeMaxValue ?_))
all_goals cases System.Platform.numBits_eq <;> simp_all
@[simp] theorem BitVec.ofInt_int8ToInt (x : Int8) : BitVec.ofInt 8 x.toInt = x.toBitVec := BitVec.eq_of_toInt_eq (by simp)
@[simp] theorem BitVec.ofInt_int16ToInt (x : Int16) : BitVec.ofInt 16 x.toInt = x.toBitVec := BitVec.eq_of_toInt_eq (by simp)
@[simp] theorem BitVec.ofInt_int32ToInt (x : Int32) : BitVec.ofInt 32 x.toInt = x.toBitVec := BitVec.eq_of_toInt_eq (by simp)
@[simp] theorem BitVec.ofInt_int64ToInt (x : Int64) : BitVec.ofInt 64 x.toInt = x.toBitVec := BitVec.eq_of_toInt_eq (by simp)
@[simp] theorem BitVec.ofInt_iSizeToInt (x : ISize) : BitVec.ofInt System.Platform.numBits x.toInt = x.toBitVec :=
BitVec.eq_of_toInt_eq (by simp)
@[simp] theorem Int8.ofIntLE_toInt (x : Int8) : Int8.ofIntLE x.toInt x.minValue_le_toInt x.toInt_le = x := Int8.toBitVec.inj (by simp)
@[simp] theorem Int16.ofIntLE_toInt (x : Int16) : Int16.ofIntLE x.toInt x.minValue_le_toInt x.toInt_le = x := Int16.toBitVec.inj (by simp)
@[simp] theorem Int32.ofIntLE_toInt (x : Int32) : Int32.ofIntLE x.toInt x.minValue_le_toInt x.toInt_le = x := Int32.toBitVec.inj (by simp)
@[simp] theorem Int64.ofIntLE_toInt (x : Int64) : Int64.ofIntLE x.toInt x.minValue_le_toInt x.toInt_le = x := Int64.toBitVec.inj (by simp)
@[simp] theorem ISize.ofIntLE_toInt (x : ISize) : ISize.ofIntLE x.toInt x.minValue_le_toInt x.toInt_le = x := ISize.toBitVec.inj (by simp)
theorem Int8.ofIntLE_int16ToInt (x : Int16) {h₁ h₂} : Int8.ofIntLE x.toInt h₁ h₂ = x.toInt8 := rfl
theorem Int8.ofIntLE_int32ToInt (x : Int32) {h₁ h₂} : Int8.ofIntLE x.toInt h₁ h₂ = x.toInt8 := rfl
theorem Int8.ofIntLE_int64ToInt (x : Int64) {h₁ h₂} : Int8.ofIntLE x.toInt h₁ h₂ = x.toInt8 := rfl
theorem Int8.ofIntLE_iSizeToInt (x : ISize) {h₁ h₂} : Int8.ofIntLE x.toInt h₁ h₂ = x.toInt8 := rfl
@[simp] theorem Int16.ofIntLE_int8ToInt (x : Int8) :
Int16.ofIntLE x.toInt (Int.le_trans (by decide) x.minValue_le_toInt) (Int.le_trans x.toInt_le (by decide)) = x.toInt16 := rfl
theorem Int16.ofIntLE_int32ToInt (x : Int32) {h₁ h₂} : Int16.ofIntLE x.toInt h₁ h₂ = x.toInt16 := rfl
theorem Int16.ofIntLE_int64ToInt (x : Int64) {h₁ h₂} : Int16.ofIntLE x.toInt h₁ h₂ = x.toInt16 := rfl
theorem Int16.ofIntLE_iSizeToInt (x : ISize) {h₁ h₂} : Int16.ofIntLE x.toInt h₁ h₂ = x.toInt16 := rfl
@[simp] theorem Int32.ofIntLE_int8ToInt (x : Int8) :
Int32.ofIntLE x.toInt (Int.le_trans (by decide) x.minValue_le_toInt) (Int.le_trans x.toInt_le (by decide)) = x.toInt32 := rfl
@[simp] theorem Int32.ofIntLE_int16ToInt (x : Int16) :
Int32.ofIntLE x.toInt (Int.le_trans (by decide) x.minValue_le_toInt) (Int.le_trans x.toInt_le (by decide)) = x.toInt32 := rfl
theorem Int32.ofIntLE_int64ToInt (x : Int64) {h₁ h₂} : Int32.ofIntLE x.toInt h₁ h₂ = x.toInt32 := rfl
theorem Int32.ofIntLE_iSizeToInt (x : ISize) {h₁ h₂} : Int32.ofIntLE x.toInt h₁ h₂ = x.toInt32 := rfl
@[simp] theorem Int64.ofIntLE_int8ToInt (x : Int8) :
Int64.ofIntLE x.toInt (Int.le_trans (by decide) x.minValue_le_toInt) (Int.le_trans x.toInt_le (by decide)) = x.toInt64 := rfl
@[simp] theorem Int64.ofIntLE_int16ToInt (x : Int16) :
Int64.ofIntLE x.toInt (Int.le_trans (by decide) x.minValue_le_toInt) (Int.le_trans x.toInt_le (by decide)) = x.toInt64 := rfl
@[simp] theorem Int64.ofIntLE_int32ToInt (x : Int32) :
Int64.ofIntLE x.toInt (Int.le_trans (by decide) x.minValue_le_toInt) (Int.le_trans x.toInt_le (by decide)) = x.toInt64 := rfl
@[simp] theorem Int64.ofIntLE_iSizeToInt (x : ISize) :
Int64.ofIntLE x.toInt x.int64MinValue_le_toInt x.toInt_le_int64MaxValue = x.toInt64 := rfl
@[simp] theorem ISize.ofIntLE_int8ToInt (x : Int8) :
ISize.ofIntLE x.toInt x.iSizeMinValue_le_toInt x.toInt_le_iSizeMaxValue = x.toISize := rfl
@[simp] theorem ISize.ofIntLE_int16ToInt (x : Int16) :
ISize.ofIntLE x.toInt x.iSizeMinValue_le_toInt x.toInt_le_iSizeMaxValue = x.toISize := rfl
@[simp] theorem ISize.ofIntLE_int32ToInt (x : Int32) :
ISize.ofIntLE x.toInt x.iSizeMinValue_le_toInt x.toInt_le_iSizeMaxValue = x.toISize := rfl
theorem ISize.ofIntLE_int64ToInt (x : Int64) {h₁ h₂} : ISize.ofIntLE x.toInt h₁ h₂ = x.toISize := rfl
@[simp] theorem Int8.ofInt_toInt (x : Int8) : Int8.ofInt x.toInt = x := Int8.toBitVec.inj (by simp)
@[simp] theorem Int16.ofInt_toInt (x : Int16) : Int16.ofInt x.toInt = x := Int16.toBitVec.inj (by simp)
@[simp] theorem Int32.ofInt_toInt (x : Int32) : Int32.ofInt x.toInt = x := Int32.toBitVec.inj (by simp)
@[simp] theorem Int64.ofInt_toInt (x : Int64) : Int64.ofInt x.toInt = x := Int64.toBitVec.inj (by simp)
@[simp] theorem ISize.ofInt_toInt (x : ISize) : ISize.ofInt x.toInt = x := ISize.toBitVec.inj (by simp)
@[simp] theorem Int8.ofInt_int16ToInt (x : Int16) : Int8.ofInt x.toInt = x.toInt8 := rfl
@[simp] theorem Int8.ofInt_int32ToInt (x : Int32) : Int8.ofInt x.toInt = x.toInt8 := rfl
@[simp] theorem Int8.ofInt_int64ToInt (x : Int64) : Int8.ofInt x.toInt = x.toInt8 := rfl
@[simp] theorem Int8.ofInt_iSizeToInt (x : ISize) : Int8.ofInt x.toInt = x.toInt8 := rfl
@[simp] theorem Int16.ofInt_int8ToInt (x : Int8) : Int16.ofInt x.toInt = x.toInt16 := rfl
@[simp] theorem Int16.ofInt_int32ToInt (x : Int32) : Int16.ofInt x.toInt = x.toInt16 := rfl
@[simp] theorem Int16.ofInt_int64ToInt (x : Int64) : Int16.ofInt x.toInt = x.toInt16 := rfl
@[simp] theorem Int16.ofInt_iSizeToInt (x : ISize) : Int16.ofInt x.toInt = x.toInt16 := rfl
@[simp] theorem Int32.ofInt_int8ToInt (x : Int8) : Int32.ofInt x.toInt = x.toInt32 := rfl
@[simp] theorem Int32.ofInt_int16ToInt (x : Int16) : Int32.ofInt x.toInt = x.toInt32 := rfl
@[simp] theorem Int32.ofInt_int64ToInt (x : Int64) : Int32.ofInt x.toInt = x.toInt32 := rfl
@[simp] theorem Int32.ofInt_iSizeToInt (x : ISize) : Int32.ofInt x.toInt = x.toInt32 := rfl
@[simp] theorem Int64.ofInt_int8ToInt (x : Int8) : Int64.ofInt x.toInt = x.toInt64 := rfl
@[simp] theorem Int64.ofInt_int16ToInt (x : Int16) : Int64.ofInt x.toInt = x.toInt64 := rfl
@[simp] theorem Int64.ofInt_int32ToInt (x : Int32) : Int64.ofInt x.toInt = x.toInt64 := rfl
@[simp] theorem Int64.ofInt_iSizeToInt (x : ISize) : Int64.ofInt x.toInt = x.toInt64 := rfl
@[simp] theorem ISize.ofInt_int8ToInt (x : Int8) : ISize.ofInt x.toInt = x.toISize := rfl
@[simp] theorem ISize.ofInt_int16ToInt (x : Int16) : ISize.ofInt x.toInt = x.toISize := rfl
@[simp] theorem ISize.ofInt_int32ToInt (x : Int32) : ISize.ofInt x.toInt = x.toISize := rfl
@[simp] theorem ISize.ofInt_int64ToInt (x : Int64) : ISize.ofInt x.toInt = x.toISize := rfl
@[simp] theorem Int8.toInt_ofIntLE {x : Int} {h₁ h₂} : (ofIntLE x h₁ h₂).toInt = x := by
rw [ofIntLE, toInt_ofInt h₁ (Int.lt_of_le_sub_one h₂)]
@[simp] theorem Int16.toInt_ofIntLE {x : Int} {h₁ h₂} : (ofIntLE x h₁ h₂).toInt = x := by
rw [ofIntLE, toInt_ofInt h₁ (Int.lt_of_le_sub_one h₂)]
@[simp] theorem Int32.toInt_ofIntLE {x : Int} {h₁ h₂} : (ofIntLE x h₁ h₂).toInt = x := by
rw [ofIntLE, toInt_ofInt h₁ (Int.lt_of_le_sub_one h₂)]
@[simp] theorem Int64.toInt_ofIntLE {x : Int} {h₁ h₂} : (ofIntLE x h₁ h₂).toInt = x := by
rw [ofIntLE, toInt_ofInt h₁ (Int.lt_of_le_sub_one h₂)]
@[simp] theorem ISize.toInt_ofIntLE {x : Int} {h₁ h₂} : (ofIntLE x h₁ h₂).toInt = x := by
rw [ofIntLE, toInt_ofInt_of_two_pow_numBits_le]
· simpa using h₁
· apply Int.lt_of_le_sub_one
simpa using h₂
theorem Int8.ofIntLE_eq_ofIntTruncate {x : Int} {h₁ h₂} : (ofIntLE x h₁ h₂) = ofIntTruncate x := by
rw [ofIntTruncate, dif_pos h₁, dif_pos h₂]
theorem Int16.ofIntLE_eq_ofIntTruncate {x : Int} {h₁ h₂} : (ofIntLE x h₁ h₂) = ofIntTruncate x := by
rw [ofIntTruncate, dif_pos h₁, dif_pos h₂]
theorem Int32.ofIntLE_eq_ofIntTruncate {x : Int} {h₁ h₂} : (ofIntLE x h₁ h₂) = ofIntTruncate x := by
rw [ofIntTruncate, dif_pos h₁, dif_pos h₂]
theorem Int64.ofIntLE_eq_ofIntTruncate {x : Int} {h₁ h₂} : (ofIntLE x h₁ h₂) = ofIntTruncate x := by
rw [ofIntTruncate, dif_pos h₁, dif_pos h₂]
theorem ISize.ofIntLE_eq_ofIntTruncate {x : Int} {h₁ h₂} : (ofIntLE x h₁ h₂) = ofIntTruncate x := by
rw [ofIntTruncate, dif_pos h₁, dif_pos h₂]
theorem Int8.toInt_ofIntTruncate {x : Int} (h₁ : Int8.minValue.toInt x)
(h₂ : x Int8.maxValue.toInt) : (Int8.ofIntTruncate x).toInt = x := by
rw [ ofIntLE_eq_ofIntTruncate (h₁ := h₁) (h₂ := h₂), toInt_ofIntLE]
theorem Int16.toInt_ofIntTruncate {x : Int} (h₁ : Int16.minValue.toInt x)
(h₂ : x Int16.maxValue.toInt) : (Int16.ofIntTruncate x).toInt = x := by
rw [ ofIntLE_eq_ofIntTruncate (h₁ := h₁) (h₂ := h₂), toInt_ofIntLE]
theorem Int32.toInt_ofIntTruncate {x : Int} (h₁ : Int32.minValue.toInt x)
(h₂ : x Int32.maxValue.toInt) : (Int32.ofIntTruncate x).toInt = x := by
rw [ ofIntLE_eq_ofIntTruncate (h₁ := h₁) (h₂ := h₂), toInt_ofIntLE]
theorem Int64.toInt_ofIntTruncate {x : Int} (h₁ : Int64.minValue.toInt x)
(h₂ : x Int64.maxValue.toInt) : (Int64.ofIntTruncate x).toInt = x := by
rw [ ofIntLE_eq_ofIntTruncate (h₁ := h₁) (h₂ := h₂), toInt_ofIntLE]
theorem ISize.toInt_ofIntTruncate {x : Int} (h₁ : ISize.minValue.toInt x)
(h₂ : x ISize.maxValue.toInt) : (ISize.ofIntTruncate x).toInt = x := by
rw [ ofIntLE_eq_ofIntTruncate (h₁ := h₁) (h₂ := h₂), toInt_ofIntLE]
@[simp] theorem Int8.ofIntTruncate_toInt (x : Int8) : Int8.ofIntTruncate x.toInt = x :=
Int8.toInt.inj (toInt_ofIntTruncate x.minValue_le_toInt x.toInt_le)
@[simp] theorem Int16.ofIntTruncate_toInt (x : Int16) : Int16.ofIntTruncate x.toInt = x :=
Int16.toInt.inj (toInt_ofIntTruncate x.minValue_le_toInt x.toInt_le)
@[simp] theorem Int32.ofIntTruncate_toInt (x : Int32) : Int32.ofIntTruncate x.toInt = x :=
Int32.toInt.inj (toInt_ofIntTruncate x.minValue_le_toInt x.toInt_le)
@[simp] theorem Int64.ofIntTruncate_toInt (x : Int64) : Int64.ofIntTruncate x.toInt = x :=
Int64.toInt.inj (toInt_ofIntTruncate x.minValue_le_toInt x.toInt_le)
@[simp] theorem ISize.ofIntTruncate_toInt (x : ISize) : ISize.ofIntTruncate x.toInt = x :=
ISize.toInt.inj (toInt_ofIntTruncate x.minValue_le_toInt x.toInt_le)
@[simp] theorem Int16.ofIntTruncate_int8ToInt (x : Int8) : Int16.ofIntTruncate x.toInt = x.toInt16 :=
Int16.toInt.inj (by
rw [toInt_ofIntTruncate, Int8.toInt_toInt16]
· exact Int.le_trans (by decide) x.minValue_le_toInt
· exact Int.le_trans x.toInt_le (by decide))
@[simp] theorem Int32.ofIntTruncate_int8ToInt (x : Int8) : Int32.ofIntTruncate x.toInt = x.toInt32 :=
Int32.toInt.inj (by
rw [toInt_ofIntTruncate, Int8.toInt_toInt32]
· exact Int.le_trans (by decide) x.minValue_le_toInt
· exact Int.le_trans x.toInt_le (by decide))
@[simp] theorem Int64.ofIntTruncate_int8ToInt (x : Int8) : Int64.ofIntTruncate x.toInt = x.toInt64 :=
Int64.toInt.inj (by
rw [toInt_ofIntTruncate, Int8.toInt_toInt64]
· exact Int.le_trans (by decide) x.minValue_le_toInt
· exact Int.le_trans x.toInt_le (by decide))
@[simp] theorem ISize.ofIntTruncate_int8ToInt (x : Int8) : ISize.ofIntTruncate x.toInt = x.toISize :=
ISize.toInt.inj (by
rw [toInt_ofIntTruncate, Int8.toInt_toISize]
· exact x.iSizeMinValue_le_toInt
· exact x.toInt_le_iSizeMaxValue)
@[simp] theorem Int32.ofIntTruncate_int16ToInt (x : Int16) : Int32.ofIntTruncate x.toInt = x.toInt32 :=
Int32.toInt.inj (by
rw [toInt_ofIntTruncate, Int16.toInt_toInt32]
· exact Int.le_trans (by decide) x.minValue_le_toInt
· exact Int.le_trans x.toInt_le (by decide))
@[simp] theorem Int64.ofIntTruncate_int16ToInt (x : Int16) : Int64.ofIntTruncate x.toInt = x.toInt64 :=
Int64.toInt.inj (by
rw [toInt_ofIntTruncate, Int16.toInt_toInt64]
· exact Int.le_trans (by decide) x.minValue_le_toInt
· exact Int.le_trans x.toInt_le (by decide))
@[simp] theorem ISize.ofIntTruncate_int16ToInt (x : Int16) : ISize.ofIntTruncate x.toInt = x.toISize :=
ISize.toInt.inj (by
rw [toInt_ofIntTruncate, Int16.toInt_toISize]
· exact x.iSizeMinValue_le_toInt
· exact x.toInt_le_iSizeMaxValue)
@[simp] theorem Int64.ofIntTruncate_int32ToInt (x : Int32) : Int64.ofIntTruncate x.toInt = x.toInt64 :=
Int64.toInt.inj (by
rw [toInt_ofIntTruncate, Int32.toInt_toInt64]
· exact Int.le_trans (by decide) x.minValue_le_toInt
· exact Int.le_trans x.toInt_le (by decide))
@[simp] theorem ISize.ofIntTruncate_int32ToInt (x : Int32) : ISize.ofIntTruncate x.toInt = x.toISize :=
ISize.toInt.inj (by
rw [toInt_ofIntTruncate, Int32.toInt_toISize]
· exact x.iSizeMinValue_le_toInt
· exact x.toInt_le_iSizeMaxValue)
@[simp] theorem Int64.ofIntTruncate_iSizeToInt (x : ISize) : Int64.ofIntTruncate x.toInt = x.toInt64 :=
Int64.toInt.inj (by
rw [toInt_ofIntTruncate, ISize.toInt_toInt64]
· exact x.int64MinValue_le_toInt
· exact x.toInt_le_int64MaxValue)
theorem Int8.le_iff_toInt_le {x y : Int8} : x y x.toInt y.toInt := BitVec.sle_iff_toInt_le
theorem Int16.le_iff_toInt_le {x y : Int16} : x y x.toInt y.toInt := BitVec.sle_iff_toInt_le
theorem Int32.le_iff_toInt_le {x y : Int32} : x y x.toInt y.toInt := BitVec.sle_iff_toInt_le
theorem Int64.le_iff_toInt_le {x y : Int64} : x y x.toInt y.toInt := BitVec.sle_iff_toInt_le
theorem ISize.le_iff_toInt_le {x y : ISize} : x y x.toInt y.toInt := BitVec.sle_iff_toInt_le
theorem Int8.cast_toNatClampNeg (x : Int8) (hx : 0 x) : x.toNatClampNeg = x.toInt := by
rw [toNatClampNeg, toInt, Int.toNat_of_nonneg (by simpa using le_iff_toInt_le.1 hx)]
theorem Int16.cast_toNatClampNeg (x : Int16) (hx : 0 x) : x.toNatClampNeg = x.toInt := by
rw [toNatClampNeg, toInt, Int.toNat_of_nonneg (by simpa using le_iff_toInt_le.1 hx)]
theorem Int32.cast_toNatClampNeg (x : Int32) (hx : 0 x) : x.toNatClampNeg = x.toInt := by
rw [toNatClampNeg, toInt, Int.toNat_of_nonneg (by simpa using le_iff_toInt_le.1 hx)]
theorem Int64.cast_toNatClampNeg (x : Int64) (hx : 0 x) : x.toNatClampNeg = x.toInt := by
rw [toNatClampNeg, toInt, Int.toNat_of_nonneg (by simpa using le_iff_toInt_le.1 hx)]
theorem ISize.cast_toNatClampNeg (x : ISize) (hx : 0 x) : x.toNatClampNeg = x.toInt := by
rw [toNatClampNeg, toInt, Int.toNat_of_nonneg (by simpa using le_iff_toInt_le.1 hx)]
theorem Int8.ofNat_toNatClampNeg (x : Int8) (hx : 0 x) : Int8.ofNat x.toNatClampNeg = x :=
Int8.toInt.inj (by rw [Int8.toInt_ofNat_of_lt x.toNatClampNeg_lt, cast_toNatClampNeg _ hx])
theorem Int16.ofNat_toNatClampNeg (x : Int16) (hx : 0 x) : Int16.ofNat x.toNatClampNeg = x :=
Int16.toInt.inj (by rw [Int16.toInt_ofNat_of_lt x.toNatClampNeg_lt, cast_toNatClampNeg _ hx])
theorem Int32.ofNat_toNatClampNeg (x : Int32) (hx : 0 x) : Int32.ofNat x.toNatClampNeg = x :=
Int32.toInt.inj (by rw [Int32.toInt_ofNat_of_lt x.toNatClampNeg_lt, cast_toNatClampNeg _ hx])
theorem Int64.ofNat_toNatClampNeg (x : Int64) (hx : 0 x) : Int64.ofNat x.toNatClampNeg = x :=
Int64.toInt.inj (by rw [Int64.toInt_ofNat_of_lt x.toNatClampNeg_lt, cast_toNatClampNeg _ hx])
theorem ISize.ofNat_toNatClampNeg (x : ISize) (hx : 0 x) : ISize.ofNat x.toNatClampNeg = x :=
ISize.toInt.inj (by rw [ISize.toInt_ofNat_of_lt_two_pow_numBits x.toNatClampNeg_lt_two_pow_numBits,
cast_toNatClampNeg _ hx])
theorem Int16.ofNat_int8ToNatClampNeg (x : Int8) (hx : 0 x) : Int16.ofNat x.toNatClampNeg = x.toInt16 :=
Int16.toInt.inj (by rw [Int16.toInt_ofNat_of_lt (Nat.lt_of_lt_of_le x.toNatClampNeg_lt (by decide)),
Int8.cast_toNatClampNeg _ hx, Int8.toInt_toInt16])
theorem Int32.ofNat_int8ToNatClampNeg (x : Int8) (hx : 0 x) : Int32.ofNat x.toNatClampNeg = x.toInt32 :=
Int32.toInt.inj (by rw [Int32.toInt_ofNat_of_lt (Nat.lt_of_lt_of_le x.toNatClampNeg_lt (by decide)),
Int8.cast_toNatClampNeg _ hx, Int8.toInt_toInt32])
theorem Int64.ofNat_int8ToNatClampNeg (x : Int8) (hx : 0 x) : Int64.ofNat x.toNatClampNeg = x.toInt64 :=
Int64.toInt.inj (by rw [Int64.toInt_ofNat_of_lt (Nat.lt_of_lt_of_le x.toNatClampNeg_lt (by decide)),
Int8.cast_toNatClampNeg _ hx, Int8.toInt_toInt64])
theorem ISize.ofNat_int8ToNatClampNeg (x : Int8) (hx : 0 x) : ISize.ofNat x.toNatClampNeg = x.toISize :=
ISize.toInt.inj (by rw [ISize.toInt_ofNat_of_lt (Nat.lt_of_lt_of_le x.toNatClampNeg_lt (by decide)),
Int8.cast_toNatClampNeg _ hx, Int8.toInt_toISize])
theorem Int32.ofNat_int16ToNatClampNeg (x : Int16) (hx : 0 x) : Int32.ofNat x.toNatClampNeg = x.toInt32 :=
Int32.toInt.inj (by rw [Int32.toInt_ofNat_of_lt (Nat.lt_of_lt_of_le x.toNatClampNeg_lt (by decide)),
Int16.cast_toNatClampNeg _ hx, Int16.toInt_toInt32])
theorem Int64.ofNat_int16ToNatClampNeg (x : Int16) (hx : 0 x) : Int64.ofNat x.toNatClampNeg = x.toInt64 :=
Int64.toInt.inj (by rw [Int64.toInt_ofNat_of_lt (Nat.lt_of_lt_of_le x.toNatClampNeg_lt (by decide)),
Int16.cast_toNatClampNeg _ hx, Int16.toInt_toInt64])
theorem ISize.ofNat_int16ToNatClampNeg (x : Int16) (hx : 0 x) : ISize.ofNat x.toNatClampNeg = x.toISize :=
ISize.toInt.inj (by rw [ISize.toInt_ofNat_of_lt (Nat.lt_of_lt_of_le x.toNatClampNeg_lt (by decide)),
Int16.cast_toNatClampNeg _ hx, Int16.toInt_toISize])
theorem Int64.ofNat_int32ToNatClampNeg (x : Int32) (hx : 0 x) : Int64.ofNat x.toNatClampNeg = x.toInt64 :=
Int64.toInt.inj (by rw [Int64.toInt_ofNat_of_lt (Nat.lt_of_lt_of_le x.toNatClampNeg_lt (by decide)),
Int32.cast_toNatClampNeg _ hx, Int32.toInt_toInt64])
theorem ISize.ofNat_int32ToNatClampNeg (x : Int32) (hx : 0 x) : ISize.ofNat x.toNatClampNeg = x.toISize :=
ISize.toInt.inj (by rw [ISize.toInt_ofNat_of_lt (Nat.lt_of_lt_of_le x.toNatClampNeg_lt (by decide)),
Int32.cast_toNatClampNeg _ hx, Int32.toInt_toISize])
@[simp] theorem Int8.toInt8_toInt16 (n : Int8) : n.toInt16.toInt8 = n :=
Int8.toInt.inj (by simp)
@[simp] theorem Int8.toInt8_toInt32 (n : Int8) : n.toInt32.toInt8 = n :=
Int8.toInt.inj (by simp)
@[simp] theorem Int8.toInt8_toInt64 (n : Int8) : n.toInt64.toInt8 = n :=
Int8.toInt.inj (by simp)
@[simp] theorem Int8.toInt8_toISize (n : Int8) : n.toISize.toInt8 = n :=
Int8.toInt.inj (by simp)
@[simp] theorem Int8.toInt16_toInt32 (n : Int8) : n.toInt32.toInt16 = n.toInt16 :=
Int16.toInt.inj (by simp)
@[simp] theorem Int8.toInt16_toInt64 (n : Int8) : n.toInt64.toInt16 = n.toInt16 :=
Int16.toInt.inj (by simp)
@[simp] theorem Int8.toInt16_toISize (n : Int8) : n.toISize.toInt16 = n.toInt16 :=
Int16.toInt.inj (by simp)
@[simp] theorem Int8.toInt32_toInt16 (n : Int8) : n.toInt16.toInt32 = n.toInt32 :=
Int32.toInt.inj (by simp)
@[simp] theorem Int8.toInt32_toInt64 (n : Int8) : n.toInt64.toInt32 = n.toInt32 :=
Int32.toInt.inj (by simp)
@[simp] theorem Int8.toInt32_toISize (n : Int8) : n.toISize.toInt32 = n.toInt32 :=
Int32.toInt.inj (by simp)
@[simp] theorem Int8.toInt64_toInt16 (n : Int8) : n.toInt16.toInt64 = n.toInt64 :=
Int64.toInt.inj (by simp)
@[simp] theorem Int8.toInt64_toInt32 (n : Int8) : n.toInt32.toInt64 = n.toInt64 :=
Int64.toInt.inj (by simp)
@[simp] theorem Int8.toInt64_toISize (n : Int8) : n.toISize.toInt64 = n.toInt64 :=
Int64.toInt.inj (by simp)
@[simp] theorem Int8.toISize_toInt16 (n : Int8) : n.toInt16.toISize = n.toISize :=
ISize.toInt.inj (by simp)
@[simp] theorem Int8.toISize_toInt32 (n : Int8) : n.toInt32.toISize = n.toISize :=
ISize.toInt.inj (by simp)
@[simp] theorem Int8.toISize_toInt64 (n : Int8) : n.toInt64.toISize = n.toISize :=
ISize.toInt.inj (by simp)
@[simp] theorem Int16.toInt8_toInt32 (n : Int16) : n.toInt32.toInt8 = n.toInt8 :=
Int8.toInt.inj (by simp)
@[simp] theorem Int16.toInt8_toInt64 (n : Int16) : n.toInt64.toInt8 = n.toInt8 :=
Int8.toInt.inj (by simp)
@[simp] theorem Int16.toInt8_toISize (n : Int16) : n.toISize.toInt8 = n.toInt8 :=
Int8.toInt.inj (by simp)
@[simp] theorem Int16.toInt16_toInt32 (n : Int16) : n.toInt32.toInt16 = n :=
Int16.toInt.inj (by simp)
@[simp] theorem Int16.toInt16_toInt64 (n : Int16) : n.toInt64.toInt16 = n :=
Int16.toInt.inj (by simp)
@[simp] theorem Int16.toInt16_toISize (n : Int16) : n.toISize.toInt16 = n :=
Int16.toInt.inj (by simp)
@[simp] theorem Int16.toInt32_toInt64 (n : Int16) : n.toInt64.toInt32 = n.toInt32 :=
Int32.toInt.inj (by simp)
@[simp] theorem Int16.toInt32_toISize (n : Int16) : n.toISize.toInt32 = n.toInt32 :=
Int32.toInt.inj (by simp)
@[simp] theorem Int16.toInt64_toInt32 (n : Int16) : n.toInt32.toInt64 = n.toInt64 :=
Int64.toInt.inj (by simp)
@[simp] theorem Int16.toInt64_toISize (n : Int16) : n.toISize.toInt64 = n.toInt64 :=
Int64.toInt.inj (by simp)
@[simp] theorem Int16.toISize_toInt32 (n : Int16) : n.toInt32.toISize = n.toISize :=
ISize.toInt.inj (by simp)
@[simp] theorem Int16.toISize_toInt64 (n : Int16) : n.toInt64.toISize = n.toISize :=
ISize.toInt.inj (by simp)
@[simp] theorem Int32.toInt8_toInt16 (n : Int32) : n.toInt16.toInt8 = n.toInt8 :=
Int8.toInt.inj (by simpa using Int.bmod_bmod_of_dvd (by decide))
@[simp] theorem Int32.toInt8_toInt64 (n : Int32) : n.toInt64.toInt8 = n.toInt8 :=
Int8.toInt.inj (by simp)
@[simp] theorem Int32.toInt8_toISize (n : Int32) : n.toISize.toInt8 = n.toInt8 :=
Int8.toInt.inj (by simp)
@[simp] theorem Int32.toInt16_toInt64 (n : Int32) : n.toInt64.toInt16 = n.toInt16 :=
Int16.toInt.inj (by simp)
@[simp] theorem Int32.toInt16_toISize (n : Int32) : n.toISize.toInt16 = n.toInt16 :=
Int16.toInt.inj (by simp)
@[simp] theorem Int32.toInt32_toInt64 (n : Int32) : n.toInt64.toInt32 = n :=
Int32.toInt.inj (by simp)
@[simp] theorem Int32.toInt32_toISize (n : Int32) : n.toISize.toInt32 = n :=
Int32.toInt.inj (by simp)
@[simp] theorem Int32.toInt64_toISize (n : Int32) : n.toISize.toInt64 = n.toInt64 :=
Int64.toInt.inj (by simp)
@[simp] theorem Int32.toISize_toInt64 (n : Int32) : n.toInt64.toISize = n.toISize :=
ISize.toInt.inj (by simp)
@[simp] theorem Int64.toInt8_toInt16 (n : Int64) : n.toInt16.toInt8 = n.toInt8 :=
Int8.toInt.inj (by simpa using Int.bmod_bmod_of_dvd (by decide))
@[simp] theorem Int64.toInt8_toInt32 (n : Int64) : n.toInt32.toInt8 = n.toInt8 :=
Int8.toInt.inj (by simpa using Int.bmod_bmod_of_dvd (by decide))
@[simp] theorem Int64.toInt8_toISize (n : Int64) : n.toISize.toInt8 = n.toInt8 :=
Int8.toInt.inj (by simpa using Int.bmod_bmod_of_dvd (by cases System.Platform.numBits_eq <;> simp_all))
@[simp] theorem Int64.toInt16_toInt32 (n : Int64) : n.toInt32.toInt16 = n.toInt16 :=
Int16.toInt.inj (by simpa using Int.bmod_bmod_of_dvd (by decide))
@[simp] theorem Int64.toInt16_toISize (n : Int64) : n.toISize.toInt16 = n.toInt16 :=
Int16.toInt.inj (by simpa using Int.bmod_bmod_of_dvd (by cases System.Platform.numBits_eq <;> simp_all))
@[simp] theorem Int64.toInt32_toISize (n : Int64) : n.toISize.toInt32 = n.toInt32 :=
Int32.toInt.inj (by simpa using Int.bmod_bmod_of_dvd (by cases System.Platform.numBits_eq <;> simp_all))
@[simp] theorem ISize.toInt8_toInt16 (n : ISize) : n.toInt16.toInt8 = n.toInt8 :=
Int8.toInt.inj (by simpa using Int.bmod_bmod_of_dvd (by decide))
@[simp] theorem ISize.toInt8_toInt32 (n : ISize) : n.toInt32.toInt8 = n.toInt8 :=
Int8.toInt.inj (by simpa using Int.bmod_bmod_of_dvd (by decide))
@[simp] theorem ISize.toInt8_toInt64 (n : ISize) : n.toInt64.toInt8 = n.toInt8 :=
Int8.toInt.inj (by simp)
@[simp] theorem ISize.toInt16_toInt32 (n : ISize) : n.toInt32.toInt16 = n.toInt16 :=
Int16.toInt.inj (by simpa using Int.bmod_bmod_of_dvd (by decide))
@[simp] theorem ISize.toInt16_toInt64 (n : ISize) : n.toInt64.toInt16 = n.toInt16 :=
Int16.toInt.inj (by simp)
@[simp] theorem ISize.toInt32_toInt64 (n : ISize) : n.toInt64.toInt32 = n.toInt32 :=
Int32.toInt.inj (by simp)
@[simp] theorem ISize.toISize_toInt64 (n : ISize) : n.toInt64.toISize = n :=
ISize.toInt.inj (by simp)
theorem UInt8.toInt8_ofNatLT {n : Nat} (hn) : (UInt8.ofNatLT n hn).toInt8 = Int8.ofNat n :=
Int8.toBitVec.inj (by simp [BitVec.ofNatLT_eq_ofNat])
theorem UInt16.toInt16_ofNatLT {n : Nat} (hn) : (UInt16.ofNatLT n hn).toInt16 = Int16.ofNat n :=
Int16.toBitVec.inj (by simp [BitVec.ofNatLT_eq_ofNat])
theorem UInt32.toInt32_ofNatLT {n : Nat} (hn) : (UInt32.ofNatLT n hn).toInt32 = Int32.ofNat n :=
Int32.toBitVec.inj (by simp [BitVec.ofNatLT_eq_ofNat])
theorem UInt64.toInt64_ofNatLT {n : Nat} (hn) : (UInt64.ofNatLT n hn).toInt64 = Int64.ofNat n :=
Int64.toBitVec.inj (by simp [BitVec.ofNatLT_eq_ofNat])
theorem USize.toISize_ofNatLT {n : Nat} (hn) : (USize.ofNatLT n hn).toISize = ISize.ofNat n :=
ISize.toBitVec.inj (by simp [BitVec.ofNatLT_eq_ofNat])
@[simp] theorem UInt8.toInt8_ofNat' {n : Nat} : (UInt8.ofNat n).toInt8 = Int8.ofNat n := rfl
@[simp] theorem UInt16.toInt16_ofNat' {n : Nat} : (UInt16.ofNat n).toInt16 = Int16.ofNat n := rfl
@[simp] theorem UInt32.toInt32_ofNat' {n : Nat} : (UInt32.ofNat n).toInt32 = Int32.ofNat n := rfl
@[simp] theorem UInt64.toInt64_ofNat' {n : Nat} : (UInt64.ofNat n).toInt64 = Int64.ofNat n := rfl
@[simp] theorem USize.toISize_ofNat' {n : Nat} : (USize.ofNat n).toISize = ISize.ofNat n := rfl
@[simp] theorem UInt8.toInt8_ofNat {n : Nat} : toInt8 (no_index (OfNat.ofNat n)) = OfNat.ofNat n := rfl
@[simp] theorem UInt16.toInt16_ofNat {n : Nat} : toInt16 (no_index (OfNat.ofNat n)) = OfNat.ofNat n := rfl
@[simp] theorem UInt32.toInt32_ofNat {n : Nat} : toInt32 (no_index (OfNat.ofNat n)) = OfNat.ofNat n := rfl
@[simp] theorem UInt64.toInt64_ofNat {n : Nat} : toInt64 (no_index (OfNat.ofNat n)) = OfNat.ofNat n := rfl
@[simp] theorem USize.toISize_ofNat {n : Nat} : toISize (no_index (OfNat.ofNat n)) = OfNat.ofNat n := rfl
@[simp] theorem UInt8.toInt8_ofBitVec (b) : (UInt8.ofBitVec b).toInt8 = Int8.ofBitVec b := rfl
@[simp] theorem UInt16.toInt16_ofBitVec (b) : (UInt16.ofBitVec b).toInt16 = Int16.ofBitVec b := rfl
@[simp] theorem UInt32.toInt32_ofBitVec (b) : (UInt32.ofBitVec b).toInt32 = Int32.ofBitVec b := rfl
@[simp] theorem UInt64.toInt64_ofBitVec (b) : (UInt64.ofBitVec b).toInt64 = Int64.ofBitVec b := rfl
@[simp] theorem USize.toInt8_ofBitVec (b) : (USize.ofBitVec b).toISize = ISize.ofBitVec b := rfl

View File

@@ -1,7 +1,7 @@
/-
Copyright (c) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura, François G. Dorais, Mario Carneiro, Mac Malone
Authors: Leonardo de Moura, François G. Dorais, Mario Carneiro, Mac Malone, Markus Himmel
-/
prelude
import Init.Data.UInt.Basic
@@ -27,7 +27,10 @@ macro "declare_uint_theorems" typeName:ident bits:term:arg : command => do
@[deprecated toNat_ofBitVec (since := "2025-02-12")]
theorem toNat_mk : (ofBitVec a).toNat = a.toNat := rfl
@[simp] theorem toNat_ofNat {n : Nat} : (ofNat n).toNat = n % 2 ^ $bits := BitVec.toNat_ofNat ..
@[simp] theorem toNat_ofNat' {n : Nat} : (ofNat n).toNat = n % 2 ^ $bits := BitVec.toNat_ofNat ..
-- Not `simp` because we have simprocs which will avoid the modulo.
theorem toNat_ofNat {n : Nat} : toNat (no_index (OfNat.ofNat n)) = n % 2 ^ $bits := toNat_ofNat'
@[simp] theorem toNat_ofNatLT {n : Nat} {h : n < size} : (ofNatLT n h).toNat = n := BitVec.toNat_ofNatLT ..
@@ -55,11 +58,16 @@ macro "declare_uint_theorems" typeName:ident bits:term:arg : command => do
theorem mk_toBitVec_eq : (a : $typeName), ofBitVec a.toBitVec = a
| _, _ => rfl
@[deprecated "Use `toNat_toBitVec` and `toNat_ofNat_of_lt`." (since := "2025-03-05")]
theorem toBitVec_eq_of_lt {a : Nat} : a < size (ofNat a).toBitVec.toNat = a :=
Nat.mod_eq_of_lt
theorem toNat_ofNat_of_lt {n : Nat} (h : n < size) : (ofNat n).toNat = n := by
rw [toNat, toBitVec_eq_of_lt h]
theorem toBitVec_ofNat' (n : Nat) : (ofNat n).toBitVec = BitVec.ofNat _ n := rfl
theorem toNat_ofNat_of_lt' {n : Nat} (h : n < size) : (ofNat n).toNat = n := by
rw [toNat, toBitVec_ofNat', BitVec.toNat_ofNat, Nat.mod_eq_of_lt h]
theorem toNat_ofNat_of_lt {n : Nat} (h : n < size) : toNat (OfNat.ofNat n) = n :=
toNat_ofNat_of_lt' h
@[int_toBitVec] theorem le_def {a b : $typeName} : a b a.toBitVec b.toBitVec := .rfl
@@ -151,10 +159,10 @@ macro "declare_uint_theorems" typeName:ident bits:term:arg : command => do
protected theorem toNat_lt_size (a : $typeName) : a.toNat < size := a.toBitVec.isLt
open $typeName (toNat_mod toNat_lt_size) in
protected theorem toNat_mod_lt {m : Nat} : (u : $typeName), m > 0 toNat (u % ofNat m) < m := by
protected theorem toNat_mod_lt {m : Nat} : (u : $typeName), 0 < m toNat (u % ofNat m) < m := by
intro u h1
by_cases h2 : m < size
· rw [toNat_mod, toNat_ofNat_of_lt h2]
· rw [toNat_mod, toNat_ofNat_of_lt' h2]
apply Nat.mod_lt _ h1
· apply Nat.lt_of_lt_of_le
· apply toNat_lt_size
@@ -258,16 +266,20 @@ theorem USize.toNat_ofNat_of_lt_32 {n : Nat} (h : n < 4294967296) : toNat (ofNat
toNat_ofNat_of_lt (Nat.lt_of_lt_of_le h USize.le_size)
theorem UInt32.toNat_lt_of_lt {n : UInt32} {m : Nat} (h : m < size) : n < ofNat m n.toNat < m := by
simp [-toNat_toBitVec, lt_def, BitVec.lt_def, UInt32.toNat, toBitVec_eq_of_lt h]
rw [lt_def, BitVec.lt_def, toNat_toBitVec, toNat_toBitVec, toNat_ofNat_of_lt' h]
exact id
theorem UInt32.lt_toNat_of_lt {n : UInt32} {m : Nat} (h : m < size) : ofNat m < n m < n.toNat := by
simp [-toNat_toBitVec, lt_def, BitVec.lt_def, UInt32.toNat, toBitVec_eq_of_lt h]
rw [lt_def, BitVec.lt_def, toNat_toBitVec, toNat_toBitVec, toNat_ofNat_of_lt' h]
exact id
theorem UInt32.toNat_le_of_le {n : UInt32} {m : Nat} (h : m < size) : n ofNat m n.toNat m := by
simp [-toNat_toBitVec, le_def, BitVec.le_def, UInt32.toNat, toBitVec_eq_of_lt h]
rw [le_def, BitVec.le_def, toNat_toBitVec, toNat_toBitVec, toNat_ofNat_of_lt' h]
exact id
theorem UInt32.le_toNat_of_le {n : UInt32} {m : Nat} (h : m < size) : ofNat m n m n.toNat := by
simp [-toNat_toBitVec, le_def, BitVec.le_def, UInt32.toNat, toBitVec_eq_of_lt h]
rw [le_def, BitVec.le_def, toNat_toBitVec, toNat_toBitVec, toNat_ofNat_of_lt' h]
exact id
@[simp] theorem UInt8.toNat_lt (n : UInt8) : n.toNat < 2 ^ 8 := n.toFin.isLt
@[simp] theorem UInt16.toNat_lt (n : UInt16) : n.toNat < 2 ^ 16 := n.toFin.isLt
@@ -311,6 +323,15 @@ theorem USize.size_dvd_uInt64Size : USize.size UInt64.size := by cases USize
@[simp] theorem mod_uInt64Size_uSizeSize (n : Nat) : n % UInt64.size % USize.size = n % USize.size :=
Nat.mod_mod_of_dvd _ USize.size_dvd_uInt64Size
@[simp] theorem USize.size_sub_one_mod_uint8Size : (USize.size - 1) % UInt8.size = UInt8.size - 1 := by
cases USize.size_eq <;> simp_all +decide
@[simp] theorem USize.size_sub_one_mod_uint16Size : (USize.size - 1) % UInt16.size = UInt16.size - 1 := by
cases USize.size_eq <;> simp_all +decide
@[simp] theorem USize.size_sub_one_mod_uint32Size : (USize.size - 1) % UInt32.size = UInt32.size - 1 := by
cases USize.size_eq <;> simp_all +decide
@[simp] theorem UInt64.size_sub_one_mod_uSizeSize : 18446744073709551615 % USize.size = USize.size - 1 := by
cases USize.size_eq <;> simp_all +decide
@[simp] theorem UInt8.toNat_mod_size (n : UInt8) : n.toNat % UInt8.size = n.toNat := Nat.mod_eq_of_lt n.toNat_lt
@[simp] theorem UInt8.toNat_mod_uInt16Size (n : UInt8) : n.toNat % UInt16.size = n.toNat := Nat.mod_eq_of_lt (Nat.lt_trans n.toNat_lt (by decide))
@[simp] theorem UInt8.toNat_mod_uInt32Size (n : UInt8) : n.toNat % UInt32.size = n.toNat := Nat.mod_eq_of_lt (Nat.lt_trans n.toNat_lt (by decide))
@@ -785,3 +806,402 @@ theorem USize.ofNatTruncate_eq_ofNat (n : Nat) (hn : n < USize.size) :
-- @[simp] theorem UInt64.toUSize_toUInt32 (n : UInt64) : n.toUInt32.toUSize = ? :=
-- @[simp] theorem USize.toUInt64_toUInt32 (n : USize) : n.toUInt32.toUInt64 = ? :=
-- @[simp] theorem USize.toUSize_toUInt32 (n : USize) : n.toInt32.toUSize = ? :=
@[simp] theorem UInt8.toNat_ofFin (x : Fin UInt8.size) : (UInt8.ofFin x).toNat = x.val := rfl
@[simp] theorem UInt16.toNat_ofFin (x : Fin UInt16.size) : (UInt16.ofFin x).toNat = x.val := rfl
@[simp] theorem UInt32.toNat_ofFin (x : Fin UInt32.size) : (UInt32.ofFin x).toNat = x.val := rfl
@[simp] theorem UInt64.toNat_ofFin (x : Fin UInt64.size) : (UInt64.ofFin x).toNat = x.val := rfl
@[simp] theorem USize.toNat_ofFin (x : Fin USize.size) : (USize.ofFin x).toNat = x.val := rfl
theorem UInt8.toNat_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt8.size) :
(UInt8.ofNatTruncate n).toNat = n := by rw [UInt8.ofNatTruncate, dif_pos hn, toNat_ofNatLT]
theorem UInt16.toNat_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt16.size) :
(UInt16.ofNatTruncate n).toNat = n := by rw [UInt16.ofNatTruncate, dif_pos hn, toNat_ofNatLT]
theorem UInt32.toNat_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt32.size) :
(UInt32.ofNatTruncate n).toNat = n := by rw [UInt32.ofNatTruncate, dif_pos hn, toNat_ofNatLT]
theorem UInt64.toNat_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt64.size) :
(UInt64.ofNatTruncate n).toNat = n := by rw [UInt64.ofNatTruncate, dif_pos hn, toNat_ofNatLT]
theorem USize.toNat_ofNatTruncate_of_lt {n : Nat} (hn : n < USize.size) :
(USize.ofNatTruncate n).toNat = n := by rw [USize.ofNatTruncate, dif_pos hn, toNat_ofNatLT]
theorem UInt8.toNat_ofNatTruncate_of_le {n : Nat} (hn : UInt8.size n) :
(UInt8.ofNatTruncate n).toNat = UInt8.size - 1 := by rw [ofNatTruncate, dif_neg (by omega), toNat_ofNatLT]
theorem UInt16.toNat_ofNatTruncate_of_le {n : Nat} (hn : UInt16.size n) :
(UInt16.ofNatTruncate n).toNat = UInt16.size - 1 := by rw [ofNatTruncate, dif_neg (by omega), toNat_ofNatLT]
theorem UInt32.toNat_ofNatTruncate_of_le {n : Nat} (hn : UInt32.size n) :
(UInt32.ofNatTruncate n).toNat = UInt32.size - 1 := by rw [ofNatTruncate, dif_neg (by omega), toNat_ofNatLT]
theorem UInt64.toNat_ofNatTruncate_of_le {n : Nat} (hn : UInt64.size n) :
(UInt64.ofNatTruncate n).toNat = UInt64.size - 1 := by rw [ofNatTruncate, dif_neg (by omega), toNat_ofNatLT]
theorem USize.toNat_ofNatTruncate_of_le {n : Nat} (hn : USize.size n) :
(USize.ofNatTruncate n).toNat = USize.size - 1 := by rw [ofNatTruncate, dif_neg (by omega), toNat_ofNatLT]
@[simp] theorem UInt8.toFin_ofNatLT {n : Nat} (hn) : (UInt8.ofNatLT n hn).toFin = n, hn := rfl
@[simp] theorem UInt16.toFin_ofNatLT {n : Nat} (hn) : (UInt16.ofNatLT n hn).toFin = n, hn := rfl
@[simp] theorem UInt32.toFin_ofNatLT {n : Nat} (hn) : (UInt32.ofNatLT n hn).toFin = n, hn := rfl
@[simp] theorem UInt64.toFin_ofNatLT {n : Nat} (hn) : (UInt64.ofNatLT n hn).toFin = n, hn := rfl
@[simp] theorem USize.toFin_ofNatLT {n : Nat} (hn) : (USize.ofNatLT n hn).toFin = n, hn := rfl
@[simp] theorem UInt8.toFin_ofNat' {n : Nat} : (UInt8.ofNat n).toFin = Fin.ofNat' _ n := rfl
@[simp] theorem UInt16.toFin_ofNat' {n : Nat} : (UInt16.ofNat n).toFin = Fin.ofNat' _ n := rfl
@[simp] theorem UInt32.toFin_ofNat' {n : Nat} : (UInt32.ofNat n).toFin = Fin.ofNat' _ n := rfl
@[simp] theorem UInt64.toFin_ofNat' {n : Nat} : (UInt64.ofNat n).toFin = Fin.ofNat' _ n := rfl
@[simp] theorem USize.toFin_ofNat' {n : Nat} : (USize.ofNat n).toFin = Fin.ofNat' _ n := rfl
@[simp] theorem UInt8.toFin_ofBitVec {b} : (UInt8.ofBitVec b).toFin = b.toFin := rfl
@[simp] theorem UInt16.toFin_ofBitVec {b} : (UInt16.ofBitVec b).toFin = b.toFin := rfl
@[simp] theorem UInt32.toFin_ofBitVec {b} : (UInt32.ofBitVec b).toFin = b.toFin := rfl
@[simp] theorem UInt64.toFin_ofBitVec {b} : (UInt64.ofBitVec b).toFin = b.toFin := rfl
@[simp] theorem USize.toFin_ofBitVec {b} : (USize.ofBitVec b).toFin = b.toFin := rfl
theorem UInt8.toFin_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt8.size) :
(UInt8.ofNatTruncate n).toFin = n, hn :=
Fin.val_inj.1 (by simp [toNat_ofNatTruncate_of_lt hn])
theorem UInt16.toFin_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt16.size) :
(UInt16.ofNatTruncate n).toFin = n, hn :=
Fin.val_inj.1 (by simp [toNat_ofNatTruncate_of_lt hn])
theorem UInt32.toFin_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt32.size) :
(UInt32.ofNatTruncate n).toFin = n, hn :=
Fin.val_inj.1 (by simp [toNat_ofNatTruncate_of_lt hn])
theorem UInt64.toFin_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt64.size) :
(UInt64.ofNatTruncate n).toFin = n, hn :=
Fin.val_inj.1 (by simp [toNat_ofNatTruncate_of_lt hn])
theorem USize.toFin_ofNatTruncate_of_lt {n : Nat} (hn : n < USize.size) :
(USize.ofNatTruncate n).toFin = n, hn :=
Fin.val_inj.1 (by simp [toNat_ofNatTruncate_of_lt hn])
theorem UInt8.toFin_ofNatTruncate_of_le {n : Nat} (hn : UInt8.size n) :
(UInt8.ofNatTruncate n).toFin = UInt8.size - 1, by decide :=
Fin.val_inj.1 (by simp [toNat_ofNatTruncate_of_le hn])
theorem UInt16.toFin_ofNatTruncate_of_le {n : Nat} (hn : UInt16.size n) :
(UInt16.ofNatTruncate n).toFin = UInt16.size - 1, by decide :=
Fin.val_inj.1 (by simp [toNat_ofNatTruncate_of_le hn])
theorem UInt32.toFin_ofNatTruncate_of_le {n : Nat} (hn : UInt32.size n) :
(UInt32.ofNatTruncate n).toFin = UInt32.size - 1, by decide :=
Fin.val_inj.1 (by simp [toNat_ofNatTruncate_of_le hn])
theorem UInt64.toFin_ofNatTruncate_of_le {n : Nat} (hn : UInt64.size n) :
(UInt64.ofNatTruncate n).toFin = UInt64.size - 1, by decide :=
Fin.val_inj.1 (by simp [toNat_ofNatTruncate_of_le hn])
theorem USize.toFin_ofNatTruncate_of_le {n : Nat} (hn : USize.size n) :
(USize.ofNatTruncate n).toFin = USize.size - 1, by cases USize.size_eq <;> simp_all :=
Fin.val_inj.1 (by simp [toNat_ofNatTruncate_of_le hn])
@[simp] theorem UInt8.toBitVec_ofNatLT {n : Nat} (hn : n < UInt8.size) :
(UInt8.ofNatLT n hn).toBitVec = BitVec.ofNatLT n hn := rfl
@[simp] theorem UInt16.toBitVec_ofNatLT {n : Nat} (hn : n < UInt16.size) :
(UInt16.ofNatLT n hn).toBitVec = BitVec.ofNatLT n hn := rfl
@[simp] theorem UInt32.toBitVec_ofNatLT {n : Nat} (hn : n < UInt32.size) :
(UInt32.ofNatLT n hn).toBitVec = BitVec.ofNatLT n hn := rfl
@[simp] theorem UInt64.toBitVec_ofNatLT {n : Nat} (hn : n < UInt64.size) :
(UInt64.ofNatLT n hn).toBitVec = BitVec.ofNatLT n hn := rfl
@[simp] theorem USize.toBitVec_ofNatLT {n : Nat} (hn : n < USize.size) :
(USize.ofNatLT n hn).toBitVec = BitVec.ofNatLT n hn := rfl
@[simp] theorem UInt8.toBitVec_ofFin (n : Fin UInt8.size) : (UInt8.ofFin n).toBitVec = BitVec.ofFin n := rfl
@[simp] theorem UInt16.toBitVec_ofFin (n : Fin UInt16.size) : (UInt16.ofFin n).toBitVec = BitVec.ofFin n := rfl
@[simp] theorem UInt32.toBitVec_ofFin (n : Fin UInt32.size) : (UInt32.ofFin n).toBitVec = BitVec.ofFin n := rfl
@[simp] theorem UInt64.toBitVec_ofFin (n : Fin UInt64.size) : (UInt64.ofFin n).toBitVec = BitVec.ofFin n := rfl
@[simp] theorem USize.toBitVec_ofFin (n : Fin USize.size) : (USize.ofFin n).toBitVec = BitVec.ofFin n := rfl
@[simp] theorem UInt8.toBitVec_ofBitVec (n) : (UInt8.ofBitVec n).toBitVec = n := rfl
@[simp] theorem UInt16.toBitVec_ofBitVec (n) : (UInt16.ofBitVec n).toBitVec = n := rfl
@[simp] theorem UInt32.toBitVec_ofBitVec (n) : (UInt32.ofBitVec n).toBitVec = n := rfl
@[simp] theorem UInt64.toBitVec_ofBitVec (n) : (UInt64.ofBitVec n).toBitVec = n := rfl
@[simp] theorem USize.toBitVec_ofBitVec (n) : (USize.ofBitVec n).toBitVec = n := rfl
theorem UInt8.toBitVec_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt8.size) :
(UInt8.ofNatTruncate n).toBitVec = BitVec.ofNatLT n hn :=
BitVec.eq_of_toNat_eq (by simp [toNat_ofNatTruncate_of_lt hn])
theorem UInt16.toBitVec_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt16.size) :
(UInt16.ofNatTruncate n).toBitVec = BitVec.ofNatLT n hn :=
BitVec.eq_of_toNat_eq (by simp [toNat_ofNatTruncate_of_lt hn])
theorem UInt32.toBitVec_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt32.size) :
(UInt32.ofNatTruncate n).toBitVec = BitVec.ofNatLT n hn :=
BitVec.eq_of_toNat_eq (by simp [toNat_ofNatTruncate_of_lt hn])
theorem UInt64.toBitVec_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt64.size) :
(UInt64.ofNatTruncate n).toBitVec = BitVec.ofNatLT n hn :=
BitVec.eq_of_toNat_eq (by simp [toNat_ofNatTruncate_of_lt hn])
theorem USize.toBitVec_ofNatTruncate_of_lt {n : Nat} (hn : n < USize.size) :
(USize.ofNatTruncate n).toBitVec = BitVec.ofNatLT n hn :=
BitVec.eq_of_toNat_eq (by simp [toNat_ofNatTruncate_of_lt hn])
theorem UInt8.toBitVec_ofNatTruncate_of_le {n : Nat} (hn : UInt8.size n) :
(UInt8.ofNatTruncate n).toBitVec = BitVec.ofNatLT (UInt8.size - 1) (by decide) :=
BitVec.eq_of_toNat_eq (by simp [toNat_ofNatTruncate_of_le hn])
theorem UInt16.toBitVec_ofNatTruncate_of_le {n : Nat} (hn : UInt16.size n) :
(UInt16.ofNatTruncate n).toBitVec = BitVec.ofNatLT (UInt16.size - 1) (by decide) :=
BitVec.eq_of_toNat_eq (by simp [toNat_ofNatTruncate_of_le hn])
theorem UInt32.toBitVec_ofNatTruncate_of_le {n : Nat} (hn : UInt32.size n) :
(UInt32.ofNatTruncate n).toBitVec = BitVec.ofNatLT (UInt32.size - 1) (by decide) :=
BitVec.eq_of_toNat_eq (by simp [toNat_ofNatTruncate_of_le hn])
theorem UInt64.toBitVec_ofNatTruncate_of_le {n : Nat} (hn : UInt64.size n) :
(UInt64.ofNatTruncate n).toBitVec = BitVec.ofNatLT (UInt64.size - 1) (by decide) :=
BitVec.eq_of_toNat_eq (by simp [toNat_ofNatTruncate_of_le hn])
theorem USize.toBitVec_ofNatTruncate_of_le {n : Nat} (hn : USize.size n) :
(USize.ofNatTruncate n).toBitVec = BitVec.ofNatLT (USize.size - 1) (by cases USize.size_eq <;> simp_all) :=
BitVec.eq_of_toNat_eq (by simp [toNat_ofNatTruncate_of_le hn])
@[simp] theorem UInt16.toUInt8_ofNatLT {n : Nat} (hn) : (UInt16.ofNatLT n hn).toUInt8 = UInt8.ofNat n := rfl
@[simp] theorem UInt32.toUInt8_ofNatLT {n : Nat} (hn) : (UInt32.ofNatLT n hn).toUInt8 = UInt8.ofNat n := rfl
@[simp] theorem UInt64.toUInt8_ofNatLT {n : Nat} (hn) : (UInt64.ofNatLT n hn).toUInt8 = UInt8.ofNat n := rfl
@[simp] theorem USize.toUInt8_ofNatLT {n : Nat} (hn) : (USize.ofNatLT n hn).toUInt8 = UInt8.ofNat n := rfl
@[simp] theorem UInt16.toUInt8_ofFin (n) : (UInt16.ofFin n).toUInt8 = UInt8.ofNat n.val := rfl
@[simp] theorem UInt32.toUInt8_ofFin (n) : (UInt32.ofFin n).toUInt8 = UInt8.ofNat n.val := rfl
@[simp] theorem UInt64.toUInt8_ofFin (n) : (UInt64.ofFin n).toUInt8 = UInt8.ofNat n.val := rfl
@[simp] theorem USize.toUInt8_ofFin (n) : (USize.ofFin n).toUInt8 = UInt8.ofNat n.val := rfl
@[simp] theorem UInt16.toUInt8_ofBitVec (b) : (UInt16.ofBitVec b).toUInt8 = UInt8.ofBitVec (b.setWidth _) := rfl
@[simp] theorem UInt32.toUInt8_ofBitVec (b) : (UInt32.ofBitVec b).toUInt8 = UInt8.ofBitVec (b.setWidth _) := rfl
@[simp] theorem UInt64.toUInt8_ofBitVec (b) : (UInt64.ofBitVec b).toUInt8 = UInt8.ofBitVec (b.setWidth _) := rfl
@[simp] theorem USize.toUInt8_ofBitVec (b) : (USize.ofBitVec b).toUInt8 = UInt8.ofBitVec (b.setWidth _) :=
UInt8.toNat.inj (by simp)
@[simp] theorem UInt16.toUInt8_ofNat' (n : Nat) : (UInt16.ofNat n).toUInt8 = UInt8.ofNat n := UInt8.toNat.inj (by simp)
@[simp] theorem UInt32.toUInt8_ofNat' (n : Nat) : (UInt32.ofNat n).toUInt8 = UInt8.ofNat n := UInt8.toNat.inj (by simp)
@[simp] theorem UInt64.toUInt8_ofNat' (n : Nat) : (UInt64.ofNat n).toUInt8 = UInt8.ofNat n := UInt8.toNat.inj (by simp)
@[simp] theorem USize.toUInt8_ofNat' (n : Nat) : (USize.ofNat n).toUInt8 = UInt8.ofNat n := UInt8.toNat.inj (by simp)
@[simp] theorem UInt16.toUInt8_ofNat {n : Nat} : toUInt8 (no_index (OfNat.ofNat n)) = OfNat.ofNat n := toUInt8_ofNat' _
@[simp] theorem UInt32.toUInt8_ofNat {n : Nat} : toUInt8 (no_index (OfNat.ofNat n)) = OfNat.ofNat n := toUInt8_ofNat' _
@[simp] theorem UInt64.toUInt8_ofNat {n : Nat} : toUInt8 (no_index (OfNat.ofNat n)) = OfNat.ofNat n := toUInt8_ofNat' _
@[simp] theorem USize.toUInt8_ofNat {n : Nat} : toUInt8 (no_index (OfNat.ofNat n)) = OfNat.ofNat n := toUInt8_ofNat' _
theorem UInt16.toUInt8_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt16.size) :
(UInt16.ofNatTruncate n).toUInt8 = UInt8.ofNat n := by rw [ofNatTruncate, dif_pos hn, toUInt8_ofNatLT]
theorem UInt32.toUInt8_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt32.size) :
(UInt32.ofNatTruncate n).toUInt8 = UInt8.ofNat n := by rw [ofNatTruncate, dif_pos hn, toUInt8_ofNatLT]
theorem UInt64.toUInt8_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt64.size) :
(UInt64.ofNatTruncate n).toUInt8 = UInt8.ofNat n := by rw [ofNatTruncate, dif_pos hn, toUInt8_ofNatLT]
theorem USize.toUInt8_ofNatTruncate_of_lt {n : Nat} (hn : n < USize.size) :
(USize.ofNatTruncate n).toUInt8 = UInt8.ofNat n := by rw [ofNatTruncate, dif_pos hn, toUInt8_ofNatLT]
theorem UInt16.toUInt8_ofNatTruncate_of_le {n : Nat} (hn : UInt16.size n) :
(UInt16.ofNatTruncate n).toUInt8 = UInt8.ofNatLT (UInt8.size - 1) (by decide) :=
UInt8.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
theorem UInt32.toUInt8_ofNatTruncate_of_le {n : Nat} (hn : UInt32.size n) :
(UInt32.ofNatTruncate n).toUInt8 = UInt8.ofNatLT (UInt8.size - 1) (by decide) :=
UInt8.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
theorem UInt64.toUInt8_ofNatTruncate_of_le {n : Nat} (hn : UInt64.size n) :
(UInt64.ofNatTruncate n).toUInt8 = UInt8.ofNatLT (UInt8.size - 1) (by decide) :=
UInt8.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
theorem USize.toUInt8_ofNatTruncate_of_le {n : Nat} (hn : USize.size n) :
(USize.ofNatTruncate n).toUInt8 = UInt8.ofNatLT (UInt8.size - 1) (by decide) :=
UInt8.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
@[simp] theorem UInt32.toUInt16_ofNatLT {n : Nat} (hn) : (UInt32.ofNatLT n hn).toUInt16 = UInt16.ofNat n := rfl
@[simp] theorem UInt64.toUInt16_ofNatLT {n : Nat} (hn) : (UInt64.ofNatLT n hn).toUInt16 = UInt16.ofNat n := rfl
@[simp] theorem USize.toUInt16_ofNatLT {n : Nat} (hn) : (USize.ofNatLT n hn).toUInt16 = UInt16.ofNat n := rfl
@[simp] theorem UInt32.toUInt16_ofFin (n) : (UInt32.ofFin n).toUInt16 = UInt16.ofNat n.val := rfl
@[simp] theorem UInt64.toUInt16_ofFin (n) : (UInt64.ofFin n).toUInt16 = UInt16.ofNat n.val := rfl
@[simp] theorem USize.toUInt16_ofFin (n) : (USize.ofFin n).toUInt16 = UInt16.ofNat n.val := rfl
@[simp] theorem UInt32.toUInt16_ofBitVec (b) : (UInt32.ofBitVec b).toUInt16 = UInt16.ofBitVec (b.setWidth _) := rfl
@[simp] theorem UInt64.toUInt16_ofBitVec (b) : (UInt64.ofBitVec b).toUInt16 = UInt16.ofBitVec (b.setWidth _) := rfl
@[simp] theorem USize.toUInt16_ofBitVec (b) : (USize.ofBitVec b).toUInt16 = UInt16.ofBitVec (b.setWidth _) :=
UInt16.toNat.inj (by simp)
@[simp] theorem UInt32.toUInt16_ofNat' (n : Nat) : (UInt32.ofNat n).toUInt16 = UInt16.ofNat n := UInt16.toNat.inj (by simp)
@[simp] theorem UInt64.toUInt16_ofNat' (n : Nat) : (UInt64.ofNat n).toUInt16 = UInt16.ofNat n := UInt16.toNat.inj (by simp)
@[simp] theorem USize.toUInt16_ofNat' (n : Nat) : (USize.ofNat n).toUInt16 = UInt16.ofNat n := UInt16.toNat.inj (by simp)
@[simp] theorem UInt32.toUInt16_ofNat {n : Nat} : toUInt16 (no_index (OfNat.ofNat n)) = OfNat.ofNat n := UInt32.toUInt16_ofNat' _
@[simp] theorem UInt64.toUInt16_ofNat {n : Nat} : toUInt16 (no_index (OfNat.ofNat n)) = OfNat.ofNat n := UInt64.toUInt16_ofNat' _
@[simp] theorem USize.toUInt16_ofNat {n : Nat} : toUInt16 (no_index (OfNat.ofNat n)) = OfNat.ofNat n := USize.toUInt16_ofNat' _
theorem UInt32.toUInt16_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt32.size) :
(UInt32.ofNatTruncate n).toUInt16 = UInt16.ofNat n := by rw [ofNatTruncate, dif_pos hn, toUInt16_ofNatLT]
theorem UInt64.toUInt16_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt64.size) :
(UInt64.ofNatTruncate n).toUInt16 = UInt16.ofNat n := by rw [ofNatTruncate, dif_pos hn, toUInt16_ofNatLT]
theorem USize.toUInt16_ofNatTruncate_of_lt {n : Nat} (hn : n < USize.size) :
(USize.ofNatTruncate n).toUInt16 = UInt16.ofNat n := by rw [ofNatTruncate, dif_pos hn, toUInt16_ofNatLT]
theorem UInt32.toUInt16_ofNatTruncate_of_le {n : Nat} (hn : UInt32.size n) :
(UInt32.ofNatTruncate n).toUInt16 = UInt16.ofNatLT (UInt16.size - 1) (by decide) :=
UInt16.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
theorem UInt64.toUInt16_ofNatTruncate_of_le {n : Nat} (hn : UInt64.size n) :
(UInt64.ofNatTruncate n).toUInt16 = UInt16.ofNatLT (UInt16.size - 1) (by decide) :=
UInt16.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
theorem USize.toUInt16_ofNatTruncate_of_le {n : Nat} (hn : USize.size n) :
(USize.ofNatTruncate n).toUInt16 = UInt16.ofNatLT (UInt16.size - 1) (by decide) :=
UInt16.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
@[simp] theorem UInt64.toUInt32_ofNatLT {n : Nat} (hn) : (UInt64.ofNatLT n hn).toUInt32 = UInt32.ofNat n := rfl
@[simp] theorem USize.toUInt32_ofNatLT {n : Nat} (hn) : (USize.ofNatLT n hn).toUInt32 = UInt32.ofNat n := rfl
@[simp] theorem UInt64.toUInt32_ofFin (n) : (UInt64.ofFin n).toUInt32 = UInt32.ofNat n.val := rfl
@[simp] theorem USize.toUInt32_ofFin (n) : (USize.ofFin n).toUInt32 = UInt32.ofNat n.val := rfl
@[simp] theorem UInt64.toUInt32_ofBitVec (b) : (UInt64.ofBitVec b).toUInt32 = UInt32.ofBitVec (b.setWidth _) := rfl
@[simp] theorem USize.toUInt32_ofBitVec (b) : (USize.ofBitVec b).toUInt32 = UInt32.ofBitVec (b.setWidth _) :=
UInt32.toNat.inj (by simp)
@[simp] theorem UInt64.toUInt32_ofNat' (n : Nat) : (UInt64.ofNat n).toUInt32 = UInt32.ofNat n := UInt32.toNat.inj (by simp)
@[simp] theorem USize.toUInt32_ofNat' (n : Nat) : (USize.ofNat n).toUInt32 = UInt32.ofNat n := UInt32.toNat.inj (by simp)
@[simp] theorem UInt64.toUInt32_ofNat {n : Nat} : toUInt32 (no_index (OfNat.ofNat n)) = OfNat.ofNat n := UInt64.toUInt32_ofNat' _
@[simp] theorem USize.toUInt32_ofNat {n : Nat} : toUInt32 (no_index (OfNat.ofNat n)) = OfNat.ofNat n := USize.toUInt32_ofNat' _
theorem UInt64.toUInt32_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt64.size) :
(UInt64.ofNatTruncate n).toUInt32 = UInt32.ofNat n := by rw [ofNatTruncate, dif_pos hn, toUInt32_ofNatLT]
theorem USize.toUInt32_ofNatTruncate_of_lt {n : Nat} (hn : n < USize.size) :
(USize.ofNatTruncate n).toUInt32 = UInt32.ofNat n := by rw [ofNatTruncate, dif_pos hn, toUInt32_ofNatLT]
theorem UInt64.toUInt32_ofNatTruncate_of_le {n : Nat} (hn : UInt64.size n) :
(UInt64.ofNatTruncate n).toUInt32 = UInt32.ofNatLT (UInt32.size - 1) (by decide) :=
UInt32.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
theorem USize.toUInt32_ofNatTruncate_of_le {n : Nat} (hn : USize.size n) :
(USize.ofNatTruncate n).toUInt32 = UInt32.ofNatLT (UInt32.size - 1) (by decide) :=
UInt32.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
@[simp] theorem UInt64.toUSize_ofNatLT {n : Nat} (hn) : (UInt64.ofNatLT n hn).toUSize = USize.ofNat n := rfl
@[simp] theorem UInt64.toUSize_ofFin (n) : (UInt64.ofFin n).toUSize = USize.ofNat n.val := rfl
@[simp] theorem UInt64.toUSize_ofBitVec (b) : (UInt64.ofBitVec b).toUSize = USize.ofBitVec (b.setWidth _) :=
USize.toNat.inj (by simp)
@[simp] theorem UInt64.toUSize_ofNat' (n : Nat) : (UInt64.ofNat n).toUSize = USize.ofNat n := USize.toNat.inj (by simp)
@[simp] theorem UInt64.toUSize_ofNat {n : Nat} : toUSize (no_index (OfNat.ofNat n)) = OfNat.ofNat n := UInt64.toUSize_ofNat' _
theorem UInt64.toUSize_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt64.size) :
(UInt64.ofNatTruncate n).toUSize = USize.ofNat n := by rw [ofNatTruncate, dif_pos hn, toUSize_ofNatLT]
theorem UInt64.toUSize_ofNatTruncate_of_le {n : Nat} (hn : UInt64.size n) :
(UInt64.ofNatTruncate n).toUSize = USize.ofNatLT (USize.size - 1) (by cases USize.size_eq <;> simp_all) :=
USize.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
theorem UInt8.toUInt16_ofNatLT {n : Nat} (h) :
(UInt8.ofNatLT n h).toUInt16 = UInt16.ofNatLT n (Nat.lt_of_lt_of_le h (by decide)) := rfl
theorem UInt8.toUInt32_ofNatLT {n : Nat} (h) :
(UInt8.ofNatLT n h).toUInt32 = UInt32.ofNatLT n (Nat.lt_of_lt_of_le h (by decide)) := rfl
theorem UInt8.toUInt64_ofNatLT {n : Nat} (h) :
(UInt8.ofNatLT n h).toUInt64 = UInt64.ofNatLT n (Nat.lt_of_lt_of_le h (by decide)) := rfl
theorem UInt8.toUSize_ofNatLT {n : Nat} (h) :
(UInt8.ofNatLT n h).toUSize = USize.ofNatLT n (Nat.lt_of_lt_of_le h size_le_usizeSize) := rfl
theorem UInt8.toUInt16_ofFin {n} :
(UInt8.ofFin n).toUInt16 = UInt16.ofNatLT n.val (Nat.lt_of_lt_of_le n.isLt (by decide)) := rfl
theorem UInt8.toUInt32_ofFin {n} :
(UInt8.ofFin n).toUInt32 = UInt32.ofNatLT n.val (Nat.lt_of_lt_of_le n.isLt (by decide)) := rfl
theorem UInt8.toUInt64_ofFin {n} :
(UInt8.ofFin n).toUInt64 = UInt64.ofNatLT n.val (Nat.lt_of_lt_of_le n.isLt (by decide)) := rfl
theorem UInt8.toUSize_ofFin {n} :
(UInt8.ofFin n).toUSize = USize.ofNatLT n.val (Nat.lt_of_lt_of_le n.isLt size_le_usizeSize) := rfl
@[simp] theorem UInt8.toUInt16_ofBitVec {b} : (UInt8.ofBitVec b).toUInt16 = UInt16.ofBitVec (b.setWidth _) := rfl
@[simp] theorem UInt8.toUInt32_ofBitVec {b} : (UInt8.ofBitVec b).toUInt32 = UInt32.ofBitVec (b.setWidth _) := rfl
@[simp] theorem UInt8.toUInt64_ofBitVec {b} : (UInt8.ofBitVec b).toUInt64 = UInt64.ofBitVec (b.setWidth _) := rfl
@[simp] theorem UInt8.toUSize_ofBitVec {b} : (UInt8.ofBitVec b).toUSize = USize.ofBitVec (b.setWidth _) :=
USize.toBitVec_inj.1 (by simp)
theorem UInt8.toUInt16_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt8.size) :
(UInt8.ofNatTruncate n).toUInt16 = UInt16.ofNatLT n (Nat.lt_of_lt_of_le hn (by decide)) :=
UInt16.toNat.inj (by simp [toNat_ofNatTruncate_of_lt hn])
theorem UInt8.toUInt32_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt8.size) :
(UInt8.ofNatTruncate n).toUInt32 = UInt32.ofNatLT n (Nat.lt_of_lt_of_le hn (by decide)) :=
UInt32.toNat.inj (by simp [toNat_ofNatTruncate_of_lt hn])
theorem UInt8.toUInt64_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt8.size) :
(UInt8.ofNatTruncate n).toUInt64 = UInt64.ofNatLT n (Nat.lt_of_lt_of_le hn (by decide)) :=
UInt64.toNat.inj (by simp [toNat_ofNatTruncate_of_lt hn])
theorem UInt8.toUSize_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt8.size) :
(UInt8.ofNatTruncate n).toUSize = USize.ofNatLT n (Nat.lt_of_lt_of_le hn size_le_usizeSize) :=
USize.toNat.inj (by simp [toNat_ofNatTruncate_of_lt hn])
theorem UInt8.toUInt16_ofNatTruncate_of_le {n : Nat} (hn : UInt8.size n) :
(UInt8.ofNatTruncate n).toUInt16 = UInt16.ofNatLT (UInt8.size - 1) (by decide) :=
UInt16.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
theorem UInt8.toUInt32_ofNatTruncate_of_le {n : Nat} (hn : UInt8.size n) :
(UInt8.ofNatTruncate n).toUInt32 = UInt32.ofNatLT (UInt8.size - 1) (by decide) :=
UInt32.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
theorem UInt8.toUInt64_ofNatTruncate_of_le {n : Nat} (hn : UInt8.size n) :
(UInt8.ofNatTruncate n).toUInt64 = UInt64.ofNatLT (UInt8.size - 1) (by decide) :=
UInt64.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
theorem UInt8.toUSize_ofNatTruncate_of_le {n : Nat} (hn : UInt8.size n) :
(UInt8.ofNatTruncate n).toUSize = USize.ofNatLT (UInt8.size - 1) (Nat.lt_of_lt_of_le (by decide) size_le_usizeSize) :=
USize.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
theorem UInt16.toUInt32_ofNatLT {n : Nat} (h) :
(UInt16.ofNatLT n h).toUInt32 = UInt32.ofNatLT n (Nat.lt_of_lt_of_le h (by decide)) := rfl
theorem UInt16.toUInt64_ofNatLT {n : Nat} (h) :
(UInt16.ofNatLT n h).toUInt64 = UInt64.ofNatLT n (Nat.lt_of_lt_of_le h (by decide)) := rfl
theorem UInt16.toUSize_ofNatLT {n : Nat} (h) :
(UInt16.ofNatLT n h).toUSize = USize.ofNatLT n (Nat.lt_of_lt_of_le h size_le_usizeSize) := rfl
theorem UInt16.toUInt32_ofFin {n} :
(UInt16.ofFin n).toUInt32 = UInt32.ofNatLT n.val (Nat.lt_of_lt_of_le n.isLt (by decide)) := rfl
theorem UInt16.toUInt64_ofFin {n} :
(UInt16.ofFin n).toUInt64 = UInt64.ofNatLT n.val (Nat.lt_of_lt_of_le n.isLt (by decide)) := rfl
theorem UInt16.toUSize_ofFin {n} :
(UInt16.ofFin n).toUSize = USize.ofNatLT n.val (Nat.lt_of_lt_of_le n.isLt size_le_usizeSize) := rfl
@[simp] theorem UInt16.toUInt32_ofBitVec {b} : (UInt16.ofBitVec b).toUInt32 = UInt32.ofBitVec (b.setWidth _) := rfl
@[simp] theorem UInt16.toUInt64_ofBitVec {b} : (UInt16.ofBitVec b).toUInt64 = UInt64.ofBitVec (b.setWidth _) := rfl
@[simp] theorem UInt16.toUSize_ofBitVec {b} : (UInt16.ofBitVec b).toUSize = USize.ofBitVec (b.setWidth _) :=
USize.toBitVec_inj.1 (by simp)
theorem UInt16.toUInt32_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt16.size) :
(UInt16.ofNatTruncate n).toUInt32 = UInt32.ofNatLT n (Nat.lt_of_lt_of_le hn (by decide)) :=
UInt32.toNat.inj (by simp [toNat_ofNatTruncate_of_lt hn])
theorem UInt16.toUInt64_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt16.size) :
(UInt16.ofNatTruncate n).toUInt64 = UInt64.ofNatLT n (Nat.lt_of_lt_of_le hn (by decide)) :=
UInt64.toNat.inj (by simp [toNat_ofNatTruncate_of_lt hn])
theorem UInt16.toUSize_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt16.size) :
(UInt16.ofNatTruncate n).toUSize = USize.ofNatLT n (Nat.lt_of_lt_of_le hn size_le_usizeSize) :=
USize.toNat.inj (by simp [toNat_ofNatTruncate_of_lt hn])
theorem UInt16.toUInt32_ofNatTruncate_of_le {n : Nat} (hn : UInt16.size n) :
(UInt16.ofNatTruncate n).toUInt32 = UInt32.ofNatLT (UInt16.size - 1) (by decide) :=
UInt32.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
theorem UInt16.toUInt64_ofNatTruncate_of_le {n : Nat} (hn : UInt16.size n) :
(UInt16.ofNatTruncate n).toUInt64 = UInt64.ofNatLT (UInt16.size - 1) (by decide) :=
UInt64.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
theorem UInt16.toUSize_ofNatTruncate_of_le {n : Nat} (hn : UInt16.size n) :
(UInt16.ofNatTruncate n).toUSize = USize.ofNatLT (UInt16.size - 1) (Nat.lt_of_lt_of_le (by decide) size_le_usizeSize) :=
USize.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
theorem UInt32.toUInt64_ofNatLT {n : Nat} (h) :
(UInt32.ofNatLT n h).toUInt64 = UInt64.ofNatLT n (Nat.lt_of_lt_of_le h (by decide)) := rfl
theorem UInt32.toUSize_ofNatLT {n : Nat} (h) :
(UInt32.ofNatLT n h).toUSize = USize.ofNatLT n (Nat.lt_of_lt_of_le h size_le_usizeSize) := rfl
theorem UInt32.toUInt64_ofFin {n} :
(UInt32.ofFin n).toUInt64 = UInt64.ofNatLT n.val (Nat.lt_of_lt_of_le n.isLt (by decide)) := rfl
theorem UInt32.toUSize_ofFin {n} :
(UInt32.ofFin n).toUSize = USize.ofNatLT n.val (Nat.lt_of_lt_of_le n.isLt size_le_usizeSize) := rfl
@[simp] theorem UInt32.toUInt64_ofBitVec {b} : (UInt32.ofBitVec b).toUInt64 = UInt64.ofBitVec (b.setWidth _) := rfl
@[simp] theorem UInt32.toUSize_ofBitVec {b} : (UInt32.ofBitVec b).toUSize = USize.ofBitVec (b.setWidth _) :=
USize.toBitVec_inj.1 (by simp)
theorem UInt32.toUInt64_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt32.size) :
(UInt32.ofNatTruncate n).toUInt64 = UInt64.ofNatLT n (Nat.lt_of_lt_of_le hn (by decide)) :=
UInt64.toNat.inj (by simp [toNat_ofNatTruncate_of_lt hn])
theorem UInt32.toUSize_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt32.size) :
(UInt32.ofNatTruncate n).toUSize = USize.ofNatLT n (Nat.lt_of_lt_of_le hn size_le_usizeSize) :=
USize.toNat.inj (by simp [toNat_ofNatTruncate_of_lt hn])
theorem UInt32.toUInt64_ofNatTruncate_of_le {n : Nat} (hn : UInt32.size n) :
(UInt32.ofNatTruncate n).toUInt64 = UInt64.ofNatLT (UInt32.size - 1) (by decide) :=
UInt64.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
theorem UInt32.toUSize_ofNatTruncate_of_le {n : Nat} (hn : UInt32.size n) :
(UInt32.ofNatTruncate n).toUSize = USize.ofNatLT (UInt32.size - 1) (Nat.lt_of_lt_of_le (by decide) size_le_usizeSize) :=
USize.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
theorem USize.toUInt64_ofNatLT {n : Nat} (h) :
(USize.ofNatLT n h).toUInt64 = UInt64.ofNatLT n (Nat.lt_of_lt_of_le h size_le_uint64Size) := rfl
theorem USize.toUInt64_ofFin {n} :
(USize.ofFin n).toUInt64 = UInt64.ofNatLT n.val (Nat.lt_of_lt_of_le n.isLt size_le_uint64Size) := rfl
@[simp] theorem USize.toUInt64_ofBitVec {b} : (USize.ofBitVec b).toUInt64 = UInt64.ofBitVec (b.setWidth _) :=
UInt64.toBitVec_inj.1 (by simp)
theorem USize.toUInt64_ofNatTruncate_of_lt {n : Nat} (hn : n < USize.size) :
(USize.ofNatTruncate n).toUInt64 = UInt64.ofNatLT n (Nat.lt_of_lt_of_le hn size_le_uint64Size) :=
UInt64.toNat.inj (by simp [toNat_ofNatTruncate_of_lt hn])
theorem USize.toUInt64_ofNatTruncate_of_le {n : Nat} (hn : USize.size n) :
(USize.ofNatTruncate n).toUInt64 = UInt64.ofNatLT (USize.size - 1) (by cases USize.size_eq <;> simp_all +decide) :=
UInt64.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])

View File

@@ -123,6 +123,7 @@ init_grind_norm
Nat.add_eq Nat.sub_eq Nat.mul_eq Nat.zero_eq Nat.le_eq
-- Int
Int.lt_eq
Int.emod_neg Int.ediv_zero Int.emod_zero
-- GT GE
ge_eq gt_eq
-- Int op folding

View File

@@ -1007,7 +1007,7 @@ boolean condition. It can also be written as `bif b then x else y`.
This is `@[macro_inline]` because `x` and `y` should not
be eagerly evaluated (see `ite`).
-/
@[macro_inline] def cond {α : Type u} (c : Bool) (x y : α) : α :=
@[macro_inline] def cond {α : Sort u} (c : Bool) (x y : α) : α :=
match c with
| true => x
| false => y

View File

@@ -75,3 +75,10 @@ Like `Promise.result`, but resolves to `dflt` if the promise is dropped without
-/
@[macro_inline] def Promise.resultD (promise : Promise α) (dflt : α) : Task α :=
promise.result?.map (sync := true) (·.getD dflt)
/--
Checks whether the promise has already been resolved, i.e. whether access to `result*` will return
immediately.
-/
def Promise.isResolved (promise : Promise α) : BaseIO Bool :=
IO.hasFinished promise.result?

View File

@@ -46,9 +46,9 @@ where go env
def addDecl (decl : Declaration) : CoreM Unit := do
-- register namespaces for newly added constants; this used to be done by the kernel itself
-- but that is incompatible with moving it to a separate task
-- NOTE: we do not use `getTopLevelNames` here so that inductive types are registered as
-- namespaces
modifyEnv (decl.getNames.foldl registerNamePrefixes)
if let .inductDecl _ _ types _ := decl then
modifyEnv (types.foldl (registerNamePrefixes · <| ·.name ++ `rec))
if !Elab.async.get ( getOptions) then
return ( doAdd)
@@ -79,7 +79,7 @@ def addDecl (decl : Declaration) : CoreM Unit := do
Core.logSnapshotTask { stx? := none, reportingRange? := endRange?, task := t, cancelTk? := cancelTk }
where doAdd := do
profileitM Exception "type checking" ( getOptions) do
withTraceNode `Kernel (fun _ => return m!"typechecking declarations {decl.getNames}") do
withTraceNode `Kernel (fun _ => return m!"typechecking declarations {decl.getTopLevelNames}") do
if !( MonadLog.hasErrors) && decl.hasSorry then
logWarning m!"declaration uses 'sorry'"
let env ( getEnv).addDeclAux ( getOptions) decl ( read).cancelTk?

View File

@@ -194,8 +194,22 @@ def Declaration.definitionVal! : Declaration → DefinitionVal
| _ => panic! "Expected a `Declaration.defnDecl`."
/--
Returns all top-level names to be defined by adding this declaration to the environment. This does
not include auxiliary definitions such as projections.
Returns all top-level names to be defined by adding this declaration to the environment, i.e.
excluding nested helper declarations generated automatically.
-/
def Declaration.getTopLevelNames : Declaration List Name
| .axiomDecl val => [val.name]
| .defnDecl val => [val.name]
| .thmDecl val => [val.name]
| .opaqueDecl val => [val.name]
| .quotDecl => [``Quot]
| .mutualDefnDecl defns => defns.map (·.name)
| .inductDecl _ _ types _ => types.map (·.name)
/--
Returns all names to be defined by adding this declaration to the environment. This does not include
auxiliary definitions such as projections added by the elaborator, nor auxiliary recursors computed
by the kernel for nested inductive types.
-/
def Declaration.getNames : Declaration List Name
| .axiomDecl val => [val.name]
@@ -204,7 +218,7 @@ def Declaration.getNames : Declaration → List Name
| .opaqueDecl val => [val.name]
| .quotDecl => [``Quot, ``Quot.mk, ``Quot.lift, ``Quot.ind]
| .mutualDefnDecl defns => defns.map (·.name)
| .inductDecl _ _ types _ => types.map (·.name)
| .inductDecl _ _ types _ => types.flatMap fun t => t.name :: (t.name.appendCore `rec) :: t.ctors.map (·.name)
@[specialize] def Declaration.foldExprM {α} {m : Type Type} [Monad m] (d : Declaration) (f : α Expr m α) (a : α) : m α :=
match d with

View File

@@ -134,9 +134,7 @@ partial def mkEnumOfNat (declName : Name) : MetaM Unit := do
let enumType := mkConst declName
let ctors := indVal.ctors.toArray
withLocalDeclD `n (mkConst ``Nat) fun n => do
-- After the next stage0 update, this can be reverted to
-- let cond := mkConst ``cond [1]
let cond := ( mkAppOptM ``cond #[enumType]).appFn!
let cond := mkConst ``cond [1]
let rec mkDecTree (low high : Nat) : Expr :=
if low + 1 == high then
mkConst ctors[low]!

View File

@@ -910,6 +910,24 @@ private def mkInductiveDecl (vars : Array Expr) (elabs : Array InductiveElabStep
let decl := Declaration.inductDecl levelParams numParams indTypes isUnsafe
Term.ensureNoUnassignedMVars decl
addDecl decl
-- For nested inductive types, the kernel adds a variable number of auxiliary recursors.
-- Let the elaborator know about them as well. (Other auxiliaries have already been
-- registered by `addDecl` via `Declaration.getNames`.)
-- NOTE: If we want to make inductive elaboration parallel, this should switch to using
-- reserved names.
for indType in indTypes do
let mut i := 1
while true do
let auxRecName := indType.name ++ `rec |>.appendIndexAfter i
let env getEnv
let some const := env.toKernelEnv.find? auxRecName | break
let res env.addConstAsync auxRecName .recursor
res.commitConst res.asyncEnv (info? := const)
res.commitCheckEnv res.asyncEnv
setEnv res.mainEnv
i := i + 1
let replaceIndFVars (e : Expr) : MetaM Expr := do
let indFVar2Const := mkIndFVar2Const views indFVars levelParams
return ( instantiateMVars e).replace fun e' =>
@@ -932,7 +950,6 @@ private def mkInductiveDecl (vars : Array Expr) (elabs : Array InductiveElabStep
if (ctor.declId.getPos? (canonicalOnly := true)).isSome then
Term.addTermInfo' ctor.declId ( mkConstWithLevelParams ctor.declName) (isBinder := true)
enableRealizationsForConst ctor.declName
enableRealizationsForConst view.declName
return res
private def mkAuxConstructions (declNames : Array Name) : TermElabM Unit := do
@@ -962,6 +979,8 @@ private def elabInductiveViews (vars : Array Expr) (elabs : Array InductiveElabS
IndPredBelow.mkBelow view0.declName
for e in elabs do
mkInjectiveTheorems e.view.declName
for e in elabs do
enableRealizationsForConst e.view.declName
return res
/-- Ensures that there are no conflicts among or between the type and constructor names defined in `elabs`. -/

View File

@@ -22,32 +22,36 @@ Returns the "const unfold" theorem (`f.eq_unfold`) for the given declaration.
This is not extensible, and always builds on the unfold theorem (`f.eq_def`).
-/
def getConstUnfoldEqnFor? (declName : Name) : MetaM (Option Name) := do
let some unfoldEqnName getUnfoldEqnFor? (nonRec := true) declName | return none
let info getConstInfo unfoldEqnName
let type forallTelescope info.type fun xs eq => do
let some (_, lhs, rhs) := eq.eq? | throwError "Unexpected unfold theorem type {info.type}"
unless lhs.getAppFn.isConstOf declName do
throwError "Unexpected unfold theorem type {info.type}"
unless lhs.getAppArgs == xs do
throwError "Unexpected unfold theorem type {info.type}"
let type mkEq lhs.getAppFn ( mkLambdaFVars xs rhs)
return type
let value withNewMCtxDepth do
let main mkFreshExprSyntheticOpaqueMVar type
if ( tryURefl main.mvarId!) then -- try to make a rfl lemma if possible
instantiateMVars main
else forallTelescope info.type fun xs _eq => do
let mut proof mkConstWithLevelParams unfoldEqnName
proof := mkAppN proof xs
for x in xs.reverse do
proof mkLambdaFVars #[x] proof
proof mkAppM ``funext #[proof]
return proof
if ( getUnfoldEqnFor? (nonRec := true) declName).isNone then
return none
let name := .str declName eqUnfoldThmSuffix
addDecl <| Declaration.thmDecl {
name, type, value
levelParams := info.levelParams
}
realizeConst declName name do
-- we have to call `getUnfoldEqnFor?` again to make `unfoldEqnName` available in this context
let some unfoldEqnName getUnfoldEqnFor? (nonRec := true) declName | unreachable!
let info getConstInfo unfoldEqnName
let type forallTelescope info.type fun xs eq => do
let some (_, lhs, rhs) := eq.eq? | throwError "Unexpected unfold theorem type {info.type}"
unless lhs.getAppFn.isConstOf declName do
throwError "Unexpected unfold theorem type {info.type}"
unless lhs.getAppArgs == xs do
throwError "Unexpected unfold theorem type {info.type}"
let type mkEq lhs.getAppFn ( mkLambdaFVars xs rhs)
return type
let value withNewMCtxDepth do
let main mkFreshExprSyntheticOpaqueMVar type
if ( tryURefl main.mvarId!) then -- try to make a rfl lemma if possible
instantiateMVars main
else forallTelescope info.type fun xs _eq => do
let mut proof mkConstWithLevelParams unfoldEqnName
proof := mkAppN proof xs
for x in xs.reverse do
proof mkLambdaFVars #[x] proof
proof mkAppM ``funext #[proof]
return proof
addDecl <| Declaration.thmDecl {
name, type, value
levelParams := info.levelParams
}
return some name

View File

@@ -416,13 +416,18 @@ def mkEqns (declName : Name) (declNames : Array Name) (tryRefl := true): MetaM (
trace[Elab.definition.eqns] "eqnType[{i}]: {eqnTypes[i]}"
let name := (Name.str declName eqnThmSuffixBase).appendIndexAfter (i+1)
thmNames := thmNames.push name
-- determinism: `type` should be independent of the environment changes since `baseName` was
-- added
realizeConst declName name (doRealize name info type)
return thmNames
where
doRealize name info type := withOptions (tactic.hygienic.set · false) do
let value mkEqnProof declName type tryRefl
let (type, value) removeUnusedEqnHypotheses type value
addDecl <| Declaration.thmDecl {
name, type, value
levelParams := info.levelParams
}
return thmNames
/--
Auxiliary method for `mkUnfoldEq`. The structure is based on `mkEqnTypes`.
@@ -465,9 +470,12 @@ partial def mkUnfoldProof (declName : Name) (mvarId : MVarId) : MetaM Unit := do
go mvarId
/-- Generate the "unfold" lemma for `declName`. -/
def mkUnfoldEq (declName : Name) (info : EqnInfoCore) : MetaM Name := withLCtx {} {} do
withOptions (tactic.hygienic.set · false) do
let baseName := declName
def mkUnfoldEq (declName : Name) (info : EqnInfoCore) : MetaM Name := do
let name := Name.str declName unfoldThmSuffix
realizeConst declName name (doRealize name)
return name
where
doRealize name := withOptions (tactic.hygienic.set · false) do
lambdaTelescope info.value fun xs body => do
let us := info.levelParams.map mkLevelParam
let type mkEq (mkAppN (Lean.mkConst declName us) xs) body
@@ -475,12 +483,10 @@ def mkUnfoldEq (declName : Name) (info : EqnInfoCore) : MetaM Name := withLCtx {
mkUnfoldProof declName goal.mvarId!
let type mkForallFVars xs type
let value mkLambdaFVars xs ( instantiateMVars goal)
let name := Name.str baseName unfoldThmSuffix
addDecl <| Declaration.thmDecl {
name, type, value
levelParams := info.levelParams
}
return name
def getUnfoldFor? (declName : Name) (getInfo? : Unit Option EqnInfoCore) : MetaM (Option Name) := do
if let some info := getInfo? () then

View File

@@ -0,0 +1,496 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Joachim Breitner
-/
prelude
import Lean.Elab.PreDefinition.Basic
/-!
This module contains the logic for figuring out, given mutually recursive predefinitions,
which parameters are “fixed”. This used to be a simple task when we only considered a fixed prefix,
but becomes a quite involved task if we allow fixed parameters also later in the parameter list,
and possibly in a different order in different modules.
The main components of this module are
* The pure `Info` data type for the bookkeeping during analysis
* The `FixedParamPerm` type, with the analysis result for one function
(effectively a mask and a permutation)
* The `FixedParamPerms` data type, with the data for a whole recursive group.
* The `getFixedParamPerms` function that calculates the fixed parameters
* Various `MetaM` functions for bringing into scope fixed and varying paramters, assembling
argument lists etc.
-/
namespace Lean.Elab.FixedParams
/--
To determine which parameters in mutually recursive predefinitions are fixed, and how they
correspond to each other, we run an analysis that aggregates information in the `Info` data type.
Abstractly, this represents
* a set `varying` of `(funIdx × paramIdx)` pairs known to be varying, initially empty
* a directed graph whose nodes are `(funIdx × paramIdx)` pairs, initially empty
We find the largest set and graph that satisfies these rules:
* Every parameter has to be related to itself: `(funIdx, paramIdx) → (funIdx, paramIdx)`.
* whenever the function with index `caller` calls `callee` and the `argIdx`'s argument is reducibly
defeq to `paramIdx`, then we have an edge `(caller, paramIdx) → (callee, argIdx)`.
* whenever the function with index `caller` calls `callee` and the `argIdx`'s argument is not reducibly
defeq to any of the `caller`'s parameters, then `(callee, argIdx) ∈ varying`.
* If we have `(caller, paramIdx₁) → (callee, argIdx)` and `(caller, paramIdx₂) → (callee, argIdx)`
with `paramIdx₁ ≠ paramIdx₂`, then `(callee, argIdx) ∈ varying`.
* The graph is transitive
* If we have `(caller, paramIdx) → (callee, argIdx)` and `(caller, paramIdx) ∈ varying`, then
`(callee, argIdx) ∈ varying`
* If the type of `funIdx`s parameter `paramIdx₂ depends on the `paramIdx₁` and
`(funIdx, paramIdx₁) ∈ varying`, then `(funIdx, paramIdx₁) ∈ varying`
* For structural recursion: The target and all its indices are `varying`.
(This is taking into account post-hoc, using `FixedParamPerms.erase`)
Under the assumption that the predefintions indeed are mutually recursive, then the resulting graph,
restricted to the non-`varying` nodes, should partition into cliques that have one member from each
function. Every such clique becomes a fixed parameter.
-/
structure Info where
/-
The concrete data structure for set and graph exploits some of the invariants:
* Once we know a parameter is varying, it's incoming edges are irrelevant.
* There can be at most one incoming edge
So we have
* `graph[callee][argIdx] = none`: `(callee, argIdx) ∈ varying`
* `graph[callee][argIdx] = some a`:
* `(callee, argIdx) ∉ varying` (yet) and
* `a[callerIdx] = none`: we have no edge to `(callee, argIdx)`
* `a[callerIdx] = some paramIdx`: we have edge `(callerIdx, paramIdx) → (callee, argIdx)`
-/
graph : Array (Array (Option (Array (Option Nat))))
/--
The dependency structure of the function parameter.
If `paramIdx₂ ∈ revDeps[funIdx][paraIdx₁]`, then the type of `paramIdx₂` depends on `parmaIdx₁`
-/
revDeps : Array (Array (Array Nat))
def Info.init (revDeps : Array (Array (Array Nat))) : Info where
graph := revDeps.map fun deps =>
mkArray deps.size (some (mkArray revDeps.size none))
revDeps
def Info.addSelfCalls (info : Info) : Info :=
{ info with graph := info.graph.mapIdx fun funIdx paramInfos =>
paramInfos.mapIdx fun paramIdx paramInfo? =>
paramInfo?.map fun callers =>
callers.set! funIdx (some paramIdx) }
/--
Is this parameter still plausibly a fixed parameter?
-/
def Info.mayBeFixed (callerIdx paramIdx : Nat) (info : Info) : Bool :=
info.graph[callerIdx]![paramIdx]!.isSome
/--
This parameter is varying. Set and propagate that information.
-/
partial def Info.setVarying (funIdx paramIdx : Nat) (info : Info) : Info := Id.run do
let mut info : Info := info
if info.mayBeFixed funIdx paramIdx then
-- Set this as varying
info := { info with graph := info.graph.modify funIdx (·.set! paramIdx none) }
-- Propagate along edges for already observed calls
for otherFunIdx in [:info.graph.size] do
for otherParamIdx in [:info.graph[otherFunIdx]!.size] do
if let some otherParamInfo := info.graph[otherFunIdx]![otherParamIdx]! then
if otherParamInfo[funIdx]! = some paramIdx then
info := Info.setVarying otherFunIdx otherParamIdx info
-- Propagate along type dependencies edges
for dependingParam in info.revDeps[funIdx]![paramIdx]! do
info := Info.setVarying funIdx dependingParam info
info
def Info.getCallerParam? (calleeIdx argIdx callerIdx : Nat) (info : Info) : Option Nat :=
info.graph[calleeIdx]![argIdx]!.bind (·[callerIdx]!)
/--
We observe a possibly valid edge.
-/
partial def Info.setCallerParam (calleeIdx argIdx callerIdx paramIdx : Nat) (info : Info) : Info :=
if info.mayBeFixed calleeIdx argIdx then
if info.mayBeFixed callerIdx paramIdx then
if let some paramIdx' := info.getCallerParam? calleeIdx argIdx callerIdx then
-- We already have an etry
if paramIdx = paramIdx' then
-- all good
info
else
-- Inconsistent information
info.setVarying calleeIdx argIdx
else
-- Set the new entry
let info := { info with graph := info.graph.modify calleeIdx (·.modify argIdx (·.map (·.set! callerIdx (some paramIdx)))) }
Id.run do
-- Propagate information about the caller
let mut info : Info := info
if let some callerParamInfo := info.graph[callerIdx]![paramIdx]! then
for h : otherFunIdx in [:callerParamInfo.size] do
if let some otherParamIdx := callerParamInfo[otherFunIdx] then
info := info.setCallerParam calleeIdx argIdx otherFunIdx otherParamIdx
-- Propagate information about the callee
for otherFunIdx in [:info.graph.size] do
for otherArgIdx in [:info.graph[otherFunIdx]!.size] do
if let some otherArgsInfo := info.graph[otherFunIdx]![otherArgIdx]! then
if let some paramIdx' := otherArgsInfo[calleeIdx]! then
if paramIdx' = argIdx then
info := info.setCallerParam otherFunIdx otherArgIdx callerIdx paramIdx
return info
else
-- Param not fixed, so argument isn't either
info.setVarying calleeIdx argIdx
else
info
def Info.format (info : Info) : Format := Format.line.joinSep <|
info.graph.toList.map fun paramInfos =>
(f!"" ++ ·) <| f!" ".joinSep <| paramInfos.toList.map fun
| .none => f!""
| .some callerInfos => .sbracket <| f!" ".joinSep <| callerInfos.toList.map fun
| Option.none => f!"?"
| .some idx => f!"#{idx+1}"
instance : ToFormat Info := Info.format
end FixedParams
open Lean Meta FixedParams
def getParamRevDeps (preDefs : Array PreDefinition) : MetaM (Array (Array (Array Nat))) := do
preDefs.mapM fun preDef =>
lambdaTelescope preDef.value (cleanupAnnotations := true) fun xs _ => do
let mut revDeps := #[]
for h : i in [:xs.size] do
let mut deps := #[]
for h : j in [i+1:xs.size] do
if ( dependsOn ( inferType xs[j]) xs[i].fvarId!) then
deps := deps.push j
revDeps := revDeps.push deps
pure revDeps
def getFixedParamsInfo (preDefs : Array PreDefinition) : MetaM FixedParams.Info := do
let revDeps getParamRevDeps preDefs
let arities := revDeps.map (·.size)
let ref IO.mkRef (Info.init revDeps)
ref.modify .addSelfCalls
for h : callerIdx in [:preDefs.size] do
let preDef := preDefs[callerIdx]
lambdaTelescope preDef.value fun params body => do
assert! params.size = arities[callerIdx]!
-- TODO: transform is overkill, a simple visit-all-subexpression that takes applications
-- as whole suffices
discard <| Meta.transform (skipConstInApp := true) body fun e => e.withApp fun f args => do
unless f.isConst do
return .continue
let n := f.constName!
let some calleeIdx := preDefs.findIdx? (·.declName = n) | return .continue
for argIdx in [:arities[calleeIdx]!] do
if ( ref.get).mayBeFixed calleeIdx argIdx then
if h : argIdx < args.size then
let arg := args[argIdx]
-- We have seen this before (or it is a self-call), so only check that one param
if let some paramIdx := ( ref.get).getCallerParam? calleeIdx argIdx callerIdx then
let param := params[paramIdx]!
unless ( withoutProofIrrelevance <| withReducible <| isDefEq param arg) do
trace[Elab.definition.fixedParams] "getFixedParams: notFixed {calleeIdx} {argIdx}:\nIn {e}\n{param} =/= {arg}"
ref.modify (Info.setVarying calleeIdx argIdx)
else
-- Try all parameters
let mut any := false
for h : paramIdx in [:params.size] do
if ( ref.get).mayBeFixed callerIdx paramIdx then
let param := params[paramIdx]
if ( withoutProofIrrelevance <| withReducible <| isDefEq param arg) then
ref.modify (Info.setCallerParam calleeIdx argIdx callerIdx paramIdx)
any := true
unless any do
trace[Elab.definition.fixedParams] "getFixedParams: notFixed {calleeIdx} {argIdx}:\nIn {e}\n{arg} not matched"
-- Argument is none of the plausible parameters, so it cannot be a fixed argument
ref.modify (Info.setVarying calleeIdx argIdx)
else
-- Underapplication
trace[Elab.definition.fixedParams] "getFixedParams: notFixed {calleeIdx} {argIdx}:\nIn {e}\ntoo few arguments for {argIdx}"
ref.modify (Info.setVarying calleeIdx argIdx)
return .continue
let info ref.get
trace[Elab.definition.fixedParams] "getFixedParams:{info.format.indentD}"
return info
/--
For a given function, a mapping from its parameters to the (indices of the) fixed parameters of the
recursive group.
The length of the array is the arity of the function, as determined from its body, consistent
with the arity used by well-founded recursion.
For the first function, they appear in order; for other functions they may be reordered.
-/
abbrev FixedParamPerm := Array (Option Nat)
/--
This data structure stores the result of the fixed parameter analysis. See `FixedParams.Info` for
details on the analysis.
-/
structure FixedParamPerms where
/-- Number of fixed parameters -/
numFixed : Nat
/--
For each function in the clique, a mapping from its parameters to the fixed parameters.
For the first function, they appear in order; for other functions they may be reordered.
-/
perms : Array FixedParamPerm
/--
The dependencies among the parameters. See `FixedParams.Info.revDeps`.
We need this for the `FixedParamsPerm.erase` operation.
-/
revDeps : Array (Array (Array Nat))
deriving Inhabited, Repr
def getFixedParamPerms (preDefs : Array PreDefinition) : MetaM FixedParamPerms := do
let info getFixedParamsInfo preDefs
lambdaTelescope preDefs[0]!.value fun xs _ => do
let paramInfos := info.graph[0]!
assert! xs.size = paramInfos.size
let mut firstPerm := #[]
let mut numFixed := 0
for paramIdx in [:xs.size], x in xs, paramInfo? in paramInfos do
if let some paramInfo := paramInfo? then
assert! paramInfo[0]! = some paramIdx
firstPerm := firstPerm.push (some numFixed)
numFixed := numFixed + 1
else
firstPerm := firstPerm.push none
let mut perms := #[firstPerm]
for h : funIdx in [1:info.graph.size] do
let paramInfos := info.graph[funIdx]
let mut perm := #[]
for paramInfo? in paramInfos do
if let some paramInfo := paramInfo? then
if let some firstParamIdx := paramInfo[0]! then
assert! firstPerm[firstParamIdx]!.isSome
perm := perm.push firstPerm[firstParamIdx]!
else
panic! "Incomplete paramInfo"
else
perm := perm.push none
perms := perms.push perm
return { numFixed, perms, revDeps := info.revDeps }
def FixedParamPerm.numFixed (perm : FixedParamPerm) : Nat :=
perm.countP Option.isSome
def FixedParamPerm.isFixed (perm : FixedParamPerm) (i : Nat) : Bool :=
perm[i]?.join.isSome
/--
Brings the fixed parameters from `type`, which should the the type of the `funIdx`'s function, into
scope.
-/
private partial def FixedParamPerm.forallTelescopeImpl (perm : FixedParamPerm)
(type : Expr) (k : Array Expr MetaM α) : MetaM α := do
go 0 type (mkArray perm.numFixed (mkSort 0))
where
go i type xs := do
match perm[i]? with
| .some (Option.some fixedParamIdx) =>
forallBoundedTelescope type (some 1) (cleanupAnnotations := true) fun xs' type => do
assert! xs'.size = 1
let x := xs'[0]!
assert! !( inferType x).hasLooseBVars
assert! fixedParamIdx < xs.size
go (i + 1) type (xs.set! fixedParamIdx x)
| .some .none =>
let type whnf type
assert! type.isForall
go (i + 1) type.bindingBody! xs
| .none =>
k xs
def FixedParamPerm.forallTelescope [MonadControlT MetaM n] [Monad n]
(perm : FixedParamPerm) (type : Expr) (k : Array Expr n α) : n α := do
map1MetaM (fun k => perm.forallTelescopeImpl type k) k
/--
If `type` is the type of the `funIdx`'s function, instantiate the fixed paramters.
-/
def FixedParamPerm.instantiateForall (perm: FixedParamPerm) (type₀ : Expr) (xs : Array Expr) : MetaM Expr := do
assert! xs.size = perm.numFixed
let mask := perm.toList
go mask type₀
where
go | [], type => pure type
| (.some fixedParamIdx)::mask, type => do
assert! fixedParamIdx < xs.size
go mask ( Meta.instantiateForall type #[xs[fixedParamIdx]!])
| .none::mask, type =>
forallBoundedTelescope type (some 1) fun ys type => do
assert! ys.size = 1
mkForallFVars ys ( go mask type)
/--
If `value` is the body of the `funIdx`'s function, instantiate the fixed paramters.
Expects enough manifest lambdas to instantiate all fixed parameters, but can handle
eta-contracted definitions beyond that.
-/
def FixedParamPerm.instantiateLambda (perm : FixedParamPerm) (value₀ : Expr) (xs : Array Expr) : MetaM Expr := do
assert! xs.size = perm.numFixed
let mask := perm.toList
go mask value₀
where
go | [], value => pure value
| (.some fixedParamIdx)::mask, value => do
assert! fixedParamIdx < xs.size
go mask ( Meta.instantiateLambda value #[xs[fixedParamIdx]!])
| .none::mask, value =>
if mask.all Option.isNone then
-- Nothing left to do. Also helpful if we may encounter an eta-contracted value
return value
else
lambdaBoundedTelescope value 1 fun ys value => do
assert! ys.size = 1
mkLambdaFVars ys ( go mask value)
/--
If `xs` are arguments to the `funIdx`'s function, pick only the fixed ones, and reorder appropriately.
Expects `xs` to match the arity of the function.
-/
def FixedParamPerm.pickFixed (perm : FixedParamPerm) (xs : Array α) : Array α := Id.run do
assert! xs.size = perm.size
if h : xs.size = 0 then
pure #[]
else
let dummy := xs[0]
let ys := mkArray perm.numFixed dummy
go (perm.zip xs).toList ys
where
go | [], ys => return ys
| (.some fixedParamIdx, x)::xs, ys => do
assert! fixedParamIdx < ys.size
go xs (ys.set! fixedParamIdx x)
| (.none, _) :: perm, ys =>
go perm ys
/--
If `xs` are arguments to the `funIdx`'s function, pick only the varying ones.
Unlike `pickFixed`, this function can handle over- or under-application.
-/
def FixedParamPerm.pickVarying (perm : FixedParamPerm) (xs : Array α) : Array α := Id.run do
let mut ys := #[]
for h : i in [:xs.size] do
if perm[i]?.join.isNone then ys := ys.push xs[i]
pure ys
/--
Intersperses the fixed and varying parameters to be in the original parameter order.
Can handle over- or und-application (extra or missing varying args), as long
as there are all varying parameters that go before fixed parameters.
(We expect to always find all fixed parameters, else they woudn't be fixed parameters.)
-/
partial def FixedParamPerm.buildArgs (perm : FixedParamPerm) (fixedArgs varyingArgs : Array α) : Array α :=
assert! fixedArgs.size = perm.numFixed
go 0 0 #[]
where
go i j (xs : Array α) :=
if _ : i < perm.size then
if let some fixedParamIdx := perm[i] then
if _ : fixedParamIdx < fixedArgs.size then
go (i + 1) j (xs.push fixedArgs[fixedParamIdx])
else
panic! "FixedParams.buildArgs: too few fixed args"
else
if _ : j < varyingArgs.size then
go (i + 1) (j + 1) (xs.push varyingArgs[j])
else
if perm[i:].all Option.isNone then
xs -- Under-application
else
panic! "FixedParams.buildArgs: too few varying args"
else
xs ++ varyingArgs[j:] -- (Possibly) over-application
/--
Are all fixed parameters a non-reordered prefix?
-/
def FixedParamPerms.fixedArePrefix (fixedParamPerms : FixedParamPerms) : Bool :=
fixedParamPerms.perms.all fun paramInfos =>
paramInfos ==
(Array.range fixedParamPerms.numFixed).map Option.some ++
mkArray (paramInfos.size - fixedParamPerms.numFixed) .none
/--
If `xs` are the fixed parameters that are in scope, and `toErase` are, for each function, the
positions of arguments that must no longer be fixed parameters, then this function splits partitions
`xs` into those to keep and those to erase, and updates `FixedParams` accordingly.
This is used in structural recursion, where we may discover that some fixed parameters are actually
indices and need to be treated as varying, including all parameters that depend on them.
-/
def FixedParamPerms.erase (fixedParamPerms : FixedParamPerms) (xs : Array Expr)
(toErase : Array (Array Nat)) : (FixedParamPerms × Array Expr × Array FVarId) := Id.run do
assert! xs.all (·.isFVar)
assert! fixedParamPerms.numFixed = xs.size
assert! toErase.size = fixedParamPerms.perms.size
-- Calculate a mask on the fixed parameters of variables to erase
let mut mask := mkArray fixedParamPerms.numFixed false
for funIdx in [:toErase.size], paramIdxs in toErase, mapping in fixedParamPerms.perms do
for paramIdx in paramIdxs do
assert! paramIdx < mapping.size
if let some fixedParamIdx := mapping[paramIdx]! then
mask := mask.set! fixedParamIdx true
-- Take the transitive closure under under `fixedParamPerms.revDeps`.
let mut changed := true
while changed do
changed := false
for h : funIdx in [:fixedParamPerms.perms.size] do
for h : paramIdx₁ in [:fixedParamPerms.perms[funIdx].size] do
if let some fixedParamIdx₁ := fixedParamPerms.perms[funIdx][paramIdx₁] then
if mask[fixedParamIdx₁]! then
for paramIdx₂ in fixedParamPerms.revDeps[funIdx]![paramIdx₁]! do
if let some fixedParamIdx₂ := fixedParamPerms.perms[funIdx][paramIdx₂]! then
if !mask[fixedParamIdx₂]! then
mask := mask.set! fixedParamIdx₂ true
changed := true
-- Calculate reindexing map, variables to keep, variables to erase
let mut reindex := #[]
let mut fvarsToErase :=#[]
let mut toKeep :=#[]
for i in [:mask.size], erase in mask, x in xs do
if erase then
reindex := reindex.push none
fvarsToErase := fvarsToErase.push x.fvarId!
else
reindex := reindex.push (Option.some toKeep.size)
toKeep := toKeep.push x
let fixedParamPerms' : FixedParamPerms := {
numFixed := toKeep.size
perms := fixedParamPerms.perms.map (·.map (·.bind (reindex[·]!)))
revDeps := fixedParamPerms.revDeps
}
return (fixedParamPerms', toKeep, fvarsToErase)
end Lean.Elab
builtin_initialize
Lean.registerTraceClass `Elab.definition.fixedParams

View File

@@ -26,24 +26,6 @@ where
withLocalDecl vals[0]!.bindingName! vals[0]!.binderInfo vals[0]!.bindingDomain! fun x =>
go (fvars.push x) (vals.map fun val => val.bindingBody!.instantiate1 x)
def getFixedPrefix (preDefs : Array PreDefinition) : MetaM Nat :=
withCommonTelescope preDefs fun xs vals => do
let resultRef IO.mkRef xs.size
for val in vals do
if ( resultRef.get) == 0 then return 0
forEachExpr' val fun e => do
if preDefs.any fun preDef => e.isAppOf preDef.declName then
let args := e.getAppArgs
resultRef.modify (min args.size ·)
for arg in args, x in xs do
if !( withoutProofIrrelevance <| withReducible <| isDefEq arg x) then
-- We continue searching if e's arguments are not a prefix of `xs`
return true
return false
else
return true
resultRef.get
def addPreDefsFromUnary (preDefs : Array PreDefinition) (preDefsNonrec : Array PreDefinition)
(unaryPreDefNonRec : PreDefinition) : TermElabM Unit := do
/-

View File

@@ -9,6 +9,7 @@ import Lean.Meta.Tactic.Rewrite
import Lean.Meta.Tactic.Split
import Lean.Elab.PreDefinition.Basic
import Lean.Elab.PreDefinition.Eqns
import Lean.Elab.PreDefinition.FixedParams
import Lean.Meta.ArgsPacker.Basic
import Init.Data.Array.Basic
import Init.Internal.Order.Basic
@@ -20,12 +21,13 @@ open Eqns
structure EqnInfo extends EqnInfoCore where
declNames : Array Name
declNameNonRec : Name
fixedPrefixSize : Nat
fixedParamPerms : FixedParamPerms
deriving Inhabited
builtin_initialize eqnInfoExt : MapDeclarationExtension EqnInfo mkMapDeclarationExtension
def registerEqnsInfo (preDefs : Array PreDefinition) (declNameNonRec : Name) (fixedPrefixSize : Nat) : MetaM Unit := do
def registerEqnsInfo (preDefs : Array PreDefinition) (declNameNonRec : Name)
(fixedParamPerms : FixedParamPerms) : MetaM Unit := do
preDefs.forM fun preDef => ensureEqnReservedNamesAvailable preDef.declName
unless preDefs.all fun p => p.kind.isTheorem do
unless ( preDefs.allM fun p => isProp p.type) do
@@ -33,7 +35,7 @@ def registerEqnsInfo (preDefs : Array PreDefinition) (declNameNonRec : Name) (fi
modifyEnv fun env =>
preDefs.foldl (init := env) fun env preDef =>
eqnInfoExt.insert env preDef.declName { preDef with
declNames, declNameNonRec, fixedPrefixSize }
declNames, declNameNonRec, fixedParamPerms }
private def deltaLHSUntilFix (declName declNameNonRec : Name) (mvarId : MVarId) : MetaM MVarId := mvarId.withContext do
let target mvarId.getType'
@@ -66,9 +68,12 @@ private def rwFixEq (mvarId : MVarId) : MetaM MVarId := mvarId.withContext do
return mvarNew.mvarId!
/-- Generate the "unfold" lemma for `declName`. -/
def mkUnfoldEq (declName : Name) (info : EqnInfo) : MetaM Name := withLCtx {} {} do
withOptions (tactic.hygienic.set · false) do
let baseName := declName
def mkUnfoldEq (declName : Name) (info : EqnInfo) : MetaM Name := do
let name := Name.str declName unfoldThmSuffix
realizeConst declName name (doRealize name)
return name
where
doRealize name := withOptions (tactic.hygienic.set · false) do
lambdaTelescope info.value fun xs body => do
let us := info.levelParams.map mkLevelParam
let type mkEq (mkAppN (Lean.mkConst declName us) xs) body
@@ -90,12 +95,10 @@ def mkUnfoldEq (declName : Name) (info : EqnInfo) : MetaM Name := withLCtx {} {}
throwError "failed to generate unfold theorem for '{declName}':\n{e.toMessageData}"
let type mkForallFVars xs type
let value mkLambdaFVars xs goal
let name := Name.str baseName unfoldThmSuffix
addDecl <| Declaration.thmDecl {
name, type, value
levelParams := info.levelParams
}
return name
def getUnfoldFor? (declName : Name) : MetaM (Option Name) := do
let name := Name.str declName unfoldThmSuffix

View File

@@ -70,21 +70,21 @@ def deriveInduction (name : Name) : MetaM Unit := do
let infos eqnInfo.declNames.mapM getConstInfoDefn
-- First open up the fixed parameters everywhere
let e' lambdaBoundedTelescope infos[0]!.value eqnInfo.fixedPrefixSize fun xs _ => do
let e' eqnInfo.fixedParamPerms.perms[0]!.forallTelescope infos[0]!.type fun xs => do
-- Now look at the body of an arbitrary of the functions (they are essentially the same
-- up to the final projections)
let body instantiateLambda infos[0]!.value xs
let body eqnInfo.fixedParamPerms.perms[0]!.instantiateLambda infos[0]!.value xs
-- The body should now be of the form of the form (fix … ).2.2.1
-- We strip the projections (if present)
let body' := PProdN.stripProjs body
let body' := PProdN.stripProjs body.eta -- TODO: Eta more carefully?
let some fixApp whnfUntil body' ``fix
| throwError "Unexpected function body {body}"
| throwError "Unexpected function body {body}, could not whnfUntil fix"
let_expr fix α instCCPOα F hmono := fixApp
| throwError "Unexpected function body {body'}"
| throwError "Unexpected function body {body'}, not an application of fix"
let instCCPOs := CCPOProdProjs infos.size instCCPOα
let types infos.mapM (instantiateForall ·.type xs)
let types infos.mapIdxM (eqnInfo.fixedParamPerms.perms[·]!.instantiateForall ·.type xs)
let packedType PProdN.pack 0 types
let motiveTypes types.mapM (mkArrow · (.sort 0))
let motiveNames := numberNames motiveTypes.size "motive"
@@ -135,7 +135,11 @@ def deriveInduction (name : Name) : MetaM Unit := do
let packedConclusion PProdN.pack 0 <|
motives.mapIdxM fun i motive => do
let f mkConstWithLevelParams infos[i]!.name
return mkApp motive (mkAppN f xs)
let fEtaExpanded lambdaTelescope infos[i]!.value fun ys _ =>
mkLambdaFVars ys (mkAppN f ys)
let fInst eqnInfo.fixedParamPerms.perms[i]!.instantiateLambda fEtaExpanded xs
let fInst := fInst.eta
return mkApp motive fInst
let e' mkExpectedTypeHint e' packedConclusion
let e' mkLambdaFVars hs e'
let e' mkLambdaFVars adms e'
@@ -228,9 +232,10 @@ def derivePartialCorrectness (name : Name) : MetaM Unit := do
throwError "{name} is not defined by partial_fixpoint"
let infos eqnInfo.declNames.mapM getConstInfoDefn
let fixedParamPerm0 := eqnInfo.fixedParamPerms.perms[0]!
-- First open up the fixed parameters everywhere
let e' lambdaBoundedTelescope infos[0]!.value eqnInfo.fixedPrefixSize fun xs _ => do
let types infos.mapM (instantiateForall ·.type xs)
let e' fixedParamPerm0.forallTelescope infos[0]!.type fun xs => do
let types infos.mapIdxM (eqnInfo.fixedParamPerms.perms[·]!.instantiateForall ·.type xs)
-- for `f : α → β → Option γ`, we expect a `motive : α → β → γ → Prop`
let motiveTypes types.mapM fun type =>

View File

@@ -18,33 +18,44 @@ open Monotonicity
open Lean.Order
private def replaceRecApps (recFnNames : Array Name) (fixedPrefixSize : Nat) (f : Expr) (e : Expr) : MetaM Expr := do
private def replaceRecApps (recFnNames : Array Name) (fixedParamPerms : FixedParamPerms) (f : Expr) (e : Expr) : MetaM Expr := do
assert! recFnNames.size = fixedParamPerms.perms.size
let t inferType f
return e.replace fun e =>
if let some idx := recFnNames.findIdx? (e.isAppOfArity · fixedPrefixSize) then
some <| PProdN.proj recFnNames.size idx t f
else
none
return e.replace fun e => do
let fn := e.getAppFn
guard fn.isConst
let idx recFnNames.idxOf? fn.constName!
let args := e.getAppArgs
let varying := fixedParamPerms.perms[idx]!.pickVarying args
return mkAppN (PProdN.proj recFnNames.size idx t f) varying
/--
For pretty error messages:
Takes `F : (fun f => e)`, where `f` is the packed function, and replaces `f` in `e` with the user-visible
constants, which are added to the environment temporarily.
-/
private def unReplaceRecApps {α} (preDefs : Array PreDefinition) (fixedArgs : Array Expr)
private def unReplaceRecApps {α} (preDefs : Array PreDefinition) (fixedParamPerms : FixedParamPerms) (fixedArgs : Array Expr)
(F : Expr) (k : Expr MetaM α) : MetaM α := do
unless F.isLambda do throwError "Expected lambda:{indentExpr F}"
withoutModifyingEnv do
preDefs.forM addAsAxiom
let fns := preDefs.map fun d =>
mkAppN (.const d.declName (d.levelParams.map mkLevelParam)) fixedArgs
let fns preDefs.mapIdxM fun funIdx preDef => do
let value fixedParamPerms.perms[funIdx]!.instantiateLambda preDef.value fixedArgs
lambdaTelescope value fun xs _ =>
let args := fixedParamPerms.perms[funIdx]!.buildArgs fixedArgs xs
let call := mkAppN (.const preDef.declName (preDef.levelParams.map mkLevelParam)) args
mkLambdaFVars (etaReduce := true) xs call
let packedFn PProdN.mk 0 fns
let e lambdaBoundedTelescope F 1 fun f e => do
let f := f[0]!
-- Replace f with calls to the constants
let e := e.replace fun e => do if e == f then return packedFn else none
-- And reduce projection redexes
let e := e.replace fun e => do
if e == f then return packedFn else none
-- And reduce projection and beta redexes
-- (This is a bit blunt; we could try harder to only replace the projection and beta-redexes
-- introduced above)
let e PProdN.reduceProjs e
let e Core.betaReduce e
pure e
k e
@@ -81,15 +92,12 @@ def partialFixpoint (preDefs : Array PreDefinition) : TermElabM Unit := do
mkAppOptM ``FlatOrder.instCCPO #[none, classicalWitness]
mkLambdaFVars xs inst
let fixedPrefixSize Mutual.getFixedPrefix preDefs
trace[Elab.definition.partialFixpoint] "fixed prefix size: {fixedPrefixSize}"
let declNames := preDefs.map (·.declName)
forallBoundedTelescope preDefs[0]!.type fixedPrefixSize fun fixedArgs _ => do
let fixedParamPerms getFixedParamPerms preDefs
fixedParamPerms.perms[0]!.forallTelescope preDefs[0]!.type fun fixedArgs => do
-- ∀ x y, CCPO (rᵢ x y)
let ccpoInsts := ccpoInsts.map (·.beta fixedArgs)
let types preDefs.mapM (instantiateForall ·.type fixedArgs)
let ccpoInsts ccpoInsts.mapIdxM (fixedParamPerms.perms[·]!.instantiateLambda · fixedArgs)
let types preDefs.mapIdxM (fixedParamPerms.perms[·]!.instantiateForall ·.type fixedArgs)
-- (∀ x y, r₁ x y) ×' (∀ x y, r₂ x y)
let packedType PProdN.pack 0 types
@@ -108,7 +116,7 @@ def partialFixpoint (preDefs : Array PreDefinition) : TermElabM Unit := do
-- Error reporting hook, presenting monotonicity errors in terms of recursive functions
let failK {α} f (monoThms : Array Name) : MetaM α := do
unReplaceRecApps preDefs fixedArgs f fun t => do
unReplaceRecApps preDefs fixedParamPerms fixedArgs f fun t => do
let extraMsg := if monoThms.isEmpty then m!"" else
m!"Tried to apply {.andList (monoThms.toList.map (m!"'{.ofConstName ·}'"))}, but failed.\n\
Possible cause: A missing `{.ofConstName ``MonoBind}` instance.\n\
@@ -122,13 +130,13 @@ def partialFixpoint (preDefs : Array PreDefinition) : TermElabM Unit := do
-- Adjust the body of each function to take the other functions as a
-- (packed) parameter
let Fs preDefs.mapM fun preDef => do
let body instantiateLambda preDef.value fixedArgs
let Fs preDefs.mapIdxM fun funIdx preDef => do
let body fixedParamPerms.perms[funIdx]!.instantiateLambda preDef.value fixedArgs
withLocalDeclD ( mkFreshUserName `f) packedType fun f => do
let body' withoutModifyingEnv do
-- replaceRecApps needs the constants in the env to typecheck things
preDefs.forM (addAsAxiom ·)
replaceRecApps declNames fixedPrefixSize f body
replaceRecApps declNames fixedParamPerms f body
mkLambdaFVars #[f] body'
-- Construct and solve monotonicity goals for each function separately
@@ -160,7 +168,7 @@ def partialFixpoint (preDefs : Array PreDefinition) : TermElabM Unit := do
trace[Elab.definition.partialFixpoint] "packedValue: {packedValue}"
let declName :=
if preDefs.size = 1 then
if preDefs.size = 1 && fixedParamPerms.fixedArePrefix then
preDefs[0]!.declName
else
preDefs[0]!.declName ++ `mutual
@@ -170,17 +178,22 @@ def partialFixpoint (preDefs : Array PreDefinition) : TermElabM Unit := do
declName := declName
type := packedType'
value := packedValue'}
let preDefsNonrec preDefs.mapIdxM fun fidx preDef => do
let us := preDefNonRec.levelParams.map mkLevelParam
let value := mkConst preDefNonRec.declName us
let value := mkAppN value fixedArgs
let value := PProdN.proj preDefs.size fidx packedType value
let value mkLambdaFVars fixedArgs value
pure { preDef with value }
forallBoundedTelescope preDef.type fixedParamPerms.perms[fidx]!.size fun params _ => do
let fixed := fixedParamPerms.perms[fidx]!.pickFixed params
let varying := fixedParamPerms.perms[fidx]!.pickVarying params
let us := preDefNonRec.levelParams.map mkLevelParam
let value := mkConst preDefNonRec.declName us
let value := mkAppN value fixed
let value := PProdN.proj preDefs.size fidx packedType value
let value := mkAppN value varying
let value mkLambdaFVars (etaReduce := true) params value
pure { preDef with value }
Mutual.addPreDefsFromUnary preDefs preDefsNonrec preDefNonRec
let preDefs Mutual.cleanPreDefs preDefs
PartialFixpoint.registerEqnsInfo preDefs preDefNonRec.declName fixedPrefixSize
PartialFixpoint.registerEqnsInfo preDefs preDefNonRec.declName fixedParamPerms
Mutual.addPreDefAttributes preDefs
end Lean.Elab

View File

@@ -155,7 +155,8 @@ private partial def replaceRecApps (recArgInfos : Array RecArgInfo) (positions :
try toBelow below recArgInfo.indGroupInst.params.size positions fnIdx recArg
catch _ => throwError "failed to eliminate recursive application{indentExpr e}"
-- We don't pass the fixed parameters, the indices and the major arg to `f`, only the rest
let (_, fArgs) := recArgInfo.pickIndicesMajor args[recArgInfo.numFixed:]
let ys := recArgInfo.fixedParamPerm.pickVarying args
let (_, fArgs) := recArgInfo.pickIndicesMajor ys
let fArgs fArgs.mapM (replaceRecApps recArgInfos positions below ·)
return mkAppN f fArgs
else

View File

@@ -10,6 +10,7 @@ import Lean.Meta.Tactic.Simp.Main
import Lean.Meta.Tactic.Apply
import Lean.Elab.PreDefinition.Basic
import Lean.Elab.PreDefinition.Eqns
import Lean.Elab.PreDefinition.FixedParams
import Lean.Elab.PreDefinition.Structural.Basic
namespace Lean.Elab
@@ -21,7 +22,7 @@ namespace Structural
structure EqnInfo extends EqnInfoCore where
recArgPos : Nat
declNames : Array Name
numFixed : Nat
fixedParamPerms : FixedParamPerms
deriving Inhabited
private partial def mkProof (declName : Name) (type : Expr) : MetaM Expr := do
@@ -74,21 +75,26 @@ def mkEqns (info : EqnInfo) : MetaM (Array Name) :=
trace[Elab.definition.structural.eqns] "eqnType {i}: {type}"
let name := (Name.str baseName eqnThmSuffixBase).appendIndexAfter (i+1)
thmNames := thmNames.push name
-- determinism: `type` should be independent of the environment changes since `baseName` was
-- added
realizeConst baseName name (doRealize name type)
return thmNames
where
doRealize name type := withOptions (tactic.hygienic.set · false) do
let value mkProof info.declName type
let (type, value) removeUnusedEqnHypotheses type value
addDecl <| Declaration.thmDecl {
name, type, value
levelParams := info.levelParams
}
return thmNames
builtin_initialize eqnInfoExt : MapDeclarationExtension EqnInfo mkMapDeclarationExtension
def registerEqnsInfo (preDef : PreDefinition) (declNames : Array Name) (recArgPos : Nat)
(numFixed : Nat) : CoreM Unit := do
(fixedParamPerms : FixedParamPerms) : CoreM Unit := do
ensureEqnReservedNamesAvailable preDef.declName
modifyEnv fun env => eqnInfoExt.insert env preDef.declName
{ preDef with recArgPos, declNames, numFixed }
{ preDef with recArgPos, declNames, fixedParamPerms }
def getEqnsFor? (declName : Name) : MetaM (Option (Array Name)) := do
if let some info := eqnInfoExt.find? ( getEnv) declName then

View File

@@ -5,6 +5,7 @@ Authors: Leonardo de Moura, Joachim Breitner
-/
prelude
import Lean.Elab.PreDefinition.TerminationMeasure
import Lean.Elab.PreDefinition.FixedParams
import Lean.Elab.PreDefinition.Structural.Basic
import Lean.Elab.PreDefinition.Structural.RecArgInfo
@@ -58,9 +59,10 @@ private def hasBadParamDep? (ys : Array Expr) (indParams : Array Expr) : MetaM (
Assemble the `RecArgInfo` for the `i`th parameter in the parameter list `xs`. This performs
various sanity checks on the parameter (is it even of inductive type etc).
-/
def getRecArgInfo (fnName : Name) (numFixed : Nat) (xs : Array Expr) (i : Nat) : MetaM RecArgInfo := do
def getRecArgInfo (fnName : Name) (fixedParamPerm : FixedParamPerm) (xs : Array Expr) (i : Nat) : MetaM RecArgInfo := do
assert! fixedParamPerm.size = xs.size
if h : i < xs.size then
if i < numFixed then
if fixedParamPerm.isFixed i then
throwError "it is unchanged in the recursive calls"
let x := xs[i]
let localDecl getFVarLocalDecl x
@@ -79,16 +81,14 @@ def getRecArgInfo (fnName : Name) (numFixed : Nat) (xs : Array Expr) (i : Nat) :
else if !indIndices.allDiff then
throwError "its type {indInfo.name} is an inductive family and indices are not pairwise distinct{indentExpr xType}"
else
let indexMinPos := getIndexMinPos xs indIndices
let numFixed := if indexMinPos < numFixed then indexMinPos else numFixed
let ys := xs[numFixed:]
let ys := fixedParamPerm.pickVarying xs
match ( hasBadIndexDep? ys indIndices) with
| some (index, y) =>
throwError "its type {indInfo.name} is an inductive family{indentExpr xType}\nand index{indentExpr index}\ndepends on the non index{indentExpr y}"
| none =>
match ( hasBadParamDep? ys indParams) with
| some (indParam, y) =>
throwError "its type is an inductive datatype{indentExpr xType}\nand the datatype parameter{indentExpr indParam}\ndepends on the function parameter{indentExpr y}\nwhich does not come before the varying parameters and before the indices of the recursion parameter."
throwError "its type is an inductive datatype{indentExpr xType}\nand the datatype parameter{indentExpr indParam}\ndepends on the function parameter{indentExpr y}\nwhich is not fixed."
| none =>
let indAll := indInfo.all.toArray
let .some indIdx := indAll.idxOf? indInfo.name | panic! "{indInfo.name} not in {indInfo.all}"
@@ -98,7 +98,7 @@ def getRecArgInfo (fnName : Name) (numFixed : Nat) (xs : Array Expr) (i : Nat) :
levels := us
params := indParams }
return { fnName := fnName
numFixed := numFixed
fixedParamPerm := fixedParamPerm
recArgPos := i
indicesPos := indicesPos
indGroupInst := indGroupInst
@@ -115,25 +115,27 @@ The `xs` are the fixed parameters, `value` the body with the fixed prefix instan
Takes the optional user annotation into account (`termMeasure?`). If this is given and the measure
is unsuitable, throw an error.
-/
def getRecArgInfos (fnName : Name) (xs : Array Expr) (value : Expr)
(termMeasure? : Option TerminationMeasure) : MetaM (Array RecArgInfo × MessageData) := do
def getRecArgInfos (fnName : Name) (fixedParamPerm : FixedParamPerm) (xs : Array Expr)
(value : Expr) (termMeasure? : Option TerminationMeasure) : MetaM (Array RecArgInfo × MessageData) := do
lambdaTelescope value fun ys _ => do
if let .some termMeasure := termMeasure? then
-- User explicitly asked to use a certain measure, so throw errors eagerly
let recArgInfo withRef termMeasure.ref do
mapError (f := (m!"cannot use specified measure for structural recursion:{indentD ·}")) do
getRecArgInfo fnName xs.size (xs ++ ys) ( termMeasure.structuralArg)
let args := fixedParamPerm.buildArgs xs ys
getRecArgInfo fnName fixedParamPerm args ( termMeasure.structuralArg)
return (#[recArgInfo], m!"")
else
let args := fixedParamPerm.buildArgs xs ys
let mut recArgInfos := #[]
let mut report : MessageData := m!""
-- No `termination_by`, so try all, and remember the errors
for idx in [:xs.size + ys.size] do
for idx in [:args.size] do
try
let recArgInfo getRecArgInfo fnName xs.size (xs ++ ys) idx
let recArgInfo getRecArgInfo fnName fixedParamPerm args idx
recArgInfos := recArgInfos.push recArgInfo
catch e =>
report := report ++ (m!"Not considering parameter {← prettyParam (xs ++ ys) idx} of {fnName}:" ++
report := report ++ (m!"Not considering parameter {← prettyParam args idx} of {fnName}:" ++
indentD e.toMessageData) ++ "\n"
trace[Elab.definition.structural] "getRecArgInfos report: {report}"
return (recArgInfos, report)
@@ -211,7 +213,7 @@ def argsInGroup (group : IndGroupInst) (xs : Array Expr) (value : Expr)
let indicesPos := indIndices.map fun index => match (xs++ys).idxOf? index with | some i => i | none => unreachable!
return .some
{ fnName := recArgInfo.fnName
numFixed := recArgInfo.numFixed
fixedParamPerm := recArgInfo.fixedParamPerm
recArgPos := recArgInfo.recArgPos
indicesPos := indicesPos
indGroupInst := group
@@ -232,13 +234,13 @@ def allCombinations (xss : Array (Array α)) : Option (Array (Array α)) :=
some (go 0 #[])
def tryAllArgs (fnNames : Array Name) (xs : Array Expr) (values : Array Expr)
(termMeasure?s : Array (Option TerminationMeasure)) (k : Array RecArgInfo M α) : M α := do
def tryAllArgs (fnNames : Array Name) (fixedParamPerms : FixedParamPerms) (xs : Array Expr)
(values : Array Expr) (termMeasure?s : Array (Option TerminationMeasure)) (k : Array RecArgInfo M α) : M α := do
let mut report := m!""
-- Gather information on all possible recursive arguments
let mut recArgInfoss := #[]
for fnName in fnNames, value in values, termMeasure? in termMeasure?s do
let (recArgInfos, thisReport) getRecArgInfos fnName xs value termMeasure?
for fnName in fnNames, value in values, termMeasure? in termMeasure?s, fixedParamPerm in fixedParamPerms.perms do
let (recArgInfos, thisReport) getRecArgInfos fnName fixedParamPerm xs value termMeasure?
report := report ++ thisReport
recArgInfoss := recArgInfoss.push recArgInfos
-- Put non-indices first
@@ -266,8 +268,6 @@ def tryAllArgs (fnNames : Array Name) (xs : Array Expr) (values : Array Expr)
-- are ok in a nested group. This logic can maybe simplified)
unless ( hasConst (group.brecOnName false 0)) do
throwError "the type {group} does not have a `.brecOn` recursor"
-- TODO: Here we used to save and restore the state. But should the `try`-`catch`
-- not suffice?
let r k comb
trace[Elab.definition.structural] "tryAllArgs report:\n{report}"
return r

View File

@@ -12,23 +12,24 @@ import Lean.Elab.PreDefinition.Structural.RecArgInfo
namespace Lean.Elab.Structural
open Meta
private def replaceIndPredRecApp (numFixed : Nat) (funType : Expr) (e : Expr) : M Expr := do
private def replaceIndPredRecApp (fixedParamPerm : FixedParamPerm) (funType : Expr) (e : Expr) : M Expr := do
withoutProofIrrelevance do
withTraceNode `Elab.definition.structural (fun _ => pure m!"eliminating recursive call {e}") do
-- We want to replace `e` with an expression of the same type
let main mkFreshExprSyntheticOpaqueMVar ( inferType e)
let args : Array Expr := e.getAppArgs[numFixed:]
let args : Array Expr := e.getAppArgs
let ys := fixedParamPerm.pickVarying args
let lctx getLCtx
let r lctx.anyM fun localDecl => do
if localDecl.isAuxDecl then return false
let (mvars, _, t) forallMetaTelescope localDecl.type -- NB: do not reduce, we want to see the `funType`
unless t.getAppFn == funType do return false
withTraceNodeBefore `Elab.definition.structural (do pure m!"trying {mkFVar localDecl.fvarId} : {localDecl.type}") do
if args.size < t.getAppNumArgs then
trace[Elab.definition.structural] "too few arguments. Underapplied recursive call?"
if ys.size < t.getAppNumArgs then
trace[Elab.definition.structural] "too few arguments, expected {t.getAppNumArgs}, found {ys.size}. Underapplied recursive call?"
return false
if ( (t.getAppArgs.zip args).allM (fun (t,s) => isDefEq t s)) then
main.mvarId!.assign (mkAppN (mkAppN localDecl.toExpr mvars) args[t.getAppNumArgs:])
if ( (t.getAppArgs.zip ys).allM (fun (t,s) => isDefEq t s)) then
main.mvarId!.assign (mkAppN (mkAppN localDecl.toExpr mvars) ys[t.getAppNumArgs:])
return mvars.allM fun v => do
unless ( v.mvarId!.isAssigned) do
trace[Elab.definition.structural] "Cannot use {mkFVar localDecl.fvarId}: parameter {v} remains unassigned"
@@ -62,7 +63,7 @@ private partial def replaceIndPredRecApps (recArgInfo : RecArgInfo) (funType : E
let processApp (e : Expr) : M Expr := do
e.withApp fun f args => do
if f.isConstOf recArgInfo.fnName then
replaceIndPredRecApp recArgInfo.numFixed funType e
replaceIndPredRecApp recArgInfo.fixedParamPerm funType e
else
return mkAppN ( loop f) ( args.mapM loop)
match ( matchMatcherApp? e) with
@@ -100,7 +101,7 @@ def mkIndPredBRecOn (recArgInfo : RecArgInfo) (value : Expr) : M Expr := do
lambdaTelescope value fun ys value => do
let type := ( inferType value).headBeta
let (indexMajorArgs, otherArgs) := recArgInfo.pickIndicesMajor ys
trace[Elab.definition.structural] "numFixed: {recArgInfo.numFixed}, indexMajorArgs: {indexMajorArgs}, otherArgs: {otherArgs}"
trace[Elab.definition.structural] "indexMajorArgs: {indexMajorArgs}, otherArgs: {otherArgs}"
let funType mkLambdaFVars ys type
withLetDecl `funType ( inferType funType) funType fun funType => do
let motive mkForallFVars otherArgs (mkAppN funType ys)

View File

@@ -5,6 +5,7 @@ Authors: Leonardo de Moura, Joachim Breitner
-/
prelude
import Lean.Elab.PreDefinition.TerminationMeasure
import Lean.Elab.PreDefinition.Mutual
import Lean.Elab.PreDefinition.Structural.Basic
import Lean.Elab.PreDefinition.Structural.FindRecArg
import Lean.Elab.PreDefinition.Structural.Preprocess
@@ -71,27 +72,9 @@ where
withLocalDecl vals[0]!.bindingName! vals[0]!.binderInfo vals[0]!.bindingDomain! fun x =>
go (fvars.push x) (vals.map fun val => val.bindingBody!.instantiate1 x)
def getMutualFixedPrefix (preDefs : Array PreDefinition) : M Nat :=
withCommonTelescope preDefs fun xs vals => do
let resultRef IO.mkRef xs.size
for val in vals do
if ( resultRef.get) == 0 then return 0
forEachExpr' val fun e => do
if preDefs.any fun preDef => e.isAppOf preDef.declName then
let args := e.getAppArgs
resultRef.modify (min args.size ·)
for arg in args, x in xs do
if !( withoutProofIrrelevance <| withReducible <| isDefEq arg x) then
-- We continue searching if e's arguments are not a prefix of `xs`
return true
return false
else
return true
resultRef.get
private def elimMutualRecursion (preDefs : Array PreDefinition) (xs : Array Expr)
(recArgInfos : Array RecArgInfo) : M (Array PreDefinition) := do
let values preDefs.mapM (instantiateLambda ·.value xs)
private def elimMutualRecursion (preDefs : Array PreDefinition) (fixedParamPerms : FixedParamPerms)
(xs : Array Expr) (recArgInfos : Array RecArgInfo) : M (Array PreDefinition) := do
let values preDefs.mapIdxM (fixedParamPerms.perms[·]!.instantiateLambda ·.value xs)
let indInfo getConstInfoInduct recArgInfos[0]!.indGroupInst.all[0]!
if isInductivePredicate indInfo.name then
-- Here we branch off to the IndPred construction, but only for non-mutual functions
@@ -102,7 +85,8 @@ private def elimMutualRecursion (preDefs : Array PreDefinition) (xs : Array Expr
let recArgInfo := recArgInfos[0]!
let value := values[0]!
let valueNew mkIndPredBRecOn recArgInfo value
let valueNew mkLambdaFVars xs valueNew
let valueNew lambdaTelescope value fun ys _ => do
mkLambdaFVars (etaReduce := true) (fixedParamPerms.perms[0]!.buildArgs xs ys) (mkAppN valueNew ys)
trace[Elab.definition.structural] "Nonrecursive value:{indentExpr valueNew}"
check valueNew
return #[{ preDef with value := valueNew }]
@@ -123,12 +107,16 @@ private def elimMutualRecursion (preDefs : Array PreDefinition) (xs : Array Expr
-- Assemble the individual `.brecOn` applications
let valuesNew (Array.zip recArgInfos values).mapIdxM fun i (r, v) =>
mkBrecOnApp positions i brecOnConst FArgs r v
-- Abstract over the fixed prefixed
let valuesNew valuesNew.mapM (mkLambdaFVars xs ·)
-- Abstract over the fixed prefixed, preserving the original parameter order
let valuesNew (values.zip valuesNew).mapIdxM fun i value, valueNew =>
lambdaTelescope value fun ys _ => do
-- NB: Do not eta-contract here, other code (e.g. FunInd) expects this to have the
-- same number of head lambdas as the original definition
mkLambdaFVars (fixedParamPerms.perms[i]!.buildArgs xs ys) (valueNew.beta ys)
return (Array.zip preDefs valuesNew).map fun preDef, valueNew => { preDef with value := valueNew }
private def inferRecArgPos (preDefs : Array PreDefinition) (termMeasure?s : Array (Option TerminationMeasure)) :
M (Array Nat × (Array PreDefinition) × Nat) := do
M (Array Nat × (Array PreDefinition) × FixedParamPerms) := do
withoutModifyingEnv do
preDefs.forM (addAsAxiom ·)
let fnNames := preDefs.map (·.declName)
@@ -136,25 +124,39 @@ private def inferRecArgPos (preDefs : Array PreDefinition) (termMeasure?s : Arra
return { preDef with value := ( preprocess preDef.value fnNames) }
-- The syntactically fixed arguments
let maxNumFixed getMutualFixedPrefix preDefs
let fixedParamPerms getFixedParamPerms preDefs
lambdaBoundedTelescope preDefs[0]!.value maxNumFixed fun xs _ => do
assert! xs.size = maxNumFixed
let values preDefs.mapM (instantiateLambda ·.value xs)
fixedParamPerms.perms[0]!.forallTelescope preDefs[0]!.type fun xs => do
let values preDefs.mapIdxM (fixedParamPerms.perms[·]!.instantiateLambda ·.value xs)
tryAllArgs fnNames xs values termMeasure?s fun recArgInfos => do
tryAllArgs fnNames fixedParamPerms xs values termMeasure?s fun recArgInfos => do
let recArgPoss := recArgInfos.map (·.recArgPos)
trace[Elab.definition.structural] "Trying argument set {recArgPoss}"
let numFixed := recArgInfos.foldl (·.min ·.numFixed) maxNumFixed
if numFixed < maxNumFixed then
trace[Elab.definition.structural] "Reduced numFixed from {maxNumFixed} to {numFixed}"
-- We may have decreased the number of arguments we consider fixed, so update
-- the recArgInfos, remove the extra arguments from local environment, and recalculate value
let recArgInfos := recArgInfos.map ({· with numFixed := numFixed })
withErasedFVars (xs.extract numFixed xs.size |>.map (·.fvarId!)) do
let xs := xs[:numFixed]
let preDefs' elimMutualRecursion preDefs xs recArgInfos
return (recArgPoss, preDefs', numFixed)
let (fixedParamPerms', xs', toErase) := fixedParamPerms.erase xs (recArgInfos.map (·.indicesAndRecArgPos))
-- We may have to turn some fixed parameters into varying parameters
let recArgInfos := recArgInfos.mapIdx fun i recArgInfo =>
{recArgInfo with fixedParamPerm := fixedParamPerms'.perms[i]!}
if xs'.size != xs.size then
trace[Elab.definition.structural] "Reduced fixed params from {xs} to {xs'}, erasing {toErase.map mkFVar}"
trace[Elab.definition.structural] "New recArgInfos {repr recArgInfos}"
-- Check that the parameters of the IndGroupInsts are still fine
for recArgInfo in recArgInfos do
for indParam in recArgInfo.indGroupInst.params do
for y in toErase do
if ( dependsOn indParam y) then
if indParam.isFVarOf y then
throwError "its type is an inductive datatype and the datatype parameter\
{indentExpr indParam}\n\
which cannot be fixed as it is an index or depends on an index, and indices \
cannot be fixed parameters when using structural recursion."
else
throwError "its type is an inductive datatype and the datatype parameter\
{indentExpr indParam}\ndepends on the function parameter{indentExpr (mkFVar y)}\n\
which cannot be fixed as it is an index or depends on an index, and indices \
cannot be fixed parameters when using structural recursion."
withErasedFVars toErase do
let preDefs' elimMutualRecursion preDefs fixedParamPerms' xs' recArgInfos
return (recArgPoss, preDefs', fixedParamPerms')
def reporttermMeasure (preDef : PreDefinition) (recArgPos : Nat) : MetaM Unit := do
if let some ref := preDef.termination.terminationBy?? then
@@ -167,7 +169,7 @@ def reporttermMeasure (preDef : PreDefinition) (recArgPos : Nat) : MetaM Unit :=
def structuralRecursion (preDefs : Array PreDefinition) (termMeasure?s : Array (Option TerminationMeasure)) : TermElabM Unit := do
let names := preDefs.map (·.declName)
let ((recArgPoss, preDefsNonRec, numFixed), state) run <| inferRecArgPos preDefs termMeasure?s
let ((recArgPoss, preDefsNonRec, fixedParamPerms), state) run <| inferRecArgPos preDefs termMeasure?s
for recArgPos in recArgPoss, preDef in preDefs do
reporttermMeasure preDef recArgPos
state.addMatchers.forM liftM
@@ -190,7 +192,7 @@ def structuralRecursion (preDefs : Array PreDefinition) (termMeasure?s : Array (
for theorems and definitions that are propositions.
See issue #2327
-/
registerEqnsInfo preDef (preDefs.map (·.declName)) recArgPos numFixed
registerEqnsInfo preDef (preDefs.map (·.declName)) recArgPos fixedParamPerms
addSmartUnfoldingDef preDef recArgPos
markAsRecursive preDef.declName
for preDef in preDefs do

View File

@@ -6,6 +6,7 @@ Authors: Leonardo de Moura, Joachim Breitner
prelude
import Lean.Meta.Basic
import Lean.Meta.ForEachExpr
import Lean.Elab.PreDefinition.FixedParams
import Lean.Elab.PreDefinition.Structural.IndGroupInfo
namespace Lean.Elab.Structural
@@ -14,18 +15,18 @@ namespace Lean.Elab.Structural
/--
Information about the argument of interest of a structurally recursive function.
The `Expr`s in this data structure expect the `fixedParams` to be in scope, but not the other
The `Expr`s in this data structure expect the fixed parameters to be in scope, but not the other
parameters of the function. This ensures that this data structure makes sense in the other functions
of a mutually recursive group.
-/
structure RecArgInfo where
/-- the name of the recursive function -/
fnName : Name
/-- the fixed prefix of arguments of the function we are trying to justify termination using structural recursion. -/
numFixed : Nat
/-- position (counted including fixed prefix) of the argument we are recursing on -/
/-- Information which arguments are fixed -/
fixedParamPerm : FixedParamPerm
/-- position of the argument we are recursing on, among all parameters -/
recArgPos : Nat
/-- position (counted including fixed prefix) of the indices of the inductive datatype we are recursing on -/
/-- position of the indices of the inductive datatype we are recursing on, among all parameters -/
indicesPos : Array Nat
/-- The inductive group (with parameters) of the argument's type -/
indGroupInst : IndGroupInst
@@ -36,23 +37,29 @@ structure RecArgInfo where
indIdx : Nat
deriving Inhabited, Repr
/-- position of the argument and its indices we are recursing on, among all parameters -/
def RecArgInfo.indicesAndRecArgPos (info : RecArgInfo) : Array Nat :=
info.indicesPos.push info.recArgPos
/--
If `xs` are the parameters of the functions (excluding fixed prefix), partitions them
into indices and major arguments, and other parameters.
If `xs` are the varing parameters of the functions, partitions them into indices and major
arguments, and other parameters.
-/
def RecArgInfo.pickIndicesMajor (info : RecArgInfo) (xs : Array Expr) : (Array Expr × Array Expr) := Id.run do
-- To simplify the index calculation, pad xs with dummy values where fixed parameters are
let xs := info.fixedParamPerm.buildArgs (mkArray info.fixedParamPerm.numFixed (mkSort 0)) xs
-- First indices and major arg, using the order they appear in `info.indicesPos`
let mut indexMajorArgs := #[]
let indexMajorPos := info.indicesPos.push info.recArgPos
for j in indexMajorPos do
assert! info.numFixed j && j - info.numFixed < xs.size
indexMajorArgs := indexMajorArgs.push xs[j - info.numFixed]!
indexMajorArgs := indexMajorArgs.push xs[j]!
-- Then the other arguments, in the order they appear in `xs`
let mut otherArgs := #[]
let mut otherVaryingArgs := #[]
for h : i in [:xs.size] do
unless indexMajorPos.contains (i + info.numFixed) do
otherArgs := otherArgs.push xs[i]
return (indexMajorArgs, otherArgs)
unless indexMajorPos.contains i do
unless info.fixedParamPerm.isFixed i do
otherVaryingArgs := otherVaryingArgs.push xs[i]
return (indexMajorArgs, otherVaryingArgs)
/--
Name of the recursive data type. Assumes that it is not one of the auxiliary ones.

View File

@@ -52,7 +52,6 @@ Elaborates a `TerminationBy` to an `TerminationMeasure`.
def TerminationMeasure.elab (funName : Name) (type : Expr) (arity extraParams : Nat)
(hint : TerminationBy) : TermElabM TerminationMeasure := withDeclName funName do
assert! extraParams arity
if h : hint.vars.size > extraParams then
let mut msg := m!"{parameters hint.vars.size} bound in `termination_by`, but the body of " ++
m!"{funName} only binds {parameters extraParams}."
@@ -64,7 +63,7 @@ def TerminationMeasure.elab (funName : Name) (type : Expr) (arity extraParams :
-- Bring parameters before the colon into scope
let r withoutErrToSorry <|
forallBoundedTelescope type (arity - extraParams) fun ys type' => do
forallBoundedTelescope (cleanupAnnotations := true) type (arity - extraParams) fun ys type' => do
-- Bring the variables bound by `termination_by` into scope.
elabFunBinders hint.vars (some type') fun xs type' => do
-- Elaborate the body in this local environment

View File

@@ -10,6 +10,7 @@ import Lean.Elab.PreDefinition.Basic
import Lean.Elab.PreDefinition.Eqns
import Lean.Meta.ArgsPacker.Basic
import Lean.Elab.PreDefinition.WF.Unfold
import Lean.Elab.PreDefinition.FixedParams
import Init.Data.Array.Basic
namespace Lean.Elab.WF
@@ -21,13 +22,15 @@ structure EqnInfo extends EqnInfoCore where
declNameNonRec : Name
fixedPrefixSize : Nat
argsPacker : ArgsPacker
fixedParamPerms : FixedParamPerms
deriving Inhabited
builtin_initialize eqnInfoExt : MapDeclarationExtension EqnInfo mkMapDeclarationExtension
def registerEqnsInfo (preDefs : Array PreDefinition) (declNameNonRec : Name) (fixedPrefixSize : Nat)
def registerEqnsInfo (preDefs : Array PreDefinition) (declNameNonRec : Name) (fixedParamPerms : FixedParamPerms)
(argsPacker : ArgsPacker) : MetaM Unit := do
let fixedPrefixSize := fixedParamPerms.numFixed
preDefs.forM fun preDef => ensureEqnReservedNamesAvailable preDef.declName
/-
See issue #2327.
@@ -40,7 +43,7 @@ def registerEqnsInfo (preDefs : Array PreDefinition) (declNameNonRec : Name) (fi
modifyEnv fun env =>
preDefs.foldl (init := env) fun env preDef =>
eqnInfoExt.insert env preDef.declName { preDef with
declNames, declNameNonRec, fixedPrefixSize, argsPacker }
declNames, declNameNonRec, fixedPrefixSize, argsPacker, fixedParamPerms }
def getEqnsFor? (declName : Name) : MetaM (Option (Array Name)) := do
if let some info := eqnInfoExt.find? ( getEnv) declName then

View File

@@ -17,6 +17,11 @@ import Lean.Util.HasConstCache
namespace Lean.Elab.WF
open Meta
register_builtin_option debug.definition.wf.replaceRecApps : Bool := {
defValue := false
descr := "Type check every step of the well-founded definition translation"
}
/-
Creates a subgoal for a recursive call, as an unsolved `MVar`. The goal is cleaned up, and
the current syntax reference is stored in the `MVar`s type as a `RecApp` marker, for
@@ -32,11 +37,13 @@ private def mkDecreasingProof (decreasingProp : Expr) : TermElabM Expr := do
private partial def replaceRecApps (recFnName : Name) (fixedPrefixSize : Nat) (F : Expr) (e : Expr) : TermElabM Expr := do
trace[Elab.definition.wf] "replaceRecApps:{indentExpr e}"
trace[Elab.definition.wf] "{F} : {← inferType F}"
loop F e |>.run' {}
trace[Elab.definition.wf] "type of functorial {F} is{indentExpr (← inferType F)}"
let e loop F e |>.run' {}
return e
where
processRec (F : Expr) (e : Expr) : StateRefT (HasConstCache #[recFnName]) TermElabM Expr := do
if e.getAppNumArgs < fixedPrefixSize + 1 then
trace[Elab.definition.wf] "replaceRecApp: eta-expanding{indentExpr e}"
loop F ( etaExpand e)
else
let args := e.getAppArgs
@@ -55,6 +62,19 @@ where
modifyGet (·.contains e)
loop (F : Expr) (e : Expr) : StateRefT (HasConstCache #[recFnName]) TermElabM Expr := do
let e' loopGo F e
if (debug.definition.wf.replaceRecApps.get ( getOptions)) then
withTransparency .all do withNewMCtxDepth do
unless ( isTypeCorrect e') do
throwError "Type error introduced when transforming{indentExpr e}\nto{indentExpr e'}"
let t1 inferType e
let t2 inferType e'
unless ( isDefEq t1 t2) do
let (t1, t2) addPPExplicitToExposeDiff t1 t2
throwError "Type not preserved transforming{indentExpr e}\nto{indentExpr e'}\nType was{indentExpr t1}\nand now is{indentExpr t2}"
return e'
loopGo (F : Expr) (e : Expr) : StateRefT (HasConstCache #[recFnName]) TermElabM Expr := do
if !( containsRecFn e) then
return e
match e with
@@ -83,7 +103,8 @@ where
unless xs.size = numParams do
throwError "unexpected matcher application alternative{indentExpr alt}\nat application{indentExpr e}"
let FAlt := xs[numParams - 1]!
mkLambdaFVars xs ( loop FAlt altBody)
let altBody' loop FAlt altBody
mkLambdaFVars xs altBody'
return { matcherApp with alts := altsNew, discrs := ( matcherApp.discrs.mapM (loop F)) }.toExpr
else
processApp F e
@@ -183,34 +204,35 @@ def groupGoalsByFunction (argsPacker : ArgsPacker) (numFuncs : Nat) (goals : Arr
r := r.modify funidx (·.push goal)
return r
def solveDecreasingGoals (argsPacker : ArgsPacker) (decrTactics : Array (Option DecreasingBy)) (value : Expr) : MetaM Expr := do
def solveDecreasingGoals (funNames : Array Name) (argsPacker : ArgsPacker) (decrTactics : Array (Option DecreasingBy)) (value : Expr) : MetaM Expr := do
let goals getMVarsNoDelayed value
let goals assignSubsumed goals
let goalss groupGoalsByFunction argsPacker decrTactics.size goals
for goals in goalss, decrTactic? in decrTactics do
for funName in funNames, goals in goalss, decrTactic? in decrTactics do
Lean.Elab.Term.TermElabM.run' do
match decrTactic? with
| none => do
for goal in goals do
let type goal.getType
let some ref := getRecAppSyntax? ( goal.getType)
| throwError "MVar not annotated as a recursive call:{indentExpr type}"
withRef ref <| applyDefaultDecrTactic goal
| some decrTactic => withRef decrTactic.ref do
unless goals.isEmpty do -- unlikely to be empty
-- make info from `runTactic` available
goals.forM fun goal => pushInfoTree (.hole goal)
let remainingGoals Tactic.run goals[0]! do
Tactic.setGoals goals.toList
applyCleanWfTactic
Tactic.withTacticInfoContext decrTactic.ref do
Tactic.evalTactic decrTactic.tactic
unless remainingGoals.isEmpty do
Term.reportUnsolvedGoals remainingGoals
Term.withDeclName funName do
match decrTactic? with
| none => do
for goal in goals do
let type goal.getType
let some ref := getRecAppSyntax? ( goal.getType)
| throwError "MVar not annotated as a recursive call:{indentExpr type}"
withRef ref <| applyDefaultDecrTactic goal
| some decrTactic => withRef decrTactic.ref do
unless goals.isEmpty do -- unlikely to be empty
-- make info from `runTactic` available
goals.forM fun goal => pushInfoTree (.hole goal)
let remainingGoals Tactic.run goals[0]! do
Tactic.setGoals goals.toList
applyCleanWfTactic
Tactic.withTacticInfoContext decrTactic.ref do
Tactic.evalTactic decrTactic.tactic
unless remainingGoals.isEmpty do
Term.reportUnsolvedGoals remainingGoals
instantiateMVars value
def mkFix (preDef : PreDefinition) (prefixArgs : Array Expr) (argsPacker : ArgsPacker)
(wfRel : Expr) (decrTactics : Array (Option DecreasingBy)) : TermElabM Expr := do
(wfRel : Expr) (funNames : Array Name) (decrTactics : Array (Option DecreasingBy)) : TermElabM Expr := do
let type instantiateForall preDef.type prefixArgs
let (wfFix, varName) forallBoundedTelescope type (some 1) fun x type => do
let x := x[0]!
@@ -233,7 +255,7 @@ def mkFix (preDef : PreDefinition) (prefixArgs : Array Expr) (argsPacker : ArgsP
let val := preDef.value.beta (prefixArgs.push x)
let val processSumCasesOn x F val fun x F val => do
processPSigmaCasesOn x F val (replaceRecApps preDef.declName prefixArgs.size)
let val solveDecreasingGoals argsPacker decrTactics val
let val solveDecreasingGoals funNames argsPacker decrTactics val
mkLambdaFVars prefixArgs (mkApp wfFix ( mkLambdaFVars #[x, F] val))
end Lean.Elab.WF

View File

@@ -13,8 +13,10 @@ import Lean.Meta.ArgsPacker
import Lean.Elab.Quotation
import Lean.Elab.RecAppSyntax
import Lean.Elab.PreDefinition.Basic
import Lean.Elab.PreDefinition.Mutual
import Lean.Elab.PreDefinition.Structural.Basic
import Lean.Elab.PreDefinition.TerminationMeasure
import Lean.Elab.PreDefinition.FixedParams
import Lean.Elab.PreDefinition.WF.Basic
import Lean.Data.Array
@@ -169,24 +171,25 @@ def withUserNames {α} (xs : Array Expr) (ns : Array Name) (k : MetaM α) : Meta
withLCtx' lctx k
/-- Create one measure for each (eligible) parameter of the given predefintion. -/
def simpleMeasures (preDefs : Array PreDefinition) (fixedPrefixSize : Nat)
def simpleMeasures (preDefs : Array PreDefinition) (fixedParamPerms : FixedParamPerms)
(userVarNamess : Array (Array Name)) : MetaM (Array (Array BasicMeasure)) := do
let is_mutual : Bool := preDefs.size > 1
preDefs.mapIdxM fun funIdx preDef => do
lambdaTelescope preDef.value fun xs _ => do
withUserNames xs[fixedPrefixSize:] userVarNamess[funIdx]! do
lambdaTelescope preDef.value fun params _ => do
let xs := fixedParamPerms.perms[funIdx]!.pickVarying params
withUserNames xs userVarNamess[funIdx]! do
let mut ret : Array BasicMeasure := #[]
for x in xs[fixedPrefixSize:] do
for x in xs do
-- If the `SizeOf` instance produces a constant (e.g. because it's type is a `Prop` or
-- `Type`), then ignore this parameter
let sizeOf whnfD ( mkAppM ``sizeOf #[x])
if sizeOf.isLit then continue
let natFn mkLambdaFVars xs ( mkAppM ``sizeOf #[x])
let natFn mkLambdaFVars params ( mkAppM ``sizeOf #[x])
-- Determine if we need to exclude `sizeOf` in the measure we show/pass on.
let fn
if mayOmitSizeOf is_mutual xs[fixedPrefixSize:] x
then mkLambdaFVars xs x
if mayOmitSizeOf is_mutual xs x
then mkLambdaFVars params x
else pure natFn
ret := ret.push { ref := .missing, structural := false, fn, natFn }
return ret
@@ -339,24 +342,26 @@ def filterSubsumed (rcs : Array RecCallWithContext ) : Array RecCallWithContext
Traverse a unary `PreDefinition`, and returns a `WithRecCall` closure for each recursive
call site.
-/
def collectRecCalls (unaryPreDef : PreDefinition) (fixedPrefixSize : Nat)
def collectRecCalls (unaryPreDef : PreDefinition) (fixedParamPerms : FixedParamPerms)
(argsPacker : ArgsPacker) : MetaM (Array RecCallWithContext) := withoutModifyingState do
addAsAxiom unaryPreDef
lambdaBoundedTelescope unaryPreDef.value (fixedPrefixSize + 1) fun xs body => do
unless xs.size == fixedPrefixSize + 1 do
lambdaBoundedTelescope unaryPreDef.value (fixedParamPerms.numFixed + 1) fun xs body => do
unless xs.size == fixedParamPerms.numFixed + 1 do
throwError "Unexpected number of lambdas in unary pre-definition"
let ys := xs[:fixedPrefixSize]
let param := xs[fixedPrefixSize]!
withRecApps unaryPreDef.declName fixedPrefixSize param body fun param args => do
unless args.size fixedPrefixSize + 1 do
let ys := xs[:fixedParamPerms.numFixed]
let param := xs[fixedParamPerms.numFixed]!
withRecApps unaryPreDef.declName fixedParamPerms.numFixed param body fun param args => do
unless args.size fixedParamPerms.numFixed + 1 do
throwError "Insufficient arguments in recursive call"
let arg := args[fixedPrefixSize]!
let arg := args[fixedParamPerms.numFixed]!
trace[Elab.definition.wf] "collectRecCalls: {unaryPreDef.declName} ({param}) → {unaryPreDef.declName} ({arg})"
let some (caller, params) := argsPacker.unpack param
| throwError "Cannot unpack param, unexpected expression:{indentExpr param}"
let some (callee, args) := argsPacker.unpack arg
| throwError "Cannot unpack arg, unexpected expression:{indentExpr arg}"
RecCallWithContext.create ( getRef) caller (ys ++ params) callee (ys ++ args)
let callerParams := fixedParamPerms.perms[caller]!.buildArgs ys params
let calleeArgs := fixedParamPerms.perms[callee]!.buildArgs ys args
RecCallWithContext.create ( getRef) caller callerParams callee calleeArgs
/-- Is the expression a `<`-like comparison of `Nat` expressions -/
def isNatCmp (e : Expr) : Option (Expr × Expr) :=
@@ -367,7 +372,7 @@ def isNatCmp (e : Expr) : Option (Expr × Expr) :=
| GE.ge α _ e₁ e₂ => if α.isConstOf ``Nat then some (e₂, e₁) else none
| _ => none
def complexMeasures (preDefs : Array PreDefinition) (fixedPrefixSize : Nat)
def complexMeasures (preDefs : Array PreDefinition) (fixedParamPerms : FixedParamPerms)
(userVarNamess : Array (Array Name)) (recCalls : Array RecCallWithContext) :
MetaM (Array (Array BasicMeasure)) := do
preDefs.mapIdxM fun funIdx _preDef => do
@@ -377,20 +382,21 @@ def complexMeasures (preDefs : Array PreDefinition) (fixedPrefixSize : Nat)
unless rc.caller = funIdx do continue
-- Only look at calls where the parameters have not been refined
unless rc.params.all (·.isFVar) do continue
let xs := rc.params.map (·.fvarId!)
let varyingParams : Array FVarId := xs[fixedPrefixSize:]
let varyingParams := fixedParamPerms.perms[funIdx]!.pickVarying rc.params
let varyingFVars := varyingParams.map (·.fvarId!)
let params := rc.params.map (·.fvarId!)
measures rc.ctxt.run do
withUserNames rc.params[fixedPrefixSize:] userVarNamess[funIdx]! do
withUserNames varyingParams userVarNamess[funIdx]! do
trace[Elab.definition.wf] "rc: {rc.caller} ({rc.params}) → {rc.callee} ({rc.args})"
let mut measures := measures
for ldecl in getLCtx do
if let some (e₁, e₂) := isNatCmp ldecl.type then
-- We only want to consider these expressions if they depend only on the function's
-- immediate arguments, so check that
if e₁.hasAnyFVar (! xs.contains ·) then continue
if e₂.hasAnyFVar (! xs.contains ·) then continue
if e₁.hasAnyFVar (! params.contains ·) then continue
if e₂.hasAnyFVar (! params.contains ·) then continue
-- If e₁ does not depend on any varying parameters, simply ignore it
let e₁_is_const := ! e₁.hasAnyFVar (varyingParams.contains ·)
let e₁_is_const := ! e₁.hasAnyFVar (varyingFVars.contains ·)
let body := if e₁_is_const then e₂ else mkNatSub e₂ e₁
-- Avoid adding simple measures
unless body.isFVar do
@@ -426,7 +432,7 @@ def GuessLexRel.toNatRel : GuessLexRel → Expr
For a given recursive call, and a choice of parameter and argument index,
try to prove equality, < or ≤.
-/
def evalRecCall (decrTactic? : Option DecreasingBy) (callerMeasures calleeMeasures : Array BasicMeasure)
def evalRecCall (callerName: Name) (decrTactic? : Option DecreasingBy) (callerMeasures calleeMeasures : Array BasicMeasure)
(rcc : RecCallWithContext) (callerMeasureIdx calleeMeasureIdx : Nat) : MetaM GuessLexRel := do
rcc.ctxt.run do
let callerMeasure := callerMeasures[callerMeasureIdx]!
@@ -446,26 +452,28 @@ def evalRecCall (decrTactic? : Option DecreasingBy) (callerMeasures calleeMeasur
if rel = .eq then
MVarId.refl mvarId
else do
Lean.Elab.Term.TermElabM.run' do Term.withoutErrToSorry do
let remainingGoals Tactic.run mvarId do Tactic.withoutRecover do
applyCleanWfTactic
let tacticStx : Syntax
match decrTactic? with
| none => pure ( `(tactic| decreasing_tactic)).raw
| some decrTactic =>
trace[Elab.definition.wf] "Using tactic {decrTactic.tactic.raw}"
pure decrTactic.tactic.raw
Tactic.evalTactic tacticStx
remainingGoals.forM fun _ => throwError "goal not solved"
Lean.Elab.Term.TermElabM.run' do Term.withDeclName callerName do
Term.withoutErrToSorry do
let remainingGoals Tactic.run mvarId do Tactic.withoutRecover do
applyCleanWfTactic
let tacticStx : Syntax
match decrTactic? with
| none => pure ( `(tactic| decreasing_tactic)).raw
| some decrTactic =>
trace[Elab.definition.wf] "Using tactic {decrTactic.tactic.raw}"
pure decrTactic.tactic.raw
Tactic.evalTactic tacticStx
remainingGoals.forM fun _ => throwError "goal not solved"
trace[Elab.definition.wf] "inspectRecCall: success!"
return rel
catch _e =>
trace[Elab.definition.wf] "Did not find {rel} proof: {goalsToMessageData [mvarId]}"
catch e =>
trace[Elab.definition.wf] "Did not find {rel} proof. Goal:{goalsToMessageData [mvarId]}\nError:{indentD e.toMessageData}"
continue
return .no_idea
/- A cache for `evalRecCall` -/
structure RecCallCache where mk'' ::
callerName : Name
decrTactic? : Option DecreasingBy
callerMeasures : Array BasicMeasure
calleeMeasures : Array BasicMeasure
@@ -473,14 +481,15 @@ structure RecCallCache where mk'' ::
cache : IO.Ref (Array (Array (Option GuessLexRel)))
/-- Create a cache to memoize calls to `evalRecCall descTactic? rcc` -/
def RecCallCache.mk (decrTactics : Array (Option DecreasingBy)) (measuress : Array (Array BasicMeasure))
def RecCallCache.mk (funNames : Array Name) (decrTactics : Array (Option DecreasingBy)) (measuress : Array (Array BasicMeasure))
(rcc : RecCallWithContext) :
BaseIO RecCallCache := do
let callerName := funNames[rcc.caller]!
let decrTactic? := decrTactics[rcc.caller]!
let callerMeasures := measuress[rcc.caller]!
let calleeMeasures := measuress[rcc.callee]!
let cache IO.mkRef <| Array.mkArray callerMeasures.size (Array.mkArray calleeMeasures.size Option.none)
return { decrTactic?, callerMeasures, calleeMeasures, rcc, cache }
return { callerName, decrTactic?, callerMeasures, calleeMeasures, rcc, cache }
/-- Run `evalRecCall` and cache there result -/
def RecCallCache.eval (rc: RecCallCache) (callerMeasureIdx calleeMeasureIdx : Nat) : MetaM GuessLexRel := do
@@ -488,7 +497,7 @@ def RecCallCache.eval (rc: RecCallCache) (callerMeasureIdx calleeMeasureIdx : Na
if let Option.some res := ( rc.cache.get)[callerMeasureIdx]![calleeMeasureIdx]! then
return res
else
let res evalRecCall rc.decrTactic? rc.callerMeasures rc.calleeMeasures rc.rcc callerMeasureIdx calleeMeasureIdx
let res evalRecCall rc.callerName rc.decrTactic? rc.callerMeasures rc.calleeMeasures rc.rcc callerMeasureIdx calleeMeasureIdx
rc.cache.modify (·.modify callerMeasureIdx (·.set! calleeMeasureIdx res))
return res
@@ -739,17 +748,18 @@ def mkProdElem (xs : Array Expr) : MetaM Expr := do
let n := xs.size
xs[0:n-1].foldrM (init:=xs[n-1]!) fun x p => mkAppM ``Prod.mk #[x,p]
def toTerminationMeasures (preDefs : Array PreDefinition) (fixedPrefixSize : Nat)
def toTerminationMeasures (preDefs : Array PreDefinition) (fixedParamPerms : FixedParamPerms)
(userVarNamess : Array (Array Name)) (measuress : Array (Array BasicMeasure))
(solution : Array MutualMeasure) : MetaM TerminationMeasures := do
preDefs.mapIdxM fun funIdx preDef => do
let measures := measuress[funIdx]!
lambdaTelescope preDef.value fun xs _ => do
withUserNames xs[fixedPrefixSize:] userVarNamess[funIdx]! do
lambdaTelescope preDef.value fun params _ => do
let xs := fixedParamPerms.perms[funIdx]!.pickVarying params
withUserNames xs userVarNamess[funIdx]! do
let args := solution.map fun
| .args tmIdxs => measures[tmIdxs[funIdx]!]!.fn.beta xs
| .args tmIdxs => measures[tmIdxs[funIdx]!]!.fn.beta params
| .func funIdx' => mkNatLit <| if funIdx' == funIdx then 1 else 0
let fn mkLambdaFVars xs ( mkProdElem args)
let fn mkLambdaFVars params ( mkProdElem args)
return { ref := .missing, structural := false, fn}
/--
@@ -777,19 +787,19 @@ terminates. See the module doc string for a high-level overview.
The `preDefs` are used to determine arity and types of parameters; the bodies are ignored.
-/
def guessLex (preDefs : Array PreDefinition) (unaryPreDef : PreDefinition)
(fixedPrefixSize : Nat) (argsPacker : ArgsPacker) :
(fixedParamPerms : FixedParamPerms) (argsPacker : ArgsPacker) :
MetaM TerminationMeasures := do
let userVarNamess argsPacker.varNamess.mapM (naryVarNames ·)
trace[Elab.definition.wf] "varNames is: {userVarNamess}"
-- Collect all recursive calls and extract their context
let recCalls collectRecCalls unaryPreDef fixedPrefixSize argsPacker
let recCalls collectRecCalls unaryPreDef fixedParamPerms argsPacker
let recCalls := filterSubsumed recCalls
-- For every function, the measures we want to use
-- (One for each non-forbiddend arg)
let basicMeassures₁ simpleMeasures preDefs fixedPrefixSize userVarNamess
let basicMeassures₂ complexMeasures preDefs fixedPrefixSize userVarNamess recCalls
let basicMeassures₁ simpleMeasures preDefs fixedParamPerms userVarNamess
let basicMeassures₂ complexMeasures preDefs fixedParamPerms userVarNamess recCalls
let basicMeasures := Array.zipWith (· ++ ·) basicMeassures₁ basicMeassures₂
-- The list of measures, including the measures that order functions.
@@ -798,16 +808,16 @@ def guessLex (preDefs : Array PreDefinition) (unaryPreDef : PreDefinition)
-- If there is only one plausible measure, use that
if let #[solution] := mutualMeasures then
let termMeasures toTerminationMeasures preDefs fixedPrefixSize userVarNamess basicMeasures #[solution]
let termMeasures toTerminationMeasures preDefs fixedParamPerms userVarNamess basicMeasures #[solution]
reportTerminationMeasures preDefs termMeasures
return termMeasures
let rcs recCalls.mapM (RecCallCache.mk (preDefs.map (·.termination.decreasingBy?)) basicMeasures ·)
let rcs recCalls.mapM (RecCallCache.mk (preDefs.map (·.declName)) (preDefs.map (·.termination.decreasingBy?)) basicMeasures ·)
let callMatrix := rcs.map (inspectCall ·)
match liftMetaM <| solve mutualMeasures callMatrix with
| .some solution => do
let termMeasures toTerminationMeasures preDefs fixedPrefixSize userVarNamess basicMeasures solution
let termMeasures toTerminationMeasures preDefs fixedParamPerms userVarNamess basicMeasures solution
reportTerminationMeasures preDefs termMeasures
return termMeasures
| .none =>

View File

@@ -23,12 +23,11 @@ def wfRecursion (preDefs : Array PreDefinition) (termMeasure?s : Array (Option T
let termMeasures? := termMeasure?s.mapM id -- Either all or none, checked by `elabTerminationByHints`
let preDefs preDefs.mapM fun preDef =>
return { preDef with value := ( floatRecApp preDef.value) }
let (fixedPrefixSize, argsPacker, unaryPreDef, wfPreprocessProofs) withoutModifyingEnv do
let (fixedParamPerms, argsPacker, unaryPreDef, wfPreprocessProofs) withoutModifyingEnv do
for preDef in preDefs do
addAsAxiom preDef
let fixedPrefixSize Mutual.getFixedPrefix preDefs
trace[Elab.definition.wf] "fixed prefix: {fixedPrefixSize}"
let varNamess preDefs.mapM (varyingVarNames fixedPrefixSize ·)
let fixedParamPerms getFixedParamPerms preDefs
let varNamess preDefs.mapIdxM fun i preDef => varyingVarNames fixedParamPerms i preDef
for varNames in varNamess, preDef in preDefs do
if varNames.isEmpty then
throwError "well-founded recursion cannot be used, '{preDef.declName}' does not take any (non-fixed) arguments"
@@ -36,33 +35,35 @@ def wfRecursion (preDefs : Array PreDefinition) (termMeasure?s : Array (Option T
let (preDefsAttached, wfPreprocessProofs) Array.unzip <$> preDefs.mapM fun preDef => do
let result preprocess preDef.value
return ({preDef with value := result.expr}, result)
return (fixedPrefixSize, argsPacker, packMutual fixedPrefixSize argsPacker preDefsAttached, wfPreprocessProofs)
let unaryPreDef packMutual fixedParamPerms argsPacker preDefsAttached
return (fixedParamPerms, argsPacker, unaryPreDef, wfPreprocessProofs)
trace[Elab.definition.wf] "unaryPreDef:{indentD unaryPreDef.value}"
let wf : TerminationMeasures do
if let some tms := termMeasures? then pure tms else
-- No termination_by here, so use GuessLex to infer one
guessLex preDefs unaryPreDef fixedPrefixSize argsPacker
guessLex preDefs unaryPreDef fixedParamPerms argsPacker
let preDefNonRec forallBoundedTelescope unaryPreDef.type fixedPrefixSize fun prefixArgs type => do
let preDefNonRec forallBoundedTelescope unaryPreDef.type fixedParamPerms.numFixed fun fixedArgs type => do
let type whnfForall type
unless type.isForall do
throwError "wfRecursion: expected unary function type: {type}"
let packedArgType := type.bindingDomain!
elabWFRel (preDefs.map (·.declName)) unaryPreDef.declName prefixArgs argsPacker packedArgType wf fun wfRel => do
elabWFRel (preDefs.map (·.declName)) unaryPreDef.declName fixedParamPerms fixedArgs argsPacker packedArgType wf fun wfRel => do
trace[Elab.definition.wf] "wfRel: {wfRel}"
let (value, envNew) withoutModifyingEnv' do
addAsAxiom unaryPreDef
let value mkFix unaryPreDef prefixArgs argsPacker wfRel (preDefs.map (·.termination.decreasingBy?))
let value mkFix unaryPreDef fixedArgs argsPacker wfRel (preDefs.map (·.declName)) (preDefs.map (·.termination.decreasingBy?))
eraseRecAppSyntaxExpr value
/- `mkFix` invokes `decreasing_tactic` which may add auxiliary theorems to the environment. -/
let value unfoldDeclsFrom envNew value
return { unaryPreDef with value }
trace[Elab.definition.wf] ">> {preDefNonRec.declName} :=\n{preDefNonRec.value}"
let preDefsNonrec preDefsFromUnaryNonRec fixedPrefixSize argsPacker preDefs preDefNonRec
let preDefsNonrec preDefsFromUnaryNonRec fixedParamPerms argsPacker preDefs preDefNonRec
Mutual.addPreDefsFromUnary preDefs preDefsNonrec preDefNonRec
let preDefs Mutual.cleanPreDefs preDefs
registerEqnsInfo preDefs preDefNonRec.declName fixedPrefixSize argsPacker
registerEqnsInfo preDefs preDefNonRec.declName fixedParamPerms argsPacker
for preDef in preDefs, wfPreprocessProof in wfPreprocessProofs do
unless preDef.kind.isTheorem do
unless ( isProp preDef.type) do

View File

@@ -6,6 +6,7 @@ Authors: Leonardo de Moura, Joachim Breitner
prelude
import Lean.Meta.ArgsPacker
import Lean.Elab.PreDefinition.Basic
import Lean.Elab.PreDefinition.FixedParams
import Lean.Elab.PreDefinition.WF.Eqns
/-!
@@ -38,7 +39,7 @@ def withAppN (n : Nat) (e : Expr) (k : Array Expr → MetaM Expr) : MetaM Expr :
/--
Processes the expression and replaces calls to the `preDefs` with calls to `f`.
-/
def packCalls (fixedPrefix : Nat) (argsPacker : ArgsPacker) (funNames : Array Name) (newF : Expr)
def packCalls (fixedParamPerms : FixedParamPerms) (argsPacker : ArgsPacker) (funNames : Array Name) (newF : Expr)
(e : Expr) : MetaM Expr := do
let fType inferType newF
unless fType.isForall do
@@ -49,16 +50,19 @@ def packCalls (fixedPrefix : Nat) (argsPacker : ArgsPacker) (funNames : Array Na
if !f.isConst then
return TransformStep.done e
if let some fidx := funNames.idxOf? f.constName! then
let arity := fixedPrefix + argsPacker.varNamess[fidx]!.size
assert! fidx < fixedParamPerms.perms.size
let mask := fixedParamPerms.perms[fidx]!.map Option.isSome
let arity := mask.size
let e' withAppN arity e fun args => do
let packedArg argsPacker.pack domain fidx args[fixedPrefix:]
let varying := fixedParamPerms.perms[fidx]!.pickVarying args
let packedArg argsPacker.pack domain fidx varying
return mkApp newF packedArg
return TransformStep.done e'
return TransformStep.done e
)
def mutualName (argsPacker : ArgsPacker) (preDefs : Array PreDefinition) : Name :=
if argsPacker.onlyOneUnary then
def mutualName (fixedParamPerms : FixedParamPerms) (argsPacker : ArgsPacker) (preDefs : Array PreDefinition) : Name :=
if fixedParamPerms.fixedArePrefix && argsPacker.onlyOneUnary then
preDefs[0]!.declName
else
if argsPacker.numFuncs > 1 then
@@ -70,13 +74,16 @@ def mutualName (argsPacker : ArgsPacker) (preDefs : Array PreDefinition) : Name
Creates a single unary function from the given `preDefs`, using the machinery in the `ArgPacker`
module.
-/
def packMutual (fixedPrefix : Nat) (argsPacker : ArgsPacker) (preDefs : Array PreDefinition) : MetaM PreDefinition := do
if argsPacker.onlyOneUnary then return preDefs[0]!
let newFn := mutualName argsPacker preDefs
def packMutual (fixedParamPerms : FixedParamPerms) (argsPacker : ArgsPacker) (preDefs : Array PreDefinition) : MetaM PreDefinition := do
let newFn := mutualName fixedParamPerms argsPacker preDefs
if newFn = preDefs[0]!.declName then
return preDefs[0]!
-- Bring the fixed prefix into scope
forallBoundedTelescope preDefs[0]!.type (some fixedPrefix) fun ys _ => do
let types preDefs.mapM (instantiateForall ·.type ys)
let vals preDefs.mapM (instantiateLambda ·.value ys)
fixedParamPerms.perms[0]!.forallTelescope preDefs[0]!.type fun ys => do
let types preDefs.mapIdxM fun i preDef =>
fixedParamPerms.perms[i]!.instantiateForall preDef.type ys
let vals preDefs.mapIdxM fun i preDef =>
fixedParamPerms.perms[i]!.instantiateLambda preDef.value ys
let type argsPacker.uncurryType types
@@ -90,12 +97,12 @@ def packMutual (fixedPrefix : Nat) (argsPacker : ArgsPacker) (preDefs : Array Pr
let f := mkAppN (mkConst newFn us) ys
let value argsPacker.uncurry vals
let value packCalls fixedPrefix argsPacker (preDefs.map (·.declName)) f value
let value packCalls fixedParamPerms argsPacker (preDefs.map (·.declName)) f value
let value mkLambdaFVars ys value
return { preDefNew with value }
/--
Collect the names of the varying variables (after the fixed prefix); this also determines the
Collect the names of the varying variables (excluding the fixed parameters); this also determines the
arity for the well-founded translations, and is turned into an `ArgsPacker`.
We use the term to determine the arity, but take the name from the type, for better names in the
```
@@ -103,26 +110,33 @@ fun : (n : Nat) → Nat | 0 => 0 | n+1 => fun n
```
idiom.
-/
def varyingVarNames (fixedPrefixSize : Nat) (preDef : PreDefinition) : MetaM (Array Name) := do
def varyingVarNames (fixedParamPerms : FixedParamPerms) (preDefIdx : Nat) (preDef : PreDefinition) : MetaM (Array Name) := do
-- We take the arity from the term, but the names from the types
let arity lambdaTelescope preDef.value fun xs _ => return xs.size
assert! fixedPrefixSize arity
forallBoundedTelescope preDef.type arity fun xs _ => do
assert! xs.size = arity
let xs : Array Expr := xs[fixedPrefixSize:]
xs.mapM (·.fvarId!.getUserName)
assert! fixedParamPerms.perms[preDefIdx]!.size = arity
let mut ns := #[]
for x in xs, paramInfo in fixedParamPerms.perms[preDefIdx]! do
if paramInfo.isSome then continue -- skip fixed parameters
ns := ns.push ( x.fvarId!.getUserName)
return ns
def preDefsFromUnaryNonRec (fixedPrefixSize : Nat) (argsPacker : ArgsPacker)
def preDefsFromUnaryNonRec (fixedParamPerms : FixedParamPerms) (argsPacker : ArgsPacker)
(preDefs : Array PreDefinition) (unaryPreDefNonRec : PreDefinition) : MetaM (Array PreDefinition) := do
withoutModifyingEnv do
let us := unaryPreDefNonRec.levelParams.map mkLevelParam
addAsAxiom unaryPreDefNonRec
preDefs.mapIdxM fun fidx preDef => do
let value forallBoundedTelescope preDef.type (some fixedPrefixSize) fun xs _ => do
let arity := fixedParamPerms.perms[fidx]!.size
let value forallBoundedTelescope preDef.type (some arity) fun params _ => do
assert! arity = params.size
let xs := fixedParamPerms.perms[fidx]!.pickFixed params
let ys := fixedParamPerms.perms[fidx]!.pickVarying params
let value := mkAppN (mkConst unaryPreDefNonRec.declName us) xs
let value argsPacker.curryProj value fidx
mkLambdaFVars xs value
let value := value.beta ys
mkLambdaFVars params value
trace[Elab.definition.wf] "{preDef.declName} := {value}"
pure { preDef with value }

View File

@@ -10,6 +10,7 @@ import Lean.Meta.Tactic.Rename
import Lean.Elab.SyntheticMVars
import Lean.Elab.PreDefinition.Basic
import Lean.Elab.PreDefinition.TerminationMeasure
import Lean.Elab.PreDefinition.FixedParams
import Lean.Meta.ArgsPacker
namespace Lean.Elab.WF
@@ -22,16 +23,18 @@ a mutual clique, they must be the same for all functions.
This ensures the preconditions for `ArgsPacker.uncurryND`.
-/
def checkCodomains (names : Array Name) (prefixArgs : Array Expr) (arities : Array Nat)
def checkCodomains (names : Array Name) (fixedParamPerms : FixedParamPerms) (fixedArgs : Array Expr) (arities : Array Nat)
(termMeasures : TerminationMeasures) : TermElabM Expr := do
let mut codomains := #[]
for name in names, arity in arities, termMeasure in termMeasures do
let type inferType (termMeasure.fn.beta prefixArgs)
let codomain forallBoundedTelescope type arity fun xs codomain => do
for name in names, funIdx in [:names.size], arity in arities, termMeasure in termMeasures do
let measureType inferType termMeasure.fn
let measureType fixedParamPerms.perms[funIdx]!.instantiateForall measureType fixedArgs
let codomain forallBoundedTelescope measureType arity fun xs codomain => do
assert! xs.size = arity
let fvars := xs.map (·.fvarId!)
if codomain.hasAnyFVar (fvars.contains ·) then
throwErrorAt termMeasure.ref m!"The termination measure's type must not depend on the " ++
m!"function's varying parameters, but {name}'s termination measure does:{indentExpr type}\n" ++
m!"function's varying parameters, but {name}'s termination measure does:{indentExpr measureType}\n" ++
"Try using `sizeOf` explicitly"
pure codomain
codomains := codomains.push codomain
@@ -51,14 +54,16 @@ If the `termMeasures` map the packed argument `argType` to `β`, then this funct
continuation a value of type `WellFoundedRelation argType` that is derived from the instance
for `WellFoundedRelation β` using `invImage`.
-/
def elabWFRel (declNames : Array Name) (unaryPreDefName : Name) (prefixArgs : Array Expr)
(argsPacker : ArgsPacker) (argType : Expr) (termMeasures : TerminationMeasures)
def elabWFRel (declNames : Array Name) (unaryPreDefName : Name) (fixedParamPerms : FixedParamPerms)
(fixedArgs : Array Expr) (argsPacker : ArgsPacker) (argType : Expr) (termMeasures : TerminationMeasures)
(k : Expr TermElabM α) : TermElabM α := withDeclName unaryPreDefName do
let α := argType
let u getLevel α
let β checkCodomains declNames prefixArgs argsPacker.arities termMeasures
let β checkCodomains declNames fixedParamPerms fixedArgs argsPacker.arities termMeasures
let v getLevel β
let packedF argsPacker.uncurryND (termMeasures.map (·.fn.beta prefixArgs))
let fns termMeasures.mapIdxM fun i measure =>
fixedParamPerms.perms[i]!.instantiateLambda measure.fn fixedArgs
let packedF argsPacker.uncurryND fns
let inst synthInstance (.app (.const ``WellFoundedRelation [v]) β)
let rel instantiateMVars (mkApp4 (.const ``invImage [u,v]) α β packedF inst)
k rel

View File

@@ -75,8 +75,9 @@ private partial def mkUnfoldProof (declName : Name) (mvarId : MVarId) : MetaM Un
throwError "failed to generate equational theorem for '{declName}'\n{MessageData.ofGoal mvarId}"
def mkUnfoldEq (preDef : PreDefinition) (unaryPreDefName : Name) (wfPreprocessProof : Simp.Result) : MetaM Unit := do
let baseName := preDef.declName
let name := Name.str baseName unfoldThmSuffix
withOptions (tactic.hygienic.set · false) do
let baseName := preDef.declName
lambdaTelescope preDef.value fun xs body => do
let us := preDef.levelParams.map mkLevelParam
let lhs := mkAppN (Lean.mkConst preDef.declName us) xs
@@ -93,7 +94,6 @@ def mkUnfoldEq (preDef : PreDefinition) (unaryPreDefName : Name) (wfPreprocessPr
let value instantiateMVars main
let type mkForallFVars xs type
let value mkLambdaFVars xs value
let name := Name.str baseName unfoldThmSuffix
addDecl <| Declaration.thmDecl {
name, type, value
levelParams := preDef.levelParams

View File

@@ -33,13 +33,7 @@ structure TacticContext where
config : BVDecideConfig
def TacticContext.new (lratPath : System.FilePath) (config : BVDecideConfig) :
Lean.Elab.TermElabM TacticContext := do
-- Account for: https://github.com/arminbiere/cadical/issues/112
let config :=
if System.Platform.isWindows then
{ config with binaryProofs := false }
else
config
TermElabM TacticContext := do
let exprDef Lean.Elab.Term.mkAuxName `_expr_def
let certDef Lean.Elab.Term.mkAuxName `_cert_def
let reflectionDef Lean.Elab.Term.mkAuxName `_reflection_def

View File

@@ -105,7 +105,7 @@ def getEqIffEnumToBitVecEqFor (declName : Name) : MetaM Name := do
let inverseValue
withLocalDeclD `x bvType fun x => do
let instBeq synthInstance (mkApp (mkConst ``BEq [0]) bvType)
let inv mkInverse x declType instBeq ctors (BitVec.ofNat bvSize 0) (mkConst ctors.head!)
let inv := mkInverse x declType instBeq ctors (BitVec.ofNat bvSize 0) (mkConst ctors.head!)
mkLambdaFVars #[x] inv
let value
@@ -144,9 +144,9 @@ def getEqIffEnumToBitVecEqFor (declName : Name) : MetaM Name := do
where
mkInverse {w : Nat} (input : Expr) (retType : Expr) (instBEq : Expr) (ctors : List Name)
(counter : BitVec w) (acc : Expr) :
MetaM Expr := do
Expr :=
match ctors with
| [] => pure acc
| [] => acc
| ctor :: ctors =>
let eq :=
mkApp4
@@ -155,10 +155,7 @@ where
instBEq
input
(toExpr counter)
-- After the next stage0 update, this can be reverted to
-- let acc := mkApp4 (mkConst ``cond [1]) retType eq (mkConst ctor) acc
-- and mkInverse can be pure again
let acc mkAppOptM ``cond #[retType, eq, mkConst ctor, acc]
let acc := mkApp4 (mkConst ``cond [1]) retType eq (mkConst ctor) acc
mkInverse input retType instBEq ctors (counter + 1) acc
/--

View File

@@ -1820,42 +1820,24 @@ def addAutoBoundImplicitsInlayHint (autos : Array Expr) (inlayHintPos : String.P
return
let autoNames autos.mapM (·.fvarId!.getUserName)
let formattedHint := s!" \{{" ".intercalate <| Array.toList <| autoNames.map toString}}"
let autoLabelParts : List (InlayHintLabelPart × Option Expr) := Array.toList <| autos.mapM fun auto => do
let name := toString <| auto.fvarId!.getUserName
return ({ value := name }, some auto)
let p value : InlayHintLabelPart × Option Expr := ({ value }, none)
let labelParts := [p " ", p "{"] ++ [p " "].intercalate (autoLabelParts.map ([·])) ++ [p "}"]
let labelParts := labelParts.toArray
let deferredResolution ih := do
let .parts ps := ih.label
| return ih
let mut ps' := #[]
for h : i in [:ps.size] do
let p := ps[i]
let some (part, some auto) := labelParts[i]?
| ps' := ps'.push p
continue
let description := "Automatically-inserted implicit parameters:"
let codeBlockStart := "```lean"
let typeInfos autos.mapM fun auto => do
let name := toString <| auto.fvarId!.getUserName
let type := toString <| Meta.ppExpr <| instantiateMVars ( inferType auto)
let tooltip := s!"{part.value} : {type}"
ps' := ps'.push { p with tooltip? := tooltip }
let some separatorPart := ps'[ps'.size - 2]?
| continue
-- We assign the leading `{` and the separation spaces the same tooltip as the auto-implicit
-- following it. The reason for this is that VS Code does not display a text cursor
-- on auto-implicits, but a regular cursor, and hitting single character auto-implicits
-- with that cursor can be a bit tricky. Adding the leading space or the opening `{` to the
-- tooltip area makes this much easier.
ps' := ps'.set! (ps'.size - 2) { separatorPart with tooltip? := tooltip }
return { ih with label := .parts ps' }
return s!"{name} : {type}"
let codeBlockEnd := "```"
let tooltip := "\n".intercalate <| description :: codeBlockStart :: typeInfos.toList ++ [codeBlockEnd]
return { ih with tooltip? := tooltip }
pushInfoLeaf <| .ofCustomInfo {
position := inlayHintPos
label := .parts <| labelParts.map (·.1)
label := .name formattedHint
textEdits := #[{
range := inlayHintPos, inlayHintPos,
newText := formattedHint
}]
kind? := some .parameter
tooltip? := "Automatically-inserted implicit parameters"
lctx := getLCtx
deferredResolution
: InlayHint

View File

@@ -420,13 +420,18 @@ private def AsyncContext.mayContain (ctx : AsyncContext) (n : Name) : Bool :=
/--
Constant info and environment extension states eventually resulting from async elaboration.
-/
structure AsyncConst where
private structure AsyncConst where
constInfo : AsyncConstantInfo
/--
Reported extension state eventually fulfilled by promise; may be missing for tasks (e.g. kernel
checking) that can eagerly guarantee they will not report any state.
-/
exts? : Option (Task (Array EnvExtensionState))
/--
`Task AsyncConsts` except for problematic recursion. The set of nested constants created while
elaborating this constant.
-/
consts : Task Dynamic
/-- Data structure holding a sequence of `AsyncConst`s optimized for efficient access. -/
private structure AsyncConsts where
@@ -436,12 +441,13 @@ private structure AsyncConsts where
map : NameMap AsyncConst
/-- Trie of declaration names without private name prefixes for fast longest-prefix access. -/
normalizedTrie : NameTrie AsyncConst
deriving Inhabited
deriving Inhabited, TypeName
private def AsyncConsts.add (aconsts : AsyncConsts) (aconst : AsyncConst) : AsyncConsts :=
let normalizedName := privateToUserName aconst.constInfo.name
if let some aconst' := aconsts.normalizedTrie.find? normalizedName then
panic! s!"AsyncConsts.add: duplicate normalized declaration name {aconst.constInfo.name} vs. {aconst'.constInfo.name}"
let _ : Inhabited AsyncConsts := aconsts
panic! s!"duplicate normalized declaration name {aconst.constInfo.name} vs. {aconst'.constInfo.name}"
else { aconsts with
size := aconsts.size + 1
revList := aconst :: aconsts.revList
@@ -458,6 +464,17 @@ private def AsyncConsts.findPrefix? (aconsts : AsyncConsts) (declName : Name) :
-- `findLongestPrefix?`
aconsts.normalizedTrie.findLongestPrefix? (privateToUserName declName)
/--
Finds constants including from other elaboration branches by recursively looking up longest
prefixes (which is sufficient by `AsyncContext.mayContain`).
-/
private partial def AsyncConsts.findRec? (aconsts : AsyncConsts) (declName : Name) : Option AsyncConst := do
let c aconsts.findPrefix? declName
if c.constInfo.name == declName then
return c
let aconsts c.consts.get.get? AsyncConsts
AsyncConsts.findRec? aconsts declName
/-- Context for `realizeConst` established by `enableRealizationsForConst`. -/
private structure RealizationContext where
/--
@@ -486,23 +503,23 @@ structure Environment where
-/
private mk ::
/--
Kernel environment not containing any asynchronously elaborated declarations. Also stores
environment extension state for the current branch of the environment.
Kernel environment containing imported constants. Also stores environment extension state for the
current branch of the environment.
Ignoring extension state, this is guaranteed to be some prior version of `checked` that is eagerly
available. Thus we prefer taking information from it instead of `checked` whenever possible.
As `base` is eagerly available, we prefer taking information from it instead of `checked` whenever
possible.
-/
checkedWithoutAsync : Kernel.Environment
base : Kernel.Environment
/--
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.
-/
checked : Task Kernel.Environment := .pure checkedWithoutAsync
checked : Task Kernel.Environment := .pure base
/--
Container of asynchronously elaborated declarations. For consistency, `updateBaseAfterKernelAdd`
makes sure this contains constants added even synchronously, i.e. this is a superset of
`checkedWithoutAsync` except for imported constants.
makes sure this contains constants added even synchronously, i.e. `base ⨃ asyncConsts` is the set
of constants known on the current environment branch, which is a subset of `checked`.
-/
private asyncConsts : AsyncConsts := default
/-- Information about this asynchronous branch of the environment, if any. -/
@@ -525,7 +542,7 @@ namespace Environment
-- used only when the kernel calls into the interpreter, and in `Lean.Kernel.Exception.mkCtx`
@[export lean_elab_environment_of_kernel_env]
def ofKernelEnv (env : Kernel.Environment) : Environment :=
{ checkedWithoutAsync := env, realizedImportedConsts? := none }
{ base := env, realizedImportedConsts? := none }
@[export lean_elab_environment_to_kernel_env]
def toKernelEnv (env : Environment) : Kernel.Environment :=
@@ -533,11 +550,11 @@ def toKernelEnv (env : Environment) : Kernel.Environment :=
/-- 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, checkedWithoutAsync := f env.checkedWithoutAsync }
{ env with checked := env.checked.map (sync := true) f, base := f env.base }
/-- Sets synchronous and asynchronous parts of the environment to the given kernel environment. -/
private def setCheckedSync (env : Environment) (newChecked : Kernel.Environment) : Environment :=
{ env with checked := .pure newChecked, checkedWithoutAsync := newChecked }
{ env with checked := .pure newChecked, base := newChecked }
/-- True while inside `realizeConst`'s `realize`. -/
def isRealizing (env : Environment) : Bool :=
@@ -573,7 +590,7 @@ def addDeclCore (env : Environment) (maxHeartbeats : USize) (decl : @& Declarati
(cancelTk? : @& Option IO.CancelToken) (doCheck := true) :
Except Kernel.Exception Environment := do
if let some ctx := env.asyncCtx? then
if let some n := decl.getNames.find? (!ctx.mayContain ·) then
if let some n := decl.getTopLevelNames.find? (!ctx.mayContain ·) then
throw <| .other s!"cannot add declaration {n} to environment as it is restricted to the \
prefix {ctx.declPrefix}"
if doCheck then
@@ -592,7 +609,12 @@ def const2ModIdx (env : Environment) : Std.HashMap Name ModuleIdx :=
-- only needed for the lakefile.lean cache
@[export lake_environment_add]
private def lakeAdd (env : Environment) (cinfo : ConstantInfo) : Environment :=
env.setCheckedSync <| env.checked.get.add cinfo
let env := env.setCheckedSync <| env.checked.get.add cinfo
{ env with asyncConsts := env.asyncConsts.add {
constInfo := .ofConstantInfo cinfo
exts? := none
consts := .pure <| .mk (α := AsyncConsts) default
} }
/--
Save an extra constant name that is used to populate `const2ModIdx` when we import
@@ -605,48 +627,44 @@ def addExtraName (env : Environment) (name : Name) : Environment :=
else
env.modifyCheckedAsync fun env => { env with extraConstNames := env.extraConstNames.insert name }
/-- Find base case: name did not match any asynchronous declaration. -/
private def findNoAsync (env : Environment) (n : Name) : Option ConstantInfo := do
/-- `findAsync?` after `base` access -/
private def findAsync?' (env : Environment) (n : Name) : Option AsyncConstantInfo := do
if let some asyncConst := env.asyncConsts.find? n then
-- Constant for which an asynchronous elaboration task was spawned
return asyncConst.constInfo
if env.asyncMayContain n then
-- Constant definitely not generated in a different environment branch: return none, callers
-- have already checked this branch.
none
else if let some _ := env.asyncConsts.findPrefix? n then
if let some c := env.asyncConsts.findRec? n then
-- Constant generated in a different environment branch: wait for final kernel environment. Rare
-- case when only proofs are elaborated asynchronously as they are rarely inspected. Could be
-- optimized in the future by having the elaboration thread publish an (incremental?) map of
-- generated declarations before kernel checking (which must wait on all previous threads).
env.checked.get.constants.find?' n
else
-- Not in the kernel environment nor in the name prefix of environment branch: undefined by
-- `addDeclCore` invariant.
none
return c.constInfo
-- Not in the kernel environment nor in the name prefix of environment branch: undefined by
-- `addDeclCore` invariant.
none
/--
Looks up the given declaration name in the environment, avoiding forcing any in-progress elaboration
tasks unless necessary.
-/
def findAsync? (env : Environment) (n : Name) : Option AsyncConstantInfo := do
-- Check declarations already added to the kernel environment (e.g. because they were imported)
-- first as that should be the most common case. It is safe to use `find?'` because we never
-- overwrite imported declarations.
if let some c := env.checkedWithoutAsync.constants.find?' n then
some <| .ofConstantInfo c
else if let some asyncConst := env.asyncConsts.find? n then
-- Constant for which an asynchronous elaboration task was spawned
return asyncConst.constInfo
else env.findNoAsync n |>.map .ofConstantInfo
-- Avoid going through `AsyncConstantInfo` for `base` access
if let some c := env.base.constants.map₁[n]? then
return .ofConstantInfo c
findAsync?' env n
/--
Looks up the given declaration name in the environment, avoiding forcing any in-progress elaboration
tasks for declaration bodies (which are not accessible from `ConstantVal`).
-/
def findConstVal? (env : Environment) (n : Name) : Option ConstantVal := do
if let some c := env.checkedWithoutAsync.constants.find?' n then
some c.toConstantVal
else if let some asyncConst := env.asyncConsts.find? n then
return asyncConst.constInfo.toConstantVal
else env.findNoAsync n |>.map (·.toConstantVal)
-- Avoid going through `AsyncConstantInfo` for `base` access
if let some c := env.base.constants.map₁[n]? then
return c.toConstantVal
env.findAsync?' n |>.map (·.toConstantVal)
/--
Allows `realizeConst` calls for imported declarations in all derived environment branches.
@@ -666,19 +684,24 @@ def enableRealizationsForImports (env : Environment) (opts : Options) : BaseIO E
/--
Allows `realizeConst` calls for the given declaration in all derived environment branches.
Realizations will run using the given environment and options to ensure deterministic results. Note
that while we check that the function isn't called too *early*, i.e. before the declaration is
actually added to the environment, we cannot automatically check that it isn't called too *late*,
i.e. before all environment extensions that may be relevant to realizations have been set. We do
check that we are not calling it from a different branch than `c` was added on, which would be
definitely too late.
that while we check that the function isn't called before the declaration is actually added to the
environment, we cannot automatically check that it isn't otherwise called too early in the sense
that helper declarations and environment extension state that may be relevant to realizations may
not have been added yet. We do check that we are not calling it from a different branch than `c` was
added on, which would be definitely too late. Thus, this function should generally be called in
elaborators calling `addDecl` (when that declaration is a plausible target for realization) at the
latest possible point, i.e. at the very end of the elaborator or just before a first realization may
be triggered if any.
-/
def enableRealizationsForConst (env : Environment) (opts : Options) (c : Name) :
BaseIO Environment := do
if env.findAsync? c |>.isNone then
panic! s!"Environment.enableRealizationsForConst: declaration {c} not found in environment"
panic! s!"declaration {c} not found in environment"
return env
if let some asyncCtx := env.asyncCtx? then
if !asyncCtx.mayContain c then
panic! s!"Environment.enableRealizationsForConst: {c} is outside current context {asyncCtx.declPrefix}"
panic! s!"{c} is outside current context {asyncCtx.declPrefix}"
return env
if env.realizedLocalConsts.contains c then
return env
return { env with realizedLocalConsts := env.realizedLocalConsts.insert c {
@@ -691,13 +714,10 @@ def enableRealizationsForConst (env : Environment) (opts : Options) (c : Name) :
Looks up the given declaration name in the environment, blocking on the corresponding elaboration
task if not yet complete.
-/
def find? (env : Environment) (n : Name) : Option ConstantInfo :=
if let some c := env.checkedWithoutAsync.constants.find?' n then
some c
else if let some asyncConst := env.asyncConsts.find? n then
return asyncConst.constInfo.toConstantInfo
else
env.findNoAsync n
def find? (env : Environment) (n : Name) : Option ConstantInfo := do
if let some c := env.base.constants.map₁[n]? then
return c
env.findAsync?' n |>.map (·.toConstantInfo)
/-- Returns debug output about the asynchronous state of the environment. -/
def dbgFormatAsyncState (env : Environment) : BaseIO String :=
@@ -710,7 +730,7 @@ def dbgFormatAsyncState (env : Environment) : BaseIO String :=
\nrealizedImportedConsts?: {repr <| (← env.realizedImportedConsts?.mapM fun ctx => do
return (← ctx.constsRef.get).toList.map fun (n, m?) =>
(n, m?.get.1.map (fun c : AsyncConst => c.constInfo.name.toString) |> toString))}
\ncheckedWithoutAsync.constants.map₂: {repr <| env.checkedWithoutAsync.constants.map₂.toList.map (·.1)}"
\nbase.constants.map₂: {repr <| env.base.constants.map₂.toList.map (·.1)}"
/-- Returns debug output about the synchronous state of the environment. -/
def dbgFormatCheckedSyncState (env : Environment) : BaseIO String :=
@@ -786,6 +806,7 @@ structure AddConstAsyncResult where
private infoPromise : IO.Promise ConstantInfo
private extensionsPromise : IO.Promise (Array EnvExtensionState)
private checkedEnvPromise : IO.Promise Kernel.Environment
private constsPromise : IO.Promise AsyncConsts
/-- Creates fallback info to be used in case promises are dropped unfulfilled. -/
private def mkFallbackConstInfo (constName : Name) (kind : ConstantKind) : ConstantInfo :=
@@ -806,7 +827,7 @@ private def mkFallbackConstInfo (constName : Name) (kind : ConstantKind) : Const
| .axiom => .axiomInfo { fallbackVal with
isUnsafe := false
}
| k => panic! s!"Environment.mkFallbackConstInfo: unsupported constant kind {repr k}"
| k => panic! s!"unsupported constant kind {repr k}"
/--
Starts the asynchronous addition of a constant to the environment. The environment is split into a
@@ -827,17 +848,23 @@ def addConstAsync (env : Environment) (constName : Name) (kind : ConstantKind) (
let infoPromise IO.Promise.new
let extensionsPromise IO.Promise.new
let checkedEnvPromise IO.Promise.new
let constsPromise IO.Promise.new
let fallbackConstInfo := mkFallbackConstInfo constName kind
-- We use a thunk here because we don't have a fallback for recursors, but that specific
-- invocation cannot fail anyway
let fallbackConstInfo := Thunk.mk fun _ => mkFallbackConstInfo constName kind
let asyncConst := {
constInfo := {
name := constName
kind
sig := sigPromise.resultD fallbackConstInfo.toConstantVal
constInfo := infoPromise.resultD fallbackConstInfo
sig := sigPromise.resultD fallbackConstInfo.get.toConstantVal
constInfo := infoPromise.resultD fallbackConstInfo.get
}
exts? := guard reportExts *> some (extensionsPromise.resultD env.toKernelEnv.extensions)
consts := constsPromise.result?.map (sync := true) fun
| some consts => .mk consts
| none => .mk (α := AsyncConsts) default
}
return {
constName, kind
@@ -847,7 +874,7 @@ def addConstAsync (env : Environment) (constName : Name) (kind : ConstantKind) (
| some kenv => .pure kenv
| none => env.checked }
asyncEnv := env.enterAsync constName
sigPromise, infoPromise, extensionsPromise, checkedEnvPromise
sigPromise, infoPromise, extensionsPromise, checkedEnvPromise, constsPromise
}
/--
@@ -862,8 +889,9 @@ def AddConstAsyncResult.commitSignature (res : AddConstAsyncResult) (sig : Const
res.sigPromise.resolve sig
/--
Commits the full constant info to the main environment branch. If `info?` is `none`, it is taken
from the given environment. The declaration name and kind must match the original values given to
Commits the full constant info as well as the current environment extension state and set of nested
asynchronous constants to the main environment branch. If `info?` is `none`, it is taken from the
given environment. The declaration name and kind must match the original values given to
`addConstAsync`. The signature must match the previous `commitSignature` call, if any.
-/
def AddConstAsyncResult.commitConst (res : AddConstAsyncResult) (env : Environment)
@@ -883,7 +911,8 @@ def AddConstAsyncResult.commitConst (res : AddConstAsyncResult) (env : Environme
if sig.type != info.type then
throw <| .userError s!"AddConstAsyncResult.commitConst: constant has type {info.type} but expected {sig.type}"
res.infoPromise.resolve info
res.extensionsPromise.resolve env.checkedWithoutAsync.extensions
res.extensionsPromise.resolve env.base.extensions
res.constsPromise.resolve env.asyncConsts
/--
Assuming `Lean.addDecl` has been run for the constant to be added on the async environment branch,
@@ -893,10 +922,10 @@ kernel additions there. All `commitConst` preconditions apply.
-/
def AddConstAsyncResult.commitCheckEnv (res : AddConstAsyncResult) (env : Environment) :
IO Unit := do
let some _ := env.findAsync? res.constName
| throw <| .userError s!"AddConstAsyncResult.checkAndCommitEnv: constant {res.constName} not \
found in async context"
res.commitConst env
-- We should skip `commitConst` in case it has already been called, perhaps with a different
-- `info?`
if !( res.infoPromise.isResolved) then
res.commitConst env
res.checkedEnvPromise.resolve env.checked.get
def contains (env : Environment) (n : Name) : Bool :=
@@ -907,11 +936,11 @@ Checks whether the given declaration is known on the current branch, in which ca
not block.
-/
def containsOnBranch (env : Environment) (n : Name) : Bool :=
(env.asyncConsts.find? n |>.isSome) || env.checkedWithoutAsync.constants.contains n
(env.asyncConsts.find? n |>.isSome) || env.base.constants.contains n
def header (env : Environment) : EnvironmentHeader :=
-- can be assumed to be in sync with `env.checked`; see `setMainModule`, the only modifier of the header
env.checkedWithoutAsync.header
env.base.header
def imports (env : Environment) : Array Import :=
env.header.imports
@@ -921,8 +950,8 @@ def allImportedModuleNames (env : Environment) : Array Name :=
def setMainModule (env : Environment) (m : Name) : Environment := Id.run do
if env.realizedImportedConsts?.isSome then
panic! "Environment.setMainModule: cannot set after `enableRealizationsForImports`"
return env
let _ : Inhabited Environment := env
return panic! "cannot set after `enableRealizationsForImports`"
env.modifyCheckedAsync ({ · with header.mainModule := m })
def mainModule (env : Environment) : Name :=
@@ -930,7 +959,7 @@ def mainModule (env : Environment) : Name :=
def getModuleIdxFor? (env : Environment) (declName : Name) : Option ModuleIdx :=
-- async constants are always from the current module
env.checkedWithoutAsync.const2ModIdx[declName]?
env.base.const2ModIdx[declName]?
def isConstructor (env : Environment) (declName : Name) : Bool :=
match env.find? declName with
@@ -1068,6 +1097,7 @@ private unsafe def setStateImpl {σ} (ext : EnvExtension σ) (exts : Array EnvEx
if h : ext.idx < exts.size then
exts.set ext.idx (unsafeCast s)
else
-- do not return an empty array on panic, avoiding follow-up out-of-bounds accesses
have : Inhabited (Array EnvExtensionState) := exts
panic! invalidExtMsg
@@ -1078,6 +1108,7 @@ private unsafe def modifyStateImpl {σ : Type} (ext : EnvExtension σ) (exts : A
let s : σ := f s
unsafeCast s
else
-- do not return an empty array on panic, avoiding follow-up out-of-bounds accesses
have : Inhabited (Array EnvExtensionState) := exts
panic! invalidExtMsg
@@ -1099,19 +1130,21 @@ Note that in modes `sync` and `async`, `f` will be called twice, on the local an
state.
-/
def modifyState {σ : Type} (ext : EnvExtension σ) (env : Environment) (f : σ σ) : Environment := Id.run do
-- for panics
let _ : Inhabited Environment := env
-- safety: `ext`'s constructor is private, so we can assume the entry at `ext.idx` is of type `σ`
match ext.asyncMode with
| .mainOnly =>
if let some asyncCtx := env.asyncCtx? then
panic! s!"Environment.modifyState: environment extension is marked as `mainOnly` but used in \
return panic! s!"environment extension is marked as `mainOnly` but used in \
{if asyncCtx.realizing then "realization" else "async"} context '{asyncCtx.declPrefix}'"
return { env with checkedWithoutAsync.extensions := unsafe ext.modifyStateImpl env.checkedWithoutAsync.extensions f }
return { env with base.extensions := unsafe ext.modifyStateImpl env.base.extensions f }
| .local =>
return { env with checkedWithoutAsync.extensions := unsafe ext.modifyStateImpl env.checkedWithoutAsync.extensions f }
return { env with base.extensions := unsafe ext.modifyStateImpl env.base.extensions f }
| _ =>
if ext.replay?.isNone then
if let some asyncCtx := env.asyncCtx?.filter (·.realizing) then
panic! s!"Environment.modifyState: environment extension must set `replay?` field to be \
return panic! s!"environment extension must set `replay?` field to be \
used in realization context '{asyncCtx.declPrefix}'"
env.modifyCheckedAsync fun env =>
{ env with extensions := unsafe ext.modifyStateImpl env.extensions f }
@@ -1129,9 +1162,9 @@ private unsafe def getStateUnsafe {σ : Type} [Inhabited σ] (ext : EnvExtension
-- safety: `ext`'s constructor is private, so we can assume the entry at `ext.idx` is of type `σ`
match asyncMode with
| .sync => ext.getStateImpl env.checked.get.extensions
| .async => panic! "EnvExtension.getState: called on `async` extension, use `findStateAsync` \
| .async => panic! "called on `async` extension, use `findStateAsync` \
instead or pass `(asyncMode := .local)` to explicitly access local state"
| _ => ext.getStateImpl env.checkedWithoutAsync.extensions
| _ => ext.getStateImpl env.base.extensions
/--
Returns the current extension state. See `AsyncMode` for details on how modifications from
@@ -1147,10 +1180,10 @@ opaque getState {σ : Type} [Inhabited σ] (ext : EnvExtension σ) (env : Enviro
private unsafe def findStateAsyncUnsafe {σ : Type} [Inhabited σ]
(ext : EnvExtension σ) (env : Environment) (declPrefix : Name) : σ :=
-- safety: `ext`'s constructor is private, so we can assume the entry at `ext.idx` is of type `σ`
if let some { exts? := some exts, .. } := env.asyncConsts.findPrefix? declPrefix then
if let some { exts? := some exts, .. } := env.asyncConsts.findRec? declPrefix then
ext.getStateImpl exts.get
else
ext.getStateImpl env.checkedWithoutAsync.extensions
ext.getStateImpl env.base.extensions
/--
Returns the final extension state on the environment branch corresponding to the passed declaration
@@ -1190,7 +1223,7 @@ def mkEmptyEnvironment (trustLevel : UInt32 := 0) : IO Environment := do
if initializing then throw (IO.userError "environment objects cannot be created during initialization")
let exts mkInitialExtensionStates
return {
checkedWithoutAsync := {
base := {
const2ModIdx := {}
constants := {}
header := { trustLevel }
@@ -1549,7 +1582,7 @@ def mkExtNameMap (startingAt : Nat) : IO (Std.HashMap Name Nat) := do
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.checkedWithoutAsync.extensions
let mut states := env.base.extensions
let extDescrs persistentEnvExtensionsRef.get
/- For extensions starting at `startingAt`, ensure their `importedEntries` array have size `mods.size`. -/
for extDescr in extDescrs[startingAt:] do
@@ -1565,7 +1598,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.checkedWithoutAsync with extensions := states }
return env.setCheckedSync { env.base with extensions := states }
/--
"Forward declaration" needed for updating the attribute table with user-defined attributes.
@@ -1581,7 +1614,7 @@ private def setImportedEntries (env : Environment) (mods : Array ModuleData) (st
@[extern 1 "lean_get_num_attributes"] opaque getNumBuiltinAttributes : IO Nat
private def ensureExtensionsArraySize (env : Environment) : IO Environment := do
let exts EnvExtension.ensureExtensionsArraySize env.checkedWithoutAsync.extensions
let exts EnvExtension.ensureExtensionsArraySize env.base.extensions
return env.modifyCheckedAsync ({ · with extensions := exts })
private partial def finalizePersistentExtensions (env : Environment) (mods : Array ModuleData) (opts : Options) : IO Environment := do
@@ -1701,7 +1734,7 @@ def finalizeImport (s : ImportState) (imports : Array Import) (opts : Options) (
let constants : ConstMap := SMap.fromHashMap constantMap false
let exts mkInitialExtensionStates
let mut env : Environment := {
checkedWithoutAsync := {
base := {
const2ModIdx, constants
quotInit := !imports.isEmpty -- We assume `core.lean` initializes quotient module
extraConstNames := {}
@@ -1785,7 +1818,7 @@ def Kernel.enableDiag (env : Lean.Environment) (flag : Bool) : Lean.Environment
env.modifyCheckedAsync (·.enableDiag flag)
def Kernel.isDiagnosticsEnabled (env : Lean.Environment) : Bool :=
env.checkedWithoutAsync.isDiagnosticsEnabled
env.base.isDiagnosticsEnabled
def Kernel.resetDiag (env : Lean.Environment) : Lean.Environment :=
env.modifyCheckedAsync (·.resetDiag)
@@ -1811,16 +1844,16 @@ def getNamespaceSet (env : Environment) : NameSSet :=
namespacesExt.getState env
@[export lean_elab_environment_update_base_after_kernel_add]
private def updateBaseAfterKernelAdd (env : Environment) (kernel : Kernel.Environment) (decl : Declaration) : Environment :=
private def updateBaseAfterKernelAdd (env : Environment) (kenv : Kernel.Environment) (decl : Declaration) : Environment :=
{ env with
checked := .pure kernel
checkedWithoutAsync := { kernel with extensions := env.checkedWithoutAsync.extensions }
checked := .pure kenv
-- make constants available in `asyncConsts` as well; see its docstring
asyncConsts := decl.getNames.foldl (init := env.asyncConsts) fun asyncConsts n =>
if asyncConsts.find? n |>.isNone then
asyncConsts.add {
constInfo := .ofConstantInfo (kernel.find? n |>.get!)
constInfo := .ofConstantInfo (kenv.find? n |>.get!)
exts? := none
consts := .pure <| .mk (α := AsyncConsts) default
}
else asyncConsts }
@@ -1832,7 +1865,7 @@ def displayStats (env : Environment) : IO Unit := do
IO.println ("number of memory-mapped modules: " ++ toString (env.header.regions.filter (·.isMemoryMapped) |>.size));
IO.println ("number of buckets for imported consts: " ++ toString env.constants.numBuckets);
IO.println ("trust level: " ++ toString env.header.trustLevel);
IO.println ("number of extensions: " ++ toString env.checkedWithoutAsync.extensions.size);
IO.println ("number of extensions: " ++ toString env.base.extensions.size);
pExtDescrs.forM fun extDescr => do
IO.println ("extension '" ++ toString extDescr.name ++ "'")
let s := extDescr.toEnvExtension.getState env
@@ -1877,7 +1910,7 @@ def realizeConst (env : Environment) (forConst : Name) (constName : Name)
IO (Environment × Dynamic) := do
let mut env := env
-- find `RealizationContext` for `forConst` in `realizedImportedConsts?` or `realizedLocalConsts`
let ctx if env.checkedWithoutAsync.const2ModIdx.contains forConst then
let ctx if env.base.const2ModIdx.contains forConst then
env.realizedImportedConsts?.getDM <|
throw <| .userError s!"Environment.realizeConst: `realizedImportedConsts` is empty"
else
@@ -1918,7 +1951,7 @@ def realizeConst (env : Environment) (forConst : Name) (constName : Name)
let consts := realizeEnv'.asyncConsts.revList.take numNewConsts |>.reverse
let consts := consts.map fun c =>
if c.exts?.isNone then
{ c with exts? := some <| .pure realizeEnv'.checkedWithoutAsync.extensions }
{ c with exts? := some <| .pure realizeEnv'.base.extensions }
else c
let exts EnvExtension.envExtensionsRef.get
let replay := (maybeAddToKernelEnv realizeEnv realizeEnv' consts · exts)
@@ -1950,17 +1983,19 @@ where
-- generator.
kenv := kenv.add info
continue
let decl := match info with
-- for panics
let _ : Inhabited Kernel.Environment := kenv
let decl match info with
| .thmInfo thm => .thmDecl thm
| .defnInfo defn => .defnDecl defn
| _ => panic! s!"Environment.realizeConst: {c.constInfo.name} must be definition/theorem"
| _ =>
return panic! s!"{c.constInfo.name} must be definition/theorem"
-- realized kernel additions cannot be interrupted - which would be bad anyway as they can be
-- reused between snapshots
match kenv.addDeclCore 0 decl none with
| .ok kenv' => kenv := kenv'
| .error e =>
let _ : Inhabited Kernel.Environment := kenv
panic! s!"Environment.realizeConst: failed to add {c.constInfo.name} to environment\n{e.toRawString}"
return panic! s!"failed to add {c.constInfo.name} to environment\n{e.toRawString}"
for ext in exts do
if let some replay := ext.replay? then
kenv := { kenv with

View File

@@ -328,6 +328,8 @@ if they are all the same.
-/
def uncurryType (types : Array Expr) : MetaM Expr := do
if types.size = 1 then
return types[0]!
let types types.mapM whnfForall
types.forM fun type => do
unless type.isForall do

View File

@@ -2271,6 +2271,10 @@ def realizeConst (forConst : Name) (constName : Name) (realize : MetaM Unit) :
-- the relevant local environment extension state when accessed on this branch.
if env.containsOnBranch constName then
return
-- TODO: remove when Mathlib passes without it
if !Elab.async.get ( getOptions) then
realize
return
withTraceNode `Meta.realizeConst (fun _ => return constName) do
let coreCtx readThe Core.Context
let coreCtx := {

View File

@@ -141,25 +141,27 @@ structure EqnsExtState where
/- We generate the equations on demand. -/
builtin_initialize eqnsExt : EnvExtension EqnsExtState
registerEnvExtension (pure {})
registerEnvExtension (pure {}) (asyncMode := .local)
/--
Simple equation theorem for nonrecursive definitions.
-/
private def mkSimpleEqThm (declName : Name) (suffix := Name.mkSimple unfoldThmSuffix) : MetaM (Option Name) := do
if let some (.defnInfo info) := ( getEnv).find? declName then
lambdaTelescope (cleanupAnnotations := true) info.value fun xs body => do
let lhs := mkAppN (mkConst info.name <| info.levelParams.map mkLevelParam) xs
let type mkForallFVars xs ( mkEq lhs body)
let value mkLambdaFVars xs ( mkEqRefl lhs)
let name := declName ++ suffix
addDecl <| Declaration.thmDecl {
name, type, value
levelParams := info.levelParams
}
return some name
let name := declName ++ suffix
realizeConst declName name (doRealize name info)
return some name
else
return none
where doRealize name info := do
lambdaTelescope (cleanupAnnotations := true) info.value fun xs body => do
let lhs := mkAppN (mkConst info.name <| info.levelParams.map mkLevelParam) xs
let type mkForallFVars xs ( mkEq lhs body)
let value mkLambdaFVars xs ( mkEqRefl lhs)
addDecl <| Declaration.thmDecl {
name, type, value
levelParams := info.levelParams
}
/--
Returns `some declName` if `thmName` is an equational theorem for `declName`.

View File

@@ -37,21 +37,27 @@ private partial def updateAlts (unrefinedArgType : Expr) (typeNew : Expr) (altNu
else
throwError "failed to add argument to matcher application, argument type was not refined by `casesOn`"
/-- Given
- matcherApp `match_i As (fun xs => motive[xs]) discrs (fun ys_1 => (alt_1 : motive (C_1[ys_1])) ... (fun ys_n => (alt_n : motive (C_n[ys_n]) remaining`, and
- expression `e : B[discrs]`,
Construct the term
`match_i As (fun xs => B[xs] -> motive[xs]) discrs (fun ys_1 (y : B[C_1[ys_1]]) => alt_1) ... (fun ys_n (y : B[C_n[ys_n]]) => alt_n) e remaining`.
/--
Given
- matcherApp `match_i As (fun xs => motive[xs]) discrs (fun ys_1 => (alt_1 : motive (C_1[ys_1])) ... (fun ys_n => (alt_n : motive (C_n[ys_n]) remaining`, and
- expression `e : B[discrs]`,
Construct the term
`match_i As (fun xs => B[xs] -> motive[xs]) discrs (fun ys_1 (y : B[C_1[ys_1]]) => alt_1) ... (fun ys_n (y : B[C_n[ys_n]]) => alt_n) e remaining`.
We use `kabstract` to abstract the discriminants from `B[discrs]`.
We only abstract discriminants that are fvars. We used to use `kabstract` to abstract all
discriminants from `B[discrs]`, but that changes the type of the arg in ways that make it no
longer compatible with the original recursive function (issue #7322).
This method assumes
- the `matcherApp.motive` is a lambda abstraction where `xs.size == discrs.size`
- each alternative is a lambda abstraction where `ys_i.size == matcherApp.altNumParams[i]`
If this is still not great, then we could try to use `kabstract`, but only on the last paramter
of the `arg` (the termination proof obligation).
This is used in `Lean.Elab.PreDefinition.WF.Fix` when replacing recursive calls with calls to
the argument provided by `fix` to refine type of the local variable used for recursive calls,
which may mention `major`. See there for how to use this function.
This method assumes
- the `matcherApp.motive` is a lambda abstraction where `xs.size == discrs.size`
- each alternative is a lambda abstraction where `ys_i.size == matcherApp.altNumParams[i]`
This is used in `Lean.Elab.PreDefinition.WF.Fix` when replacing recursive calls with calls to
the argument provided by `fix` to refine type of the local variable used for recursive calls,
which may mention `major`. See there for how to use this function.
-/
def addArg (matcherApp : MatcherApp) (e : Expr) : MetaM MatcherApp :=
lambdaTelescope matcherApp.motive fun motiveArgs motiveBody => do
@@ -59,11 +65,13 @@ def addArg (matcherApp : MatcherApp) (e : Expr) : MetaM MatcherApp :=
-- This error can only happen if someone implemented a transformation that rewrites the motive created by `mkMatcher`.
throwError "unexpected matcher application, motive must be lambda expression with #{matcherApp.discrs.size} arguments"
let eType inferType e
let eTypeAbst matcherApp.discrs.size.foldRevM (init := eType) fun i _ eTypeAbst => do
let motiveArg := motiveArgs[i]!
let eTypeAbst := matcherApp.discrs.size.foldRev (init := eType) fun i _ eTypeAbst =>
let discr := matcherApp.discrs[i]
let eTypeAbst kabstract eTypeAbst discr
return eTypeAbst.instantiate1 motiveArg
if discr.isFVar then
let motiveArg := motiveArgs[i]!
eTypeAbst.replaceFVar discr motiveArg
else
eTypeAbst
let motiveBody mkArrow eTypeAbst motiveBody
let matcherLevels match matcherApp.uElimPos? with
| none => pure matcherApp.matcherLevels

View File

@@ -695,10 +695,10 @@ def deriveUnaryInduction (name : Name) : MetaM Name := do
| fix@WellFounded.fix α _motive rel wf body target =>
unless params.back! == target do
throwError "functional induction: expected the target as last parameter{indentExpr e}"
let fixedParams := params.pop
let fixedParamPerms := params.pop
let motiveType mkForallFVars #[target] (.sort levelZero)
withLocalDeclD `motive motiveType fun motive => do
let fn := mkAppN ( mkConstWithLevelParams name) fixedParams
let fn := mkAppN ( mkConstWithLevelParams name) fixedParamPerms
let isRecCall : Expr Option Expr := fun e =>
if e.isApp && e.appFn!.isFVarOf motive.fvarId! then
mkApp fn e.appArg!
@@ -732,7 +732,7 @@ def deriveUnaryInduction (name : Name) : MetaM Name := do
-- induction principle match the type of the function better.
-- But this leads to avoidable parameters that make functional induction strictly less
-- useful (e.g. when the unsued parameter mentions bound variables in the users' goal)
let (paramMask, e') mkLambdaFVarsMasked fixedParams e'
let (paramMask, e') mkLambdaFVarsMasked fixedParamPerms e'
let e' instantiateMVars e'
return (e', paramMask)
| _ =>
@@ -787,16 +787,25 @@ def projectMutualInduct (names : Array Name) (mutualInduct : Name) : MetaM Unit
For a (non-mutual!) definition of `name`, uses the `FunIndInfo` associated with the `unaryInduct` and
derives the one for the n-ary function.
-/
def setNaryFunIndInfo (name : Name) (arity : Nat) (unaryInduct : Name) : MetaM Unit := do
let inductName := getFunInductName name
unless inductName = unaryInduct do
let some unaryFunIndInfo getFunIndInfoForInduct? unaryInduct
| throwError "Expected {unaryInduct} to have FunIndInfo"
setFunIndInfo {
unaryFunIndInfo with
funIndName := inductName
params := unaryFunIndInfo.params.filter (· != .target) ++ mkArray arity .target
}
def setNaryFunIndInfo (fixedParamPerms : FixedParamPerms) (name : Name) (unaryInduct : Name) : MetaM Unit := do
assert! fixedParamPerms.perms.size = 1 -- only non-mutual for now
let funIndName := getFunInductName name
unless funIndName = unaryInduct do
let some unaryFunIndInfo getFunIndInfoForInduct? unaryInduct
| throwError "Expected {unaryInduct} to have FunIndInfo"
let fixedParamPerm := fixedParamPerms.perms[0]!
let mut params := #[]
let mut j := 0
for h : i in [:fixedParamPerm.size] do
if fixedParamPerm[i].isSome then
assert! j + 1 < unaryFunIndInfo.params.size
params := params.push unaryFunIndInfo.params[j]!
j := j + 1
else
params := params.push .target
assert! j + 1 = unaryFunIndInfo.params.size
setFunIndInfo { unaryFunIndInfo with funIndName, params }
/--
In the type of `value`, reduces
@@ -920,13 +929,15 @@ Given a recursive definition `foo` defined via structural recursion, derive `foo
if needed, and `foo.induct` for all functions in the group.
See module doc for details.
-/
def deriveInductionStructural (names : Array Name) (numFixed : Nat) : MetaM Unit := do
def deriveInductionStructural (names : Array Name) (fixedParamPerms : FixedParamPerms) : MetaM Unit := do
let infos names.mapM getConstInfoDefn
assert! infos.size > 0
-- First open up the fixed parameters everywhere
let (e', paramMask, motiveArities) lambdaBoundedTelescope infos[0]!.value numFixed fun xs _ => do
let (e', paramMask, motiveArities) fixedParamPerms.perms[0]!.forallTelescope infos[0]!.type fun xs => do
-- Now look at the body of an arbitrary of the functions (they are essentially the same
-- up to the final projections)
let body instantiateLambda infos[0]!.value xs
let body fixedParamPerms.perms[0]!.instantiateLambda infos[0]!.value xs
let body := body.eta
lambdaTelescope body fun ys body => do
-- The body is of the form (brecOn … ).2.2.1 extra1 extra2 etc; ignore the
@@ -938,7 +949,7 @@ def deriveInductionStructural (names : Array Name) (numFixed : Nat) : MetaM Unit
let body := PProdN.stripProjs body
let .const brecOnName us := f |
throwError "{infos[0]!.name}: unexpected body:{indentExpr infos[0]!.value}"
throwError "{infos[0]!.name}: unexpected body:{indentExpr infos[0]!.value}\ninstantiated to{indentExpr body}"
unless isBRecOnRecursor ( getEnv) brecOnName do
throwError "{infos[0]!.name}: expected .brecOn application, found:{indentExpr body}"
@@ -975,12 +986,13 @@ def deriveInductionStructural (names : Array Name) (numFixed : Nat) : MetaM Unit
motives.mapM fun motive =>
forallTelescopeReducing motive fun xs _ => pure xs.size
let recArgInfos infos.mapM fun info => do
-- Recreate the recArgInfos. Maybe more robust and simpler to store relevant parts in the EqnInfos?
let recArgInfos infos.mapIdxM fun funIdx info => do
let some eqnInfo := Structural.eqnInfoExt.find? ( getEnv) info.name | throwError "{info.name} missing eqnInfo"
let value instantiateLambda info.value xs
let value fixedParamPerms.perms[funIdx]!.instantiateLambda info.value xs
let recArgInfo' lambdaTelescope value fun ys _ =>
Structural.getRecArgInfo info.name numFixed (xs ++ ys) eqnInfo.recArgPos
let args := fixedParamPerms.perms[funIdx]!.buildArgs xs ys
Structural.getRecArgInfo info.name fixedParamPerms.perms[funIdx]! args eqnInfo.recArgPos
let #[recArgInfo] Structural.argsInGroup group xs value #[recArgInfo']
| throwError "Structural.argsInGroup did not return a recArgInfo"
pure recArgInfo
@@ -998,23 +1010,24 @@ def deriveInductionStructural (names : Array Name) (numFixed : Nat) : MetaM Unit
-- context, and are the parts relevant for every function in the mutual group
-- Calculate the types of the induction motives (natural argument order) for each function
let motiveTypes infos.mapM fun info => do
lambdaTelescope ( instantiateLambda info.value xs) fun ys _ =>
let motiveTypes infos.mapIdxM fun funIdx info => do
lambdaTelescope ( fixedParamPerms.perms[funIdx]!.instantiateLambda info.value xs) fun ys _ =>
mkForallFVars ys (.sort levelZero)
let motiveArities infos.mapM fun info => do
lambdaTelescope ( instantiateLambda info.value xs) fun ys _ => pure ys.size
let motiveArities infos.mapIdxM fun funIdx info => do
lambdaTelescope ( fixedParamPerms.perms[funIdx]!.instantiateLambda info.value xs) fun ys _ =>
pure ys.size
let motiveNames := Array.ofFn (n := infos.size) fun i, _ =>
if infos.size = 1 then .mkSimple "motive" else .mkSimple s!"motive_{i+1}"
withLocalDeclsDND (motiveNames.zip motiveTypes) fun motives => do
-- Prepare the `isRecCall` that recognizes recursive calls
let fns := infos.map fun info =>
mkAppN (.const info.name (info.levelParams.map mkLevelParam)) xs
let isRecCall : Expr Option Expr := fun e => do
if let .some i := motives.idxOf? e.getAppFn then
if e.getAppNumArgs = motiveArities[i]! then
return mkAppN fns[i]! e.getAppArgs
if let .some funIdx := motives.idxOf? e.getAppFn then
if e.getAppNumArgs = motiveArities[funIdx]! then
let info := infos[funIdx]!
let args := fixedParamPerms.perms[funIdx]!.buildArgs xs e.getAppArgs
return mkAppN (.const info.name (info.levelParams.map mkLevelParam)) args
.none
-- Motives with parameters reordered, to put indices and major first
@@ -1062,8 +1075,8 @@ def deriveInductionStructural (names : Array Name) (numFixed : Nat) : MetaM Unit
for info in infos, recArgInfo in recArgInfos, idx in [:infos.size] do
-- Take care to pick the `ys` from the type, to get the variable names expected
-- by the user, but use the value arity
let arity lambdaTelescope ( instantiateLambda info.value xs) fun ys _ => pure ys.size
let e forallBoundedTelescope ( instantiateForall info.type xs) arity fun ys _ => do
let arity lambdaTelescope ( fixedParamPerms.perms[idx]!.instantiateLambda info.value xs) fun ys _ => pure ys.size
let e forallBoundedTelescope ( fixedParamPerms.perms[idx]!.instantiateForall info.type xs) arity fun ys _ => do
let (indicesMajor, rest) := recArgInfo.pickIndicesMajor ys
-- Find where in the function packing we are (TODO: abstract out)
let some indIdx := positions.findIdx? (·.contains idx) | panic! "invalid positions"
@@ -1205,9 +1218,9 @@ def deriveInduction (name : Name) : MetaM Unit := do
let unpackedInductName unpackMutualInduction eqnInfo unaryInductName
projectMutualInduct eqnInfo.declNames unpackedInductName
if eqnInfo.argsPacker.numFuncs = 1 then
setNaryFunIndInfo eqnInfo.declNames[0]! eqnInfo.argsPacker.arities[0]! unaryInductName
setNaryFunIndInfo eqnInfo.fixedParamPerms eqnInfo.declNames[0]! unaryInductName
else if let some eqnInfo := Structural.eqnInfoExt.find? ( getEnv) name then
deriveInductionStructural eqnInfo.declNames eqnInfo.numFixed
deriveInductionStructural eqnInfo.declNames eqnInfo.fixedParamPerms
else
throwError "constant '{name}' is not structurally or well-founded recursive"

View File

@@ -21,7 +21,7 @@ inductive FunIndParamKind where
| dropped
| param
| target
deriving BEq, Repr
deriving BEq, Repr, Inhabited
/--
A `FunIndInfo` indicates how a function's arguments map to the arguments of the functional induction

View File

@@ -16,6 +16,7 @@ import Lean.Meta.Tactic.Grind.Arith.Cutsat.Var
import Lean.Meta.Tactic.Grind.Arith.Cutsat.EqCnstr
import Lean.Meta.Tactic.Grind.Arith.Cutsat.SearchM
import Lean.Meta.Tactic.Grind.Arith.Cutsat.Model
import Lean.Meta.Tactic.Grind.Arith.Cutsat.DivMod
namespace Lean
@@ -54,5 +55,9 @@ 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.conflict
builtin_initialize registerTraceClass `grind.debug.cutsat.assign
builtin_initialize registerTraceClass `grind.debug.cutsat.subst
end Lean

View File

@@ -0,0 +1,42 @@
/-
Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
prelude
import Lean.Meta.Tactic.Grind.PropagatorAttr
import Lean.Meta.Tactic.Grind.Arith.Cutsat.Util
import Lean.Meta.Tactic.Grind.Canon
import Lean.Meta.Tactic.Grind.Core
namespace Lean.Meta.Grind.Arith.Cutsat
private def assertFact (h : Expr) : GoalM Unit := do
let prop shareCommon ( canon ( inferType h))
trace[Meta.debug] "{prop}"
add prop h 0
private def expandDivMod (a : Expr) (b : Int) : GoalM Unit := do
if b == 0 then return ()
if ( get').divMod.contains (a, b) then return ()
modify' fun s => { s with divMod := s.divMod.insert (a, b) }
let n : Int := 1 - b.natAbs
let b := mkIntLit b
assertFact <| mkApp2 (mkConst ``Int.Linear.ediv_emod) a b
assertFact <| mkApp3 (mkConst ``Int.Linear.emod_nonneg) a b reflBoolTrue
assertFact <| mkApp4 (mkConst ``Int.Linear.emod_le) a b (toExpr n) reflBoolTrue
builtin_grind_propagator propagateDiv HDiv.hDiv := fun e => do
let_expr HDiv.hDiv _ _ _ inst a b e | return ()
unless ( isInstHDivInt inst) do return ()
let some b getIntValue? b | return ()
-- Remark: we currently do not consider the case where `b` is in the equivalence class of a numeral.
expandDivMod a b
builtin_grind_propagator propagateMod HMod.hMod := fun e => do
let_expr HMod.hMod _ _ _ inst a b e | return ()
unless ( isInstHModInt inst) do return ()
let some b getIntValue? b | return ()
expandDivMod a b
end Lean.Meta.Grind.Arith.Cutsat

View File

@@ -12,19 +12,16 @@ import Lean.Meta.Tactic.Grind.Arith.Cutsat.Proof
namespace Lean.Meta.Grind.Arith.Cutsat
def mkDvdCnstr (d : Int) (p : Poly) (h : DvdCnstrProof) : GoalM DvdCnstr := do
return { d, p, h, id := ( mkCnstrId) }
def DvdCnstr.norm (c : DvdCnstr) : GoalM DvdCnstr := do
let c if c.p.isSorted then
pure c
def DvdCnstr.norm (c : DvdCnstr) : DvdCnstr :=
let c := if c.p.isSorted then
c
else
mkDvdCnstr c.d c.p.norm (.norm c)
{ d := c.d, p := c.p.norm, h :=.norm c }
let g := c.p.gcdCoeffs c.d
if c.p.getConst % g == 0 && g != 1 then
mkDvdCnstr (c.d/g) (c.p.div g) (.divCoeffs c)
{ d := c.d/g, p := c.p.div g, h := .divCoeffs c }
else
return c
c
/--
Given an equation `c₁` containing the monomial `a*x`, and a divisibility constraint `c₂`
@@ -36,7 +33,7 @@ def DvdCnstr.applyEq (a : Int) (x : Var) (c₁ : EqCnstr) (b : Int) (c₂ : DvdC
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}"
mkDvdCnstr d p (.subst x c₁ c₂)
return { d, p, h := .subst x c₁ c₂ }
partial def DvdCnstr.applySubsts (c : DvdCnstr) : GoalM DvdCnstr := withIncRecDepth do
let some (b, x, c₁) c.p.findVarToSubst | return c
@@ -47,9 +44,10 @@ 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 ()
let c c.norm
let c c.applySubsts
trace[grind.cutsat.dvd] "{← c.pp}"
let c c.norm.applySubsts
if c.isUnsat then
trace[grind.cutsat.dvd.unsat] "{← c.pp}"
setInconsistent (.dvd c)
return ()
if c.isTrivial then
@@ -74,13 +72,13 @@ partial def DvdCnstr.assert (c : DvdCnstr) : GoalM Unit := withIncRecDepth do
-/
let α_d₂_p₁ := p₁.mul (α*d₂)
let β_d₁_p₂ := p₂.mul (β*d₁)
let combine mkDvdCnstr (d₁*d₂) (.add d x (α_d₂_p₁.combine β_d₁_p₂)) (.solveCombine c c')
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 mkDvdCnstr d (a₂_p₁.combine a₁_p₂) (.solveElim c c')
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
@@ -96,7 +94,7 @@ builtin_grind_propagator propagateDvd ↓Dvd.dvd := fun e => do
return ()
if ( isEqTrue e) then
let p toPoly b
let c mkDvdCnstr d p (.expr ( mkOfEqTrue ( mkEqTrueProof e)))
let c := { d, p, h := .expr ( mkOfEqTrue ( mkEqTrueProof e)) : DvdCnstr }
trace[grind.cutsat.assert.dvd] "{← c.pp}"
c.assert
else if ( isEqFalse e) then

View File

@@ -14,23 +14,21 @@ namespace Lean.Meta.Grind.Arith.Cutsat
private def _root_.Int.Linear.Poly.substVar (p : Poly) : GoalM (Option (Var × EqCnstr × Poly)) := do
let some (a, x, c) p.findVarToSubst | return none
let b := c.p.coeff x
let p := p.mul (-b) |>.combine (c.p.mul a)
return some (x, c, p)
let p' := p.mul (-b) |>.combine (c.p.mul a)
trace[grind.debug.cutsat.subst] "{← p.pp}, {a}, {← getVar x}, {← c.pp}, {b}, {← p'.pp}"
return some (x, c, p')
def EqCnstr.norm (c : EqCnstr) : GoalM EqCnstr := do
let c if c.p.isSorted then
pure c
def EqCnstr.norm (c : EqCnstr) : EqCnstr :=
if c.p.isSorted then
c
else
mkEqCnstr c.p.norm (.norm c)
{ p := c.p.norm, h := .norm c }
def mkDiseqCnstr (p : Poly) (h : DiseqCnstrProof) : GoalM DiseqCnstr := do
return { p, h, id := ( mkCnstrId) }
def DiseqCnstr.norm (c : DiseqCnstr) : GoalM DiseqCnstr := do
let c if c.p.isSorted then
pure c
def DiseqCnstr.norm (c : DiseqCnstr) : DiseqCnstr :=
if c.p.isSorted then
c
else
mkDiseqCnstr c.p.norm (.norm c)
{ p := c.p.norm, h := .norm c }
/--
Given an equation `c₁` containing the monomial `a*x`, and a disequality constraint `c₂`
@@ -41,13 +39,12 @@ def DiseqCnstr.applyEq (a : Int) (x : Var) (c₁ : EqCnstr) (b : Int) (c₂ : Di
let q := c₂.p
let p := p.mul b |>.combine (q.mul (-a))
trace[grind.cutsat.subst] "{← getVar x}, {← c₁.pp}, {← c₂.pp}"
mkDiseqCnstr p (.subst x c₁ c₂)
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}"
let c mkDiseqCnstr p (.subst x c₁ c)
applySubsts c
applySubsts { p, h := .subst x c₁ c }
/--
Given a disequality `c`, tries to find an inequality to be refined using
@@ -61,8 +58,7 @@ private def DiseqCnstr.findLe (c : DiseqCnstr) : GoalM Bool := do
for c' in cs' do
if c.p == c'.p || c.p.isNegEq c'.p then
c'.erase
let le mkLeCnstr (c'.p.addConst 1) (.ofLeDiseq c' c)
le.assert
{ p := c'.p.addConst 1, h := .ofLeDiseq c' c : LeCnstr }.assert
return true
return false
go true <||> go false
@@ -70,8 +66,7 @@ private def DiseqCnstr.findLe (c : DiseqCnstr) : GoalM Bool := do
def DiseqCnstr.assert (c : DiseqCnstr) : GoalM Unit := do
if ( inconsistent) then return ()
trace[grind.cutsat.assert] "{← c.pp}"
let c c.norm
let c c.applySubsts
let c c.norm.applySubsts
if c.p.isUnsatDiseq then
setInconsistent (.diseq c)
return ()
@@ -79,10 +74,10 @@ def DiseqCnstr.assert (c : DiseqCnstr) : GoalM Unit := do
trace[grind.cutsat.diseq.trivial] "{← c.pp}"
return ()
let k := c.p.gcdCoeffs c.p.getConst
let c if k == 1 then
pure c
let c := if k == 1 then
c
else
mkDiseqCnstr (c.p.div k) (.divCoeffs c)
{ p := c.p.div k, h := .divCoeffs c }
if ( c.findLe) then
return ()
let .add _ x _ := c.p | c.throwUnexpected
@@ -114,8 +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}"
let c mkEqCnstr p (.subst x c₁ c)
applySubsts c
applySubsts { p, h := .subst x c₁ c : EqCnstr }
private def updateDvdCnstr (a : Int) (x : Var) (c : EqCnstr) (y : Var) : GoalM Unit := do
let some c' := ( get').dvds[y]! | return ()
@@ -201,36 +195,35 @@ private def updateOccs (k : Int) (x : Var) (c : EqCnstr) : GoalM Unit := do
def EqCnstr.assertImpl (c : EqCnstr) : GoalM Unit := do
if ( inconsistent) then return ()
trace[grind.cutsat.assert] "{← c.pp}"
let c c.norm
let c c.applySubsts
let c c.norm.applySubsts
if c.p.isUnsatEq then
setInconsistent (.eq c)
return ()
if c.isTrivial then
trace[grind.cutsat.le.trivial] "{← c.pp}"
trace[grind.cutsat.eq.trivial] "{← c.pp}"
return ()
let k := c.p.gcdCoeffs'
if c.p.getConst % k > 0 then
setInconsistent (.eq c)
return ()
let c if k == 1 then
pure c
let c := if k == 1 then
c
else
mkEqCnstr (c.p.div k) (.divCoeffs c)
{ 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}"
modify' fun s => { s with
elimEqs := s.elimEqs.set x (some c)
elimStack := x :: s.elimStack
}
updateOccs k x c
if ( inconsistent) then return ()
-- assert a divisibility constraint IF `|k| != 1`
if k.natAbs != 1 then
let p := c.p.insert (-k) x
let d := Int.ofNat k.natAbs
let c mkDvdCnstr d p (.ofEq x c)
c.assert
modify' fun s => { s with
elimEqs := s.elimEqs.set x (some c)
elimStack := x :: s.elimStack
}
{ d, p, h := .ofEq x c : DvdCnstr }.assert
private def exprAsPoly (a : Expr) : GoalM Poly := do
if let some p := ( get').terms.find? { expr := a } then
@@ -248,8 +241,7 @@ def processNewEqImpl (a b : Expr) : GoalM Unit := do
let p₁ exprAsPoly a
let p₂ exprAsPoly b
let p := p₁.combine (p₂.mul (-1))
let c mkEqCnstr p (.core p₁ p₂ ( mkEqProof a b))
c.assert
{ p, h := .core p₁ p₂ ( mkEqProof a b) : EqCnstr }.assert
@[export lean_process_cutsat_eq_lit]
def processNewEqLitImpl (a ke : Expr) : GoalM Unit := do
@@ -258,11 +250,11 @@ def processNewEqLitImpl (a ke : Expr) : GoalM Unit := do
let p₁ exprAsPoly a
let h mkEqProof a ke
let c if k == 0 then
mkEqCnstr p₁ (.expr h)
pure { p := p₁, h := .expr h : EqCnstr }
else
let p₂ exprAsPoly ke
let p := p₁.combine (p₂.mul (-1))
mkEqCnstr p (.core p₁ p₂ h)
pure { p, h := .core p₁ p₂ h : EqCnstr }
c.assert
@[export lean_process_cutsat_diseq]
@@ -272,11 +264,11 @@ def processNewDiseqImpl (a b : Expr) : GoalM Unit := do
let some h mkDiseqProof? a b
| throwError "internal `grind` error, failed to build disequality proof for{indentExpr a}\nand{indentExpr b}"
let c if let some 0 getIntValue? b then
mkDiseqCnstr p₁ (.expr h)
pure { p := p₁, h := .expr h : DiseqCnstr }
else
let p₂ exprAsPoly b
let p := p₁.combine (p₂.mul (-1))
mkDiseqCnstr p (.core p₁ p₂ h)
pure {p, h := .core p₁ p₂ h : DiseqCnstr }
c.assert
/-- Different kinds of terms internalized by this module. -/

View File

@@ -11,19 +11,17 @@ import Lean.Meta.Tactic.Grind.Arith.Cutsat.Util
import Lean.Meta.Tactic.Grind.Arith.Cutsat.Proof
namespace Lean.Meta.Grind.Arith.Cutsat
def mkLeCnstr (p : Poly) (h : LeCnstrProof) : GoalM LeCnstr := do
return { p, h, id := ( mkCnstrId) }
def LeCnstr.norm (c : LeCnstr) : GoalM LeCnstr := do
let c if c.p.isSorted then
pure c
def LeCnstr.norm (c : LeCnstr) : LeCnstr :=
let c := if c.p.isSorted then
c
else
mkLeCnstr c.p.norm (.norm c)
{ p := c.p.norm, h := .norm c }
let k := c.p.gcdCoeffs'
if k != 1 then
mkLeCnstr (c.p.div k) (.divCoeffs c)
{ p := c.p.div k, h := .divCoeffs c }
else
return c
c
/--
Given an equation `c₁` containing the monomial `a*x`, and an inequality constraint `c₂`
@@ -37,7 +35,7 @@ def LeCnstr.applyEq (a : Int) (x : Var) (c₁ : EqCnstr) (b : Int) (c₂ : LeCns
else
p.mul b |>.combine (q.mul (-a))
trace[grind.cutsat.subst] "{← getVar x}, {← c₁.pp}, {← c₂.pp}"
mkLeCnstr p (.subst x c₁ c₂)
return { p, h := .subst x c₁ c₂ }
partial def LeCnstr.applySubsts (c : LeCnstr) : GoalM LeCnstr := withIncRecDepth do
let some (b, x, c₁) c.p.findVarToSubst | return c
@@ -70,7 +68,8 @@ private def findEq (c : LeCnstr) : GoalM Bool := do
for c' in cs' do
if c.p.isNegEq c'.p then
c'.erase
let eq mkEqCnstr c.p (.ofLeGe c c')
let eq := { p := c.p, h := .ofLeGe c c' : EqCnstr }
trace[grind.debug.cutsat.eq] "new eq: {← eq.pp}"
eq.assert
return true
return false
@@ -93,14 +92,14 @@ where
if c.p == c'.p || c.p.isNegEq c'.p then
-- Remove `c'`
modify' fun s => { s with diseqs := s.diseqs.modify x fun cs' => cs'.filter fun c => c.p != c'.p }
return some ( mkLeCnstr (c.p.addConst 1) (.ofLeDiseq c c'))
return some { p := c.p.addConst 1, h := .ofLeDiseq c c' }
return none
def LeCnstr.assert (c : LeCnstr) : GoalM Unit := do
if ( inconsistent) then return ()
let c c.norm
let c c.applySubsts
let c c.norm.applySubsts
if c.isUnsat then
trace[grind.cutsat.le.unsat] "{← c.pp}"
setInconsistent (.le c)
return ()
if c.isTrivial then
@@ -140,9 +139,9 @@ integer inequality, asserts it to the cutsat state.
def propagateIfIntLe (e : Expr) (eqTrue : Bool) : GoalM Unit := do
let some p toPolyLe? e | return ()
let c if eqTrue then
mkLeCnstr p (.expr ( mkOfEqTrue ( mkEqTrueProof e)))
pure { p, h := .expr ( mkOfEqTrue ( mkEqTrueProof e)) : LeCnstr }
else
mkLeCnstr (p.mul (-1) |>.addConst 1) (.notExpr p ( mkOfEqFalse ( mkEqFalseProof e)))
pure { p := p.mul (-1) |>.addConst 1, h := .notExpr p ( mkOfEqFalse ( mkEqFalseProof e)) : LeCnstr }
trace[grind.cutsat.assert.le] "{← c.pp}"
c.assert

View File

@@ -94,6 +94,6 @@ def mkModel (goal : Goal) : MetaM (Array (Expr × Rat)) := do
for (e, v) in model do
unless isInterpretedTerm e do
r := r.push (e, v)
return r
return r.qsort fun (e₁, _) (e₂, _) => e₁.lt e₂
end Lean.Meta.Grind.Arith.Cutsat

View File

@@ -14,7 +14,7 @@ private def DvdCnstr.get_d_a (c : DvdCnstr) : GoalM (Int × Int) := do
return (d, a)
mutual
partial def EqCnstr.toExprProof (c' : EqCnstr) : ProofM Expr := c'.caching do
partial def EqCnstr.toExprProof (c' : EqCnstr) : ProofM Expr := caching c' do
match c'.h with
| .expr h =>
return h
@@ -34,7 +34,7 @@ partial def EqCnstr.toExprProof (c' : EqCnstr) : ProofM Expr := c'.caching do
( getContext) (toExpr c₁.p) (toExpr c₂.p)
reflBoolTrue ( c₁.toExprProof) ( c₂.toExprProof)
partial def DvdCnstr.toExprProof (c' : DvdCnstr) : ProofM Expr := c'.caching do
partial def DvdCnstr.toExprProof (c' : DvdCnstr) : ProofM Expr := caching c' do
match c'.h with
| .expr h =>
return h
@@ -87,7 +87,7 @@ partial def DvdCnstr.toExprProof (c' : DvdCnstr) : ProofM Expr := c'.caching do
return mkApp10 (mkConst thmName)
( getContext) (toExpr p₁) (toExpr p₂) (toExpr c₃.p) (toExpr c₃.d) (toExpr s.k) (toExpr c'.d) (toExpr c'.p) ( s.toExprProof) reflBoolTrue
partial def LeCnstr.toExprProof (c' : LeCnstr) : ProofM Expr := c'.caching do
partial def LeCnstr.toExprProof (c' : LeCnstr) : ProofM Expr := caching c' do
match c'.h with
| .expr h =>
return h
@@ -127,7 +127,7 @@ partial def LeCnstr.toExprProof (c' : LeCnstr) : ProofM Expr := c'.caching do
let { c₁, c₂, c₃?, left } := s.pred
let p₁ := c₁.p
let p₂ := c₂.p
let coeff := if left then p.leadCoeff else p.leadCoeff
let coeff := if left then p.leadCoeff else p.leadCoeff
match c₃? with
| none =>
let thmName := if left then ``Int.Linear.cooper_left_split_ineq else ``Int.Linear.cooper_right_split_ineq
@@ -138,7 +138,7 @@ partial def LeCnstr.toExprProof (c' : LeCnstr) : ProofM Expr := c'.caching do
return mkApp10 (mkConst thmName)
( getContext) (toExpr p₁) (toExpr p₂) (toExpr c₃.p) (toExpr c₃.d) (toExpr s.k) (toExpr coeff) (toExpr c'.p) ( s.toExprProof) reflBoolTrue
partial def DiseqCnstr.toExprProof (c' : DiseqCnstr) : ProofM Expr := c'.caching do
partial def DiseqCnstr.toExprProof (c' : DiseqCnstr) : ProofM Expr := caching c' do
match c'.h with
| .expr h =>
return h
@@ -156,7 +156,7 @@ partial def DiseqCnstr.toExprProof (c' : DiseqCnstr) : ProofM Expr := c'.caching
( getContext) (toExpr x) (toExpr c₁.p) (toExpr c₂.p) (toExpr c'.p)
reflBoolTrue ( c₁.toExprProof) ( c₂.toExprProof)
partial def CooperSplit.toExprProof (s : CooperSplit) : ProofM Expr := caching s.id do
partial def CooperSplit.toExprProof (s : CooperSplit) : ProofM Expr := caching s do
match s.h with
| .dec h => return mkFVar h
| .last hs _ =>
@@ -190,7 +190,7 @@ partial def CooperSplit.toExprProof (s : CooperSplit) : ProofM Expr := caching s
let h c.toExprProofCore -- proof of `False`
-- `hNotCase` is a proof for `¬ pred (k-1)`
let hNotCase := mkLambda `h .default type (h.abstract #[mkFVar fvarId])
result := mkApp4 (mkConst ``Int.Linear.orOver_resolve) (toExpr k) pred result hNotCase
result := mkApp4 (mkConst ``Int.Linear.orOver_resolve) (toExpr (k-1)) pred result hNotCase
k := k - 1
-- `result` is now a proof of `OrOver 1 pred`
return mkApp2 (mkConst ``Int.Linear.orOver_one) pred result
@@ -198,21 +198,23 @@ partial def CooperSplit.toExprProof (s : CooperSplit) : ProofM Expr := caching s
partial def UnsatProof.toExprProofCore (h : UnsatProof) : ProofM Expr := do
match h with
| .le c =>
trace[grind.cutsat.le.unsat] "{← c.pp}"
return mkApp4 (mkConst ``Int.Linear.le_unsat) ( getContext) (toExpr c.p) reflBoolTrue ( c.toExprProof)
| .dvd c =>
trace[grind.cutsat.dvd.unsat] "{← c.pp}"
return mkApp5 (mkConst ``Int.Linear.dvd_unsat) ( getContext) (toExpr c.d) (toExpr c.p) reflBoolTrue ( c.toExprProof)
| .eq c =>
trace[grind.cutsat.eq.unsat] "{← c.pp}"
if c.p.isUnsatEq then
return mkApp4 (mkConst ``Int.Linear.eq_unsat) ( getContext) (toExpr c.p) reflBoolTrue ( c.toExprProof)
else
let k := c.p.gcdCoeffs'
return mkApp5 (mkConst ``Int.Linear.eq_unsat_coeff) ( getContext) (toExpr c.p) (toExpr (Int.ofNat k)) reflBoolTrue ( c.toExprProof)
| .diseq c =>
trace[grind.cutsat.diseq.unsat] "{← c.pp}"
return mkApp4 (mkConst ``Int.Linear.diseq_unsat) ( getContext) (toExpr c.p) reflBoolTrue ( c.toExprProof)
| .cooper c₁ c₂ c₃ =>
let .add c _ _ := c₃.p | c₃.throwUnexpected
let d := c₃.d
let (_, α, β) := gcdExt c d
let h := mkApp7 (mkConst ``Int.Linear.cooper_unsat) ( getContext) (toExpr c₁.p) (toExpr c₂.p) (toExpr c₃.p) (toExpr c₃.d) (toExpr α) (toExpr β)
return mkApp4 h reflBoolTrue ( c₁.toExprProof) ( c₂.toExprProof) ( c₃.toExprProof)
end
@@ -220,6 +222,7 @@ 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 }
@@ -233,14 +236,14 @@ We collect them and perform non chronological backtracking.
-/
structure CollectDecVars.State where
visited : Std.HashSet Nat := {}
visited : Std.HashSet UInt64 := {}
found : FVarIdSet := {}
abbrev CollectDecVarsM := ReaderT FVarIdSet (StateM CollectDecVars.State)
private def alreadyVisited (id : Nat) : CollectDecVarsM Bool := do
if ( get).visited.contains id then return true
modify fun s => { s with visited := s.visited.insert id }
private def alreadyVisited (c : α) : CollectDecVarsM Bool := do
let addr := unsafe (ptrAddrUnsafe c).toUInt64 >>> 2
if ( get).visited.contains addr then return true
modify fun s => { s with visited := s.visited.insert addr }
return false
private def markAsFound (fvarId : FVarId) : CollectDecVarsM Unit := do
@@ -252,26 +255,30 @@ private def collectExpr (e : Expr) : CollectDecVarsM Unit := do
markAsFound fvarId
mutual
partial def EqCnstr.collectDecVars (c' : EqCnstr) : CollectDecVarsM Unit := do unless ( alreadyVisited c'.id) do
partial def EqCnstr.collectDecVars (c' : EqCnstr) : CollectDecVarsM Unit := do unless ( alreadyVisited c') do
match c'.h with
| .expr h => collectExpr h
| .core .. => 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
partial def CooperSplit.collectDecVars (s : CooperSplit) : CollectDecVarsM Unit := do unless ( alreadyVisited s.id) do
partial def CooperSplit.collectDecVars (s : CooperSplit) : CollectDecVarsM Unit := do unless ( alreadyVisited s) do
s.pred.c₁.collectDecVars
s.pred.c₂.collectDecVars
if let some c₃ := s.pred.c₃? then
c₃.collectDecVars
match s.h with
| .dec h => markAsFound h
| .last (decVars := decVars) .. => decVars.forM markAsFound
partial def DvdCnstr.collectDecVars (c' : DvdCnstr) : CollectDecVarsM Unit := do unless ( alreadyVisited c'.id) do
partial def DvdCnstr.collectDecVars (c' : DvdCnstr) : CollectDecVarsM Unit := do unless ( alreadyVisited c') do
match c'.h with
| .expr h => collectExpr h
| .cooper₁ c | .cooper₂ c
| .norm c | .elim c | .divCoeffs c | .ofEq _ c => c.collectDecVars
| .solveCombine c₁ c₂ | .solveElim c₁ c₂ | .subst _ c₁ c₂ => c₁.collectDecVars; c₂.collectDecVars
partial def LeCnstr.collectDecVars (c' : LeCnstr) : CollectDecVarsM Unit := do unless ( alreadyVisited c'.id) do
partial def LeCnstr.collectDecVars (c' : LeCnstr) : CollectDecVarsM Unit := do unless ( alreadyVisited c') do
match c'.h with
| .expr h => collectExpr h
| .notExpr .. => return () -- This kind of proof is used for connecting with the `grind` core.
@@ -279,7 +286,7 @@ partial def LeCnstr.collectDecVars (c' : LeCnstr) : CollectDecVarsM Unit := do u
| .combine c₁ c₂ | .subst _ c₁ c₂ | .ofLeDiseq c₁ c₂ => c₁.collectDecVars; c₂.collectDecVars
| .ofDiseqSplit (decVars := decVars) .. => decVars.forM markAsFound
partial def DiseqCnstr.collectDecVars (c' : DiseqCnstr) : CollectDecVarsM Unit := do unless ( alreadyVisited c'.id) do
partial def DiseqCnstr.collectDecVars (c' : DiseqCnstr) : CollectDecVarsM Unit := do unless ( alreadyVisited c') do
match c'.h with
| .expr h => collectExpr h
| .core .. => return () -- Disequalities coming from the core never contain cutsat decision variables
@@ -291,6 +298,7 @@ end
def UnsatProof.collectDecVars (h : UnsatProof) : CollectDecVarsM Unit := do
match h with
| .le c | .dvd c | .eq c | .diseq c => c.collectDecVars
| .cooper c₁ c₂ c₃ => c₁.collectDecVars; c₂.collectDecVars; c₃.collectDecVars
abbrev CollectDecVarsM.run (x : CollectDecVarsM Unit) (decVars : FVarIdSet) : FVarIdSet :=
let (_, s) := x decVars |>.run {}

View File

@@ -26,25 +26,30 @@ def CooperSplit.assert (cs : CooperSplit) : GoalM Unit := do
let b := p₂.leadCoeff
let p₁' := p.mul b |>.combine (q.mul (-a))
let p₁' := p₁'.addConst <| if left then b*k else (-a)*k
let c₁' mkLeCnstr p₁' (.cooper cs)
let c₁' := { p := p₁', h := .cooper cs : LeCnstr }
trace[grind.debug.cutsat.cooper] "{← c₁'.pp}"
c₁'.assert
if ( inconsistent) then return ()
let p₂' := if left then p else q
let p₂' := p₂'.addConst k
let c₂' mkDvdCnstr (if left then a else b) p₂' (.cooper₁ cs)
let c₂' := { d := if left then a else b, 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 ()
let p₃ := c₃.p
let d := c₃.d
let s := p₃.tail
let c := p₃.leadCoeff
let c₃' if left then
let c₃' := if left then
let p₃' := p.mul c |>.combine (s.mul (-a))
let p₃' := p₃'.addConst (c*k)
mkDvdCnstr (a*d) p₃' (.cooper₂ cs)
{ d := a*d, p := p₃', h := .cooper₂ cs : DvdCnstr }
else
let p₃' := q.mul (-c) |>.combine (s.mul b)
let p₃' := p₃'.addConst (-c*k)
mkDvdCnstr (b*d) p₃' (.cooper₂ cs)
{ 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
@@ -81,6 +86,7 @@ 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
@@ -178,7 +184,7 @@ def resolveDvdConflict (c : DvdCnstr) : GoalM Unit := do
trace[grind.cutsat.conflict] "{← c.pp}"
let d := c.d
let .add a _ p := c.p | c.throwUnexpected
( mkDvdCnstr (a.gcd d) p (.elim c)).assert
{ d := a.gcd d, p, h := .elim c : DvdCnstr }.assert
/--
Given a divisibility constraint solution space `s := { b, d }`,
@@ -266,17 +272,33 @@ def resolveRealLowerUpperConflict (c₁ c₂ : LeCnstr) : GoalM Bool := do
let .add a₂ _ p₂ := c₂.p | c₂.throwUnexpected
let p := p₁.mul a₂.natAbs |>.combine (p₂.mul a₁.natAbs)
if ( p.satisfiedLe) != .false then
trace[grind.cutsat.conflict] "not resolved"
return false
else
let c mkLeCnstr p (.combine c₁ c₂)
let c := { p, h := .combine c₁ c₂ : LeCnstr }
trace[grind.cutsat.conflict] "resolved: {← c.pp}"
c.assert
return true
def resolveCooperUnary (pred : CooperSplitPred) : SearchM Bool := do
let some c₃ := pred.c₃? | return false
let .add (-1) _ (.num a) := pred.c₁.p | return false
let .add 1 _ (.num b) := pred.c₂.p | return false
let .add c _ (.num e) := c₃.p | return false
let d := c₃.d
let (1, α, _) := gcdExt c d | return false
unless -b < Int.Linear.cdiv (a - -α * e % d) d * d + -α * e % d do
return false
setInconsistent (.cooper pred.c₁ pred.c₂ c₃)
return true
def resolveCooperPred (pred : CooperSplitPred) : SearchM Unit := do
trace[grind.cutsat.conflict] "[{pred.numCases}]: {← pred.pp}"
if ( resolveCooperUnary pred) then
return
let n := pred.numCases
let fvarId mkCase (.cooper pred #[])
let s : CooperSplit := { pred, k := n - 1, id := ( mkCnstrId), h := .dec fvarId }
s.assert
let fvarId mkCase (.cooper pred #[] {})
{ pred, k := n - 1, h := .dec fvarId : CooperSplit }.assert
def resolveCooper (c₁ c₂ : LeCnstr) : SearchM Unit := do
let left : Bool := c₁.p.leadCoeff.natAbs < c₂.p.leadCoeff.natAbs
@@ -295,10 +317,10 @@ splits `c` and resolve with `c₁`.
Recall that a disequality
-/
def resolveRatDiseq (c₁ : LeCnstr) (c : DiseqCnstr) : SearchM Unit := do
let c if c.p.leadCoeff < 0 then
mkDiseqCnstr (c.p.mul (-1)) (.neg c)
let c := if c.p.leadCoeff < 0 then
{ p := c.p.mul (-1), h := .neg c : DiseqCnstr }
else
pure c
c
let fvarId if let some fvarId := ( get').diseqSplits.find? c.p then
trace[grind.debug.cutsat.diseq.split] "{← c.pp}, reusing {fvarId.name}"
pure fvarId
@@ -308,12 +330,13 @@ def resolveRatDiseq (c₁ : LeCnstr) (c : DiseqCnstr) : SearchM Unit := do
modify' fun s => { s with diseqSplits := s.diseqSplits.insert c.p fvarId }
pure fvarId
let p₂ := c.p.addConst 1
let c₂ mkLeCnstr p₂ (.expr (mkFVar fvarId))
let c₂ := { p := p₂, h := .expr (mkFVar fvarId) : LeCnstr }
let b resolveRealLowerUpperConflict c₁ c₂
assert! b
def processVar (x : Var) : SearchM Unit := do
if ( eliminated x) then
trace[grind.debug.cutsat.search] "eliminated: {← getVar x}"
/-
Variable has been eliminated, and will be assigned later after we have assigned
variables that have not been eliminated.
@@ -325,6 +348,7 @@ def processVar (x : Var) : SearchM Unit := do
if let some solutions c.getSolutions? then
pure solutions
else
trace[grind.debug.cutsat.search] "dvd conflict: {← c.pp}"
resolveDvdConflict c
return ()
else
@@ -392,36 +416,57 @@ private def findCase (decVars : FVarIdSet) : SearchM Case := do
trace[grind.debug.cutsat.backtrack] "skipping {case.fvarId.name}"
unreachable!
def resolveConflict (h : UnsatProof) : SearchM Bool := do
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}"
let decVars := h.collectDecVars.run ( get).decVars
trace[grind.debug.cutsat.backtrack] "dec vars: {decVars.toList.map (·.name)}"
if decVars.isEmpty then
trace[grind.debug.cutsat.backtrack] "close goal: {← h.pp}"
closeGoal ( h.toExprProof)
return false
return ()
let c findCase decVars
modify' fun _ => c.saved
trace[grind.debug.cutsat.backtrack] "backtracking {c.fvarId.name}"
let decVars := decVars.erase c.fvarId
match c.kind with
| .diseq c₁ =>
let decVars := decVars.erase c.fvarId |>.toArray
let decVars := decVars.toArray
let p' := c₁.p.mul (-1) |>.addConst 1
let c' mkLeCnstr p' (.ofDiseqSplit c₁ c.fvarId h decVars)
let c' := { p := p', h := .ofDiseqSplit c₁ c.fvarId h decVars : LeCnstr }
trace[grind.debug.cutsat.backtrack] "resolved diseq split: {← c'.pp}"
c'.assert
return true
| _ => throwError "NIY resolve conflict"
| .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}"
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)}"
pure { pred, k := 0, h := .last hs decVars' : CooperSplit }
s.assert
/-- Search for an assignment/model for the linear constraints. -/
def searchAssigmentMain : SearchM Unit := do
repeat
trace[grind.debug.cutsat.search] "main loop"
if ( hasAssignment) then
return ()
if ( isInconsistent) then
-- `grind` state is inconsistent
return ()
if let some c := ( get').conflict? then
unless ( resolveConflict c) do
return ()
let x : Var := ( get').assignment.size
processVar x
resolveConflict c
else
let x : Var := ( get').assignment.size
trace[grind.debug.cutsat.search] "next var: {← getVar x}"
processVar x
def traceModel : GoalM Unit := do
if ( isTracingEnabledFor `grind.cutsat.model) then
@@ -436,6 +481,7 @@ def searchAssigment : GoalM Unit := do
trace[grind.debug.cutsat.search] "restart using Cooper resolution"
modify' fun s => { s with assignment := {} }
searchAssigmentMain .int |>.run' {}
trace[grind.debug.cutsat.search] "after search int model, inconsistent: {← isInconsistent}"
if ( isInconsistent) then return ()
assignElimVars
traceModel

View File

@@ -14,7 +14,7 @@ In principle, we only need to support two kinds of case split.
-/
inductive CaseKind where
| diseq (d : DiseqCnstr)
| cooper (s : CooperSplitPred) (hs : Array (FVarId × UnsatProof))
| cooper (s : CooperSplitPred) (hs : Array (FVarId × UnsatProof)) (decVars : FVarIdSet)
deriving Inhabited
structure Case where
@@ -74,6 +74,7 @@ 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

View File

@@ -75,7 +75,6 @@ mutual
structure EqCnstr where
p : Poly
h : EqCnstrProof
id : Nat
inductive EqCnstrProof where
| expr (h : Expr)
@@ -90,8 +89,6 @@ structure DvdCnstr where
d : Int
p : Poly
h : DvdCnstrProof
/-- Unique id for caching proofs in `ProofM` -/
id : Nat
/--
The predicate of type `Nat → Prop`, which serves as the conclusion of the
@@ -118,7 +115,6 @@ structure CooperSplit where
pred : CooperSplitPred
k : Nat
h : CooperSplitProof
id : Nat
/--
The `cooper_left`, `cooper_right`, `cooper_dvd_left`, and `cooper_dvd_right` theorems have a resulting type
@@ -150,7 +146,6 @@ inductive DvdCnstrProof where
structure LeCnstr where
p : Poly
h : LeCnstrProof
id : Nat
inductive LeCnstrProof where
| expr (h : Expr)
@@ -169,7 +164,6 @@ inductive LeCnstrProof where
structure DiseqCnstr where
p : Poly
h : DiseqCnstrProof
id : Nat
inductive DiseqCnstrProof where
| expr (h : Expr)
@@ -188,20 +182,21 @@ inductive UnsatProof where
| le (c : LeCnstr)
| eq (c : EqCnstr)
| diseq (c : DiseqCnstr)
| cooper (c₁ c₂ : LeCnstr) (c₃ : DvdCnstr)
end
instance : Inhabited LeCnstr where
default := { p := .num 0, h := .expr default, id := 0 }
default := { p := .num 0, h := .expr default }
instance : Inhabited DvdCnstr where
default := { d := 0, p := .num 0, h := .expr default, id := 0 }
default := { d := 0, p := .num 0, h := .expr default }
instance : Inhabited CooperSplitPred where
default := { left := false, c₁ := default, c₂ := default, c₃? := none }
instance : Inhabited CooperSplit where
default := { pred := default, k := 0, h := .dec default, id := 0 }
default := { pred := default, k := 0, h := .dec default }
abbrev VarSet := RBTree Var compare
@@ -272,10 +267,14 @@ structure State where
This is necessary because the same disequality may be in different conflicts.
-/
diseqSplits : PHashMap Poly FVarId := {}
/-
TODO: Model-based theory combination.
/--
Pairs `(x, n)` s.t. we have expanded the theorems
- `Int.Linear.ediv_emod`
- `Int.Linear.emod_nonneg`
- `Int.Linear.emod_le`
-/
divMod : PHashSet (Expr × Int) := {}
/- TODO: Model-based theory combination. -/
deriving Inhabited
end Lean.Meta.Grind.Arith.Cutsat

View File

@@ -64,9 +64,6 @@ def mkCnstrId : GoalM Nat := do
modify' fun s => { s with nextCnstrId := id + 1 }
return id
def mkEqCnstr (p : Poly) (h : EqCnstrProof) : GoalM EqCnstr := do
return { p, h, id := ( mkCnstrId) }
@[extern "lean_grind_cutsat_assert_eq"] -- forward definition
opaque EqCnstr.assert (c : EqCnstr) : GoalM Unit
@@ -189,7 +186,7 @@ def toContextExpr : GoalM Expr := do
return RArray.toExpr (mkConst ``Int) id (RArray.leaf (mkIntLit 0))
structure ProofM.State where
cache : Std.HashMap Nat Expr := {}
cache : Std.HashMap UInt64 Expr := {}
/-- Auxiliary monad for constructing cutsat proofs. -/
abbrev ProofM := ReaderT Expr (StateRefT ProofM.State GoalM)
@@ -198,19 +195,15 @@ abbrev ProofM := ReaderT Expr (StateRefT ProofM.State GoalM)
abbrev getContext : ProofM Expr := do
read
abbrev caching (id : Nat) (k : ProofM Expr) : ProofM Expr := do
if let some h := ( get).cache[id]? then
abbrev caching (c : α) (k : ProofM Expr) : ProofM Expr := do
let addr := unsafe (ptrAddrUnsafe c).toUInt64 >>> 2
if let some h := ( get).cache[addr]? then
return h
else
let h k
modify fun s => { s with cache := s.cache.insert id h }
modify fun s => { s with cache := s.cache.insert addr h }
return h
abbrev DvdCnstr.caching (c : DvdCnstr) (k : ProofM Expr) : ProofM Expr := Cutsat.caching c.id k
abbrev LeCnstr.caching (c : LeCnstr) (k : ProofM Expr) : ProofM Expr := Cutsat.caching c.id k
abbrev EqCnstr.caching (c : EqCnstr) (k : ProofM Expr) : ProofM Expr := Cutsat.caching c.id k
abbrev DiseqCnstr.caching (c : DiseqCnstr) (k : ProofM Expr) : ProofM Expr := Cutsat.caching c.id k
abbrev withProofContext (x : ProofM Expr) : GoalM Expr := do
withLetDecl `ctx (mkApp (mkConst ``RArray) (mkConst ``Int)) ( toContextExpr) fun ctx => do
let h x ctx |>.run' {}
@@ -291,4 +284,12 @@ def CooperSplitPred.numCases (pred : CooperSplitPred) : Nat :=
else
Int.lcm b (b * d / Int.gcd (b * d) c)
def CooperSplitPred.pp (pred : CooperSplitPred) : GoalM MessageData := do
return m!"{← pred.c₁.pp}, {← pred.c₂.pp}, {← if let some c₃ := pred.c₃? then c₃.pp else pure "none"}"
def UnsatProof.pp (h : UnsatProof) : GoalM MessageData := do
match h with
| .le c | .eq c | .dvd c | .diseq c => c.pp
| .cooper c₁ c₂ c₃ => return m!"{← c₁.pp}, {← c₂.pp}, {← c₃.pp}"
end Lean.Meta.Grind.Arith.Cutsat

View File

@@ -8,6 +8,7 @@ import Init.Grind.Util
import Lean.Util.PtrSet
import Lean.Meta.Basic
import Lean.Meta.InferType
import Lean.Meta.Tactic.Grind.Util
namespace Lean.Meta.Grind
@@ -21,6 +22,13 @@ where
if let some r := ( get).find? e then
return r
let prop inferType e
/-
We must unfold reducible constants occurring in `prop` because the congruence closure
module in `grind` assumes they have been expanded.
See `grind_mark_nested_proofs_bug.lean` for an example.
TODO: We may have to normalize `prop` too.
-/
let prop unfoldReducible prop
let e' := mkApp2 (mkConst ``Lean.Grind.nestedProof) prop e
modify fun s => s.insert e e'
return e'

View File

@@ -24,10 +24,11 @@ def withEnv [Monad m] [MonadFinally m] [MonadEnv m] (env : Environment) (x : m
finally
setEnv saved
def isInductive [Monad m] [MonadEnv m] (declName : Name) : m Bool := do
match ( getEnv).find? declName with
| some (ConstantInfo.inductInfo ..) => return true
| _ => return false
def isInductiveCore (env : Environment) (declName : Name) : Bool :=
env.find? declName matches some (.inductInfo ..)
def isInductive [Monad m] [MonadEnv m] (declName : Name) : m Bool :=
return isInductiveCore ( getEnv) declName
def isRecCore (env : Environment) (declName : Name) : Bool :=
match env.find? declName with

View File

@@ -22,10 +22,6 @@ after replaying any inductive definitions occurring in `constantMap`.
* a verifier for an `Environment`, by sending everything to the kernel, or
* a mechanism to safely transfer constants from one `Environment` to another.
## Deprecation note
We've decided that this code should live in the `https://github.com/leanprover/lean4checker` repository,
rather than the core distribution. This code is now deprecated, and will be removed in a future release.
-/
namespace Lean.Environment
@@ -36,7 +32,7 @@ structure Context where
newConstants : Std.HashMap Name ConstantInfo
structure State where
env : Kernel.Environment
env : Environment
remaining : NameSet := {}
pending : NameSet := {}
postponedConstructors : NameSet := {}
@@ -157,7 +153,6 @@ open Replay
Throws a `IO.userError` if the kernel rejects a constant,
or if there are malformed recursors or constructors for inductive types.
-/
@[deprecated "Use the `lean4checker` package instead" (since := "2025-03-04")]
def replay (newConstants : Std.HashMap Name ConstantInfo) (env : Environment) : IO Environment := do
let mut remaining : NameSet :=
for (n, ci) in newConstants.toList do
@@ -165,10 +160,10 @@ def replay (newConstants : Std.HashMap Name ConstantInfo) (env : Environment) :
-- Later we may want to handle partial constants.
if !ci.isUnsafe && !ci.isPartial then
remaining := remaining.insert n
let (_, s) StateRefT'.run (s := { env := env.toKernelEnv, remaining }) do
let (_, s) StateRefT'.run (s := { env, remaining }) do
ReaderT.run (r := { newConstants }) do
for n in remaining do
replayConstant n
checkPostponedConstructors
checkPostponedRecursors
return .ofKernelEnv s.env
return s.env

View File

@@ -98,15 +98,17 @@ def applyEditToHint? (hintMod : Name) (ihi : Elab.InlayHintInfo) (range : String
}
structure InlayHintState where
oldInlayHints : Array Elab.InlayHintInfo
oldFinishedSnaps : Nat
lastEditTimestamp? : Option Nat
oldInlayHints : Array Elab.InlayHintInfo
oldFinishedSnaps : Nat
lastEditTimestamp? : Option Nat
isFirstRequestAfterEdit : Bool
deriving TypeName, Inhabited
def InlayHintState.init : InlayHintState := {
oldInlayHints := #[]
oldFinishedSnaps := 0
lastEditTimestamp? := none
isFirstRequestAfterEdit := false
}
def handleInlayHints (p : InlayHintParams) (s : InlayHintState) :
@@ -115,6 +117,20 @@ def handleInlayHints (p : InlayHintParams) (s : InlayHintState) :
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.
-- The reason for this is that in VS Code, when it hasn't received a new set of inlay hints,
-- edits to the document visually move all old inlay hints, but do not actually update other
-- fields, like the `textEdit` field. This means that e.g. inlay hint insertion will insert
-- the inlay hint at the wrong position.
-- 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 r := { response := lspInlayHints, isComplete := false }
let s := { s with isFirstRequestAfterEdit := false }
return (r, s)
-- We delay sending inlay hints by 3000ms to avoid inlay hint flickering on the client.
-- VS Code already has a mechanism for this, but it is not sufficient.
let inlayHintEditDelayMs := 3000
@@ -126,6 +142,16 @@ def handleInlayHints (p : InlayHintParams) (s : InlayHintState) :
let timeSinceLastEditMs := timestamp - lastEditTimestamp
inlayHintEditDelayMs - timeSinceLastEditMs
let (snaps, _, isComplete) ctx.doc.cmdSnaps.getFinishedPrefixWithConsistentLatency editDelayMs.toUInt32 (cancelTk? := ctx.cancelTk.cancellationTask)
if IO.hasFinished ctx.cancelTk.cancellationTask then
-- Inlay hint request has been cancelled, either by a cancellation request or another edit.
-- In the former case, we will simply discard the result and respond with a request error
-- denoting cancellation.
-- 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 r := { response := lspInlayHints, isComplete := false }
return (r, s)
let snaps := snaps.toArray
let finishedSnaps := snaps.size
let oldFinishedSnaps := s.oldFinishedSnaps
@@ -170,6 +196,7 @@ def handleInlayHintsDidChange (p : DidChangeTextDocumentParams)
oldInlayHints := updatedOldInlayHints
oldFinishedSnaps := 0
lastEditTimestamp?
isFirstRequestAfterEdit := true
}
where
@@ -186,8 +213,13 @@ where
return some mod
let modName match modName? with
| .ok (some modName) => pure modName
| .ok none => pure .anonymous -- `.anonymous` occurs in untitled files
| .error err => throw <| .ofIoError err
-- `.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

View File

@@ -210,6 +210,26 @@ instance [BEq α] [Hashable α] : ForM m (DHashMap α β) ((a : α) × β a) whe
instance [BEq α] [Hashable α] : ForIn m (DHashMap α β) ((a : α) × β a) where
forIn m init f := m.forIn (fun a b acc => f a, b acc) init
namespace Const
variable {β : Type v}
/-!
We do not define `ForM` and `ForIn` instances that are specialized to constant `β`. Instead, we
define uncurried versions of `forM` and `forIn` that will be used in the `Const` lemmas and to
define the `ForM` and `ForIn` instances for `HashMap`.
-/
@[inline, inherit_doc forM] def forMUncurried (f : α × β m PUnit)
(b : DHashMap α (fun _ => β)) : m PUnit :=
b.forM fun a b => f a, b
@[inline, inherit_doc forIn] def forInUncurried
(f : α × β δ m (ForInStep δ)) (init : δ) (b : DHashMap α (fun _ => β)) : m δ :=
b.forIn (init := init) fun a b d => f a, b d
end Const
section Unverified
/-! We currently do not provide lemmas for the functions below. -/

View File

@@ -48,7 +48,7 @@ cf. https://github.com/leanprover/lean4/issues/4157
(scrambleHash hash).toUSize &&& (sz.toUSize - 1), by
-- This proof is a good test for our USize API
by_cases h' : sz < USize.size
· rw [USize.toNat_and, USize.toNat_sub_of_le, USize.toNat_ofNat_of_lt h']
· rw [USize.toNat_and, USize.toNat_sub_of_le, USize.toNat_ofNat_of_lt' h']
· exact Nat.lt_of_le_of_lt Nat.and_le_right (Nat.sub_lt h (by simp))
· simp [USize.le_iff_toNat_le, Nat.mod_eq_of_lt h', Nat.succ_le_of_lt h]
· exact Nat.lt_of_lt_of_le (USize.toNat_lt_size _) (Nat.le_of_not_lt h')

View File

@@ -32,181 +32,95 @@ theorem insert_eq [BEq α] [Hashable α] {m : Raw α β} (h : m.WF) {a : α} {b
m.insert a b = (Raw₀.insert m, h.size_buckets_pos a b).1 := by
simp [Raw.insert, h.size_buckets_pos]
theorem insert_val [BEq α] [Hashable α] {m : Raw₀ α β} {a : α} {b : β a} :
m.val.insert a b = m.insert a b := by
simp [Raw.insert, m.2]
theorem insertIfNew_eq [BEq α] [Hashable α] {m : Raw α β} (h : m.WF) {a : α} {b : β a} :
m.insertIfNew a b = (Raw₀.insertIfNew m, h.size_buckets_pos a b).1 := by
simp [Raw.insertIfNew, h.size_buckets_pos]
theorem insertIfNew_val [BEq α] [Hashable α] {m : Raw₀ α β} {a : α} {b : β a} :
m.val.insertIfNew a b = m.insertIfNew a b := by
simp [Raw.insertIfNew, m.2]
theorem containsThenInsert_snd_eq [BEq α] [Hashable α] {m : Raw α β} (h : m.WF) {a : α} {b : β a} :
(m.containsThenInsert a b).2 = (Raw₀.containsThenInsert m, h.size_buckets_pos a b).2.1 := by
simp [Raw.containsThenInsert, h.size_buckets_pos]
theorem containsThenInsert_snd_val [BEq α] [Hashable α] {m : Raw₀ α β} {a : α} {b : β a} :
(m.val.containsThenInsert a b).2 = (m.containsThenInsert a b).2.1 := by
simp [Raw.containsThenInsert, m.2]
theorem containsThenInsert_fst_eq [BEq α] [Hashable α] {m : Raw α β} (h : m.WF) {a : α} {b : β a} :
(m.containsThenInsert a b).1 = (Raw₀.containsThenInsert m, h.size_buckets_pos a b).1 := by
simp [Raw.containsThenInsert, h.size_buckets_pos]
theorem containsThenInsert_fst_val [BEq α] [Hashable α] {m : Raw₀ α β} {a : α} {b : β a} :
(m.val.containsThenInsert a b).1 = (m.containsThenInsert a b).1 := by
simp [Raw.containsThenInsert, m.2]
theorem containsThenInsertIfNew_snd_eq [BEq α] [Hashable α] {m : Raw α β} (h : m.WF) {a : α}
{b : β a} : (m.containsThenInsertIfNew a b).2 =
(Raw₀.containsThenInsertIfNew m, h.size_buckets_pos a b).2.1 := by
simp [Raw.containsThenInsertIfNew, h.size_buckets_pos]
theorem containsThenInsertIfNew_snd_val [BEq α] [Hashable α] {m : Raw₀ α β} {a : α} {b : β a} :
(m.val.containsThenInsertIfNew a b).2 = (m.containsThenInsertIfNew a b).2.1 := by
simp [Raw.containsThenInsertIfNew, m.2]
theorem containsThenInsertIfNew_fst_eq [BEq α] [Hashable α] {m : Raw α β} (h : m.WF) {a : α}
{b : β a} : (m.containsThenInsertIfNew a b).1 =
(Raw₀.containsThenInsertIfNew m, h.size_buckets_pos a b).1 := by
simp [Raw.containsThenInsertIfNew, h.size_buckets_pos]
theorem containsThenInsertIfNew_fst_val [BEq α] [Hashable α] {m : Raw₀ α β} {a : α} {b : β a} :
(m.val.containsThenInsertIfNew a b).1 = (m.containsThenInsertIfNew a b).1 := by
simp [Raw.containsThenInsertIfNew, m.2]
theorem getThenInsertIfNew?_snd_eq [BEq α] [Hashable α] [LawfulBEq α] {m : Raw α β} (h : m.WF)
{a : α} {b : β a} : (m.getThenInsertIfNew? a b).2 =
(Raw₀.getThenInsertIfNew? m, h.size_buckets_pos a b).2.1 := by
simp [Raw.getThenInsertIfNew?, h.size_buckets_pos]
theorem getThenInsertIfNew?_snd_val [BEq α] [Hashable α] [LawfulBEq α] {m : Raw₀ α β} {a : α}
{b : β a} : (m.val.getThenInsertIfNew? a b).2 = (m.getThenInsertIfNew? a b).2.1 := by
simp [Raw.getThenInsertIfNew?, m.2]
theorem getThenInsertIfNew?_fst_eq [BEq α] [Hashable α] [LawfulBEq α] {m : Raw α β} (h : m.WF)
{a : α} {b : β a} : (m.getThenInsertIfNew? a b).1 =
(Raw₀.getThenInsertIfNew? m, h.size_buckets_pos a b).1 := by
simp [Raw.getThenInsertIfNew?, h.size_buckets_pos]
theorem getThenInsertIfNew?_fst_val [BEq α] [Hashable α] [LawfulBEq α] {m : Raw₀ α β} {a : α}
{b : β a} : (m.val.getThenInsertIfNew? a b).1 = (m.getThenInsertIfNew? a b).1 := by
simp [Raw.getThenInsertIfNew?, m.2]
theorem get?_eq [BEq α] [Hashable α] [LawfulBEq α] {m : Raw α β} (h : m.WF) {a : α} :
m.get? a = Raw₀.get? m, h.size_buckets_pos a := by
simp [Raw.get?, h.size_buckets_pos]
theorem get?_val [BEq α] [Hashable α] [LawfulBEq α] {m : Raw₀ α β} {a : α} :
m.val.get? a = m.get? a := by
simp [Raw.get?, m.2]
theorem contains_eq [BEq α] [Hashable α] {m : Raw α β} (h : m.WF) {a : α} :
m.contains a = Raw₀.contains m, h.size_buckets_pos a := by
simp [Raw.contains, h.size_buckets_pos]
theorem contains_val [BEq α] [Hashable α] {m : Raw₀ α β} {a : α} :
m.val.contains a = m.contains a := by
simp [Raw.contains, m.2]
theorem get_eq [BEq α] [Hashable α] [LawfulBEq α] {m : Raw α β} {a : α} {h : a m} :
m.get a h = Raw₀.get m, by change dite .. = true at h; split at h <;> simp_all a
(by change dite .. = true at h; split at h <;> simp_all) := rfl
theorem get_val [BEq α] [Hashable α] [LawfulBEq α] {m : Raw₀ α β} {a : α} {h : a m.val} :
m.val.get a h = m.get a (contains_val (m := m) h) := rfl
theorem getD_eq [BEq α] [Hashable α] [LawfulBEq α] {m : Raw α β} (h : m.WF) {a : α}
{fallback : β a} : m.getD a fallback = Raw₀.getD m, h.size_buckets_pos a fallback := by
simp [Raw.getD, h.size_buckets_pos]
theorem getD_val [BEq α] [Hashable α] [LawfulBEq α] {m : Raw₀ α β} {a : α} {fallback : β a} :
m.val.getD a fallback = m.getD a fallback := by
simp [Raw.getD, m.2]
theorem get!_eq [BEq α] [Hashable α] [LawfulBEq α] {m : Raw α β} (h : m.WF) {a : α}
[Inhabited (β a)] : m.get! a = Raw₀.get! m, h.size_buckets_pos a := by
simp [Raw.get!, h.size_buckets_pos]
theorem get!_val [BEq α] [Hashable α] [LawfulBEq α] {m : Raw₀ α β} {a : α} [Inhabited (β a)] :
m.val.get! a = m.get! a := by
simp [Raw.get!, m.2]
theorem getKey?_eq [BEq α] [Hashable α] {m : Raw α β} (h : m.WF) {a : α} :
m.getKey? a = Raw₀.getKey? m, h.size_buckets_pos a := by
simp [Raw.getKey?, h.size_buckets_pos]
theorem getKey?_val [BEq α] [Hashable α] {m : Raw₀ α β} {a : α} :
m.val.getKey? a = m.getKey? a := by
simp [Raw.getKey?, m.2]
theorem getKey_eq [BEq α] [Hashable α] {m : Raw α β} {a : α} {h : a m} :
m.getKey a h = Raw₀.getKey m, by change dite .. = true at h; split at h <;> simp_all a
(by change dite .. = true at h; split at h <;> simp_all) := rfl
theorem getKey_val [BEq α] [Hashable α] {m : Raw₀ α β} {a : α} {h : a m.val} :
m.val.getKey a h = m.getKey a (contains_val (m := m) h) := rfl
theorem getKeyD_eq [BEq α] [Hashable α] {m : Raw α β} (h : m.WF) {a fallback : α} :
m.getKeyD a fallback = Raw₀.getKeyD m, h.size_buckets_pos a fallback := by
simp [Raw.getKeyD, h.size_buckets_pos]
theorem getKeyD_val [BEq α] [Hashable α] {m : Raw₀ α β} {a fallback : α} :
m.val.getKeyD a fallback = m.getKeyD a fallback := by
simp [Raw.getKeyD, m.2]
theorem getKey!_eq [BEq α] [Hashable α] [Inhabited α] {m : Raw α β} (h : m.WF) {a : α} :
m.getKey! a = Raw₀.getKey! m, h.size_buckets_pos a := by
simp [Raw.getKey!, h.size_buckets_pos]
theorem getKey!_val [BEq α] [Hashable α] [Inhabited α] {m : Raw₀ α β} {a : α} :
m.val.getKey! a = m.getKey! a := by
simp [Raw.getKey!, m.2]
theorem erase_eq [BEq α] [Hashable α] {m : Raw α β} (h : m.WF) {a : α} :
m.erase a = Raw₀.erase m, h.size_buckets_pos a := by
simp [Raw.erase, h.size_buckets_pos]
theorem erase_val [BEq α] [Hashable α] {m : Raw₀ α β} {a : α} :
m.val.erase a = m.erase a := by
simp [Raw.erase, m.2]
theorem filterMap_eq [BEq α] [Hashable α] {m : Raw α β} (h : m.WF)
{f : (a : α) β a Option (δ a)} : m.filterMap f =
Raw₀.filterMap f m, h.size_buckets_pos := by
simp [Raw.filterMap, h.size_buckets_pos]
theorem filterMap_val [BEq α] [Hashable α] {m : Raw₀ α β} {f : (a : α) β a Option (δ a)} :
m.val.filterMap f = m.filterMap f := by
simp [Raw.filterMap, m.2]
theorem map_eq [BEq α] [Hashable α] {m : Raw α β} (h : m.WF) {f : (a : α) β a δ a} :
m.map f = Raw₀.map f m, h.size_buckets_pos := by
simp [Raw.map, h.size_buckets_pos]
theorem map_val [BEq α] [Hashable α] {m : Raw₀ α β} {f : (a : α) β a δ a} :
m.val.map f = m.map f := by
simp [Raw.map, m.2]
theorem filter_eq [BEq α] [Hashable α] {m : Raw α β} (h : m.WF) {f : (a : α) β a Bool} :
m.filter f = Raw₀.filter f m, h.size_buckets_pos := by
simp [Raw.filter, h.size_buckets_pos]
theorem filter_val [BEq α] [Hashable α] {m : Raw₀ α β} {f : (a : α) β a Bool} :
m.val.filter f = m.filter f := by
simp [Raw.filter, m.2]
theorem insertMany_eq [BEq α] [Hashable α] {m : Raw α β} (h : m.WF) {ρ : Type w} [ForIn Id ρ ((a : α) × β a)] {l : ρ} :
m.insertMany l = Raw₀.insertMany m, h.size_buckets_pos l := by
simp [Raw.insertMany, h.size_buckets_pos]
theorem insertMany_val [BEq α][Hashable α] {m : Raw₀ α β} {ρ : Type w} [ForIn Id ρ ((a : α) × β a)] {l : ρ} :
m.val.insertMany l = m.insertMany l := by
simp [Raw.insertMany, m.2]
theorem ofList_eq [BEq α] [Hashable α] {l : List ((a : α) × β a)} :
Raw.ofList l = Raw₀.insertMany Raw₀.empty l := by
simp only [Raw.ofList, Raw.insertMany, (Raw.WF.empty).size_buckets_pos , reduceDIte]
@@ -228,10 +142,6 @@ theorem Const.insertMany_eq [BEq α] [Hashable α] {m : Raw α (fun _ => β)} (h
Raw.Const.insertMany m l = Raw₀.Const.insertMany m, h.size_buckets_pos l := by
simp [Raw.Const.insertMany, h.size_buckets_pos]
theorem Const.insertMany_val [BEq α][Hashable α] {m : Raw₀ α (fun _ => β)} {ρ : Type w} [ForIn Id ρ (α × β)] {l : ρ} :
Raw.Const.insertMany m.val l = Raw₀.Const.insertMany m l := by
simp [Raw.Const.insertMany, m.2]
theorem Const.ofList_eq [BEq α] [Hashable α] {l : List (α × β)} :
Raw.Const.ofList l = Raw₀.Const.insertMany Raw₀.empty l := by
simp only [Raw.Const.ofList, Raw.Const.insertMany, (Raw.WF.empty).size_buckets_pos , reduceDIte]
@@ -242,11 +152,6 @@ theorem Const.insertManyIfNewUnit_eq {ρ : Type w} [ForIn Id ρ α] [BEq α] [Ha
Raw.Const.insertManyIfNewUnit m l = Raw₀.Const.insertManyIfNewUnit m, h.size_buckets_pos l := by
simp [Raw.Const.insertManyIfNewUnit, h.size_buckets_pos]
theorem Const.insertManyIfNewUnit_val {ρ : Type w} [ForIn Id ρ α] [BEq α] [Hashable α]
{m : Raw₀ α (fun _ => Unit)} {l : ρ} :
Raw.Const.insertManyIfNewUnit m.val l = Raw₀.Const.insertManyIfNewUnit m l := by
simp [Raw.Const.insertManyIfNewUnit, m.2]
theorem Const.unitOfList_eq [BEq α] [Hashable α] {l : List α} :
Raw.Const.unitOfList l = Raw₀.Const.insertManyIfNewUnit Raw₀.empty l := by
simp only [Raw.Const.unitOfList, Raw.Const.insertManyIfNewUnit, (Raw.WF.empty).size_buckets_pos ,
@@ -257,56 +162,31 @@ theorem Const.get?_eq [BEq α] [Hashable α] {m : Raw α (fun _ => β)} (h : m.W
Raw.Const.get? m a = Raw₀.Const.get? m, h.size_buckets_pos a := by
simp [Raw.Const.get?, h.size_buckets_pos]
theorem Const.get?_val [BEq α] [Hashable α] {m : Raw₀ α (fun _ => β)} {a : α} :
Raw.Const.get? m.val a = Raw₀.Const.get? m a := by
simp [Raw.Const.get?, m.2]
theorem Const.get_eq [BEq α] [Hashable α] {m : Raw α (fun _ => β)} {a : α} {h : a m} :
Raw.Const.get m a h = Raw₀.Const.get
m, by change dite .. = true at h; split at h <;> simp_all a
(by change dite .. = true at h; split at h <;> simp_all) :=
rfl
theorem Const.get_val [BEq α] [Hashable α] {m : Raw₀ α (fun _ => β)} {a : α} {h : a m.val} :
Raw.Const.get m.val a h = Raw₀.Const.get m a (contains_val (m := m) h) := rfl
theorem Const.getD_eq [BEq α] [Hashable α] {m : Raw α (fun _ => β)} (h : m.WF) {a : α}
{fallback : β} : Raw.Const.getD m a fallback =
Raw₀.Const.getD m, h.size_buckets_pos a fallback := by
simp [Raw.Const.getD, h.size_buckets_pos]
theorem Const.getD_val [BEq α] [Hashable α] {m : Raw₀ α (fun _ => β)} {a : α} {fallback : β} :
Raw.Const.getD m.val a fallback = Raw₀.Const.getD m a fallback := by
simp [Raw.Const.getD, m.2]
theorem Const.get!_eq [BEq α] [Hashable α] [Inhabited β] {m : Raw α (fun _ => β)} (h : m.WF)
{a : α} : Raw.Const.get! m a = Raw₀.Const.get! m, h.size_buckets_pos a := by
simp [Raw.Const.get!, h.size_buckets_pos]
theorem Const.get!_val [BEq α] [Hashable α] [Inhabited β] {m : Raw₀ α (fun _ => β)} {a : α} :
Raw.Const.get! m.val a = Raw₀.Const.get! m a := by
simp [Raw.Const.get!, m.2]
theorem Const.getThenInsertIfNew?_snd_eq [BEq α] [Hashable α] {m : Raw α (fun _ => β)} (h : m.WF)
{a : α} {b : β} : (Raw.Const.getThenInsertIfNew? m a b).2 =
(Raw₀.Const.getThenInsertIfNew? m, h.size_buckets_pos a b).2.1 := by
simp [Raw.Const.getThenInsertIfNew?, h.size_buckets_pos]
theorem Const.getThenInsertIfNew?_snd_val [BEq α] [Hashable α] {m : Raw₀ α (fun _ => β)} {a : α}
{b : β} : (Raw.Const.getThenInsertIfNew? m.val a b).2 =
(Raw₀.Const.getThenInsertIfNew? m a b).2.1 := by
simp [Raw.Const.getThenInsertIfNew?, m.2]
theorem Const.getThenInsertIfNew?_fst_eq [BEq α] [Hashable α] {m : Raw α (fun _ => β)} (h : m.WF)
{a : α} {b : β} : (Raw.Const.getThenInsertIfNew? m a b).1 =
(Raw₀.Const.getThenInsertIfNew? m, h.size_buckets_pos a b).1 := by
simp [Raw.Const.getThenInsertIfNew?, h.size_buckets_pos]
theorem Const.getThenInsertIfNew?_fst_val [BEq α] [Hashable α] {m : Raw₀ α (fun _ => β)} {a : α}
{b : β} : (Raw.Const.getThenInsertIfNew? m.val a b).1 =
(Raw₀.Const.getThenInsertIfNew? m a b).1 := by
simp [Raw.Const.getThenInsertIfNew?, m.2]
theorem Const.alter_eq [BEq α] [EquivBEq α] [Hashable α] {m : Raw α (fun _ => β)} (h : m.WF) {k : α} {f : Option β Option β} :
Raw.Const.alter m k f = Raw₀.Const.alter m, h.size_buckets_pos k f := by
simp [Raw.Const.alter, h.size_buckets_pos]

File diff suppressed because it is too large Load Diff

View File

@@ -1081,6 +1081,11 @@ theorem wfImp_insertMany [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α
Raw.WFImp (m.insertMany l).1.1 :=
Raw.WF.out ((m.insertMany l).2 _ Raw.WF.insert₀ (.wf m.2 h))
theorem wf_insertMany₀ [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α] {ρ : Type w}
[ForIn Id ρ ((a : α) × β a)] {m : Raw α β} {h : 0 < m.buckets.size} {l : ρ} (h' : m.WF) :
(Raw₀.insertMany m, h l).1.1.WF :=
(Raw₀.insertMany m, h l).2 _ Raw.WF.insert₀ h'
theorem toListModel_insertMany_list [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α]
{m : Raw₀ α β} {l : List ((a : α) × (β a))} (h : Raw.WFImp m.1) :
Perm (toListModel (insertMany m l).1.1.buckets)
@@ -1117,6 +1122,11 @@ theorem Const.wfImp_insertMany {β : Type v} [BEq α] [Hashable α] [EquivBEq α
{l : ρ} (h : Raw.WFImp m.1) : Raw.WFImp (Const.insertMany m l).1.1 :=
Raw.WF.out ((Const.insertMany m l).2 _ Raw.WF.insert₀ (.wf m.2 h))
theorem Const.wf_insertMany₀ {β : Type v} [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α]
{ρ : Type w} [ForIn Id ρ (α × β)] {m : Raw α (fun _ => β)} {h : 0 < m.buckets.size}
{l : ρ} (h' : m.WF) : (Const.insertMany m, h l).1.1.WF :=
(Raw₀.Const.insertMany m, h l).2 _ Raw.WF.insert₀ h'
/-! # `Const.insertListIfNewUnitₘ` -/
theorem Const.toListModel_insertListIfNewUnitₘ [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α]
@@ -1145,4 +1155,9 @@ theorem Const.wfImp_insertManyIfNewUnit [BEq α] [Hashable α] [EquivBEq α] [La
Raw.WFImp (Const.insertManyIfNewUnit m l).1.1 :=
Raw.WF.out ((Const.insertManyIfNewUnit m l).2 _ Raw.WF.insertIfNew₀ (.wf m.2 h))
theorem Const.wf_insertManyIfNewUnit₀ [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α]
{ρ : Type w} [ForIn Id ρ α] {m : Raw α (fun _ => Unit)} {h : 0 < m.buckets.size}
{l : ρ} (h' : m.WF) : (Const.insertManyIfNewUnit m, h l).1.1.WF :=
(Raw₀.Const.insertManyIfNewUnit m, h l).2 _ Raw.WF.insertIfNew₀ h'
end Raw₀

View File

@@ -1155,6 +1155,23 @@ theorem forIn_eq_forIn_toList [Monad m'] [LawfulMonad m']
ForIn.forIn m init f = ForIn.forIn m.toList init f :=
Raw₀.forIn_eq_forIn_toList m.1, m.2.size_buckets_pos
theorem foldM_eq_foldlM_keys [Monad m'] [LawfulMonad m'] {f : δ α m' δ} {init : δ} :
m.foldM (fun d a _ => f d a) init = m.keys.foldlM f init :=
Raw₀.foldM_eq_foldlM_keys m.1, m.2.size_buckets_pos
theorem fold_eq_foldl_keys {f : δ α δ} {init : δ} :
m.fold (fun d a _ => f d a) init = m.keys.foldl f init :=
Raw₀.fold_eq_foldl_keys m.1, m.2.size_buckets_pos
theorem forM_eq_forM_keys [Monad m'] [LawfulMonad m'] {f : α m' PUnit} :
ForM.forM m (fun a => f a.1) = m.keys.forM f :=
Raw₀.forM_eq_forM_keys m.1, m.2.size_buckets_pos
theorem forIn_eq_forIn_keys [Monad m'] [LawfulMonad m'] {f : α δ m' (ForInStep δ)}
{init : δ} :
ForIn.forIn m init (fun a d => f a.1 d) = ForIn.forIn m.keys init f :=
Raw₀.forIn_eq_forIn_keys m.1, m.2.size_buckets_pos
namespace Const
variable {β : Type v} {m : DHashMap α (fun _ => β)}
@@ -1168,34 +1185,62 @@ theorem fold_eq_foldl_toList {f : δ → (a : α) → β → δ} {init : δ} :
m.fold f init = (Const.toList m).foldl (fun a b => f a b.1 b.2) init :=
Raw₀.Const.fold_eq_foldl_toList m.1, m.2.size_buckets_pos
theorem forM_eq_forM_toList [Monad m'] [LawfulMonad m'] {f : (a : α) β m' PUnit} :
m.forM f = (Const.toList m).forM (fun a => f a.1 a.2) :=
theorem forM_eq_forMUncurried [Monad m'] [LawfulMonad m'] {f : α β m' PUnit} :
DHashMap.forM f m = forMUncurried (fun a => f a.1 a.2) m := rfl
theorem forMUncurried_eq_forM_toList [Monad m'] [LawfulMonad m'] {f : α × β m' PUnit} :
Const.forMUncurried f m = (Const.toList m).forM f :=
Raw₀.Const.forM_eq_forM_toList m.1, m.2.size_buckets_pos
/--
Deprecated, use `forMUncurried_eq_forM_toList` together with `forM_eq_forMUncurried` instead.
-/
@[deprecated forMUncurried_eq_forM_toList (since := "2025-03-02")]
theorem forM_eq_forM_toList [Monad m'] [LawfulMonad m'] {f : α β m' PUnit} :
DHashMap.forM f m = (Const.toList m).forM (fun a => f a.1 a.2) :=
Raw₀.Const.forM_eq_forM_toList m.1, m.2.size_buckets_pos
theorem forIn_eq_forInUncurried [Monad m'] [LawfulMonad m']
{f : α β δ m' (ForInStep δ)} {init : δ} :
DHashMap.forIn f init m = forInUncurried (fun a b => f a.1 a.2 b) init m := rfl
theorem forInUncurried_eq_forIn_toList [Monad m'] [LawfulMonad m']
{f : α × β δ m' (ForInStep δ)} {init : δ} :
Const.forInUncurried f init m = ForIn.forIn (Const.toList m) init f :=
Raw₀.Const.forIn_eq_forIn_toList m.1, m.2.size_buckets_pos
/--
Deprecated, use `forInUncurried_eq_forIn_toList` together with `forIn_eq_forInUncurried` instead.
-/
@[deprecated forInUncurried_eq_forIn_toList (since := "2025-03-02")]
theorem forIn_eq_forIn_toList [Monad m'] [LawfulMonad m']
{f : (a : α) β δ m' (ForInStep δ)} {init : δ} :
m.forIn f init = ForIn.forIn (Const.toList m) init (fun a b => f a.1 a.2 b) :=
{f : α × β δ m' (ForInStep δ)} {init : δ} :
Const.forInUncurried f init m = ForIn.forIn (Const.toList m) init f :=
Raw₀.Const.forIn_eq_forIn_toList m.1, m.2.size_buckets_pos
variable {m : DHashMap α (fun _ => Unit)}
@[deprecated DHashMap.foldM_eq_foldlM_keys (since := "2025-02-28")]
theorem foldM_eq_foldlM_keys [Monad m'] [LawfulMonad m']
{f : δ α m' δ} {init : δ} :
m.foldM (fun d a _ => f d a) init = m.keys.foldlM f init :=
Raw₀.Const.foldM_eq_foldlM_keys m.1, m.2.size_buckets_pos
Raw₀.foldM_eq_foldlM_keys m.1, m.2.size_buckets_pos
@[deprecated DHashMap.fold_eq_foldl_keys (since := "2025-02-28")]
theorem fold_eq_foldl_keys {f : δ α δ} {init : δ} :
m.fold (fun d a _ => f d a) init = m.keys.foldl f init :=
Raw₀.Const.fold_eq_foldl_keys m.1, m.2.size_buckets_pos
Raw₀.fold_eq_foldl_keys m.1, m.2.size_buckets_pos
@[deprecated DHashMap.forM_eq_forM_keys (since := "2025-02-28")]
theorem forM_eq_forM_keys [Monad m'] [LawfulMonad m'] {f : α m' PUnit} :
m.forM (fun a _ => f a) = m.keys.forM f :=
Raw₀.Const.forM_eq_forM_keys m.1, m.2.size_buckets_pos
Raw₀.forM_eq_forM_keys m.1, m.2.size_buckets_pos
@[deprecated DHashMap.forIn_eq_forIn_keys (since := "2025-02-28")]
theorem forIn_eq_forIn_keys [Monad m'] [LawfulMonad m']
{f : α δ m' (ForInStep δ)} {init : δ} :
m.forIn (fun a _ d => f a d) init = ForIn.forIn m.keys init f :=
Raw₀.Const.forIn_eq_forIn_keys m.1, m.2.size_buckets_pos
Raw₀.forIn_eq_forIn_keys m.1, m.2.size_buckets_pos
end Const

View File

@@ -373,6 +373,26 @@ instance : ForM m (Raw α β) ((a : α) × β a) where
instance : ForIn m (Raw α β) ((a : α) × β a) where
forIn m init f := m.forIn (fun a b acc => f a, b acc) init
namespace Const
variable {β : Type v}
/-!
We do not define `ForM` and `ForIn` instances that are specialized to constant `β`. Instead, we
define uncurried versions of `forM` and `forIn` that will be used in the `Const` lemmas and to
define the `ForM` and `ForIn` instances for `HashMap.Raw`.
-/
@[inline, inherit_doc forM] def forMUncurried (f : α × β m PUnit)
(b : Raw α (fun _ => β)) : m PUnit :=
b.forM fun a b => f a, b
@[inline, inherit_doc forIn] def forInUncurried
(f : α × β δ m (ForInStep δ)) (init : δ) (b : Raw α (fun _ => β)) : m δ :=
b.forIn (init := init) fun a b d => f a, b d
end Const
section Unverified
/-! We currently do not provide lemmas for the functions below. -/

View File

@@ -32,38 +32,19 @@ open Lean Elab Meta Tactic
private def baseNames : Array Name :=
#[``Raw.empty_eq, ``Raw.emptyc_eq,
``insert_eq, ``insert_val,
``insertIfNew_eq, ``insertIfNew_val,
``containsThenInsert_snd_eq, ``containsThenInsert_snd_val,
``containsThenInsertIfNew_snd_eq, ``containsThenInsertIfNew_snd_val,
``getThenInsertIfNew?_snd_eq, ``getThenInsertIfNew?_snd_val,
``map_eq, ``map_val,
``filter_eq, ``filter_val,
``erase_eq, ``erase_val,
``filterMap_eq, ``filterMap_val,
``Const.getThenInsertIfNew?_snd_eq, ``Const.getThenInsertIfNew?_snd_val,
``containsThenInsert_fst_eq, ``containsThenInsert_fst_val,
``containsThenInsertIfNew_fst_eq, ``containsThenInsertIfNew_fst_val,
``Const.get?_eq, ``Const.get?_val,
``Const.get_eq, ``Const.get_val,
``Const.getD_eq, ``Const.getD_val,
``Const.get!_eq, ``Const.get!_val,
``getThenInsertIfNew?_fst_eq, ``getThenInsertIfNew?_fst_val,
``Const.getThenInsertIfNew?_fst_eq, ``Const.getThenInsertIfNew?_fst_val,
``get?_eq, ``get?_val,
``contains_eq, ``contains_val,
``get_eq, ``get_val,
``getD_eq, ``getD_val,
``get!_eq, ``get!_val,
``getKey?_eq, ``getKey?_val,
``getKey_eq, ``getKey_val,
``getKey!_eq, ``getKey!_val,
``getKeyD_eq, ``getKeyD_val,
``insertMany_eq, ``insertMany_val,
``Const.insertMany_eq, ``Const.insertMany_val,
``Const.insertManyIfNewUnit_eq, ``Const.insertManyIfNewUnit_val,
``insert_eq, ``insertIfNew_eq, ``erase_eq, ``contains_eq,
``containsThenInsert_fst_eq, ``containsThenInsert_snd_eq,
``containsThenInsertIfNew_fst_eq, ``containsThenInsertIfNew_snd_eq,
``getThenInsertIfNew?_fst_eq, ``getThenInsertIfNew?_snd_eq,
``Const.getThenInsertIfNew?_snd_eq, ``Const.getThenInsertIfNew?_fst_eq,
``map_eq, ``filter_eq, ``filterMap_eq,
``get?_eq, ``get_eq, ``get!_eq, ``getD_eq,
``Const.get?_eq, ``Const.get_eq, ``Const.getD_eq, ``Const.get!_eq,
``getKey?_eq, ``getKey_eq, ``getKey!_eq, ``getKeyD_eq,
``insertMany_eq, ``Const.insertMany_eq, ``Const.insertManyIfNewUnit_eq,
``ofList_eq, ``Const.ofList_eq, ``Const.unitOfList_eq,
``alter_eq, ``Const.alter_eq, ``modify_eq, ``Const.modify_eq]
``alter_eq, ``Const.alter_eq, ``modify_eq, ``Const.modify_eq,
``Subtype.eta]
/-- Internal implementation detail of the hash map -/
scoped syntax "simp_to_raw" ("using" term)? : tactic
@@ -73,9 +54,9 @@ open Internal.Raw₀
macro_rules
| `(tactic| simp_to_raw $[using $using?]?) => do
`(tactic|
(try simp (discharger := wf_trivial) only [$[$(Array.map Lean.mkIdent baseNames):term],*]
(try simp (discharger := with_reducible wf_trivial) only [$[$(Array.map Lean.mkIdent baseNames):term],*]
$[apply $(using?.toArray):term];*)
<;> wf_trivial)
<;> with_reducible try wf_trivial)
end Internal.Raw
@@ -1255,6 +1236,24 @@ theorem forIn_eq_forIn_toList [Monad m'] [LawfulMonad m']
ForIn.forIn m init f = ForIn.forIn m.toList init f :=
Raw₀.forIn_eq_forIn_toList m, h.size_buckets_pos
theorem foldM_eq_foldlM_keys [Monad m'] [LawfulMonad m'] (h : m.WF)
{f : δ α m' δ} {init : δ} :
m.foldM (fun d a _ => f d a) init = m.keys.foldlM f init :=
Raw₀.foldM_eq_foldlM_keys m, h.size_buckets_pos
theorem fold_eq_foldl_keys (h : m.WF) {f : δ α δ} {init : δ} :
m.fold (fun d a _ => f d a) init = m.keys.foldl f init :=
Raw₀.fold_eq_foldl_keys m, h.size_buckets_pos
theorem forM_eq_forM_keys [Monad m'] [LawfulMonad m'] (h : m.WF) {f : α m' PUnit} :
ForM.forM m (fun a => f a.1) = m.keys.forM f :=
Raw₀.forM_eq_forM_keys m, h.size_buckets_pos
theorem forIn_eq_forIn_keys [Monad m'] [LawfulMonad m'] (h : m.WF)
{f : α δ m' (ForInStep δ)} {init : δ} :
ForIn.forIn m init (fun a d => f a.1 d) = ForIn.forIn m.keys init f :=
Raw₀.forIn_eq_forIn_keys m, h.size_buckets_pos
namespace Const
variable {β : Type v} {m : Raw α (fun _ => β)}
@@ -1268,10 +1267,39 @@ theorem fold_eq_foldl_toList (h : m.WF) {f : δ → (a : α) → β → δ} {ini
m.fold f init = (Raw.Const.toList m).foldl (fun a b => f a b.1 b.2) init :=
Raw₀.Const.fold_eq_foldl_toList m, h.size_buckets_pos
omit [BEq α] [Hashable α] in
theorem forM_eq_forMUncurried [Monad m'] [LawfulMonad m']
{f : α β m' PUnit} :
Raw.forM f m = Const.forMUncurried (fun a => f a.1 a.2) m := rfl
theorem forMUncurried_eq_forM_toList [Monad m'] [LawfulMonad m'] (h : m.WF)
{f : α × β m' PUnit} :
forMUncurried f m = (toList m).forM f :=
Raw₀.Const.forM_eq_forM_toList m, h.size_buckets_pos
/--
Deprecated, use `forMUncurried_eq_forM_toList` together with `forM_eq_forMUncurried` instead.
-/
@[deprecated forMUncurried_eq_forM_toList (since := "2025-03-02")]
theorem forM_eq_forM_toList [Monad m'] [LawfulMonad m'] (h : m.WF) {f : (a : α) β m' PUnit} :
m.forM f = (Raw.Const.toList m).forM (fun a => f a.1 a.2) :=
Raw₀.Const.forM_eq_forM_toList m, h.size_buckets_pos
omit [BEq α] [Hashable α] in
@[simp]
theorem forIn_eq_forInUncurried [Monad m'] [LawfulMonad m']
{f : α β δ m' (ForInStep δ)} {init : δ} :
forIn f init m = forInUncurried (fun a b => f a.1 a.2 b) init m := rfl
theorem forInUncurried_eq_forIn_toList [Monad m'] [LawfulMonad m'] (h : m.WF)
{f : α × β δ m' (ForInStep δ)} {init : δ} :
forInUncurried f init m = ForIn.forIn (toList m) init f :=
Raw₀.Const.forIn_eq_forIn_toList m, h.size_buckets_pos
/--
Deprecated, use `forInUncurried_eq_forIn_toList` together with `forIn_eq_forInUncurried` instead.
-/
@[deprecated forInUncurried_eq_forIn_toList (since := "2025-03-02")]
theorem forIn_eq_forIn_toList [Monad m'] [LawfulMonad m'] (h : m.WF)
{f : (a : α) β δ m' (ForInStep δ)} {init : δ} :
m.forIn f init = ForIn.forIn (Raw.Const.toList m) init (fun a b => f a.1 a.2 b) :=
@@ -1279,23 +1307,27 @@ theorem forIn_eq_forIn_toList [Monad m'] [LawfulMonad m'] (h : m.WF)
variable {m : Raw α (fun _ => Unit)}
@[deprecated Raw.foldM_eq_foldlM_keys (since := "2025-02-28")]
theorem foldM_eq_foldlM_keys [Monad m'] [LawfulMonad m'] (h : m.WF)
{f : δ α m' δ} {init : δ} :
m.foldM (fun d a _ => f d a) init = m.keys.foldlM f init :=
Raw₀.Const.foldM_eq_foldlM_keys m, h.size_buckets_pos
Raw₀.foldM_eq_foldlM_keys m, h.size_buckets_pos
@[deprecated Raw.fold_eq_foldl_keys (since := "2025-02-28")]
theorem fold_eq_foldl_keys (h : m.WF) {f : δ α δ} {init : δ} :
m.fold (fun d a _ => f d a) init = m.keys.foldl f init :=
Raw₀.Const.fold_eq_foldl_keys m, h.size_buckets_pos
Raw₀.fold_eq_foldl_keys m, h.size_buckets_pos
@[deprecated Raw.forM_eq_forM_keys (since := "2025-02-28")]
theorem forM_eq_forM_keys [Monad m'] [LawfulMonad m'] (h : m.WF) {f : α m' PUnit} :
m.forM (fun a _ => f a) = m.keys.forM f :=
Raw₀.Const.forM_eq_forM_keys m, h.size_buckets_pos
Raw₀.forM_eq_forM_keys m, h.size_buckets_pos
@[deprecated Raw.forIn_eq_forIn_keys (since := "2025-02-28")]
theorem forIn_eq_forIn_keys [Monad m'] [LawfulMonad m'] (h : m.WF)
{f : α δ m' (ForInStep δ)} {init : δ} :
m.forIn (fun a _ d => f a d) init = ForIn.forIn m.keys init f :=
Raw₀.Const.forIn_eq_forIn_keys m, h.size_buckets_pos
Raw₀.forIn_eq_forIn_keys m, h.size_buckets_pos
end Const

View File

@@ -820,7 +820,7 @@ def forM (f : (a : α) → β a → m PUnit) (t : DTreeMap α β cmp) : m PUnit
/-- Support for the `for` loop construct in `do` blocks. Iteration happens in ascending order. -/
@[inline]
def forIn (f : (a : α) β a δ m (ForInStep δ)) (init : δ) (t : DTreeMap α β cmp) : m δ :=
t.inner.forIn (fun c a b => f a b c) init
t.inner.forIn f init
instance : ForM m (DTreeMap α β cmp) ((a : α) × β a) where
forM t f := t.forM (fun a b => f a, b)
@@ -828,6 +828,26 @@ instance : ForM m (DTreeMap α β cmp) ((a : α) × β a) where
instance : ForIn m (DTreeMap α β cmp) ((a : α) × β a) where
forIn m init f := m.forIn (fun a b acc => f a, b acc) init
namespace Const
variable {β : Type v}
/-!
We do not define `ForM` and `ForIn` instances that are specialized to constant `β`. Instead, we
define uncurried versions of `forM` and `forIn` that will be used in the `Const` lemmas and to
define the `ForM` and `ForIn` instances for `DTreeMap`.
-/
@[inline, inherit_doc DTreeMap.forM]
def forMUncurried (f : α × β m PUnit) (t : DTreeMap α β cmp) : m PUnit :=
t.inner.forM fun a b => f a, b
@[inline, inherit_doc DTreeMap.forIn]
def forInUncurried (f : α × β δ m (ForInStep δ)) (init : δ) (t : DTreeMap α β cmp) : m δ :=
t.inner.forIn (fun a b acc => f a, b acc) init
end Const
/-- Check if any element satisfes the predicate, short-circuiting if a predicate fails. -/
@[inline]
def any (t : DTreeMap α β cmp) (p : (a : α) β a Bool) : Bool := Id.run $ do

View File

@@ -20,7 +20,7 @@ set_option autoImplicit false
open Std.Internal.List
open Std.Internal
universe u v
universe u v w
namespace Std.DTreeMap.Internal.Impl
@@ -60,7 +60,10 @@ private def queryNames : Array Name :=
``getD_eq_getValueCastD, ``Const.getD_eq_getValueD,
``getKey?_eq_getKey?, ``getKey_eq_getKey,
``getKey!_eq_getKey!, ``getKeyD_eq_getKeyD,
``keys_eq_keys, ``toList_eq_toListModel, ``Const.toList_eq_toListModel_map]
``keys_eq_keys, ``toList_eq_toListModel, ``Const.toList_eq_toListModel_map,
``foldlM_eq_foldlM_toListModel, ``foldl_eq_foldl,
``foldrM_eq_foldrM, ``foldr_eq_foldr,
``forM_eq_forM, ``forIn_eq_forIn_toListModel]
private def modifyMap : Std.HashMap Name Name :=
.ofList
@@ -1571,4 +1574,95 @@ theorem distinct_keys_toList [TransOrd α] (h : t.WF) :
end Const
section monadic
variable {t : Impl α β} {δ : Type w} {m : Type w Type w}
theorem foldlM_eq_foldlM_toList [Monad m] [LawfulMonad m]
{f : δ (a : α) β a m δ} {init : δ} :
t.foldlM f init = t.toList.foldlM (fun a b => f a b.1 b.2) init := by
simp_to_model
theorem foldl_eq_foldl_toList {f : δ (a : α) β a δ} {init : δ} :
t.foldl f init = t.toList.foldl (fun a b => f a b.1 b.2) init := by
simp_to_model
theorem foldrM_eq_foldrM_toList [Monad m] [LawfulMonad m]
{f : (a : α) β a δ m δ} {init : δ} :
t.foldrM f init = t.toList.foldrM (fun a b => f a.1 a.2 b) init := by
simp_to_model
theorem foldr_eq_foldr_toList {f : (a : α) β a δ δ} {init : δ} :
t.foldr f init = t.toList.foldr (fun a b => f a.1 a.2 b) init := by
simp_to_model
theorem forM_eq_forM_toList [Monad m] [LawfulMonad m] {f : (a : α) × β a m PUnit} :
t.forM (fun k v => f k, v) = ForM.forM t.toList f := by
simp_to_model using rfl
theorem forIn_eq_forIn_toList [Monad m] [LawfulMonad m]
{f : (a : α) × β a δ m (ForInStep δ)} {init : δ} :
t.forIn (fun k v => f k, v) init = ForIn.forIn t.toList init f := by
simp_to_model
theorem foldlM_eq_foldlM_keys [Monad m] [LawfulMonad m] {f : δ α m δ} {init : δ} :
t.foldlM (fun d a _ => f d a) init = t.keys.foldlM f init := by
simp_to_model using List.foldlM_eq_foldlM_keys
theorem foldl_eq_foldl_keys {f : δ α δ} {init : δ} :
t.foldl (fun d a _ => f d a) init = t.keys.foldl f init := by
simp_to_model using List.foldl_eq_foldl_keys
theorem foldrM_eq_foldrM_keys [Monad m] [LawfulMonad m] {f : α δ m δ} {init : δ} :
t.foldrM (fun a _ d => f a d) init = t.keys.foldrM f init := by
simp_to_model using List.foldrM_eq_foldrM_keys
theorem foldr_eq_foldr_keys {f : α δ δ} {init : δ} :
t.foldr (fun a _ d => f a d) init = t.keys.foldr f init := by
simp_to_model using List.foldr_eq_foldr_keys
theorem forM_eq_forM_keys [Monad m] [LawfulMonad m] {f : α m PUnit} :
t.forM (fun a _ => f a) = t.keys.forM f := by
simp_to_model using List.forM_eq_forM_keys
theorem forIn_eq_forIn_keys [Monad m] [LawfulMonad m] {f : α δ m (ForInStep δ)}
{init : δ} :
t.forIn (fun a _ d => f a d) init = ForIn.forIn t.keys init f := by
simp_to_model using List.forIn_eq_forIn_keys
namespace Const
variable {β : Type v} {t : Impl α β}
theorem foldlM_eq_foldlM_toList [Monad m] [LawfulMonad m]
{f : δ (a : α) β m δ} {init : δ} :
t.foldlM f init = (Const.toList t).foldlM (fun a b => f a b.1 b.2) init := by
simp_to_model using List.foldlM_eq_foldlM_toProd
theorem foldl_eq_foldl_toList {f : δ (a : α) β δ} {init : δ} :
t.foldl f init = (Const.toList t).foldl (fun a b => f a b.1 b.2) init := by
simp_to_model using List.foldl_eq_foldl_toProd
theorem foldrM_eq_foldrM_toList [Monad m] [LawfulMonad m]
{f : (a : α) β δ m δ} {init : δ} :
t.foldrM f init = (Const.toList t).foldrM (fun a b => f a.1 a.2 b) init := by
simp_to_model using List.foldrM_eq_foldrM_toProd
theorem foldr_eq_foldr_toList {f : (a : α) β δ δ} {init : δ} :
t.foldr f init = (Const.toList t).foldr (fun a b => f a.1 a.2 b) init := by
simp_to_model using List.foldr_eq_foldr_toProd
theorem forM_eq_forM_toList [Monad m] [LawfulMonad m] {f : (a : α) β m PUnit} :
t.forM f = (Const.toList t).forM (fun a => f a.1 a.2) := by
simp_to_model using List.forM_eq_forM_toProd
theorem forIn_eq_forIn_toList [Monad m] [LawfulMonad m]
{f : α β δ m (ForInStep δ)} {init : δ} :
t.forIn f init = ForIn.forIn (Const.toList t) init (fun a b => f a.1 a.2 b) := by
simp_to_model using List.forIn_eq_forIn_toProd
end Const
end monadic
end Std.DTreeMap.Internal.Impl

View File

@@ -217,20 +217,20 @@ def forM {m} [Monad m] (f : (a : α) → β a → m PUnit) (t : Impl α β) : m
/-- Implementation detail. -/
@[specialize]
def forInStep {m} [Monad m] (f : δ (a : α) β a m (ForInStep δ)) (init : δ) :
def forInStep {m} [Monad m] (f : (a : α) β a δ m (ForInStep δ)) (init : δ) :
Impl α β m (ForInStep δ)
| .leaf => pure (.yield init)
| .inner _ k v l r => do
match forInStep f init l with
| ForInStep.done d => return (.done d)
| ForInStep.yield d =>
match f d k v with
match f k v d with
| ForInStep.done d => return (.done d)
| ForInStep.yield d => forInStep f d r
/-- Support for the `for` construct in `do` blocks. -/
@[inline]
def forIn {m} [Monad m] (f : δ (a : α) β a m (ForInStep δ)) (init : δ) (t : Impl α β) : m δ := do
def forIn {m} [Monad m] (f : (a : α) β a δ m (ForInStep δ)) (init : δ) (t : Impl α β) : m δ := do
match forInStep f init t with
| ForInStep.done d => return d
| ForInStep.yield d => return d

View File

@@ -1110,7 +1110,7 @@ theorem ordered_mergeWith [Ord α] [TransOrd α] [LawfulEqOrd α] {t₁ t₂ : I
### foldlM
-/
theorem foldlM_eq_foldlM {t : Impl α β} {m δ} [Monad m] [LawfulMonad m]
theorem foldlM_eq_foldlM_toListModel {t : Impl α β} {m δ} [Monad m] [LawfulMonad m]
{f : δ (a : α) β a m δ} {init} :
t.foldlM (init := init) f = t.toListModel.foldlM (init := init) fun acc p => f acc p.1 p.2 := by
induction t generalizing init with
@@ -1119,13 +1119,18 @@ theorem foldlM_eq_foldlM {t : Impl α β} {m δ} [Monad m] [LawfulMonad m]
simp only [foldlM, toListModel_inner, List.foldl_append, List.foldl_cons]
simp [ihl, ihr]
theorem foldlM_toListModel_eq_foldlM {t : Impl α β} {m δ} [Monad m] [LawfulMonad m]
{f : δ ((a : α) × β a) m δ} {init} :
t.toListModel.foldlM (init := init) f = t.foldlM (init := init) fun acc k v => f acc k, v := by
rw [foldlM_eq_foldlM_toListModel]
/-!
### foldl
-/
theorem foldl_eq_foldl {t : Impl α β} {δ} {f : δ (a : α) β a δ} {init} :
t.foldl (init := init) f = t.toListModel.foldl (init := init) fun acc p => f acc p.1 p.2 := by
rw [foldl, foldlM_eq_foldlM, List.foldl_eq_foldlM, Id.run]
rw [foldl, foldlM_eq_foldlM_toListModel, List.foldl_eq_foldlM, Id.run]
/-!
### foldrM
@@ -1173,6 +1178,60 @@ theorem keys_eq_keys {t : Impl α β} :
simp [ih]
rw [List.keys.eq_def]
/-!
### forM
-/
theorem forM_eq_forM {t: Impl α β} {m : Type w Type w} [Monad m] [LawfulMonad m]
{f : (a : α) β a m PUnit} :
t.forM f = t.toListModel.forM (fun a => f a.1 a.2) := by
simp only [Impl.forM, foldlM_eq_foldlM_toListModel]
induction t.toListModel with
| nil => rfl
| cons e es ih => simp [ih]
/-!
### forIn
-/
theorem forInStep_eq_foldlM {δ : Type w} {t : Impl α β} {m : Type w Type w} [Monad m] [LawfulMonad m]
{f : (a : α) β a δ m (ForInStep δ)} {init : δ} :
t.forInStep f init = t.foldlM (init := .yield init) fun
| .yield d => fun k v => f k v d
| .done d => fun _ _ => pure (.done d) := by
induction t generalizing init with
| leaf => simp only [forInStep, foldlM]
| inner sz k v l r ihl ihr =>
simp [forInStep, foldlM, ihl, ihr]
congr; ext step
cases step
case yield =>
simp
congr; ext step
cases step
· simp
clear ihl ihr
apply Eq.symm
induction r <;> simp [foldlM, *]
· simp
case done =>
simp only [pure_bind]
clear ihl ihr
apply Eq.symm
induction r <;> simp [foldlM, *]
theorem forIn_eq_forIn_toListModel {δ : Type w} {t : Impl α β} {m : Type w Type w} [Monad m] [LawfulMonad m]
{f : (a : α) β a δ m (ForInStep δ)} {init : δ} :
t.forIn f init = ForIn.forIn t.toListModel init (fun a d => f a.1 a.2 d) := by
rw [Impl.forIn, forInStep_eq_foldlM, List.forIn_eq_foldlM, foldlM_eq_foldlM_toListModel]
induction t.toListModel with
| nil => simp
| cons e es ih =>
simp only [List.foldlM_cons, bind_assoc, map_bind, map_eq_pure_bind]
congr; ext step
congr <;> ext step' <;> cases step' <;> rfl
namespace Const
variable {β : Type v}

View File

@@ -19,7 +19,7 @@ open Std.DTreeMap.Internal
set_option linter.missingDocs true
set_option autoImplicit false
universe u v
universe u v w
namespace Std.DTreeMap
@@ -1036,4 +1036,128 @@ theorem distinct_keys_toList [TransCmp cmp] :
end Const
section monadic
variable {δ : Type w} {m : Type w Type w}
theorem foldlM_eq_foldlM_toList [Monad m] [LawfulMonad m]
{f : δ (a : α) β a m δ} {init : δ} :
t.foldlM f init = t.toList.foldlM (fun a b => f a b.1 b.2) init :=
Impl.foldlM_eq_foldlM_toList
theorem foldl_eq_foldl_toList {f : δ (a : α) β a δ} {init : δ} :
t.foldl f init = t.toList.foldl (fun a b => f a b.1 b.2) init :=
Impl.foldl_eq_foldl_toList
theorem foldrM_eq_foldrM_toList [Monad m] [LawfulMonad m] {f : (a : α) β a δ m δ} {init : δ} :
t.foldrM f init = t.toList.foldrM (fun a b => f a.1 a.2 b) init :=
Impl.foldrM_eq_foldrM_toList
theorem foldr_eq_foldr_toList {f : (a : α) β a δ δ} {init : δ} :
t.foldr f init = t.toList.foldr (fun a b => f a.1 a.2 b) init :=
Impl.foldr_eq_foldr_toList
@[simp]
theorem forM_eq_forM [Monad m] [LawfulMonad m] {f : (a : α) β a m PUnit} :
t.forM f = ForM.forM t (fun a => f a.1 a.2) := rfl
theorem forM_eq_forM_toList [Monad m] [LawfulMonad m] {f : (a : α) × β a m PUnit} :
ForM.forM t f = ForM.forM t.toList f :=
Impl.forM_eq_forM_toList
@[simp]
theorem forIn_eq_forIn [Monad m] [LawfulMonad m]
{f : (a : α) β a δ m (ForInStep δ)} {init : δ} :
t.forIn f init = ForIn.forIn t init (fun a b => f a.1 a.2 b) := rfl
theorem forIn_eq_forIn_toList [Monad m] [LawfulMonad m]
{f : (a : α) × β a δ m (ForInStep δ)} {init : δ} :
ForIn.forIn t init f = ForIn.forIn t.toList init f :=
Impl.forIn_eq_forIn_toList (f := f)
theorem foldlM_eq_foldlM_keys [Monad m] [LawfulMonad m] {f : δ α m δ} {init : δ} :
t.foldlM (fun d a _ => f d a) init = t.keys.foldlM f init :=
Impl.foldlM_eq_foldlM_keys
theorem foldl_eq_foldl_keys {f : δ α δ} {init : δ} :
t.foldl (fun d a _ => f d a) init = t.keys.foldl f init :=
Impl.foldl_eq_foldl_keys
theorem foldrM_eq_foldrM_keys [Monad m] [LawfulMonad m]
{f : α δ m δ} {init : δ} :
t.foldrM (fun a _ d => f a d) init = t.keys.foldrM f init :=
Impl.foldrM_eq_foldrM_keys
theorem foldr_eq_foldr_keys {f : α δ δ} {init : δ} :
t.foldr (fun a _ d => f a d) init = t.keys.foldr f init :=
Impl.foldr_eq_foldr_keys
theorem forM_eq_forM_keys [Monad m] [LawfulMonad m] {f : α m PUnit} :
ForM.forM t (fun a => f a.1) = t.keys.forM f :=
Impl.forM_eq_forM_keys
theorem forIn_eq_forIn_keys [Monad m] [LawfulMonad m]
{f : α δ m (ForInStep δ)} {init : δ} :
ForIn.forIn t init (fun a d => f a.1 d) = ForIn.forIn t.keys init f :=
Impl.forIn_eq_forIn_keys
namespace Const
variable {β : Type v} {t : DTreeMap α β cmp}
theorem foldlM_eq_foldlM_toList [Monad m] [LawfulMonad m]
{f : δ α β m δ} {init : δ} :
t.foldlM f init = (Const.toList t).foldlM (fun a b => f a b.1 b.2) init :=
Impl.Const.foldlM_eq_foldlM_toList
theorem foldl_eq_foldl_toList {f : δ α β δ} {init : δ} :
t.foldl f init = (Const.toList t).foldl (fun a b => f a b.1 b.2) init :=
Impl.Const.foldl_eq_foldl_toList
theorem foldrM_eq_foldrM_toList [Monad m] [LawfulMonad m]
{f : α β δ m δ} {init : δ} :
t.foldrM f init = (Const.toList t).foldrM (fun a b => f a.1 a.2 b) init :=
Impl.Const.foldrM_eq_foldrM_toList
theorem foldr_eq_foldr_toList {f : α β δ δ} {init : δ} :
t.foldr f init = (Const.toList t).foldr (fun a b => f a.1 a.2 b) init :=
Impl.Const.foldr_eq_foldr_toList
theorem forM_eq_forMUncurried [Monad m] [LawfulMonad m] {f : α β m PUnit} :
t.forM f = forMUncurried (fun a => f a.1 a.2) t := rfl
theorem forMUncurried_eq_forM_toList [Monad m] [LawfulMonad m] {f : α × β m PUnit} :
forMUncurried f t = (Const.toList t).forM f :=
Impl.Const.forM_eq_forM_toList
/--
Deprecated, use `forMUncurried_eq_forM_toList` together with `forM_eq_forMUncurried` instead.
-/
@[deprecated forMUncurried_eq_forM_toList (since := "2025-03-02")]
theorem forM_eq_forM_toList [Monad m] [LawfulMonad m] {f : α β m PUnit} :
t.forM f = (Const.toList t).forM (fun a => f a.1 a.2) :=
Impl.Const.forM_eq_forM_toList
theorem forIn_eq_forInUncurried [Monad m] [LawfulMonad m]
{f : α β δ m (ForInStep δ)} {init : δ} :
t.forIn f init = forInUncurried (fun a b => f a.1 a.2 b) init t := rfl
theorem forInUncurried_eq_forIn_toList [Monad m] [LawfulMonad m]
{f : α × β δ m (ForInStep δ)} {init : δ} :
forInUncurried f init t = ForIn.forIn (Const.toList t) init f :=
Impl.Const.forIn_eq_forIn_toList
/--
Deprecated, use `forInUncurried_eq_forIn_toList` together with `forIn_eq_forInUncurried` instead.
-/
@[deprecated forInUncurried_eq_forIn_toList (since := "2025-03-02")]
theorem forIn_eq_forIn_toList [Monad m] [LawfulMonad m]
{f : α β δ m (ForInStep δ)} {init : δ} :
t.forIn f init = ForIn.forIn (Const.toList t) init (fun a b => f a.1 a.2 b) :=
Impl.Const.forIn_eq_forIn_toList
end Const
end monadic
end Std.DTreeMap

View File

@@ -584,7 +584,7 @@ def forM (f : (a : α) → β a → m PUnit) (t : Raw α β cmp) : m PUnit :=
@[inline, inherit_doc DTreeMap.forIn]
def forIn (f : (a : α) β a δ m (ForInStep δ)) (init : δ) (t : Raw α β cmp) : m δ :=
t.inner.forIn (fun c a b => f a b c) init
t.inner.forIn f init
instance : ForM m (Raw α β cmp) ((a : α) × β a) where
forM t f := t.forM (fun a b => f a, b)
@@ -592,6 +592,26 @@ instance : ForM m (Raw α β cmp) ((a : α) × β a) where
instance : ForIn m (Raw α β cmp) ((a : α) × β a) where
forIn t init f := t.forIn (fun a b acc => f a, b acc) init
namespace Const
variable {β : Type v}
/-!
We do not define `ForM` and `ForIn` instances that are specialized to constant `β`. Instead, we
define uncurried versions of `forM` and `forIn` that will be used in the `Const` lemmas and to
define the `ForM` and `ForIn` instances for `DTreeMap.Raw`.
-/
@[inline, inherit_doc Raw.forM]
def forMUncurried (f : α × β m PUnit) (t : Raw α β cmp) : m PUnit :=
t.inner.forM fun a b => f a, b
@[inline, inherit_doc Raw.forIn]
def forInUncurried (f : α × β δ m (ForInStep δ)) (init : δ) (t : Raw α β cmp) : m δ :=
t.inner.forIn (fun a b d => f a, b d) init
end Const
@[inline, inherit_doc DTreeMap.any]
def any (t : Raw α β cmp) (p : (a : α) β a Bool) : Bool := Id.run $ do
for a, b in t do

View File

@@ -20,7 +20,7 @@ set_option autoImplicit false
open Std.DTreeMap.Internal
universe u v
universe u v w
namespace Std.DTreeMap.Raw
@@ -1045,4 +1045,124 @@ theorem distinct_keys_toList [TransCmp cmp] (h : t.WF) :
end Const
section monadic
variable {δ : Type w} {m : Type w Type w}
theorem foldlM_eq_foldlM_toList [Monad m] [LawfulMonad m] {f : δ (a : α) β a m δ} {init : δ} :
t.foldlM f init = t.toList.foldlM (fun a b => f a b.1 b.2) init :=
Impl.foldlM_eq_foldlM_toList
theorem foldl_eq_foldl_toList {f : δ (a : α) β a δ} {init : δ} :
t.foldl f init = t.toList.foldl (fun a b => f a b.1 b.2) init :=
Impl.foldl_eq_foldl_toList
theorem foldrM_eq_foldrM_toList [Monad m] [LawfulMonad m] {f : (a : α) β a δ m δ} {init : δ} :
t.foldrM f init = t.toList.foldrM (fun a b => f a.1 a.2 b) init :=
Impl.foldrM_eq_foldrM_toList
theorem foldr_eq_foldr_toList {f : (a : α) β a δ δ} {init : δ} :
t.foldr f init = t.toList.foldr (fun a b => f a.1 a.2 b) init :=
Impl.foldr_eq_foldr_toList
@[simp]
theorem forM_eq_forM [Monad m] [LawfulMonad m] {f : (a : α) β a m PUnit} :
t.forM f = ForM.forM t (fun a => f a.1 a.2) := rfl
theorem forM_eq_forM_toList [Monad m] [LawfulMonad m] {f : (a : α) × β a m PUnit} :
ForM.forM t f = ForM.forM t.toList f :=
Impl.forM_eq_forM_toList
@[simp]
theorem forIn_eq_forIn [Monad m] [LawfulMonad m]
{f : (a : α) β a δ m (ForInStep δ)} {init : δ} :
t.forIn f init = ForIn.forIn t init (fun a b => f a.1 a.2 b) := rfl
theorem forIn_eq_forIn_toList [Monad m] [LawfulMonad m]
{f : (a : α) × β a δ m (ForInStep δ)} {init : δ} :
ForIn.forIn t init f = ForIn.forIn t.toList init f :=
Impl.forIn_eq_forIn_toList (f := f)
theorem foldlM_eq_foldlM_keys [Monad m] [LawfulMonad m] {f : δ α m δ} {init : δ} :
t.foldlM (fun d a _ => f d a) init = t.keys.foldlM f init :=
Impl.foldlM_eq_foldlM_keys
theorem foldl_eq_foldl_keys {f : δ α δ} {init : δ} :
t.foldl (fun d a _ => f d a) init = t.keys.foldl f init :=
Impl.foldl_eq_foldl_keys
theorem foldrM_eq_foldrM_keys [Monad m] [LawfulMonad m] {f : α δ m δ} {init : δ} :
t.foldrM (fun a _ d => f a d) init = t.keys.foldrM f init :=
Impl.foldrM_eq_foldrM_keys
theorem foldr_eq_foldr_keys {f : α δ δ} {init : δ} :
t.foldr (fun a _ d => f a d) init = t.keys.foldr f init :=
Impl.foldr_eq_foldr_keys
theorem forM_eq_forM_keys [Monad m] [LawfulMonad m] {f : α m PUnit} :
ForM.forM t (fun a => f a.1) = t.keys.forM f :=
Impl.forM_eq_forM_keys
theorem forIn_eq_forIn_keys [Monad m] [LawfulMonad m]
{f : α δ m (ForInStep δ)} {init : δ} :
ForIn.forIn t init (fun a d => f a.1 d) = ForIn.forIn t.keys init f :=
Impl.forIn_eq_forIn_keys
namespace Const
variable {β : Type v} {t : Raw α β cmp}
theorem foldlM_eq_foldlM_toList [Monad m] [LawfulMonad m] {f : δ α β m δ} {init : δ} :
t.foldlM f init = (Const.toList t).foldlM (fun a b => f a b.1 b.2) init :=
Impl.Const.foldlM_eq_foldlM_toList
theorem foldl_eq_foldl_toList {f : δ α β δ} {init : δ} :
t.foldl f init = (Const.toList t).foldl (fun a b => f a b.1 b.2) init :=
Impl.Const.foldl_eq_foldl_toList
theorem foldrM_eq_foldrM_toList [Monad m] [LawfulMonad m] {f : α β δ m δ} {init : δ} :
t.foldrM f init = (Const.toList t).foldrM (fun a b => f a.1 a.2 b) init :=
Impl.Const.foldrM_eq_foldrM_toList
theorem foldr_eq_foldr_toList {f : α β δ δ} {init : δ} :
t.foldr f init = (Const.toList t).foldr (fun a b => f a.1 a.2 b) init :=
Impl.Const.foldr_eq_foldr_toList
theorem forM_eq_forMUncurried [Monad m] [LawfulMonad m] {f : α β m PUnit} :
t.forM f = forMUncurried (fun a => f a.1 a.2) t := rfl
theorem forMUncurried_eq_forM_toList [Monad m] [LawfulMonad m] {f : α × β m PUnit} :
forMUncurried f t = (Const.toList t).forM f :=
Impl.Const.forM_eq_forM_toList
/--
Deprecated, use `forMUncurried_eq_forM_toList` together with `forM_eq_forMUncurried` instead.
-/
@[deprecated forMUncurried_eq_forM_toList (since := "2025-03-02")]
theorem forM_eq_forM_toList [Monad m] [LawfulMonad m] {f : α β m PUnit} :
t.forM f = (Const.toList t).forM (fun a => f a.1 a.2) :=
Impl.Const.forM_eq_forM_toList
theorem forIn_eq_forInUncurried [Monad m] [LawfulMonad m]
{f : α β δ m (ForInStep δ)} {init : δ} :
t.forIn f init = forInUncurried (fun a b => f a.1 a.2 b) init t := rfl
theorem forInUncurried_eq_forIn_toList [Monad m] [LawfulMonad m]
{f : α × β δ m (ForInStep δ)} {init : δ} :
forInUncurried f init t = ForIn.forIn (Const.toList t) init f :=
Impl.Const.forIn_eq_forIn_toList
/--
Deprecated, use `forInUncurried_eq_forIn_toList` together with `forIn_eq_forInUncurried` instead.
-/
@[deprecated forInUncurried_eq_forIn_toList (since := "2025-03-02")]
theorem forIn_eq_forIn_toList [Monad m] [LawfulMonad m]
{f : α β δ m (ForInStep δ)} {init : δ} :
t.forIn f init = ForIn.forIn (Const.toList t) init (fun a b => f a.1 a.2 b) :=
Impl.Const.forIn_eq_forIn_toList
end Const
end monadic
end Std.DTreeMap.Raw

View File

@@ -822,7 +822,7 @@ theorem forM_eq_forM [Monad m'] [LawfulMonad m'] {f : (a : α) → β → m' PUn
theorem forM_eq_forM_toList [Monad m'] [LawfulMonad m'] {f : α × β m' PUnit} :
ForM.forM m f = ForM.forM m.toList f :=
DHashMap.Const.forM_eq_forM_toList
DHashMap.Const.forMUncurried_eq_forM_toList
@[simp]
theorem forIn_eq_forIn [Monad m'] [LawfulMonad m']
@@ -832,27 +832,25 @@ theorem forIn_eq_forIn [Monad m'] [LawfulMonad m']
theorem forIn_eq_forIn_toList [Monad m'] [LawfulMonad m']
{f : α × β δ m' (ForInStep δ)} {init : δ} :
ForIn.forIn m init f = ForIn.forIn m.toList init f :=
DHashMap.Const.forIn_eq_forIn_toList
variable {m : DHashMap α (fun _ => Unit)}
DHashMap.Const.forInUncurried_eq_forIn_toList
theorem foldM_eq_foldlM_keys [Monad m'] [LawfulMonad m']
{f : δ α m' δ} {init : δ} :
m.foldM (fun d a _ => f d a) init = m.keys.foldlM f init :=
DHashMap.Const.foldM_eq_foldlM_keys
DHashMap.foldM_eq_foldlM_keys
theorem fold_eq_foldl_keys {f : δ α δ} {init : δ} :
m.fold (fun d a _ => f d a) init = m.keys.foldl f init :=
DHashMap.Const.fold_eq_foldl_keys
DHashMap.fold_eq_foldl_keys
theorem forM_eq_forM_keys [Monad m'] [LawfulMonad m'] {f : α m' PUnit} :
m.forM (fun a _ => f a) = m.keys.forM f :=
DHashMap.Const.forM_eq_forM_keys
ForM.forM m (fun a => f a.1) = m.keys.forM f :=
DHashMap.forM_eq_forM_keys
theorem forIn_eq_forIn_keys [Monad m'] [LawfulMonad m']
{f : α δ m' (ForInStep δ)} {init : δ} :
m.forIn (fun a _ d => f a d) init = ForIn.forIn m.keys init f :=
DHashMap.Const.forIn_eq_forIn_keys
ForIn.forIn m init (fun a d => f a.1 d) = ForIn.forIn m.keys init f :=
DHashMap.forIn_eq_forIn_keys
end monadic

View File

@@ -835,7 +835,7 @@ theorem forM_eq_forM [Monad m'] [LawfulMonad m'] {f : (a : α) → β → m' PUn
theorem forM_eq_forM_toList [Monad m'] [LawfulMonad m'] (h : m.WF) {f : α × β m' PUnit} :
ForM.forM m f = ForM.forM m.toList f :=
DHashMap.Raw.Const.forM_eq_forM_toList h.out
DHashMap.Raw.Const.forMUncurried_eq_forM_toList h.out
omit [BEq α] [Hashable α] in
@[simp]
@@ -846,27 +846,25 @@ theorem forIn_eq_forIn [Monad m'] [LawfulMonad m']
theorem forIn_eq_forIn_toList [Monad m'] [LawfulMonad m'] (h : m.WF)
{f : α × β δ m' (ForInStep δ)} {init : δ} :
ForIn.forIn m init f = ForIn.forIn m.toList init f :=
DHashMap.Raw.Const.forIn_eq_forIn_toList h.out
variable {m : Raw α Unit}
DHashMap.Raw.Const.forInUncurried_eq_forIn_toList h.out
theorem foldM_eq_foldlM_keys [Monad m'] [LawfulMonad m'] (h : m.WF)
{f : δ α m' δ} {init : δ} :
m.foldM (fun d a _ => f d a) init = m.keys.foldlM f init :=
DHashMap.Raw.Const.foldM_eq_foldlM_keys h.out
DHashMap.Raw.foldM_eq_foldlM_keys h.out
theorem fold_eq_foldl_keys (h : m.WF) {f : δ α δ} {init : δ} :
m.fold (fun d a _ => f d a) init = m.keys.foldl f init :=
DHashMap.Raw.Const.fold_eq_foldl_keys h.out
DHashMap.Raw.fold_eq_foldl_keys h.out
theorem forM_eq_forM_keys [Monad m'] [LawfulMonad m'] (h : m.WF) {f : α m' PUnit} :
m.forM (fun a _ => f a) = m.keys.forM f :=
DHashMap.Raw.Const.forM_eq_forM_keys h.out
ForM.forM m (fun a => f a.1) = m.keys.forM f :=
DHashMap.Raw.forM_eq_forM_keys h.out
theorem forIn_eq_forIn_keys [Monad m'] [LawfulMonad m'] (h : m.WF)
{f : α δ m' (ForInStep δ)} {init : δ} :
m.forIn (fun a _ d => f a d) init = ForIn.forIn m.keys init f :=
DHashMap.Raw.Const.forIn_eq_forIn_keys h.out
ForIn.forIn m init (fun a d => f a.1 d) = ForIn.forIn m.keys init f :=
DHashMap.Raw.forIn_eq_forIn_keys h.out
end monadic

View File

@@ -442,7 +442,7 @@ theorem forM_eq_forM_toList [Monad m'] [LawfulMonad m'] {f : α → m' PUnit} :
@[simp]
theorem forIn_eq_forIn [Monad m'] [LawfulMonad m']
{f : α δ m' (ForInStep δ)} {init : δ} :
m.forIn f init = ForIn.forIn m init f := rfl
ForIn.forIn m init f = ForIn.forIn m init f := rfl
theorem forIn_eq_forIn_toList [Monad m'] [LawfulMonad m']
{f : α δ m' (ForInStep δ)} {init : δ} :

View File

@@ -2156,6 +2156,14 @@ theorem foldl_eq_foldl_toProd {β : Type v} {δ : Type w}
| cons hd tl ih => simp [ih]
theorem foldrM_eq_foldrM_toProd {β : Type v} {δ : Type w} {m' : Type w Type w} [Monad m']
[LawfulMonad m'] {l : List ((_ : α) × β)} {f : (a : α) β δ m' δ} {init : δ} :
l.foldrM (fun a b => f a.1 a.2 b) init =
(l.map fun x => (x.1, x.2)).foldrM (fun a b => f a.1 a.2 b) init := by
induction l generalizing init with
| nil => simp
| cons hd tl ih => simp [ih]
theorem foldrM_eq_foldrM_toProd' {β : Type v} {δ : Type w} {m' : Type w Type w} [Monad m']
[LawfulMonad m'] {l : List ((_ : α) × β)} {f : δ (a : α) β m' δ} {init : δ} :
l.foldrM (fun a b => f b a.1 a.2) init =
(l.map fun x => (x.1, x.2)).foldrM (fun a b => f b a.1 a.2) init := by
@@ -2164,6 +2172,14 @@ theorem foldrM_eq_foldrM_toProd {β : Type v} {δ : Type w} {m' : Type w → Typ
| cons hd tl ih => simp [ih]
theorem foldr_eq_foldr_toProd {β : Type v} {δ : Type w}
{l : List ((_ : α) × β)} {f : (a : α) β δ δ} {init : δ} :
l.foldr (fun a b => f a.1 a.2 b) init =
(l.map fun x => (x.1, x.2)).foldr (fun a b => f a.1 a.2 b) init := by
induction l generalizing init with
| nil => simp
| cons hd tl ih => simp [ih]
theorem foldr_eq_foldr_toProd' {β : Type v} {δ : Type w}
{l : List ((_ : α) × β)} {f : δ (a : α) β δ} {init : δ} :
l.foldr (fun a b => f b a.1 a.2) init =
(l.map fun x => (x.1, x.2)).foldr (fun a b => f b a.1 a.2) init := by
@@ -2187,7 +2203,7 @@ theorem forIn_eq_forIn_toProd {β : Type v} {δ : Type w} {m' : Type w → Type
| cons hd tl => simp
theorem foldlM_eq_foldlM_keys {δ : Type w} {m' : Type w Type w} [Monad m'] [LawfulMonad m']
{l : List ((_ : α) × Unit)} {f : δ α m' δ} {init : δ} :
{l : List ((a : α) × β a)} {f : δ α m' δ} {init : δ} :
l.foldlM (fun a b => f a b.1) init = (keys l).foldlM f init := by
induction l generalizing init with
| nil => simp
@@ -2197,14 +2213,22 @@ theorem foldlM_eq_foldlM_keys {δ : Type w} {m' : Type w → Type w} [Monad m']
simp [ih]
theorem foldl_eq_foldl_keys {δ : Type w}
{l : List ((_ : α) × Unit)} {f : δ α δ} {init : δ} :
{l : List ((a : α) × β a)} {f : δ α δ} {init : δ} :
l.foldl (fun a b => f a b.1) init = (keys l).foldl f init := by
induction l generalizing init with
| nil => simp
| cons hd tl ih => simp [List.foldlM_cons, keys, ih]
theorem foldrM_eq_foldrM_keys {δ : Type w} {m' : Type w Type w} [Monad m'] [LawfulMonad m']
{l : List ((_ : α) × Unit)} {f : δ α m' δ} {init : δ} :
{l : List ((a : α) × β a)} {f : α δ m' δ} {init : δ} :
l.foldrM (fun a b => f a.1 b) init = (keys l).foldrM f init := by
induction l generalizing init with
| nil => simp
| cons hd tl ih =>
simp [keys, ih]
theorem foldrM_eq_foldrM_keys' {δ : Type w} {m' : Type w Type w} [Monad m'] [LawfulMonad m']
{l : List ((a : α) × β a)} {f : δ α m' δ} {init : δ} :
l.foldrM (fun a b => f b a.1) init = (keys l).foldrM (fun a b => f b a) init := by
induction l generalizing init with
| nil => simp
@@ -2212,14 +2236,21 @@ theorem foldrM_eq_foldrM_keys {δ : Type w} {m' : Type w → Type w} [Monad m']
simp [keys, ih]
theorem foldr_eq_foldr_keys {δ : Type w}
{l : List ((_ : α) × Unit)} {f : δ α δ} {init : δ} :
{l : List ((a : α) × β a)} {f : α δ δ} {init : δ} :
l.foldr (fun a b => f a.1 b) init = (keys l).foldr f init := by
induction l generalizing init with
| nil => simp
| cons hd tl ih => simp [keys, ih]
theorem foldr_eq_foldr_keys' {δ : Type w}
{l : List ((a : α) × β a)} {f : δ α δ} {init : δ} :
l.foldr (fun a b => f b a.1) init = (keys l).foldr (fun a b => f b a) init := by
induction l generalizing init with
| nil => simp
| cons hd tl ih => simp [keys, ih]
theorem forM_eq_forM_keys {m' : Type w Type w} [Monad m'] [LawfulMonad m']
{l : List ((_ : α) × Unit)} {f : α m' PUnit} :
{l : List ((a : α) × β a)} {f : α m' PUnit} :
l.forM (fun a => f a.1) = (keys l).forM f := by
induction l with
| nil => simp
@@ -2230,7 +2261,7 @@ theorem forM_eq_forM_keys {m' : Type w → Type w} [Monad m'] [LawfulMonad m']
apply ih
theorem forIn_eq_forIn_keys {δ : Type w} {m' : Type w Type w} [Monad m'] [LawfulMonad m']
{f : α δ m' (ForInStep δ)} {init : δ} {l : List ((_ : α) × Unit)} :
{f : α δ m' (ForInStep δ)} {init : δ} {l : List ((a : α) × β a)} :
ForIn.forIn l init (fun a d => f a.fst d) = ForIn.forIn (keys l) init f := by
induction l generalizing init with
| nil => simp

View File

@@ -17,7 +17,7 @@ This file contains lemmas about `Std.Data.TreeMap`. Most of the lemmas require
set_option linter.missingDocs true
set_option autoImplicit false
universe u v
universe u v w
namespace Std.TreeMap
@@ -727,4 +727,68 @@ theorem distinct_keys_toList [TransCmp cmp] :
(toList t).Pairwise (fun a b => ¬ cmp a.1 b.1 = .eq) :=
DTreeMap.Const.distinct_keys_toList
section monadic
variable {δ : Type w} {m : Type w Type w}
theorem foldlM_eq_foldlM_toList [Monad m] [LawfulMonad m] {f : δ α β m δ} {init : δ} :
t.foldlM f init = t.toList.foldlM (fun a b => f a b.1 b.2) init :=
DTreeMap.Const.foldlM_eq_foldlM_toList
theorem foldl_eq_foldl_toList {f : δ α β δ} {init : δ} :
t.foldl f init = t.toList.foldl (fun a b => f a b.1 b.2) init :=
DTreeMap.Const.foldl_eq_foldl_toList
theorem foldrM_eq_foldrM_toList [Monad m] [LawfulMonad m] {f : α β δ m δ} {init : δ} :
t.foldrM f init = t.toList.foldrM (fun a b => f a.1 a.2 b) init :=
DTreeMap.Const.foldrM_eq_foldrM_toList
theorem foldr_eq_foldr_toList {f : α β δ δ} {init : δ} :
t.foldr f init = t.toList.foldr (fun a b => f a.1 a.2 b) init :=
DTreeMap.Const.foldr_eq_foldr_toList
@[simp]
theorem forM_eq_forM [Monad m] [LawfulMonad m] {f : α β m PUnit} :
t.forM f = ForM.forM t (fun a => f a.1 a.2) := rfl
theorem forM_eq_forM_toList [Monad m] [LawfulMonad m] {f : α × β m PUnit} :
ForM.forM t f = ForM.forM t.toList f :=
DTreeMap.Const.forMUncurried_eq_forM_toList (f := f)
@[simp]
theorem forIn_eq_forIn [Monad m] [LawfulMonad m]
{f : α β δ m (ForInStep δ)} {init : δ} :
t.forIn f init = ForIn.forIn t init (fun a d => f a.1 a.2 d) := rfl
theorem forIn_eq_forIn_toList [Monad m] [LawfulMonad m]
{f : α × β δ m (ForInStep δ)} {init : δ} :
ForIn.forIn t init f = ForIn.forIn t.toList init f :=
DTreeMap.Const.forInUncurried_eq_forIn_toList
theorem foldlM_eq_foldlM_keys [Monad m] [LawfulMonad m] {f : δ α m δ} {init : δ} :
t.foldlM (fun d a _ => f d a) init = t.keys.foldlM f init :=
DTreeMap.foldlM_eq_foldlM_keys
theorem foldl_eq_foldl_keys {f : δ α δ} {init : δ} :
t.foldl (fun d a _ => f d a) init = t.keys.foldl f init :=
DTreeMap.foldl_eq_foldl_keys
theorem foldrM_eq_foldrM_keys [Monad m] [LawfulMonad m] {f : α δ m δ} {init : δ} :
t.foldrM (fun a _ d => f a d) init = t.keys.foldrM f init :=
DTreeMap.foldrM_eq_foldrM_keys
theorem foldr_eq_foldr_keys {f : α δ δ} {init : δ} :
t.foldr (fun a _ d => f a d) init = t.keys.foldr f init :=
DTreeMap.foldr_eq_foldr_keys
theorem forM_eq_forM_keys [Monad m] [LawfulMonad m] {f : α m PUnit} :
ForM.forM t (fun a => f a.1) = t.keys.forM f :=
DTreeMap.forM_eq_forM_keys
theorem forIn_eq_forIn_keys [Monad m] [LawfulMonad m] {f : α δ m (ForInStep δ)} {init : δ} :
ForIn.forIn t init (fun a d => f a.1 d) = ForIn.forIn t.keys init f :=
DTreeMap.forIn_eq_forIn_keys
end monadic
end Std.TreeMap

View File

@@ -18,7 +18,7 @@ These proofs can be obtained from `Std.Data.TreeMap.Raw.WF`.
set_option linter.missingDocs true
set_option autoImplicit false
universe u v
universe u v w
namespace Std.TreeMap.Raw
@@ -735,4 +735,68 @@ theorem distinct_keys_toList [TransCmp cmp] (h : t.WF) :
(toList t).Pairwise (fun a b => ¬ cmp a.1 b.1 = .eq) :=
DTreeMap.Raw.Const.distinct_keys_toList h
section monadic
variable {δ : Type w} {m : Type w Type w}
theorem foldlM_eq_foldlM_toList [Monad m] [LawfulMonad m] {f : δ α β m δ} {init : δ} :
t.foldlM f init = t.toList.foldlM (fun a b => f a b.1 b.2) init :=
DTreeMap.Raw.Const.foldlM_eq_foldlM_toList
theorem foldl_eq_foldl_toList {f : δ α β δ} {init : δ} :
t.foldl f init = t.toList.foldl (fun a b => f a b.1 b.2) init :=
DTreeMap.Raw.Const.foldl_eq_foldl_toList
theorem foldrM_eq_foldrM_toList [Monad m] [LawfulMonad m] {f : α β δ m δ} {init : δ} :
t.foldrM f init = t.toList.foldrM (fun a b => f a.1 a.2 b) init :=
DTreeMap.Raw.Const.foldrM_eq_foldrM_toList
theorem foldr_eq_foldr_toList {f : α β δ δ} {init : δ} :
t.foldr f init = t.toList.foldr (fun a b => f a.1 a.2 b) init :=
DTreeMap.Raw.Const.foldr_eq_foldr_toList
@[simp]
theorem forM_eq_forM [Monad m] [LawfulMonad m] {f : α β m PUnit} :
t.forM f = ForM.forM t (fun a => f a.1 a.2) := rfl
theorem forM_eq_forM_toList [Monad m] [LawfulMonad m] {f : α × β m PUnit} :
ForM.forM t f = t.toList.forM f :=
DTreeMap.Raw.Const.forMUncurried_eq_forM_toList (f := f)
@[simp]
theorem forIn_eq_forIn [Monad m] [LawfulMonad m] {f : α β δ m (ForInStep δ)} {init : δ} :
t.forIn f init = ForIn.forIn t init (fun a d => f a.1 a.2 d) := rfl
theorem forIn_eq_forIn_toList [Monad m] [LawfulMonad m]
{f : α × β δ m (ForInStep δ)} {init : δ} :
ForIn.forIn t init f = ForIn.forIn t.toList init f :=
DTreeMap.Raw.Const.forInUncurried_eq_forIn_toList
theorem foldlM_eq_foldlM_keys [Monad m] [LawfulMonad m] {f : δ α m δ} {init : δ} :
t.foldlM (fun d a _ => f d a) init = t.keys.foldlM f init :=
DTreeMap.Raw.foldlM_eq_foldlM_keys
theorem foldl_eq_foldl_keys {f : δ α δ} {init : δ} :
t.foldl (fun d a _ => f d a) init = t.keys.foldl f init :=
DTreeMap.Raw.foldl_eq_foldl_keys
theorem foldrM_eq_foldrM_keys [Monad m] [LawfulMonad m] {f : α δ m δ} {init : δ} :
t.foldrM (fun a _ d => f a d) init = t.keys.foldrM f init :=
DTreeMap.Raw.foldrM_eq_foldrM_keys
theorem foldr_eq_foldr_keys {f : α δ δ} {init : δ} :
t.foldr (fun a _ d => f a d) init = t.keys.foldr f init :=
DTreeMap.Raw.foldr_eq_foldr_keys
theorem forM_eq_forM_keys [Monad m] [LawfulMonad m] {f : α m PUnit} :
ForM.forM t (fun a => f a.1) = t.keys.forM f :=
DTreeMap.Raw.forM_eq_forM_keys
theorem forIn_eq_forIn_keys [Monad m] [LawfulMonad m]
{f : α δ m (ForInStep δ)} {init : δ} :
ForIn.forIn t init (fun a d => f a.1 d) = ForIn.forIn t.keys init f :=
DTreeMap.Raw.forIn_eq_forIn_keys
end monadic
end Std.TreeMap.Raw

View File

@@ -17,7 +17,7 @@ This file contains lemmas about `Std.Data.TreeSet`. Most of the lemmas require
set_option linter.missingDocs true
set_option autoImplicit false
universe u v
universe u v w
namespace Std.TreeSet
@@ -358,4 +358,42 @@ theorem distinct_toList [TransCmp cmp] :
t.toList.Pairwise (fun a b => ¬ cmp a b = .eq) :=
DTreeMap.distinct_keys
section monadic
variable {δ : Type w} {m : Type w Type w}
theorem foldlM_eq_foldlM_toList [Monad m] [LawfulMonad m] {f : δ α m δ} {init : δ} :
t.foldlM f init = t.toList.foldlM f init :=
TreeMap.foldlM_eq_foldlM_keys
theorem foldl_eq_foldl_toList {f : δ α δ} {init : δ} :
t.foldl f init = t.toList.foldl f init :=
TreeMap.foldl_eq_foldl_keys
theorem foldrM_eq_foldrM_toList [Monad m] [LawfulMonad m] {f : α δ m δ} {init : δ} :
t.foldrM f init = t.toList.foldrM f init :=
TreeMap.foldrM_eq_foldrM_keys
theorem foldr_eq_foldr_toList {f : α δ δ} {init : δ} :
t.foldr f init = t.toList.foldr f init :=
TreeMap.foldr_eq_foldr_keys
@[simp]
theorem forM_eq_forM [Monad m] [LawfulMonad m] {f : α m PUnit} :
t.forM f = ForM.forM t f := rfl
theorem forM_eq_forM_toList [Monad m] [LawfulMonad m] {f : α m PUnit} :
ForM.forM t f = t.toList.forM f :=
TreeMap.forM_eq_forM_keys
@[simp]
theorem forIn_eq_forIn [Monad m] [LawfulMonad m] {f : α δ m (ForInStep δ)} {init : δ} :
t.forIn f init = ForIn.forIn t init f := rfl
theorem forIn_eq_forIn_toList [Monad m] [LawfulMonad m] {f : α δ m (ForInStep δ)} {init : δ} :
ForIn.forIn t init f = ForIn.forIn t.toList init f :=
TreeMap.forIn_eq_forIn_keys
end monadic
end Std.TreeSet

View File

@@ -18,7 +18,7 @@ These proofs can be obtained from `Std.Data.TreeSet.Raw.WF`.
set_option linter.missingDocs true
set_option autoImplicit false
universe u v
universe u v w
namespace Std.TreeSet.Raw
@@ -359,4 +359,37 @@ theorem distinct_toList [TransCmp cmp] (h : t.WF) :
t.toList.Pairwise (fun a b => ¬ cmp a b = .eq) :=
DTreeMap.Raw.distinct_keys h
section monadic
variable {δ : Type w} {m : Type w Type w}
theorem foldlM_eq_foldlM_toList [Monad m] [LawfulMonad m]
{f : δ α m δ} {init : δ} :
t.foldlM f init = t.toList.foldlM f init :=
TreeMap.Raw.foldlM_eq_foldlM_keys
theorem foldl_eq_foldl_toList {f : δ α δ} {init : δ} :
t.foldl f init = t.toList.foldl f init :=
TreeMap.Raw.foldl_eq_foldl_keys
theorem foldrM_eq_foldrM_toList [Monad m] [LawfulMonad m]
{f : α δ m δ} {init : δ} :
t.foldrM f init = t.toList.foldrM f init :=
TreeMap.Raw.foldrM_eq_foldrM_keys
theorem foldr_eq_foldr_toList {f : α δ δ} {init : δ} :
t.foldr f init = t.toList.foldr f init :=
TreeMap.Raw.foldr_eq_foldr_keys
theorem forM_eq_forM_toList [Monad m] [LawfulMonad m] {f : α m PUnit} :
ForM.forM t f = t.toList.forM f :=
TreeMap.Raw.forM_eq_forM_keys
theorem forIn_eq_forIn_toList [Monad m] [LawfulMonad m]
{f : α δ m (ForInStep δ)} {init : δ} :
ForIn.forIn t init f = ForIn.forIn t.toList init f :=
TreeMap.Raw.forIn_eq_forIn_keys
end monadic
end Std.TreeSet.Raw

View File

@@ -21,7 +21,6 @@ structure BVDecideConfig where
trimProofs : Bool := true
/--
Whether to use the binary LRAT proof format.
Currently set to false and ignored on Windows due to a bug in CaDiCal.
-/
binaryProofs : Bool := true
/--

View File

@@ -618,7 +618,6 @@ template<typename F> optional<expr> type_checker::reduce_bin_nat_pred(F const &
}
optional<expr> type_checker::reduce_nat(expr const & e) {
if (has_fvar(e)) return none_expr();
unsigned nargs = get_app_num_args(e);
if (nargs == 1) {
expr const & f = app_fn(e);

View File

@@ -163,5 +163,7 @@ clean:
.PRECIOUS: $(BC_OUT)/%.bc $(C_OUT)/%.c $(TEMP_OUT)/%.o $(TEMP_OUT)/%.o.export
ifndef C_ONLY
ifndef UNSAFE_ASSUME_OLD
include $(DEPS)
endif
endif

View File

@@ -1,7 +1,5 @@
#include "util/options.h"
// please update stage0
namespace lean {
options get_default_options() {
options opts;

Binary file not shown.

Binary file not shown.

Binary file not shown.

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