From ab90b0bc6a82a9a60d072b75231a19e3f5de73eb Mon Sep 17 00:00:00 2001
From: Michael Sammler <noreply@sammler.me>
Date: Thu, 11 Apr 2024 09:12:51 +0200
Subject: [PATCH] update file structure

---
 CHANGELOG.md                                  |   11 +-
 _CoqProject                                   |    3 +-
 stdpp_bitvector/bitvector.v                   | 1369 +----------------
 stdpp_bitvector/definitions.v                 | 1369 +++++++++++++++++
 stdpp_bitvector/tactics.v                     |    2 +-
 stdpp_unstable/bitblast.v                     |    2 +-
 ...itvector.ref => bitvector_definitions.ref} |    0
 .../{bitvector.v => bitvector_definitions.v}  |    2 +-
 8 files changed, 1381 insertions(+), 1377 deletions(-)
 create mode 100644 stdpp_bitvector/definitions.v
 rename tests/{bitvector.ref => bitvector_definitions.ref} (100%)
 rename tests/{bitvector.v => bitvector_definitions.v} (87%)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1948142c..ab928753 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -32,11 +32,12 @@ Coq 8.19 is newly supported by this version of std++.
   `{[ (x, y) : nat * nat | x = y ]}`. (by Thibaut Pérami)
 - Add `inv select` and `inversion select` tactics that allow selecting the
   to-be-inverted hypothesis with a pattern.
-- Create a `coq-stdpp-bitvector` package containing the previously
-  unstable `bitvector` library. Users of the library need to change
-  the import path from `stdpp.unstable.bitvector` to
-  `stdpp.bitvector.bitvector` and from `stdpp.unstable.bitvector_tactics` to
-  `stdpp.bitvector.tactics`.
+- The new `coq-stdpp-bitvector` package contains a library for `n`-bit
+  bitvectors (i.e., fixed-size integers with `n` bits).
+  Users of the previous unstable version need to change the import path from
+  `stdpp.unstable.bitvector` to `stdpp.bitvector.definitions` and from
+  `stdpp.unstable.bitvector_tactics` to `stdpp.bitvector.tactics`. The
+  complete library can be imported with `stdpp.bitvector.bitvector`.
 
 The following `sed` script should perform most of the renaming
 (on macOS, replace `sed` by `gsed`, installed via e.g. `brew install gnu-sed`).
diff --git a/_CoqProject b/_CoqProject
index 6b8307ce..126eeeb2 100644
--- a/_CoqProject
+++ b/_CoqProject
@@ -56,7 +56,8 @@ stdpp/telescopes.v
 stdpp/binders.v
 stdpp/ssreflect.v
 
-stdpp_bitvector/bitvector.v
+stdpp_bitvector/definitions.v
 stdpp_bitvector/tactics.v
+stdpp_bitvector/bitvector.v
 
 stdpp_unstable/bitblast.v
diff --git a/stdpp_bitvector/bitvector.v b/stdpp_bitvector/bitvector.v
index 10105bef..5a5f694b 100644
--- a/stdpp_bitvector/bitvector.v
+++ b/stdpp_bitvector/bitvector.v
@@ -1,1369 +1,2 @@
-(** This file is maintained by Michael Sammler. *)
-From stdpp Require Export numbers.
-From stdpp Require Import countable finite.
+From stdpp.bitvector Require Export definitions tactics.
 From stdpp Require Import options.
-
-(** * bitvector library *)
-(** This file provides the [bv n] type for representing [n]-bit
-integers with the standard operations. It also provides the
-[bv_saturate] tactic for learning facts about the range of bit vector
-variables in context. More extensive automation can be found in
-[bitvector_auto.v].
-
-Additionally, this file provides the [bvn] type for representing a
-bitvector of arbitrary size. *)
-
-(** * Settings *)
-Local Open Scope Z_scope.
-
-(** * Preliminary definitions *)
-Definition bv_modulus (n : N) : Z := 2 ^ (Z.of_N n).
-Definition bv_half_modulus (n : N) : Z := bv_modulus n `div` 2.
-Definition bv_wrap (n : N) (z : Z) : Z := z `mod` bv_modulus n.
-Definition bv_swrap (n : N) (z : Z) : Z := bv_wrap n (z + bv_half_modulus n) - bv_half_modulus n.
-
-Lemma bv_modulus_pos n :
-  0 < bv_modulus n.
-Proof. apply Z.pow_pos_nonneg; lia. Qed.
-Lemma bv_modulus_gt_1 n :
-  n ≠ 0%N →
-  1 < bv_modulus n.
-Proof. intros ?. apply Z.pow_gt_1; lia. Qed.
-Lemma bv_half_modulus_nonneg n :
-  0 ≤ bv_half_modulus n.
-Proof. apply Z.div_pos; [|done]. pose proof bv_modulus_pos n. lia. Qed.
-
-Lemma bv_modulus_add n1 n2 :
-  bv_modulus (n1 + n2) = bv_modulus n1 * bv_modulus n2.
-Proof. unfold bv_modulus. rewrite N2Z.inj_add. eapply Z.pow_add_r; lia. Qed.
-
-Lemma bv_half_modulus_twice n:
-  n ≠ 0%N →
-  bv_half_modulus n + bv_half_modulus n = bv_modulus n.
-Proof.
-  intros. unfold bv_half_modulus, bv_modulus.
-  rewrite Z.add_diag. symmetry. apply Z_div_exact_2; [lia|].
-  rewrite <-Z.pow_pred_r by lia. rewrite Z.mul_comm. by apply Z.mod_mul.
-Qed.
-
-Lemma bv_half_modulus_lt_modulus n:
-  bv_half_modulus n < bv_modulus n.
-Proof.
-  pose proof bv_modulus_pos n.
-  apply Z_div_lt; [done| lia].
-Qed.
-
-Lemma bv_modulus_le_mono n m:
-  (n ≤ m)%N →
-  bv_modulus n ≤ bv_modulus m.
-Proof. intros. apply Z.pow_le_mono; [done|lia]. Qed.
-Lemma bv_half_modulus_le_mono n m:
-  (n ≤ m)%N →
-  bv_half_modulus n ≤ bv_half_modulus m.
-Proof. intros. apply Z.div_le_mono; [done|]. by apply bv_modulus_le_mono. Qed.
-
-Lemma bv_modulus_0:
-  bv_modulus 0 = 1.
-Proof. done. Qed.
-Lemma bv_half_modulus_0:
-  bv_half_modulus 0 = 0.
-Proof. done. Qed.
-
-Lemma bv_half_modulus_twice_mult n:
-  bv_half_modulus n + bv_half_modulus n = (Z.of_N n `min` 1) * bv_modulus n.
-Proof. destruct (decide (n = 0%N)); subst; [ rewrite bv_half_modulus_0 | rewrite bv_half_modulus_twice]; lia. Qed.
-
-Lemma bv_wrap_in_range n z:
-  0 ≤ bv_wrap n z < bv_modulus n.
-Proof. apply Z.mod_pos_bound. apply bv_modulus_pos. Qed.
-
-Lemma bv_swrap_in_range n z:
-  n ≠ 0%N →
-  - bv_half_modulus n ≤ bv_swrap n z < bv_half_modulus n.
-Proof.
-  intros ?. unfold bv_swrap.
-  pose proof bv_half_modulus_twice n.
-  pose proof bv_wrap_in_range n (z + bv_half_modulus n).
-  lia.
-Qed.
-
-Lemma bv_wrap_small n z :
-  0 ≤ z < bv_modulus n → bv_wrap n z = z.
-Proof. intros. by apply Z.mod_small. Qed.
-
-Lemma bv_swrap_small n z :
-  - bv_half_modulus n ≤ z < bv_half_modulus n →
-  bv_swrap n z = z.
-Proof.
-  intros Hrange. unfold bv_swrap.
-  destruct (decide (n = 0%N)); subst.
-  { rewrite bv_half_modulus_0 in Hrange. lia. }
-  pose proof bv_half_modulus_twice n.
-  rewrite bv_wrap_small by lia. lia.
-Qed.
-
-Lemma bv_wrap_0 n :
-  bv_wrap n 0 = 0.
-Proof. done. Qed.
-Lemma bv_swrap_0 n :
-  bv_swrap n 0 = 0.
-Proof.
-  pose proof bv_half_modulus_lt_modulus n.
-  pose proof bv_half_modulus_nonneg n.
-  unfold bv_swrap. rewrite bv_wrap_small; lia.
-Qed.
-
-Lemma bv_wrap_idemp n b : bv_wrap n (bv_wrap n b) = bv_wrap n b.
-Proof. unfold bv_wrap. by rewrite Zmod_mod. Qed.
-
-Definition bv_wrap_factor (n : N) (x z : Z) :=
-  x = - z `div` bv_modulus n.
-
-Lemma bv_wrap_factor_intro n z :
-  ∃ x, bv_wrap_factor n x z ∧ bv_wrap n z = z + x * bv_modulus n.
-Proof.
-  eexists _. split; [done|].
-  pose proof (bv_modulus_pos n). unfold bv_wrap. rewrite Z.mod_eq; lia.
-Qed.
-
-Lemma bv_wrap_add_modulus c n z:
-  bv_wrap n (z + c * bv_modulus n) = bv_wrap n z.
-Proof. apply Z_mod_plus_full. Qed.
-Lemma bv_wrap_add_modulus_1 n z:
-  bv_wrap n (z + bv_modulus n) = bv_wrap n z.
-Proof. rewrite <-(bv_wrap_add_modulus 1 n z). f_equal. lia. Qed.
-Lemma bv_wrap_sub_modulus c n z:
-  bv_wrap n (z - c * bv_modulus n) = bv_wrap n z.
-Proof. rewrite <-(bv_wrap_add_modulus (-c) n z). f_equal. lia. Qed.
-Lemma bv_wrap_sub_modulus_1 n z:
-  bv_wrap n (z - bv_modulus n) = bv_wrap n z.
-Proof. rewrite <-(bv_wrap_add_modulus (-1) n z). done. Qed.
-
-Lemma bv_wrap_add_idemp n x y :
-  bv_wrap n (bv_wrap n x + bv_wrap n y) = bv_wrap n (x + y).
-Proof. symmetry. apply Zplus_mod. Qed.
-Lemma bv_wrap_add_idemp_l n x y :
-  bv_wrap n (bv_wrap n x + y) = bv_wrap n (x + y).
-Proof. apply Zplus_mod_idemp_l. Qed.
-Lemma bv_wrap_add_idemp_r n x y :
-  bv_wrap n (x + bv_wrap n y) = bv_wrap n (x + y).
-Proof. apply Zplus_mod_idemp_r. Qed.
-
-Lemma bv_wrap_opp_idemp n x :
-  bv_wrap n (- bv_wrap n x) = bv_wrap n (- x).
-Proof.
-  unfold bv_wrap. pose proof (bv_modulus_pos n).
-  destruct (decide (x `mod` bv_modulus n = 0)) as [Hx|Hx].
-  - rewrite !Z.mod_opp_l_z; [done |lia|done|lia|by rewrite Hx].
-  - rewrite !Z.mod_opp_l_nz, Z.mod_mod;
-      [done|lia|lia|done|lia|by rewrite Z.mod_mod by lia].
-Qed.
-
-Lemma bv_wrap_mul_idemp n x y :
-  bv_wrap n (bv_wrap n x * bv_wrap n y) = bv_wrap n (x * y).
-Proof. etrans; [| apply Zmult_mod_idemp_r]. apply Zmult_mod_idemp_l. Qed.
-Lemma bv_wrap_mul_idemp_l n x y :
-  bv_wrap n (bv_wrap n x * y) = bv_wrap n (x * y).
-Proof. apply Zmult_mod_idemp_l. Qed.
-Lemma bv_wrap_mul_idemp_r n x y :
-  bv_wrap n (x * bv_wrap n y) = bv_wrap n (x * y).
-Proof. apply Zmult_mod_idemp_r. Qed.
-
-Lemma bv_wrap_sub_idemp n x y :
-  bv_wrap n (bv_wrap n x - bv_wrap n y) = bv_wrap n (x - y).
-Proof.
-  by rewrite <-!Z.add_opp_r, <-bv_wrap_add_idemp_r,
-    bv_wrap_opp_idemp, bv_wrap_add_idemp.
- Qed.
-Lemma bv_wrap_sub_idemp_l n x y :
-  bv_wrap n (bv_wrap n x - y) = bv_wrap n (x - y).
-Proof. by rewrite <-!Z.add_opp_r, bv_wrap_add_idemp_l. Qed.
-Lemma bv_wrap_sub_idemp_r n x y :
-  bv_wrap n (x - bv_wrap n y) = bv_wrap n (x - y).
-Proof.
-  by rewrite <-!Z.add_opp_r, <-bv_wrap_add_idemp_r,
-    bv_wrap_opp_idemp, bv_wrap_add_idemp_r.
-Qed.
-
-Lemma bv_wrap_succ_idemp n x :
-  bv_wrap n (Z.succ (bv_wrap n x)) = bv_wrap n (Z.succ x).
-Proof. by rewrite <-!Z.add_1_r, bv_wrap_add_idemp_l. Qed.
-Lemma bv_wrap_pred_idemp n x :
-  bv_wrap n (Z.pred (bv_wrap n x)) = bv_wrap n (Z.pred x).
-Proof. by rewrite <-!Z.sub_1_r, bv_wrap_sub_idemp_l. Qed.
-
-Lemma bv_wrap_add_inj n x1 x2 y :
-  bv_wrap n x1 = bv_wrap n x2 ↔ bv_wrap n (x1 + y) = bv_wrap n (x2 + y).
-Proof.
-  split; intros Heq.
-  - by rewrite <-bv_wrap_add_idemp_l, Heq, bv_wrap_add_idemp_l.
-  - pose proof (bv_wrap_factor_intro n (x1 + y)) as [f1[? Hx1]].
-    pose proof (bv_wrap_factor_intro n (x2 + y)) as [f2[? Hx2]].
-    assert (x1 = x2 + f2 * bv_modulus n - f1 * bv_modulus n) as -> by lia.
-    by rewrite bv_wrap_sub_modulus, bv_wrap_add_modulus.
-Qed.
-
-Lemma bv_swrap_wrap n z:
-  bv_swrap n (bv_wrap n z) = bv_swrap n z.
-Proof. unfold bv_swrap, bv_wrap. by rewrite Zplus_mod_idemp_l. Qed.
-
-Lemma bv_wrap_bv_wrap n1 n2 bv :
-  (n1 ≤ n2)%N →
-  bv_wrap n1 (bv_wrap n2 bv) = bv_wrap n1 bv.
-Proof.
-  intros ?. unfold bv_wrap.
-  rewrite <-Znumtheory.Zmod_div_mod; [done| apply bv_modulus_pos.. |].
-  unfold bv_modulus. eexists (2 ^ (Z.of_N n2 - Z.of_N n1)).
-  rewrite <-Z.pow_add_r by lia. f_equal. lia.
-Qed.
-
-Lemma bv_wrap_land n z :
-  bv_wrap n z = Z.land z (Z.ones (Z.of_N n)).
-Proof. by rewrite Z.land_ones by lia. Qed.
-Lemma bv_wrap_spec n z i:
-  0 ≤ i →
-  Z.testbit (bv_wrap n z) i = bool_decide (i < Z.of_N n) && Z.testbit z i.
-Proof.
-  intros ?. rewrite bv_wrap_land, Z.land_spec, Z.ones_spec by lia.
-  case_bool_decide; simpl; by rewrite ?andb_true_r, ?andb_false_r.
-Qed.
-Lemma bv_wrap_spec_low n z i:
-  0 ≤ i < Z.of_N n →
-  Z.testbit (bv_wrap n z) i = Z.testbit z i.
-Proof. intros ?. rewrite bv_wrap_spec; [|lia]. case_bool_decide; [done|]. lia. Qed.
-Lemma bv_wrap_spec_high n z i:
-  Z.of_N n ≤ i →
-  Z.testbit (bv_wrap n z) i = false.
-Proof. intros ?. rewrite bv_wrap_spec; [|lia]. case_bool_decide; [|done]. lia. Qed.
-
-(** * [BvWf] *)
-(** The [BvWf] typeclass checks that the integer [z] can be
-interpreted as a [n]-bit integer. [BvWf] is a typeclass such that it
-can be automatically inferred for bitvector constants. *)
-Class BvWf (n : N) (z : Z) : Prop :=
-    bv_wf : (0 <=? z) && (z <? bv_modulus n)
-.
-Global Hint Mode BvWf + + : typeclass_instances.
-Global Instance bv_wf_pi n z : ProofIrrel (BvWf n z).
-Proof. unfold BvWf. apply _. Qed.
-Global Instance bv_wf_dec n z : Decision (BvWf n z).
-Proof. unfold BvWf. apply _. Defined.
-
-Global Typeclasses Opaque BvWf.
-
-Ltac solve_BvWf :=
-  lazymatch goal with
-    |- BvWf ?n ?v =>
-      is_closed_term n;
-      is_closed_term v;
-      try (vm_compute; exact I);
-      fail "Bitvector constant" v "does not fit into" n "bits"
-  end.
-Global Hint Extern 10 (BvWf _ _) => solve_BvWf : typeclass_instances.
-
-Lemma bv_wf_in_range n z:
-  BvWf n z ↔ 0 ≤ z < bv_modulus n.
-Proof. unfold BvWf. by rewrite andb_True, !Is_true_true, Z.leb_le, Z.ltb_lt. Qed.
-
-Lemma bv_wrap_wf n z :
-  BvWf n (bv_wrap n z).
-Proof. apply bv_wf_in_range. apply bv_wrap_in_range. Qed.
-
-Lemma bv_wf_bitwise_op {n} op bop n1 n2 :
-  (∀ k, Z.testbit (op n1 n2) k = bop (Z.testbit n1 k) (Z.testbit n2 k)) →
-  (0 ≤ n1 → 0 ≤ n2 → 0 ≤ op n1 n2) →
-  bop false false = false →
-  BvWf n n1 →
-  BvWf n n2 →
-  BvWf n (op n1 n2).
-Proof.
-  intros Hbits Hnonneg Hop [? Hok1]%bv_wf_in_range [? Hok2]%bv_wf_in_range. apply bv_wf_in_range.
-  split; [lia|].
-  apply Z.bounded_iff_bits_nonneg; [lia..|]. intros l ?.
-  eapply Z.bounded_iff_bits_nonneg in Hok1;[|try done; lia..].
-  eapply Z.bounded_iff_bits_nonneg in Hok2;[|try done; lia..].
-  by rewrite Hbits, Hok1, Hok2.
-Qed.
-
-(** * Definition of [bv n] *)
-Record bv (n : N) := BV {
-  bv_unsigned : Z;
-  bv_is_wf : BvWf n bv_unsigned;
-}.
-Global Arguments bv_unsigned {_}.
-Global Arguments bv_is_wf {_}.
-Global Arguments BV _ _ {_}.
-Add Printing Constructor bv.
-
-Global Arguments bv_unsigned : simpl never.
-
-Definition bv_signed {n} (b : bv n) := bv_swrap n (bv_unsigned b).
-
-Lemma bv_eq n (b1 b2 : bv n) :
-  b1 = b2 ↔ b1.(bv_unsigned) = b2.(bv_unsigned).
-Proof.
-  destruct b1, b2. unfold bv_unsigned. split; [ naive_solver|].
-  intros. subst. f_equal. apply proof_irrel.
-Qed.
-
-Lemma bv_neq n (b1 b2 : bv n) :
-  b1 ≠ b2 ↔ b1.(bv_unsigned) ≠ b2.(bv_unsigned).
-Proof. unfold not. by rewrite bv_eq. Qed.
-
-Global Instance bv_unsigned_inj n : Inj (=) (=) (@bv_unsigned n).
-Proof. intros ???. by apply bv_eq. Qed.
-
-Definition Z_to_bv_checked (n : N) (z : Z) : option (bv n) :=
-  H ← guard (BvWf n z); Some (@BV n z H).
-
-Program Definition Z_to_bv (n : N) (z : Z) : bv n :=
-  @BV n (bv_wrap n z) _.
-Next Obligation. apply bv_wrap_wf. Qed.
-
-Lemma Z_to_bv_unsigned n z:
-  bv_unsigned (Z_to_bv n z) = bv_wrap n z.
-Proof. done. Qed.
-
-Lemma Z_to_bv_signed n z:
-  bv_signed (Z_to_bv n z) = bv_swrap n z.
-Proof. apply bv_swrap_wrap. Qed.
-
-Lemma Z_to_bv_small n z:
-  0 ≤ z < bv_modulus n →
-  bv_unsigned (Z_to_bv n z) = z.
-Proof. rewrite Z_to_bv_unsigned. apply bv_wrap_small. Qed.
-
-Lemma bv_unsigned_BV n z Hwf:
-  bv_unsigned (@BV n z Hwf) = z.
-Proof. done. Qed.
-
-Lemma bv_signed_BV n z Hwf:
-  bv_signed (@BV n z Hwf) = bv_swrap n z.
-Proof. done. Qed.
-
-Lemma bv_unsigned_in_range n (b : bv n):
-  0 ≤ bv_unsigned b < bv_modulus n.
-Proof. apply bv_wf_in_range. apply bv_is_wf. Qed.
-
-Lemma bv_wrap_bv_unsigned n (b : bv n):
-  bv_wrap n (bv_unsigned b) = bv_unsigned b.
-Proof. rewrite bv_wrap_small; [done|apply bv_unsigned_in_range]. Qed.
-
-Lemma Z_to_bv_bv_unsigned n (b : bv n):
-  Z_to_bv n (bv_unsigned b) = b.
-Proof. apply bv_eq. by rewrite Z_to_bv_unsigned, bv_wrap_bv_unsigned. Qed.
-
-Lemma bv_eq_wrap n (b1 b2 : bv n) :
-  b1 = b2 ↔ bv_wrap n b1.(bv_unsigned) = bv_wrap n b2.(bv_unsigned).
-Proof.
-  rewrite !bv_wrap_small; [apply bv_eq | apply bv_unsigned_in_range..].
-Qed.
-
-Lemma bv_neq_wrap n (b1 b2 : bv n) :
-  b1 ≠ b2 ↔ bv_wrap n b1.(bv_unsigned) ≠ bv_wrap n b2.(bv_unsigned).
-Proof. unfold not. by rewrite bv_eq_wrap. Qed.
-
-Lemma bv_eq_signed n (b1 b2 : bv n) :
-  b1 = b2 ↔ bv_signed b1 = bv_signed b2.
-Proof.
-  split; [naive_solver |].
-  unfold bv_signed, bv_swrap. intros ?.
-  assert (bv_wrap n (bv_unsigned b1 + bv_half_modulus n)
-          = bv_wrap n (bv_unsigned b2 + bv_half_modulus n)) as ?%bv_wrap_add_inj by lia.
-  by apply bv_eq_wrap.
-Qed.
-
-Lemma bv_signed_in_range n (b : bv n):
-  n ≠ 0%N →
-  - bv_half_modulus n ≤ bv_signed b < bv_half_modulus n.
-Proof. apply bv_swrap_in_range. Qed.
-
-Lemma bv_unsigned_spec_high i n (b : bv n) :
-  Z.of_N n ≤ i →
-  Z.testbit (bv_unsigned b) i = false.
-Proof.
-  intros ?. pose proof (bv_unsigned_in_range _ b). unfold bv_modulus in *.
-  eapply Z.bounded_iff_bits_nonneg; [..|done]; lia.
-Qed.
-
-Lemma bv_unsigned_N_0 (b : bv 0):
-  bv_unsigned b = 0.
-Proof.
-  pose proof bv_unsigned_in_range 0 b as H.
-  rewrite bv_modulus_0 in H. lia.
-Qed.
-Lemma bv_signed_N_0 (b : bv 0):
-  bv_signed b = 0.
-Proof. unfold bv_signed. by rewrite bv_unsigned_N_0, bv_swrap_0. Qed.
-
-Lemma bv_swrap_bv_signed n (b : bv n):
-  bv_swrap n (bv_signed b) = bv_signed b.
-Proof.
-  destruct (decide (n = 0%N)); subst.
-  { by rewrite bv_signed_N_0, bv_swrap_0. }
-  apply bv_swrap_small. by apply bv_signed_in_range.
-Qed.
-
-Lemma Z_to_bv_checked_bv_unsigned n (b : bv n):
-  Z_to_bv_checked n (bv_unsigned b) = Some b.
-Proof.
-  unfold Z_to_bv_checked. case_guard; simplify_option_eq.
-  - f_equal. by apply bv_eq.
-  - by pose proof bv_is_wf b.
-Qed.
-Lemma Z_to_bv_checked_Some n a (b : bv n):
-  Z_to_bv_checked n a = Some b ↔ a = bv_unsigned b.
-Proof.
-  split.
-  - unfold Z_to_bv_checked. case_guard; [|done]. intros ?. by simplify_option_eq.
-  - intros ->. apply Z_to_bv_checked_bv_unsigned.
-Qed.
-
-(** * Typeclass instances for [bv n] *)
-Global Program Instance bv_eq_dec n : EqDecision (bv n) := λ '(@BV _ v1 p1) '(@BV _ v2 p2),
-   match decide (v1 = v2) with
-   | left eqv => left _
-   | right eqv => right _
-   end.
-Next Obligation.
-  (* TODO: Can we get a better proof term here? *)
-  intros n b1 v1 p1 ? b2 v2 p2 ????. subst.
-  rewrite (proof_irrel p1 p2). exact eq_refl.
-Defined.
-Next Obligation. intros. by injection. Qed.
-
-Global Instance bv_countable n : Countable (bv n) :=
-  inj_countable bv_unsigned (Z_to_bv_checked n) (Z_to_bv_checked_bv_unsigned n).
-
-Global Program Instance bv_finite n : Finite (bv n) :=
-  {| enum := Z_to_bv n <$> (seqZ 0 (bv_modulus n)) |}.
-Next Obligation.
-  intros n. apply NoDup_alt. intros i j x.
-  rewrite !list_lookup_fmap.
-  intros [? [[??]%lookup_seqZ ?]]%fmap_Some.
-  intros [? [[??]%lookup_seqZ Hz]]%fmap_Some. subst.
-  apply bv_eq in Hz. rewrite !Z_to_bv_small in Hz; lia.
-Qed.
-Next Obligation.
-  intros n x. apply elem_of_list_lookup. eexists (Z.to_nat (bv_unsigned x)).
-  rewrite list_lookup_fmap. apply fmap_Some. eexists _.
-  pose proof (bv_unsigned_in_range _ x). split.
-  - apply lookup_seqZ. split; [done|]. rewrite Z2Nat.id; lia.
-  - apply bv_eq. rewrite Z_to_bv_small; rewrite Z2Nat.id; lia.
-Qed.
-
-Lemma bv_1_ind (P : bv 1 → Prop) :
-  P (@BV 1 1 I) → P (@BV 1 0 I) → ∀ b : bv 1, P b.
-Proof.
-  intros ??. apply Forall_finite. repeat constructor.
-  - by assert ((@BV 1 0 I) = (Z_to_bv 1 (Z.of_nat 0 + 0))) as <- by by apply bv_eq.
-  - by assert ((@BV 1 1 I) = (Z_to_bv 1 (Z.of_nat 1 + 0))) as <- by by apply bv_eq.
-Qed.
-
-(** * [bv_saturate]: Add range facts about bit vectors to the context *)
-Lemma bv_unsigned_in_range_alt n (b : bv n):
-  -1 < bv_unsigned b < bv_modulus n.
-Proof. pose proof (bv_unsigned_in_range _ b). lia. Qed.
-
-Ltac bv_saturate :=
-  repeat match goal with b : bv _ |- _ => first [
-     clear b | (* Clear if unused *)
-     (* We use [bv_unsigned_in_range_alt] instead of
-     [bv_unsigned_in_range] since hypothesis of the form [0 ≤ ... < ...]
-     can cause significant slowdowns in
-     [Z.euclidean_division_equations_cleanup] due to
-     https://github.com/coq/coq/pull/17984 . *)
-     learn_hyp (bv_unsigned_in_range_alt _ b) |
-     learn_hyp (bv_signed_in_range _ b)
-  ] end.
-
-Ltac bv_saturate_unsigned :=
-  repeat match goal with b : bv _ |- _ => first [
-     clear b | (* Clear if unused *)
-     (* See comment in [bv_saturate]. *)
-     learn_hyp (bv_unsigned_in_range_alt _ b)
-  ] end.
-
-(** * Operations on [bv n] *)
-Program Definition bv_0 (n : N) :=
-  @BV n 0 _.
-Next Obligation.
-  intros n. apply bv_wf_in_range. split; [done| apply bv_modulus_pos].
-Qed.
-Global Instance bv_inhabited n : Inhabited (bv n) := populate (bv_0 n).
-
-Definition bv_succ {n} (x : bv n) : bv n :=
-  Z_to_bv n (Z.succ (bv_unsigned x)).
-Definition bv_pred {n} (x : bv n) : bv n :=
-  Z_to_bv n (Z.pred (bv_unsigned x)).
-
-Definition bv_add {n} (x y : bv n) : bv n := (* SMT: bvadd *)
-  Z_to_bv n (Z.add (bv_unsigned x) (bv_unsigned y)).
-Definition bv_sub {n} (x y : bv n) : bv n := (* SMT: bvsub *)
-  Z_to_bv n (Z.sub (bv_unsigned x) (bv_unsigned y)).
-Definition bv_opp {n} (x : bv n) : bv n := (* SMT: bvneg *)
-  Z_to_bv n (Z.opp (bv_unsigned x)).
-
-Definition bv_mul {n} (x y : bv n) : bv n := (* SMT: bvmul *)
-  Z_to_bv n (Z.mul (bv_unsigned x) (bv_unsigned y)).
-Program Definition bv_divu {n} (x y : bv n) : bv n := (* SMT: bvudiv *)
-  @BV n (Z.div (bv_unsigned x) (bv_unsigned y)) _.
-Next Obligation.
-  intros n x y. apply bv_wf_in_range. bv_saturate.
-  destruct (decide (bv_unsigned y = 0)) as [->|?].
-  { rewrite Zdiv_0_r. lia. }
-  split; [ apply Z.div_pos; lia |].
-  apply (Z.le_lt_trans _ (bv_unsigned x)); [|lia].
-  apply Z.div_le_upper_bound; [ lia|]. nia.
-Qed.
-Program Definition bv_modu {n} (x y : bv n) : bv n := (* SMT: bvurem *)
-  @BV n (Z.modulo (bv_unsigned x) (bv_unsigned y)) _.
-Next Obligation.
-  intros n x y. apply bv_wf_in_range. bv_saturate.
-  destruct (decide (bv_unsigned y = 0)) as [->|?].
-  { rewrite Zmod_0_r. lia. }
-  split; [ apply Z.mod_pos; lia |].
-  apply (Z.le_lt_trans _ (bv_unsigned x)); [|lia].
-  apply Z.mod_le; lia.
-Qed.
-Definition bv_divs {n} (x y : bv n) : bv n :=
-  Z_to_bv n (Z.div (bv_signed x) (bv_signed y)).
-Definition bv_quots {n} (x y : bv n) : bv n := (* SMT: bvsdiv *)
-  Z_to_bv n (Z.quot (bv_signed x) (bv_signed y)).
-Definition bv_mods {n} (x y : bv n) : bv n := (* SMT: bvsmod *)
-  Z_to_bv n (Z.modulo (bv_signed x) (bv_signed y)).
-Definition bv_rems {n} (x y : bv n) : bv n := (* SMT: bvsrem *)
-  Z_to_bv n (Z.rem (bv_signed x) (bv_signed y)).
-
-Definition bv_shiftl {n} (x y : bv n) : bv n := (* SMT: bvshl *)
-  Z_to_bv n (Z.shiftl (bv_unsigned x) (bv_unsigned y)).
-Program Definition bv_shiftr {n} (x y : bv n) : bv n := (* SMT: bvlshr *)
-  @BV n (Z.shiftr (bv_unsigned x) (bv_unsigned y)) _.
-Next Obligation.
-  intros n x y. apply bv_wf_in_range. bv_saturate.
-  split; [ apply Z.shiftr_nonneg; lia|].
-  rewrite Z.shiftr_div_pow2; [|lia].
-  apply (Z.le_lt_trans _ (bv_unsigned x)); [|lia].
-  pose proof (Z.pow_pos_nonneg 2 (bv_unsigned y)).
-  apply Z.div_le_upper_bound; [ lia|]. nia.
-Qed.
-Definition bv_ashiftr {n} (x y : bv n) : bv n := (* SMT: bvashr *)
-  Z_to_bv n (Z.shiftr (bv_signed x) (bv_unsigned y)).
-
-Program Definition bv_or {n} (x y : bv n) : bv n := (* SMT: bvor *)
-  @BV n (Z.lor (bv_unsigned x) (bv_unsigned y)) _.
-Next Obligation.
-  intros. eapply bv_wf_bitwise_op; [ apply Z.lor_spec |
-    by intros; eapply Z.lor_nonneg | done | apply bv_is_wf..].
-Qed.
-Program Definition bv_and {n} (x y : bv n) : bv n := (* SMT: bvand *)
-  @BV n (Z.land (bv_unsigned x) (bv_unsigned y)) _.
-Next Obligation.
-  intros. eapply bv_wf_bitwise_op; [ apply Z.land_spec |
-    intros; eapply Z.land_nonneg; by left | done | apply bv_is_wf..].
-Qed.
-Program Definition bv_xor {n} (x y : bv n) : bv n := (* SMT: bvxor *)
-  @BV n (Z.lxor (bv_unsigned x) (bv_unsigned y)) _.
-Next Obligation.
-  intros. eapply bv_wf_bitwise_op; [ apply Z.lxor_spec |
-    intros; eapply Z.lxor_nonneg; naive_solver | done | apply bv_is_wf..].
-Qed.
-Program Definition bv_not {n} (x : bv n) : bv n := (* SMT: bvnot *)
-  Z_to_bv n (Z.lnot (bv_unsigned x)).
-
-(* [bv_zero_extends z b] extends [b] to [z] bits with 0. If [z] is
-smaller than [n], [b] is truncated. Note that [z] gives the resulting
-size instead of the number of bits to add (as SMTLIB does) to avoid a
-type-level [_ + _] *)
-Program Definition bv_zero_extend {n} (z : N) (b : bv n) : bv z := (* SMT: zero_extend *)
-  Z_to_bv z (bv_unsigned b).
-
-Program Definition bv_sign_extend {n} (z : N) (b : bv n) : bv z := (* SMT: sign_extend *)
-  Z_to_bv z (bv_signed b).
-
-(* s is start index and l is length. Note that this is different from
-extract in SMTLIB which uses [extract (inclusive upper bound)
-(inclusive lower bound)]. The version here is phrased in a way that
-makes it impossible to use an upper bound that is lower than the lower
-bound. *)
-Definition bv_extract {n} (s l : N) (b : bv n) : bv l :=
-  Z_to_bv l (bv_unsigned b ≫ Z.of_N s).
-
-(* Note that we should always have n1 + n2 = n, but we use a parameter to avoid a type-level (_ + _) *)
-Program Definition bv_concat n {n1 n2} (b1 : bv n1) (b2 : bv n2) : bv n := (* SMT: concat *)
-  Z_to_bv n (Z.lor (bv_unsigned b1 ≪ Z.of_N n2) (bv_unsigned b2)).
-
-Definition bv_to_little_endian m n (z : Z) : list (bv n) :=
-  (λ b, Z_to_bv n b) <$> Z_to_little_endian m (Z.of_N n) z.
-
-Definition little_endian_to_bv n (bs : list (bv n)) : Z :=
-  little_endian_to_Z (Z.of_N n) (bv_unsigned <$> bs).
-
-(** * Operations on [bv n] and Z *)
-Definition bv_add_Z {n} (x : bv n) (y : Z) : bv n :=
-  Z_to_bv n (Z.add (bv_unsigned x) y).
-Definition bv_sub_Z {n} (x : bv n) (y : Z) : bv n :=
-  Z_to_bv n (Z.sub (bv_unsigned x) y).
-Definition bv_mul_Z {n} (x : bv n) (y : Z) : bv n :=
-  Z_to_bv n (Z.mul (bv_unsigned x) y).
-
-Definition bv_seq {n} (x : bv n) (len : Z) : list (bv n) :=
-  (bv_add_Z x) <$> seqZ 0 len.
-
-(** * Operations on [bv n] and bool *)
-Definition bool_to_bv (n : N) (b : bool) : bv n :=
-  Z_to_bv n (bool_to_Z b).
-
-Definition bv_to_bits {n} (b : bv n) : list bool :=
-  (λ i, Z.testbit (bv_unsigned b) i) <$> seqZ 0 (Z.of_N n).
-
-(** * Notation for [bv] operations *)
-Declare Scope bv_scope.
-Delimit Scope bv_scope with bv.
-Bind Scope bv_scope with bv.
-
-Infix "+" := bv_add : bv_scope.
-Infix "-" := bv_sub : bv_scope.
-Notation "- x" := (bv_opp x) : bv_scope.
-Infix "*" := bv_mul : bv_scope.
-Infix "`divu`" := bv_divu (at level 35) : bv_scope.
-Infix "`modu`" := bv_modu (at level 35) : bv_scope.
-Infix "`divs`" := bv_divs (at level 35) : bv_scope.
-Infix "`quots`" := bv_quots (at level 35) : bv_scope.
-Infix "`mods`" := bv_mods (at level 35) : bv_scope.
-Infix "`rems`" := bv_rems (at level 35) : bv_scope.
-Infix "≪" := bv_shiftl : bv_scope.
-Infix "≫" := bv_shiftr : bv_scope.
-Infix "`ashiftr`" := bv_ashiftr (at level 35) : bv_scope.
-
-Infix "`+Z`" := bv_add_Z (at level 50) : bv_scope.
-Infix "`-Z`" := bv_sub_Z (at level 50) : bv_scope.
-Infix "`*Z`" := bv_mul_Z (at level 40) : bv_scope.
-
-(** This adds number notations into [bv_scope].
-If the number literal is positive or 0, it gets expanded to [BV _ {num} _].
-If the number literal is negative, it gets expanded as [Z_to_bv _ {num}].
-In the negative case, the notation is parsing only and the [Z_to_bv] call will be
-printed explicitly. *)
-Inductive bv_number_notation := BVNumNonNeg (z : Z) | BVNumNeg (z : Z).
-Definition bv_number_notation_to_Z (n : bv_number_notation) : option Z :=
-  match n with
-  | BVNumNonNeg z => Some z
-  (** Don't use the notation for negative numbers for printing. *)
-  | BVNumNeg z => None
-  end.
-Definition Z_to_bv_number_notation (z : Z) :=
-  match z with
-  | Zneg _ => BVNumNeg z
-  | _ => BVNumNonNeg z
-  end.
-
-(** We need to temporarily change the implicit arguments of BV and
-Z_to_bv such that we can pass them to [Number Notation]. *)
-Local Arguments Z_to_bv {_} _.
-Local Arguments BV {_} _ {_}.
-Number Notation bv Z_to_bv_number_notation bv_number_notation_to_Z
-  (via bv_number_notation mapping [[BV] => BVNumNonNeg, [Z_to_bv] => BVNumNeg]) : bv_scope.
-Local Arguments BV _ _ {_}.
-Local Arguments Z_to_bv : clear implicits.
-
-(** * [bv_wrap_simplify]: typeclass-based automation for simplifying [bv_wrap] *)
-(** The [bv_wrap_simplify] tactic removes [bv_wrap] where possible by
-using the fact that [bv_wrap n (bv_warp n z) = bv_wrap n z]. The main
-use case for this tactic is for proving the lemmas about the
-operations of [bv n] below. Users should use the more extensive
-automation provided by [bitvector_auto.v]. *)
-Create HintDb bv_wrap_simplify_db discriminated.
-Global Hint Constants Opaque : bv_wrap_simplify_db.
-Global Hint Variables Opaque : bv_wrap_simplify_db.
-
-Class BvWrapSimplify (n : N) (z z' : Z) := {
-  bv_wrap_simplify_proof : bv_wrap n z = bv_wrap n z';
-}.
-Global Arguments bv_wrap_simplify_proof _ _ _ {_}.
-Global Hint Mode BvWrapSimplify + + - : bv_wrap_simplify_db.
-
-(** Default instance to end search. *)
-Lemma bv_wrap_simplify_id n z :
-  BvWrapSimplify n z z.
-Proof. by constructor. Qed.
-Global Hint Resolve bv_wrap_simplify_id | 1000 : bv_wrap_simplify_db.
-
-(** [bv_wrap_simplify_bv_wrap] performs the actual simplification. *)
-Lemma bv_wrap_simplify_bv_wrap n z z' :
-  BvWrapSimplify n z z' →
-  BvWrapSimplify n (bv_wrap n z) z'.
-Proof. intros [->]. constructor. by rewrite bv_wrap_bv_wrap. Qed.
-Global Hint Resolve bv_wrap_simplify_bv_wrap | 10 : bv_wrap_simplify_db.
-
-(** The rest of the instances propagate [BvWrapSimplify].  *)
-Lemma bv_wrap_simplify_succ n z z' :
-  BvWrapSimplify n z z' →
-  BvWrapSimplify n (Z.succ z) (Z.succ z').
-Proof.
-  intros [Hz]. constructor. by rewrite <-bv_wrap_succ_idemp, Hz, bv_wrap_succ_idemp.
-Qed.
-Global Hint Resolve bv_wrap_simplify_succ | 10 : bv_wrap_simplify_db.
-
-Lemma bv_wrap_simplify_pred n z z' :
-  BvWrapSimplify n z z' →
-  BvWrapSimplify n (Z.pred z) (Z.pred z').
-Proof.
-  intros [Hz]. constructor. by rewrite <-bv_wrap_pred_idemp, Hz, bv_wrap_pred_idemp.
-Qed.
-Global Hint Resolve bv_wrap_simplify_pred | 10 : bv_wrap_simplify_db.
-
-Lemma bv_wrap_simplify_opp n z z' :
-  BvWrapSimplify n z z' →
-  BvWrapSimplify n (- z) (- z').
-Proof.
-  intros [Hz]. constructor. by rewrite <-bv_wrap_opp_idemp, Hz, bv_wrap_opp_idemp.
-Qed.
-Global Hint Resolve bv_wrap_simplify_opp | 10 : bv_wrap_simplify_db.
-
-Lemma bv_wrap_simplify_add n z1 z1' z2 z2' :
-  BvWrapSimplify n z1 z1' →
-  BvWrapSimplify n z2 z2' →
-  BvWrapSimplify n (z1 + z2) (z1' + z2').
-Proof.
-  intros [Hz1] [Hz2]. constructor.
-  by rewrite <-bv_wrap_add_idemp, Hz1, Hz2, bv_wrap_add_idemp.
-Qed.
-Global Hint Resolve bv_wrap_simplify_add | 10 : bv_wrap_simplify_db.
-
-Lemma bv_wrap_simplify_sub n z1 z1' z2 z2' :
-  BvWrapSimplify n z1 z1' →
-  BvWrapSimplify n z2 z2' →
-  BvWrapSimplify n (z1 - z2) (z1' - z2').
-Proof.
-  intros [Hz1] [Hz2]. constructor.
-  by rewrite <-bv_wrap_sub_idemp, Hz1, Hz2, bv_wrap_sub_idemp.
-Qed.
-Global Hint Resolve bv_wrap_simplify_sub | 10 : bv_wrap_simplify_db.
-
-Lemma bv_wrap_simplify_mul n z1 z1' z2 z2' :
-  BvWrapSimplify n z1 z1' →
-  BvWrapSimplify n z2 z2' →
-  BvWrapSimplify n (z1 * z2) (z1' * z2').
-Proof.
-  intros [Hz1] [Hz2]. constructor.
-  by rewrite <-bv_wrap_mul_idemp, Hz1, Hz2, bv_wrap_mul_idemp.
-Qed.
-Global Hint Resolve bv_wrap_simplify_mul | 10 : bv_wrap_simplify_db.
-
-(** [bv_wrap_simplify_left] applies for goals of the form [bv_wrap n z1 = _] and
- tries to simplify them by removing any [bv_wrap] inside z1. *)
-Ltac bv_wrap_simplify_left :=
-  lazymatch goal with |- bv_wrap _ _ = _ => idtac end;
-  etrans; [ notypeclasses refine (bv_wrap_simplify_proof _ _ _);
-            typeclasses eauto with bv_wrap_simplify_db | ]
-.
-
-(** [bv_wrap_simplify] applies for goals of the form [bv_wrap n z1 = bv_wrap n z2] and
-[bv_swrap n z1 = bv_swrap n z2] and tries to simplify them by removing any [bv_wrap]
-and [bv_swrap] inside z1 and z2. *)
-Ltac bv_wrap_simplify :=
-  unfold bv_signed, bv_swrap;
-  try match goal with | |- _ - _ = _ - _ => f_equal end;
-  bv_wrap_simplify_left;
-  symmetry;
-  bv_wrap_simplify_left;
-  symmetry.
-
-Ltac bv_wrap_simplify_solve :=
-  bv_wrap_simplify; f_equal; lia.
-
-
-(** * Lemmas about [bv n] operations *)
-
-(** ** Unfolding lemmas for the operations. *)
-Section unfolding.
-  Context {n : N}.
-  Implicit Types (b : bv n).
-
-  Lemma bv_0_unsigned :
-    bv_unsigned (bv_0 n) = 0.
-  Proof. done. Qed.
-  Lemma bv_0_signed :
-    bv_signed (bv_0 n) = 0.
-  Proof. unfold bv_0. by rewrite bv_signed_BV, bv_swrap_0. Qed.
-
-  Lemma bv_succ_unsigned b :
-    bv_unsigned (bv_succ b) = bv_wrap n (Z.succ (bv_unsigned b)).
-  Proof. done. Qed.
-  Lemma bv_succ_signed b :
-    bv_signed (bv_succ b) = bv_swrap n (Z.succ (bv_signed b)).
-  Proof. unfold bv_succ. rewrite Z_to_bv_signed. bv_wrap_simplify_solve. Qed.
-
-  Lemma bv_pred_unsigned b :
-    bv_unsigned (bv_pred b) = bv_wrap n (Z.pred (bv_unsigned b)).
-  Proof. done. Qed.
-  Lemma bv_pred_signed b :
-    bv_signed (bv_pred b) = bv_swrap n (Z.pred (bv_signed b)).
-  Proof. unfold bv_pred. rewrite Z_to_bv_signed. bv_wrap_simplify_solve. Qed.
-
-  Lemma bv_add_unsigned b1 b2 :
-    bv_unsigned (b1 + b2) = bv_wrap n (bv_unsigned b1 + bv_unsigned b2).
-  Proof. done. Qed.
-  Lemma bv_add_signed b1 b2 :
-    bv_signed (b1 + b2) = bv_swrap n (bv_signed b1 + bv_signed b2).
-  Proof. unfold bv_add. rewrite Z_to_bv_signed. bv_wrap_simplify_solve. Qed.
-
-  Lemma bv_sub_unsigned b1 b2 :
-    bv_unsigned (b1 - b2) = bv_wrap n (bv_unsigned b1 - bv_unsigned b2).
-  Proof. done. Qed.
-  Lemma bv_sub_signed b1 b2 :
-    bv_signed (b1 - b2) = bv_swrap n (bv_signed b1 - bv_signed b2).
-  Proof. unfold bv_sub. rewrite Z_to_bv_signed. bv_wrap_simplify_solve. Qed.
-
-  Lemma bv_opp_unsigned b :
-    bv_unsigned (- b) = bv_wrap n (- bv_unsigned b).
-  Proof. done. Qed.
-  Lemma bv_opp_signed b :
-    bv_signed (- b) = bv_swrap n (- bv_signed b).
-  Proof. unfold bv_opp. rewrite Z_to_bv_signed. bv_wrap_simplify_solve. Qed.
-
-  Lemma bv_mul_unsigned b1 b2 :
-    bv_unsigned (b1 * b2) = bv_wrap n (bv_unsigned b1 * bv_unsigned b2).
-  Proof. done. Qed.
-  Lemma bv_mul_signed b1 b2 :
-    bv_signed (b1 * b2) = bv_swrap n (bv_signed b1 * bv_signed b2).
-  Proof. unfold bv_mul. rewrite Z_to_bv_signed. bv_wrap_simplify_solve. Qed.
-
-  Lemma bv_divu_unsigned b1 b2 :
-    bv_unsigned (b1 `divu` b2) = bv_unsigned b1 `div` bv_unsigned b2.
-  Proof. done. Qed.
-  Lemma bv_divu_signed b1 b2 :
-    bv_signed (b1 `divu` b2) = bv_swrap n (bv_unsigned b1 `div` bv_unsigned b2).
-  Proof. done. Qed.
-
-  Lemma bv_modu_unsigned b1 b2 :
-    bv_unsigned (b1 `modu` b2) = bv_unsigned b1 `mod` bv_unsigned b2.
-  Proof. done. Qed.
-  Lemma bv_modu_signed b1 b2 :
-    bv_signed (b1 `modu` b2) = bv_swrap n (bv_unsigned b1 `mod` bv_unsigned b2).
-  Proof. done. Qed.
-
-  Lemma bv_divs_unsigned b1 b2 :
-    bv_unsigned (b1 `divs` b2) = bv_wrap n (bv_signed b1 `div` bv_signed b2).
-  Proof. done. Qed.
-  Lemma bv_divs_signed b1 b2 :
-    bv_signed (b1 `divs` b2) = bv_swrap n (bv_signed b1 `div` bv_signed b2).
-  Proof. unfold bv_divs. rewrite Z_to_bv_signed. done. Qed.
-
-  Lemma bv_quots_unsigned b1 b2 :
-    bv_unsigned (b1 `quots` b2) = bv_wrap n (bv_signed b1 `quot` bv_signed b2).
-  Proof. done. Qed.
-  Lemma bv_quots_signed b1 b2 :
-    bv_signed (b1 `quots` b2) = bv_swrap n (bv_signed b1 `quot` bv_signed b2).
-  Proof. unfold bv_quots. rewrite Z_to_bv_signed. done. Qed.
-
-  Lemma bv_mods_unsigned b1 b2 :
-    bv_unsigned (b1 `mods` b2) = bv_wrap n (bv_signed b1 `mod` bv_signed b2).
-  Proof. done. Qed.
-  Lemma bv_mods_signed b1 b2 :
-    bv_signed (b1 `mods` b2) = bv_swrap n (bv_signed b1 `mod` bv_signed b2).
-  Proof. unfold bv_mods. rewrite Z_to_bv_signed. done. Qed.
-
-  Lemma bv_rems_unsigned b1 b2 :
-    bv_unsigned (b1 `rems` b2) = bv_wrap n (bv_signed b1 `rem` bv_signed b2).
-  Proof. done. Qed.
-  Lemma bv_rems_signed b1 b2 :
-    bv_signed (b1 `rems` b2) = bv_swrap n (bv_signed b1 `rem` bv_signed b2).
-  Proof. unfold bv_rems. rewrite Z_to_bv_signed. done. Qed.
-
-  Lemma bv_shiftl_unsigned b1 b2 :
-    bv_unsigned (b1 ≪ b2) = bv_wrap n (bv_unsigned b1 ≪ bv_unsigned b2).
-  Proof. done. Qed.
-  Lemma bv_shiftl_signed b1 b2 :
-    bv_signed (b1 ≪ b2) = bv_swrap n (bv_unsigned b1 ≪ bv_unsigned b2).
-  Proof. unfold bv_shiftl. rewrite Z_to_bv_signed. done. Qed.
-
-  Lemma bv_shiftr_unsigned b1 b2 :
-    bv_unsigned (b1 ≫ b2) = bv_unsigned b1 ≫ bv_unsigned b2.
-  Proof. done. Qed.
-  Lemma bv_shiftr_signed b1 b2 :
-    bv_signed (b1 ≫ b2) = bv_swrap n (bv_unsigned b1 ≫ bv_unsigned b2).
-  Proof. done. Qed.
-
-  Lemma bv_ashiftr_unsigned b1 b2 :
-    bv_unsigned (b1 `ashiftr` b2) = bv_wrap n (bv_signed b1 ≫ bv_unsigned b2).
-  Proof. done. Qed.
-  Lemma bv_ashiftr_signed b1 b2 :
-    bv_signed (b1 `ashiftr` b2) = bv_swrap n (bv_signed b1 ≫ bv_unsigned b2).
-  Proof. unfold bv_ashiftr. rewrite Z_to_bv_signed. done. Qed.
-
-  Lemma bv_or_unsigned b1 b2 :
-    bv_unsigned (bv_or b1 b2) = Z.lor (bv_unsigned b1) (bv_unsigned b2).
-  Proof. done. Qed.
-  Lemma bv_or_signed b1 b2 :
-    bv_signed (bv_or b1 b2) = bv_swrap n (Z.lor (bv_unsigned b1) (bv_unsigned b2)).
-  Proof. done. Qed.
-
-  Lemma bv_and_unsigned b1 b2 :
-    bv_unsigned (bv_and b1 b2) = Z.land (bv_unsigned b1) (bv_unsigned b2).
-  Proof. done. Qed.
-  Lemma bv_and_signed b1 b2 :
-    bv_signed (bv_and b1 b2) = bv_swrap n (Z.land (bv_unsigned b1) (bv_unsigned b2)).
-  Proof. done. Qed.
-
-  Lemma bv_xor_unsigned b1 b2 :
-    bv_unsigned (bv_xor b1 b2) = Z.lxor (bv_unsigned b1) (bv_unsigned b2).
-  Proof. done. Qed.
-  Lemma bv_xor_signed b1 b2 :
-    bv_signed (bv_xor b1 b2) = bv_swrap n (Z.lxor (bv_unsigned b1) (bv_unsigned b2)).
-  Proof. done. Qed.
-
-  Lemma bv_not_unsigned b :
-    bv_unsigned (bv_not b) = bv_wrap n (Z.lnot (bv_unsigned b)).
-  Proof. done. Qed.
-  Lemma bv_not_signed b :
-    bv_signed (bv_not b) = bv_swrap n (Z.lnot (bv_unsigned b)).
-  Proof. unfold bv_not. rewrite Z_to_bv_signed. done. Qed.
-
-  Lemma bv_zero_extend_unsigned' z b :
-    bv_unsigned (bv_zero_extend z b) = bv_wrap z (bv_unsigned b).
-  Proof. done. Qed.
-  (* [bv_zero_extend_unsigned] is the version that we want, but it
-  only holds with a precondition. *)
-  Lemma bv_zero_extend_unsigned z b :
-    (n ≤ z)%N →
-    bv_unsigned (bv_zero_extend z b) = bv_unsigned b.
-  Proof.
-    intros ?. rewrite bv_zero_extend_unsigned', bv_wrap_small; [done|].
-    bv_saturate. pose proof (bv_modulus_le_mono n z). lia.
-  Qed.
-  Lemma bv_zero_extend_signed z b :
-    bv_signed (bv_zero_extend z b) = bv_swrap z (bv_unsigned b).
-  Proof. unfold bv_zero_extend. rewrite Z_to_bv_signed. done. Qed.
-
-  Lemma bv_sign_extend_unsigned z b :
-    bv_unsigned (bv_sign_extend z b) = bv_wrap z (bv_signed b).
-  Proof. done. Qed.
-  Lemma bv_sign_extend_signed' z b :
-    bv_signed (bv_sign_extend z b) = bv_swrap z (bv_signed b).
-  Proof. unfold bv_sign_extend. rewrite Z_to_bv_signed. done. Qed.
-  (* [bv_sign_extend_signed] is the version that we want, but it
-  only holds with a precondition. *)
-  Lemma bv_sign_extend_signed z b :
-    (n ≤ z)%N →
-    bv_signed (bv_sign_extend z b) = bv_signed b.
-  Proof.
-    intros ?. rewrite bv_sign_extend_signed'.
-    destruct (decide (n = 0%N)); subst.
-    { by rewrite bv_signed_N_0, bv_swrap_0. }
-    apply bv_swrap_small. bv_saturate.
-    pose proof bv_half_modulus_le_mono n z. lia.
-  Qed.
-
-  Lemma bv_extract_unsigned s l b :
-    bv_unsigned (bv_extract s l b) = bv_wrap l (bv_unsigned b ≫ Z.of_N s).
-  Proof. done. Qed.
-  Lemma bv_extract_signed s l b :
-    bv_signed (bv_extract s l b) = bv_swrap l (bv_unsigned b ≫ Z.of_N s).
-  Proof. unfold bv_extract. rewrite Z_to_bv_signed. done. Qed.
-
-  Lemma bv_concat_unsigned' m  n2 b1 (b2 : bv n2) :
-    bv_unsigned (bv_concat m b1 b2) = bv_wrap m (Z.lor (bv_unsigned b1 ≪ Z.of_N n2) (bv_unsigned b2)).
-  Proof. done. Qed.
-  (* [bv_concat_unsigned] is the version that we want, but it
-  only holds with a precondition. *)
-  Lemma bv_concat_unsigned m n2 b1 (b2 : bv n2) :
-    (m = n + n2)%N →
-    bv_unsigned (bv_concat m b1 b2) = Z.lor (bv_unsigned b1 ≪ Z.of_N n2) (bv_unsigned b2).
-  Proof.
-    intros ->. rewrite bv_concat_unsigned', bv_wrap_small; [done|].
-    apply Z.bounded_iff_bits_nonneg'; [lia | |].
-    { apply Z.lor_nonneg. bv_saturate. split; [|lia]. apply Z.shiftl_nonneg. lia. }
-    intros k ?. rewrite Z.lor_spec, Z.shiftl_spec; [|lia].
-    apply orb_false_intro; (eapply Z.bounded_iff_bits_nonneg; [..|done]); bv_saturate; try lia.
-    - apply (Z.lt_le_trans _ (bv_modulus n)); [lia|]. apply Z.pow_le_mono_r; lia.
-    - apply (Z.lt_le_trans _ (bv_modulus n2)); [lia|]. apply Z.pow_le_mono_r; lia.
-  Qed.
-  Lemma bv_concat_signed m n2 b1 (b2 : bv n2) :
-    bv_signed (bv_concat m b1 b2) = bv_swrap m (Z.lor (bv_unsigned b1 ≪ Z.of_N n2) (bv_unsigned b2)).
-  Proof. unfold bv_concat. rewrite Z_to_bv_signed. done. Qed.
-
-  Lemma bv_add_Z_unsigned b z :
-    bv_unsigned (b `+Z` z) = bv_wrap n (bv_unsigned b + z).
-  Proof. done. Qed.
-  Lemma bv_add_Z_signed b z :
-    bv_signed (b `+Z` z) = bv_swrap n (bv_signed b + z).
-  Proof. unfold bv_add_Z. rewrite Z_to_bv_signed. bv_wrap_simplify_solve. Qed.
-
-  Lemma bv_sub_Z_unsigned b z :
-    bv_unsigned (b `-Z` z) = bv_wrap n (bv_unsigned b - z).
-  Proof. done. Qed.
-  Lemma bv_sub_Z_signed b z :
-    bv_signed (b `-Z` z) = bv_swrap n (bv_signed b - z).
-  Proof. unfold bv_sub_Z. rewrite Z_to_bv_signed. bv_wrap_simplify_solve. Qed.
-
-  Lemma bv_mul_Z_unsigned b z:
-    bv_unsigned (b `*Z` z) = bv_wrap n (bv_unsigned b * z).
-  Proof. done. Qed.
-  Lemma bv_mul_Z_signed b z :
-    bv_signed (b `*Z` z) = bv_swrap n (bv_signed b * z).
-  Proof. unfold bv_mul_Z. rewrite Z_to_bv_signed. bv_wrap_simplify_solve. Qed.
-End unfolding.
-
-(** ** Properties of bv operations *)
-Section properties.
-  Context {n : N}.
-  Implicit Types (b : bv n).
-  Local Open Scope bv_scope.
-
-  Lemma bv_sub_add_opp b1 b2:
-    b1 - b2 = b1 + - b2.
-  Proof.
-    apply bv_eq. unfold bv_sub, bv_add, bv_opp. rewrite !Z_to_bv_unsigned.
-    bv_wrap_simplify_solve.
-  Qed.
-
-  Global Instance bv_add_assoc : Assoc (=) (@bv_add n).
-  Proof.
-    intros ???. unfold bv_add. apply bv_eq. rewrite !Z_to_bv_unsigned.
-    bv_wrap_simplify_solve.
-  Qed.
-
-  Global Instance bv_mul_assoc : Assoc (=) (@bv_mul n).
-  Proof.
-    intros ???. unfold bv_mul. apply bv_eq. rewrite !Z_to_bv_unsigned.
-    bv_wrap_simplify_solve.
-  Qed.
-
-  Lemma bv_add_0_l b1 b2 :
-    bv_unsigned b1 = 0%Z →
-    b1 + b2 = b2.
-  Proof.
-    intros Hb. apply bv_eq.
-    rewrite bv_add_unsigned, Hb, Z.add_0_l, bv_wrap_small; [done|apply bv_unsigned_in_range].
-  Qed.
-
-  Lemma bv_add_0_r b1 b2 :
-    bv_unsigned b2 = 0%Z →
-    b1 + b2 = b1.
-  Proof.
-    intros Hb. apply bv_eq.
-    rewrite bv_add_unsigned, Hb, Z.add_0_r, bv_wrap_small; [done|apply bv_unsigned_in_range].
-  Qed.
-
-  Lemma bv_add_Z_0 b : b `+Z` 0 = b.
-  Proof.
-    unfold bv_add_Z. rewrite Z.add_0_r.
-    apply bv_eq. apply Z_to_bv_small. apply bv_unsigned_in_range.
-  Qed.
-
-  Lemma bv_add_Z_add_r b m o:
-    b `+Z` (m + o) = (b `+Z` o) `+Z` m.
-  Proof.
-    apply bv_eq. unfold bv_add_Z. rewrite !Z_to_bv_unsigned.
-     bv_wrap_simplify_solve.
-  Qed.
-
-  Lemma bv_add_Z_add_l b m o:
-    b `+Z` (m + o) = (b `+Z` m) `+Z` o.
-  Proof.
-    apply bv_eq. unfold bv_add_Z. rewrite !Z_to_bv_unsigned.
-     bv_wrap_simplify_solve.
-  Qed.
-
-  Lemma bv_add_Z_succ b m:
-    b `+Z` Z.succ m = (b `+Z` 1) `+Z` m.
-  Proof.
-    apply bv_eq. unfold bv_add_Z. rewrite !Z_to_bv_unsigned.
-    bv_wrap_simplify_solve.
-  Qed.
-
-  Lemma bv_add_Z_inj_l b i j:
-    0 ≤ i < bv_modulus n →
-    0 ≤ j < bv_modulus n →
-    b `+Z` i = b `+Z` j ↔ i = j.
-  Proof.
-    intros ??. split; [|naive_solver].
-    intros Heq%bv_eq. rewrite !bv_add_Z_unsigned, !(Z.add_comm (bv_unsigned _)) in Heq.
-    by rewrite <-bv_wrap_add_inj, !bv_wrap_small in Heq.
-  Qed.
-
-  Lemma bv_opp_not b:
-    - b `-Z` 1 = bv_not b.
-  Proof.
-    apply bv_eq.
-    rewrite bv_not_unsigned, bv_sub_Z_unsigned, bv_opp_unsigned, <-Z.opp_lnot.
-    bv_wrap_simplify_solve.
-  Qed.
-
-  Lemma bv_and_comm b1 b2:
-    bv_and b1 b2 = bv_and b2 b1.
-  Proof. apply bv_eq. by rewrite !bv_and_unsigned, Z.land_comm. Qed.
-
-  Lemma bv_or_comm b1 b2:
-    bv_or b1 b2 = bv_or b2 b1.
-  Proof. apply bv_eq. by rewrite !bv_or_unsigned, Z.lor_comm. Qed.
-
-  Lemma bv_or_0_l b1 b2 :
-    bv_unsigned b1 = 0%Z →
-    bv_or b1 b2 = b2.
-  Proof. intros Hb. apply bv_eq. by rewrite bv_or_unsigned, Hb, Z.lor_0_l. Qed.
-
-  Lemma bv_or_0_r b1 b2 :
-    bv_unsigned b2 = 0%Z →
-    bv_or b1 b2 = b1.
-  Proof. intros Hb. apply bv_eq. by rewrite bv_or_unsigned, Hb, Z.lor_0_r. Qed.
-
-  Lemma bv_extract_0_unsigned l b:
-    bv_unsigned (bv_extract 0 l b) = bv_wrap l (bv_unsigned b).
-  Proof. rewrite bv_extract_unsigned, Z.shiftr_0_r. done. Qed.
-
-  Lemma bv_extract_0_bv_add_distr l b1 b2:
-    (l ≤ n)%N →
-    bv_extract 0 l (bv_add b1 b2) = bv_add (bv_extract 0 l b1) (bv_extract 0 l b2).
-  Proof.
-    intros ?.
-    apply bv_eq. rewrite !bv_extract_0_unsigned, !bv_add_unsigned, !bv_extract_0_unsigned.
-    rewrite bv_wrap_bv_wrap by done.
-    bv_wrap_simplify_solve.
-  Qed.
-
-  Lemma bv_concat_0 m n2 b1 (b2 : bv n2) :
-    bv_unsigned b1 = 0%Z →
-    bv_concat m b1 b2 = bv_zero_extend m b2.
-  Proof.
-    intros Hb1. apply bv_eq.
-    by rewrite bv_zero_extend_unsigned', bv_concat_unsigned', Hb1, Z.shiftl_0_l, Z.lor_0_l.
-  Qed.
-
-  Lemma bv_zero_extend_idemp b:
-    bv_zero_extend n b = b.
-  Proof. apply bv_eq. by rewrite bv_zero_extend_unsigned. Qed.
-
-  Lemma bv_sign_extend_idemp b:
-    bv_sign_extend n b = b.
-  Proof. apply bv_eq_signed. by rewrite bv_sign_extend_signed. Qed.
-End properties.
-
-(** ** Lemmas about [bv_to_little] and [bv_of_little] *)
-Section little.
-
-  Lemma bv_to_litte_endian_unsigned m n z:
-    0 ≤ m →
-    bv_unsigned <$> bv_to_little_endian m n z = Z_to_little_endian m (Z.of_N n) z.
-  Proof.
-    intros ?. apply list_eq. intros i. unfold bv_to_little_endian.
-    rewrite list_lookup_fmap, list_lookup_fmap.
-    destruct (Z_to_little_endian m (Z.of_N n) z !! i) eqn: Heq; [simpl |done].
-    rewrite Z_to_bv_small; [done|].
-    eapply (Forall_forall (λ z, _ ≤ z < _)); [ |by eapply elem_of_list_lookup_2].
-    eapply Z_to_little_endian_bound; lia.
-  Qed.
-
-  Lemma bv_to_little_endian_to_bv m n bs:
-    m = Z.of_nat (length bs) →
-    bv_to_little_endian m n (little_endian_to_bv n bs) = bs.
-  Proof.
-    intros ->. apply (inj (fmap bv_unsigned)).
-    rewrite bv_to_litte_endian_unsigned; [|lia].
-    apply Z_to_little_endian_to_Z; [by rewrite fmap_length | lia |].
-    apply Forall_forall. intros ? [?[->?]]%elem_of_list_fmap_2. apply bv_unsigned_in_range.
-  Qed.
-
-  Lemma little_endian_to_bv_to_little_endian m n z:
-    0 ≤ m →
-    little_endian_to_bv n (bv_to_little_endian m n z) = z `mod` 2 ^ (m * Z.of_N n).
-  Proof.
-    intros ?. unfold little_endian_to_bv.
-    rewrite bv_to_litte_endian_unsigned; [|lia].
-    apply little_endian_to_Z_to_little_endian; lia.
-  Qed.
-
-  Lemma bv_to_little_endian_length m n z :
-    0 ≤ m →
-    length (bv_to_little_endian m n z) = Z.to_nat m.
-  Proof.
-    intros ?. unfold bv_to_little_endian. rewrite fmap_length.
-    apply Nat2Z.inj. rewrite Z_to_little_endian_length, ?Z2Nat.id; try lia.
-  Qed.
-
-  Lemma little_endian_to_bv_bound n bs :
-    0 ≤ little_endian_to_bv n bs < 2 ^ (Z.of_nat (length bs) * Z.of_N n).
-  Proof.
-    unfold little_endian_to_bv. rewrite <-(fmap_length bv_unsigned bs).
-    apply little_endian_to_Z_bound; [lia|].
-    apply Forall_forall. intros ? [? [-> ?]]%elem_of_list_fmap.
-    apply bv_unsigned_in_range.
-  Qed.
-
-  Lemma Z_to_bv_little_endian_to_bv_to_little_endian x m n (b : bv x):
-    0 ≤ m →
-    x = (Z.to_N m * n)%N →
-    Z_to_bv x (little_endian_to_bv n (bv_to_little_endian m n (bv_unsigned b))) = b.
-  Proof.
-    intros ? ->. rewrite little_endian_to_bv_to_little_endian, Z.mod_small; [| |lia].
-    - apply bv_eq. rewrite Z_to_bv_small; [done|]. apply bv_unsigned_in_range.
-    - pose proof bv_unsigned_in_range _ b as Hr. unfold bv_modulus in Hr.
-      by rewrite N2Z.inj_mul, Z2N.id in Hr.
-  Qed.
-
-  Lemma bv_to_little_endian_lookup_Some m n z (i : nat) x:
-    0 ≤ m → bv_to_little_endian m n z !! i = Some x ↔
-      Z.of_nat i < m ∧ x = Z_to_bv n (z ≫ (Z.of_nat i * Z.of_N n)).
-  Proof.
-    unfold bv_to_little_endian. intros Hm. rewrite list_lookup_fmap, fmap_Some.
-    split.
-    - intros [?[[??]%Z_to_little_endian_lookup_Some ?]]; [|lia..]; subst. split; [done|].
-      rewrite <-bv_wrap_land. apply bv_eq. by rewrite !Z_to_bv_unsigned, bv_wrap_bv_wrap.
-    - intros [?->]. eexists _. split; [apply Z_to_little_endian_lookup_Some; try done; lia| ].
-      rewrite <-bv_wrap_land. apply bv_eq. by rewrite !Z_to_bv_unsigned, bv_wrap_bv_wrap.
-  Qed.
-
-  Lemma little_endian_to_bv_spec n bs i b:
-    0 ≤ i → n ≠ 0%N →
-    bs !! Z.to_nat (i `div` Z.of_N n) = Some b →
-    Z.testbit (little_endian_to_bv n bs) i = Z.testbit (bv_unsigned b) (i `mod` Z.of_N n).
-  Proof.
-    intros ???. unfold little_endian_to_bv. apply little_endian_to_Z_spec; [lia|lia| |].
-    { apply Forall_fmap. apply Forall_true. intros ?; simpl. apply bv_unsigned_in_range. }
-    rewrite list_lookup_fmap. apply fmap_Some. naive_solver.
-  Qed.
-End little.
-
-(** ** Lemmas about [bv_seq] *)
-Section bv_seq.
-  Context {n : N}.
-  Implicit Types (b : bv n).
-
-  Lemma bv_seq_length b len:
-    length (bv_seq b len) = Z.to_nat len.
-  Proof. unfold bv_seq. by rewrite fmap_length, seqZ_length. Qed.
-
-  Lemma bv_seq_succ b m:
-    0 ≤ m →
-    bv_seq b (Z.succ m) = b :: bv_seq (b `+Z` 1) m.
-  Proof.
-    intros. unfold bv_seq. rewrite seqZ_cons by lia. csimpl.
-    rewrite bv_add_Z_0. f_equal.
-    assert (Z.succ 0 = 1 + 0) as -> by lia.
-    rewrite <-fmap_add_seqZ, <-list_fmap_compose, Z.pred_succ. apply list_fmap_ext.
-    intros i x. simpl. by rewrite bv_add_Z_add_l.
-  Qed.
-
-  Lemma NoDup_bv_seq b z:
-    0 ≤ z ≤ bv_modulus n →
-    NoDup (bv_seq b z).
-  Proof.
-    intros ?. apply NoDup_alt. intros i j b'. unfold bv_seq. rewrite !list_lookup_fmap.
-    intros [?[[??]%lookup_seqZ ?]]%fmap_Some ; simplify_eq.
-    intros [?[[->?]%lookup_seqZ ?%bv_add_Z_inj_l]]%fmap_Some; lia.
-  Qed.
-End bv_seq.
-
-(** ** Lemmas about [bv] and [bool] *)
-Section bv_bool.
-  Implicit Types (b : bool).
-
-  Lemma bool_to_bv_unsigned n b:
-    n ≠ 0%N →
-    bv_unsigned (bool_to_bv n b) = bool_to_Z b.
-  Proof.
-    intros ?. pose proof (bv_modulus_gt_1 n).
-    apply Z_to_bv_small. destruct b; simpl; lia.
-  Qed.
-
-  Lemma bv_extract_bool_to_bv n n2 b:
-    n ≠ 0%N → n2 ≠ 0%N →
-    bv_extract 0 n (bool_to_bv n2 b) = bool_to_bv n b.
-  Proof.
-    intros ??. apply bv_eq. pose proof (bv_modulus_gt_1 n).
-    rewrite bv_extract_unsigned, !bool_to_bv_unsigned, Z.shiftr_0_r by done.
-    rewrite bv_wrap_small; [done|]. destruct b; simpl; lia.
-  Qed.
-
-  Lemma bv_not_bool_to_bv b:
-    bv_not (bool_to_bv 1 b) = bool_to_bv 1 (negb b).
-  Proof. apply bv_eq. by destruct b. Qed.
-
-  Lemma bool_decide_bool_to_bv_0 b:
-    bool_decide (bv_unsigned (bool_to_bv 1 b) = 0) = negb b.
-  Proof. by destruct b. Qed.
-  Lemma bool_decide_bool_to_bv_1 b:
-    bool_decide (bv_unsigned (bool_to_bv 1 b) = 1) = b.
-  Proof. by destruct b. Qed.
-End bv_bool.
-
-Section bv_bits.
-  Context {n : N}.
-  Implicit Types (b : bv n).
-
-  Lemma bv_to_bits_length b : length (bv_to_bits b) = N.to_nat n.
-  Proof. unfold bv_to_bits. rewrite fmap_length, seqZ_length, <-Z_N_nat, N2Z.id. done. Qed.
-
-  Lemma bv_to_bits_lookup_Some b i x:
-    bv_to_bits b !! i = Some x ↔ (i < N.to_nat n)%nat ∧ x = Z.testbit (bv_unsigned b) (Z.of_nat i).
-  Proof.
-    unfold bv_to_bits. rewrite list_lookup_fmap, fmap_Some.
-    split.
-    - intros [?[?%lookup_seqZ?]]. naive_solver lia.
-    - intros [??]. eexists _. split; [|done]. apply lookup_seqZ. lia.
-  Qed.
-
-  Global Instance bv_to_bits_inj : Inj eq eq (@bv_to_bits n).
-  Proof.
-    unfold bv_to_bits. intros x y Hf.
-    apply bv_eq_wrap. apply Z.bits_inj_iff'. intros i Hi.
-    rewrite !bv_wrap_spec; [|lia..]. case_bool_decide; simpl; [|done].
-    eapply list_fmap_inj_1 in Hf; [done|]. apply elem_of_seqZ. lia.
-  Qed.
-End bv_bits.
-
-
-(** * [bvn] *)
-Record bvn := bv_to_bvn {
-  bvn_n : N;
-  bvn_val : bv bvn_n;
-}.
-Global Arguments bv_to_bvn {_} _.
-Add Printing Constructor bvn.
-
-Definition bvn_unsigned (b : bvn) := bv_unsigned (b.(bvn_val)).
-
-Lemma bvn_eq (b1 b2 : bvn) :
-  b1 = b2 ↔ b1.(bvn_n) = b2.(bvn_n) ∧ bvn_unsigned b1 = bvn_unsigned b2.
-Proof. split; [ naive_solver|]. destruct b1, b2; simpl; intros [??]. subst. f_equal. by apply bv_eq. Qed.
-
-Global Program Instance bvn_eq_dec : EqDecision bvn := λ '(@bv_to_bvn n1 b1) '(@bv_to_bvn n2 b2),
-   cast_if_and (decide (n1 = n2)) (decide (bv_unsigned b1 = bv_unsigned b2)).
-(* TODO: The following does not compute to eq_refl*)
-Next Obligation. intros. apply bvn_eq. naive_solver. Qed.
-Next Obligation. intros. intros ?%bvn_eq. naive_solver. Qed.
-Next Obligation. intros. intros ?%bvn_eq. naive_solver. Qed.
-
-Definition bvn_to_bv (n : N) (b : bvn) : option (bv n) :=
-  match decide (b.(bvn_n) = n) with
-  | left eq => Some (eq_rect (bvn_n b) (λ n0 : N, bv n0) (bvn_val b) n eq)
-  | right _ => None
-  end.
-Global Arguments bvn_to_bv !_ !_ /.
-
-Global Coercion bv_to_bvn : bv >-> bvn.
-
-(** * Opaqueness *)
-(** We mark all functions on bitvectors as opaque. *)
-Global Hint Opaque Z_to_bv
-       bv_0 bv_succ bv_pred
-       bv_add bv_sub bv_opp
-       bv_mul bv_divu bv_modu
-       bv_divs bv_quots bv_mods bv_rems
-       bv_shiftl bv_shiftr bv_ashiftr bv_or
-       bv_and bv_xor bv_not bv_zero_extend
-       bv_sign_extend bv_extract bv_concat
-       bv_add_Z bv_sub_Z bv_mul_Z
-       bool_to_bv bv_to_bits : typeclass_instances.
-Global Opaque Z_to_bv
-       bv_0 bv_succ bv_pred
-       bv_add bv_sub bv_opp
-       bv_mul bv_divu bv_modu
-       bv_divs bv_quots bv_mods bv_rems
-       bv_shiftl bv_shiftr bv_ashiftr bv_or
-       bv_and bv_xor bv_not bv_zero_extend
-       bv_sign_extend bv_extract bv_concat
-       bv_add_Z bv_sub_Z bv_mul_Z
-       bool_to_bv bv_to_bits.
diff --git a/stdpp_bitvector/definitions.v b/stdpp_bitvector/definitions.v
new file mode 100644
index 00000000..10105bef
--- /dev/null
+++ b/stdpp_bitvector/definitions.v
@@ -0,0 +1,1369 @@
+(** This file is maintained by Michael Sammler. *)
+From stdpp Require Export numbers.
+From stdpp Require Import countable finite.
+From stdpp Require Import options.
+
+(** * bitvector library *)
+(** This file provides the [bv n] type for representing [n]-bit
+integers with the standard operations. It also provides the
+[bv_saturate] tactic for learning facts about the range of bit vector
+variables in context. More extensive automation can be found in
+[bitvector_auto.v].
+
+Additionally, this file provides the [bvn] type for representing a
+bitvector of arbitrary size. *)
+
+(** * Settings *)
+Local Open Scope Z_scope.
+
+(** * Preliminary definitions *)
+Definition bv_modulus (n : N) : Z := 2 ^ (Z.of_N n).
+Definition bv_half_modulus (n : N) : Z := bv_modulus n `div` 2.
+Definition bv_wrap (n : N) (z : Z) : Z := z `mod` bv_modulus n.
+Definition bv_swrap (n : N) (z : Z) : Z := bv_wrap n (z + bv_half_modulus n) - bv_half_modulus n.
+
+Lemma bv_modulus_pos n :
+  0 < bv_modulus n.
+Proof. apply Z.pow_pos_nonneg; lia. Qed.
+Lemma bv_modulus_gt_1 n :
+  n ≠ 0%N →
+  1 < bv_modulus n.
+Proof. intros ?. apply Z.pow_gt_1; lia. Qed.
+Lemma bv_half_modulus_nonneg n :
+  0 ≤ bv_half_modulus n.
+Proof. apply Z.div_pos; [|done]. pose proof bv_modulus_pos n. lia. Qed.
+
+Lemma bv_modulus_add n1 n2 :
+  bv_modulus (n1 + n2) = bv_modulus n1 * bv_modulus n2.
+Proof. unfold bv_modulus. rewrite N2Z.inj_add. eapply Z.pow_add_r; lia. Qed.
+
+Lemma bv_half_modulus_twice n:
+  n ≠ 0%N →
+  bv_half_modulus n + bv_half_modulus n = bv_modulus n.
+Proof.
+  intros. unfold bv_half_modulus, bv_modulus.
+  rewrite Z.add_diag. symmetry. apply Z_div_exact_2; [lia|].
+  rewrite <-Z.pow_pred_r by lia. rewrite Z.mul_comm. by apply Z.mod_mul.
+Qed.
+
+Lemma bv_half_modulus_lt_modulus n:
+  bv_half_modulus n < bv_modulus n.
+Proof.
+  pose proof bv_modulus_pos n.
+  apply Z_div_lt; [done| lia].
+Qed.
+
+Lemma bv_modulus_le_mono n m:
+  (n ≤ m)%N →
+  bv_modulus n ≤ bv_modulus m.
+Proof. intros. apply Z.pow_le_mono; [done|lia]. Qed.
+Lemma bv_half_modulus_le_mono n m:
+  (n ≤ m)%N →
+  bv_half_modulus n ≤ bv_half_modulus m.
+Proof. intros. apply Z.div_le_mono; [done|]. by apply bv_modulus_le_mono. Qed.
+
+Lemma bv_modulus_0:
+  bv_modulus 0 = 1.
+Proof. done. Qed.
+Lemma bv_half_modulus_0:
+  bv_half_modulus 0 = 0.
+Proof. done. Qed.
+
+Lemma bv_half_modulus_twice_mult n:
+  bv_half_modulus n + bv_half_modulus n = (Z.of_N n `min` 1) * bv_modulus n.
+Proof. destruct (decide (n = 0%N)); subst; [ rewrite bv_half_modulus_0 | rewrite bv_half_modulus_twice]; lia. Qed.
+
+Lemma bv_wrap_in_range n z:
+  0 ≤ bv_wrap n z < bv_modulus n.
+Proof. apply Z.mod_pos_bound. apply bv_modulus_pos. Qed.
+
+Lemma bv_swrap_in_range n z:
+  n ≠ 0%N →
+  - bv_half_modulus n ≤ bv_swrap n z < bv_half_modulus n.
+Proof.
+  intros ?. unfold bv_swrap.
+  pose proof bv_half_modulus_twice n.
+  pose proof bv_wrap_in_range n (z + bv_half_modulus n).
+  lia.
+Qed.
+
+Lemma bv_wrap_small n z :
+  0 ≤ z < bv_modulus n → bv_wrap n z = z.
+Proof. intros. by apply Z.mod_small. Qed.
+
+Lemma bv_swrap_small n z :
+  - bv_half_modulus n ≤ z < bv_half_modulus n →
+  bv_swrap n z = z.
+Proof.
+  intros Hrange. unfold bv_swrap.
+  destruct (decide (n = 0%N)); subst.
+  { rewrite bv_half_modulus_0 in Hrange. lia. }
+  pose proof bv_half_modulus_twice n.
+  rewrite bv_wrap_small by lia. lia.
+Qed.
+
+Lemma bv_wrap_0 n :
+  bv_wrap n 0 = 0.
+Proof. done. Qed.
+Lemma bv_swrap_0 n :
+  bv_swrap n 0 = 0.
+Proof.
+  pose proof bv_half_modulus_lt_modulus n.
+  pose proof bv_half_modulus_nonneg n.
+  unfold bv_swrap. rewrite bv_wrap_small; lia.
+Qed.
+
+Lemma bv_wrap_idemp n b : bv_wrap n (bv_wrap n b) = bv_wrap n b.
+Proof. unfold bv_wrap. by rewrite Zmod_mod. Qed.
+
+Definition bv_wrap_factor (n : N) (x z : Z) :=
+  x = - z `div` bv_modulus n.
+
+Lemma bv_wrap_factor_intro n z :
+  ∃ x, bv_wrap_factor n x z ∧ bv_wrap n z = z + x * bv_modulus n.
+Proof.
+  eexists _. split; [done|].
+  pose proof (bv_modulus_pos n). unfold bv_wrap. rewrite Z.mod_eq; lia.
+Qed.
+
+Lemma bv_wrap_add_modulus c n z:
+  bv_wrap n (z + c * bv_modulus n) = bv_wrap n z.
+Proof. apply Z_mod_plus_full. Qed.
+Lemma bv_wrap_add_modulus_1 n z:
+  bv_wrap n (z + bv_modulus n) = bv_wrap n z.
+Proof. rewrite <-(bv_wrap_add_modulus 1 n z). f_equal. lia. Qed.
+Lemma bv_wrap_sub_modulus c n z:
+  bv_wrap n (z - c * bv_modulus n) = bv_wrap n z.
+Proof. rewrite <-(bv_wrap_add_modulus (-c) n z). f_equal. lia. Qed.
+Lemma bv_wrap_sub_modulus_1 n z:
+  bv_wrap n (z - bv_modulus n) = bv_wrap n z.
+Proof. rewrite <-(bv_wrap_add_modulus (-1) n z). done. Qed.
+
+Lemma bv_wrap_add_idemp n x y :
+  bv_wrap n (bv_wrap n x + bv_wrap n y) = bv_wrap n (x + y).
+Proof. symmetry. apply Zplus_mod. Qed.
+Lemma bv_wrap_add_idemp_l n x y :
+  bv_wrap n (bv_wrap n x + y) = bv_wrap n (x + y).
+Proof. apply Zplus_mod_idemp_l. Qed.
+Lemma bv_wrap_add_idemp_r n x y :
+  bv_wrap n (x + bv_wrap n y) = bv_wrap n (x + y).
+Proof. apply Zplus_mod_idemp_r. Qed.
+
+Lemma bv_wrap_opp_idemp n x :
+  bv_wrap n (- bv_wrap n x) = bv_wrap n (- x).
+Proof.
+  unfold bv_wrap. pose proof (bv_modulus_pos n).
+  destruct (decide (x `mod` bv_modulus n = 0)) as [Hx|Hx].
+  - rewrite !Z.mod_opp_l_z; [done |lia|done|lia|by rewrite Hx].
+  - rewrite !Z.mod_opp_l_nz, Z.mod_mod;
+      [done|lia|lia|done|lia|by rewrite Z.mod_mod by lia].
+Qed.
+
+Lemma bv_wrap_mul_idemp n x y :
+  bv_wrap n (bv_wrap n x * bv_wrap n y) = bv_wrap n (x * y).
+Proof. etrans; [| apply Zmult_mod_idemp_r]. apply Zmult_mod_idemp_l. Qed.
+Lemma bv_wrap_mul_idemp_l n x y :
+  bv_wrap n (bv_wrap n x * y) = bv_wrap n (x * y).
+Proof. apply Zmult_mod_idemp_l. Qed.
+Lemma bv_wrap_mul_idemp_r n x y :
+  bv_wrap n (x * bv_wrap n y) = bv_wrap n (x * y).
+Proof. apply Zmult_mod_idemp_r. Qed.
+
+Lemma bv_wrap_sub_idemp n x y :
+  bv_wrap n (bv_wrap n x - bv_wrap n y) = bv_wrap n (x - y).
+Proof.
+  by rewrite <-!Z.add_opp_r, <-bv_wrap_add_idemp_r,
+    bv_wrap_opp_idemp, bv_wrap_add_idemp.
+ Qed.
+Lemma bv_wrap_sub_idemp_l n x y :
+  bv_wrap n (bv_wrap n x - y) = bv_wrap n (x - y).
+Proof. by rewrite <-!Z.add_opp_r, bv_wrap_add_idemp_l. Qed.
+Lemma bv_wrap_sub_idemp_r n x y :
+  bv_wrap n (x - bv_wrap n y) = bv_wrap n (x - y).
+Proof.
+  by rewrite <-!Z.add_opp_r, <-bv_wrap_add_idemp_r,
+    bv_wrap_opp_idemp, bv_wrap_add_idemp_r.
+Qed.
+
+Lemma bv_wrap_succ_idemp n x :
+  bv_wrap n (Z.succ (bv_wrap n x)) = bv_wrap n (Z.succ x).
+Proof. by rewrite <-!Z.add_1_r, bv_wrap_add_idemp_l. Qed.
+Lemma bv_wrap_pred_idemp n x :
+  bv_wrap n (Z.pred (bv_wrap n x)) = bv_wrap n (Z.pred x).
+Proof. by rewrite <-!Z.sub_1_r, bv_wrap_sub_idemp_l. Qed.
+
+Lemma bv_wrap_add_inj n x1 x2 y :
+  bv_wrap n x1 = bv_wrap n x2 ↔ bv_wrap n (x1 + y) = bv_wrap n (x2 + y).
+Proof.
+  split; intros Heq.
+  - by rewrite <-bv_wrap_add_idemp_l, Heq, bv_wrap_add_idemp_l.
+  - pose proof (bv_wrap_factor_intro n (x1 + y)) as [f1[? Hx1]].
+    pose proof (bv_wrap_factor_intro n (x2 + y)) as [f2[? Hx2]].
+    assert (x1 = x2 + f2 * bv_modulus n - f1 * bv_modulus n) as -> by lia.
+    by rewrite bv_wrap_sub_modulus, bv_wrap_add_modulus.
+Qed.
+
+Lemma bv_swrap_wrap n z:
+  bv_swrap n (bv_wrap n z) = bv_swrap n z.
+Proof. unfold bv_swrap, bv_wrap. by rewrite Zplus_mod_idemp_l. Qed.
+
+Lemma bv_wrap_bv_wrap n1 n2 bv :
+  (n1 ≤ n2)%N →
+  bv_wrap n1 (bv_wrap n2 bv) = bv_wrap n1 bv.
+Proof.
+  intros ?. unfold bv_wrap.
+  rewrite <-Znumtheory.Zmod_div_mod; [done| apply bv_modulus_pos.. |].
+  unfold bv_modulus. eexists (2 ^ (Z.of_N n2 - Z.of_N n1)).
+  rewrite <-Z.pow_add_r by lia. f_equal. lia.
+Qed.
+
+Lemma bv_wrap_land n z :
+  bv_wrap n z = Z.land z (Z.ones (Z.of_N n)).
+Proof. by rewrite Z.land_ones by lia. Qed.
+Lemma bv_wrap_spec n z i:
+  0 ≤ i →
+  Z.testbit (bv_wrap n z) i = bool_decide (i < Z.of_N n) && Z.testbit z i.
+Proof.
+  intros ?. rewrite bv_wrap_land, Z.land_spec, Z.ones_spec by lia.
+  case_bool_decide; simpl; by rewrite ?andb_true_r, ?andb_false_r.
+Qed.
+Lemma bv_wrap_spec_low n z i:
+  0 ≤ i < Z.of_N n →
+  Z.testbit (bv_wrap n z) i = Z.testbit z i.
+Proof. intros ?. rewrite bv_wrap_spec; [|lia]. case_bool_decide; [done|]. lia. Qed.
+Lemma bv_wrap_spec_high n z i:
+  Z.of_N n ≤ i →
+  Z.testbit (bv_wrap n z) i = false.
+Proof. intros ?. rewrite bv_wrap_spec; [|lia]. case_bool_decide; [|done]. lia. Qed.
+
+(** * [BvWf] *)
+(** The [BvWf] typeclass checks that the integer [z] can be
+interpreted as a [n]-bit integer. [BvWf] is a typeclass such that it
+can be automatically inferred for bitvector constants. *)
+Class BvWf (n : N) (z : Z) : Prop :=
+    bv_wf : (0 <=? z) && (z <? bv_modulus n)
+.
+Global Hint Mode BvWf + + : typeclass_instances.
+Global Instance bv_wf_pi n z : ProofIrrel (BvWf n z).
+Proof. unfold BvWf. apply _. Qed.
+Global Instance bv_wf_dec n z : Decision (BvWf n z).
+Proof. unfold BvWf. apply _. Defined.
+
+Global Typeclasses Opaque BvWf.
+
+Ltac solve_BvWf :=
+  lazymatch goal with
+    |- BvWf ?n ?v =>
+      is_closed_term n;
+      is_closed_term v;
+      try (vm_compute; exact I);
+      fail "Bitvector constant" v "does not fit into" n "bits"
+  end.
+Global Hint Extern 10 (BvWf _ _) => solve_BvWf : typeclass_instances.
+
+Lemma bv_wf_in_range n z:
+  BvWf n z ↔ 0 ≤ z < bv_modulus n.
+Proof. unfold BvWf. by rewrite andb_True, !Is_true_true, Z.leb_le, Z.ltb_lt. Qed.
+
+Lemma bv_wrap_wf n z :
+  BvWf n (bv_wrap n z).
+Proof. apply bv_wf_in_range. apply bv_wrap_in_range. Qed.
+
+Lemma bv_wf_bitwise_op {n} op bop n1 n2 :
+  (∀ k, Z.testbit (op n1 n2) k = bop (Z.testbit n1 k) (Z.testbit n2 k)) →
+  (0 ≤ n1 → 0 ≤ n2 → 0 ≤ op n1 n2) →
+  bop false false = false →
+  BvWf n n1 →
+  BvWf n n2 →
+  BvWf n (op n1 n2).
+Proof.
+  intros Hbits Hnonneg Hop [? Hok1]%bv_wf_in_range [? Hok2]%bv_wf_in_range. apply bv_wf_in_range.
+  split; [lia|].
+  apply Z.bounded_iff_bits_nonneg; [lia..|]. intros l ?.
+  eapply Z.bounded_iff_bits_nonneg in Hok1;[|try done; lia..].
+  eapply Z.bounded_iff_bits_nonneg in Hok2;[|try done; lia..].
+  by rewrite Hbits, Hok1, Hok2.
+Qed.
+
+(** * Definition of [bv n] *)
+Record bv (n : N) := BV {
+  bv_unsigned : Z;
+  bv_is_wf : BvWf n bv_unsigned;
+}.
+Global Arguments bv_unsigned {_}.
+Global Arguments bv_is_wf {_}.
+Global Arguments BV _ _ {_}.
+Add Printing Constructor bv.
+
+Global Arguments bv_unsigned : simpl never.
+
+Definition bv_signed {n} (b : bv n) := bv_swrap n (bv_unsigned b).
+
+Lemma bv_eq n (b1 b2 : bv n) :
+  b1 = b2 ↔ b1.(bv_unsigned) = b2.(bv_unsigned).
+Proof.
+  destruct b1, b2. unfold bv_unsigned. split; [ naive_solver|].
+  intros. subst. f_equal. apply proof_irrel.
+Qed.
+
+Lemma bv_neq n (b1 b2 : bv n) :
+  b1 ≠ b2 ↔ b1.(bv_unsigned) ≠ b2.(bv_unsigned).
+Proof. unfold not. by rewrite bv_eq. Qed.
+
+Global Instance bv_unsigned_inj n : Inj (=) (=) (@bv_unsigned n).
+Proof. intros ???. by apply bv_eq. Qed.
+
+Definition Z_to_bv_checked (n : N) (z : Z) : option (bv n) :=
+  H ← guard (BvWf n z); Some (@BV n z H).
+
+Program Definition Z_to_bv (n : N) (z : Z) : bv n :=
+  @BV n (bv_wrap n z) _.
+Next Obligation. apply bv_wrap_wf. Qed.
+
+Lemma Z_to_bv_unsigned n z:
+  bv_unsigned (Z_to_bv n z) = bv_wrap n z.
+Proof. done. Qed.
+
+Lemma Z_to_bv_signed n z:
+  bv_signed (Z_to_bv n z) = bv_swrap n z.
+Proof. apply bv_swrap_wrap. Qed.
+
+Lemma Z_to_bv_small n z:
+  0 ≤ z < bv_modulus n →
+  bv_unsigned (Z_to_bv n z) = z.
+Proof. rewrite Z_to_bv_unsigned. apply bv_wrap_small. Qed.
+
+Lemma bv_unsigned_BV n z Hwf:
+  bv_unsigned (@BV n z Hwf) = z.
+Proof. done. Qed.
+
+Lemma bv_signed_BV n z Hwf:
+  bv_signed (@BV n z Hwf) = bv_swrap n z.
+Proof. done. Qed.
+
+Lemma bv_unsigned_in_range n (b : bv n):
+  0 ≤ bv_unsigned b < bv_modulus n.
+Proof. apply bv_wf_in_range. apply bv_is_wf. Qed.
+
+Lemma bv_wrap_bv_unsigned n (b : bv n):
+  bv_wrap n (bv_unsigned b) = bv_unsigned b.
+Proof. rewrite bv_wrap_small; [done|apply bv_unsigned_in_range]. Qed.
+
+Lemma Z_to_bv_bv_unsigned n (b : bv n):
+  Z_to_bv n (bv_unsigned b) = b.
+Proof. apply bv_eq. by rewrite Z_to_bv_unsigned, bv_wrap_bv_unsigned. Qed.
+
+Lemma bv_eq_wrap n (b1 b2 : bv n) :
+  b1 = b2 ↔ bv_wrap n b1.(bv_unsigned) = bv_wrap n b2.(bv_unsigned).
+Proof.
+  rewrite !bv_wrap_small; [apply bv_eq | apply bv_unsigned_in_range..].
+Qed.
+
+Lemma bv_neq_wrap n (b1 b2 : bv n) :
+  b1 ≠ b2 ↔ bv_wrap n b1.(bv_unsigned) ≠ bv_wrap n b2.(bv_unsigned).
+Proof. unfold not. by rewrite bv_eq_wrap. Qed.
+
+Lemma bv_eq_signed n (b1 b2 : bv n) :
+  b1 = b2 ↔ bv_signed b1 = bv_signed b2.
+Proof.
+  split; [naive_solver |].
+  unfold bv_signed, bv_swrap. intros ?.
+  assert (bv_wrap n (bv_unsigned b1 + bv_half_modulus n)
+          = bv_wrap n (bv_unsigned b2 + bv_half_modulus n)) as ?%bv_wrap_add_inj by lia.
+  by apply bv_eq_wrap.
+Qed.
+
+Lemma bv_signed_in_range n (b : bv n):
+  n ≠ 0%N →
+  - bv_half_modulus n ≤ bv_signed b < bv_half_modulus n.
+Proof. apply bv_swrap_in_range. Qed.
+
+Lemma bv_unsigned_spec_high i n (b : bv n) :
+  Z.of_N n ≤ i →
+  Z.testbit (bv_unsigned b) i = false.
+Proof.
+  intros ?. pose proof (bv_unsigned_in_range _ b). unfold bv_modulus in *.
+  eapply Z.bounded_iff_bits_nonneg; [..|done]; lia.
+Qed.
+
+Lemma bv_unsigned_N_0 (b : bv 0):
+  bv_unsigned b = 0.
+Proof.
+  pose proof bv_unsigned_in_range 0 b as H.
+  rewrite bv_modulus_0 in H. lia.
+Qed.
+Lemma bv_signed_N_0 (b : bv 0):
+  bv_signed b = 0.
+Proof. unfold bv_signed. by rewrite bv_unsigned_N_0, bv_swrap_0. Qed.
+
+Lemma bv_swrap_bv_signed n (b : bv n):
+  bv_swrap n (bv_signed b) = bv_signed b.
+Proof.
+  destruct (decide (n = 0%N)); subst.
+  { by rewrite bv_signed_N_0, bv_swrap_0. }
+  apply bv_swrap_small. by apply bv_signed_in_range.
+Qed.
+
+Lemma Z_to_bv_checked_bv_unsigned n (b : bv n):
+  Z_to_bv_checked n (bv_unsigned b) = Some b.
+Proof.
+  unfold Z_to_bv_checked. case_guard; simplify_option_eq.
+  - f_equal. by apply bv_eq.
+  - by pose proof bv_is_wf b.
+Qed.
+Lemma Z_to_bv_checked_Some n a (b : bv n):
+  Z_to_bv_checked n a = Some b ↔ a = bv_unsigned b.
+Proof.
+  split.
+  - unfold Z_to_bv_checked. case_guard; [|done]. intros ?. by simplify_option_eq.
+  - intros ->. apply Z_to_bv_checked_bv_unsigned.
+Qed.
+
+(** * Typeclass instances for [bv n] *)
+Global Program Instance bv_eq_dec n : EqDecision (bv n) := λ '(@BV _ v1 p1) '(@BV _ v2 p2),
+   match decide (v1 = v2) with
+   | left eqv => left _
+   | right eqv => right _
+   end.
+Next Obligation.
+  (* TODO: Can we get a better proof term here? *)
+  intros n b1 v1 p1 ? b2 v2 p2 ????. subst.
+  rewrite (proof_irrel p1 p2). exact eq_refl.
+Defined.
+Next Obligation. intros. by injection. Qed.
+
+Global Instance bv_countable n : Countable (bv n) :=
+  inj_countable bv_unsigned (Z_to_bv_checked n) (Z_to_bv_checked_bv_unsigned n).
+
+Global Program Instance bv_finite n : Finite (bv n) :=
+  {| enum := Z_to_bv n <$> (seqZ 0 (bv_modulus n)) |}.
+Next Obligation.
+  intros n. apply NoDup_alt. intros i j x.
+  rewrite !list_lookup_fmap.
+  intros [? [[??]%lookup_seqZ ?]]%fmap_Some.
+  intros [? [[??]%lookup_seqZ Hz]]%fmap_Some. subst.
+  apply bv_eq in Hz. rewrite !Z_to_bv_small in Hz; lia.
+Qed.
+Next Obligation.
+  intros n x. apply elem_of_list_lookup. eexists (Z.to_nat (bv_unsigned x)).
+  rewrite list_lookup_fmap. apply fmap_Some. eexists _.
+  pose proof (bv_unsigned_in_range _ x). split.
+  - apply lookup_seqZ. split; [done|]. rewrite Z2Nat.id; lia.
+  - apply bv_eq. rewrite Z_to_bv_small; rewrite Z2Nat.id; lia.
+Qed.
+
+Lemma bv_1_ind (P : bv 1 → Prop) :
+  P (@BV 1 1 I) → P (@BV 1 0 I) → ∀ b : bv 1, P b.
+Proof.
+  intros ??. apply Forall_finite. repeat constructor.
+  - by assert ((@BV 1 0 I) = (Z_to_bv 1 (Z.of_nat 0 + 0))) as <- by by apply bv_eq.
+  - by assert ((@BV 1 1 I) = (Z_to_bv 1 (Z.of_nat 1 + 0))) as <- by by apply bv_eq.
+Qed.
+
+(** * [bv_saturate]: Add range facts about bit vectors to the context *)
+Lemma bv_unsigned_in_range_alt n (b : bv n):
+  -1 < bv_unsigned b < bv_modulus n.
+Proof. pose proof (bv_unsigned_in_range _ b). lia. Qed.
+
+Ltac bv_saturate :=
+  repeat match goal with b : bv _ |- _ => first [
+     clear b | (* Clear if unused *)
+     (* We use [bv_unsigned_in_range_alt] instead of
+     [bv_unsigned_in_range] since hypothesis of the form [0 ≤ ... < ...]
+     can cause significant slowdowns in
+     [Z.euclidean_division_equations_cleanup] due to
+     https://github.com/coq/coq/pull/17984 . *)
+     learn_hyp (bv_unsigned_in_range_alt _ b) |
+     learn_hyp (bv_signed_in_range _ b)
+  ] end.
+
+Ltac bv_saturate_unsigned :=
+  repeat match goal with b : bv _ |- _ => first [
+     clear b | (* Clear if unused *)
+     (* See comment in [bv_saturate]. *)
+     learn_hyp (bv_unsigned_in_range_alt _ b)
+  ] end.
+
+(** * Operations on [bv n] *)
+Program Definition bv_0 (n : N) :=
+  @BV n 0 _.
+Next Obligation.
+  intros n. apply bv_wf_in_range. split; [done| apply bv_modulus_pos].
+Qed.
+Global Instance bv_inhabited n : Inhabited (bv n) := populate (bv_0 n).
+
+Definition bv_succ {n} (x : bv n) : bv n :=
+  Z_to_bv n (Z.succ (bv_unsigned x)).
+Definition bv_pred {n} (x : bv n) : bv n :=
+  Z_to_bv n (Z.pred (bv_unsigned x)).
+
+Definition bv_add {n} (x y : bv n) : bv n := (* SMT: bvadd *)
+  Z_to_bv n (Z.add (bv_unsigned x) (bv_unsigned y)).
+Definition bv_sub {n} (x y : bv n) : bv n := (* SMT: bvsub *)
+  Z_to_bv n (Z.sub (bv_unsigned x) (bv_unsigned y)).
+Definition bv_opp {n} (x : bv n) : bv n := (* SMT: bvneg *)
+  Z_to_bv n (Z.opp (bv_unsigned x)).
+
+Definition bv_mul {n} (x y : bv n) : bv n := (* SMT: bvmul *)
+  Z_to_bv n (Z.mul (bv_unsigned x) (bv_unsigned y)).
+Program Definition bv_divu {n} (x y : bv n) : bv n := (* SMT: bvudiv *)
+  @BV n (Z.div (bv_unsigned x) (bv_unsigned y)) _.
+Next Obligation.
+  intros n x y. apply bv_wf_in_range. bv_saturate.
+  destruct (decide (bv_unsigned y = 0)) as [->|?].
+  { rewrite Zdiv_0_r. lia. }
+  split; [ apply Z.div_pos; lia |].
+  apply (Z.le_lt_trans _ (bv_unsigned x)); [|lia].
+  apply Z.div_le_upper_bound; [ lia|]. nia.
+Qed.
+Program Definition bv_modu {n} (x y : bv n) : bv n := (* SMT: bvurem *)
+  @BV n (Z.modulo (bv_unsigned x) (bv_unsigned y)) _.
+Next Obligation.
+  intros n x y. apply bv_wf_in_range. bv_saturate.
+  destruct (decide (bv_unsigned y = 0)) as [->|?].
+  { rewrite Zmod_0_r. lia. }
+  split; [ apply Z.mod_pos; lia |].
+  apply (Z.le_lt_trans _ (bv_unsigned x)); [|lia].
+  apply Z.mod_le; lia.
+Qed.
+Definition bv_divs {n} (x y : bv n) : bv n :=
+  Z_to_bv n (Z.div (bv_signed x) (bv_signed y)).
+Definition bv_quots {n} (x y : bv n) : bv n := (* SMT: bvsdiv *)
+  Z_to_bv n (Z.quot (bv_signed x) (bv_signed y)).
+Definition bv_mods {n} (x y : bv n) : bv n := (* SMT: bvsmod *)
+  Z_to_bv n (Z.modulo (bv_signed x) (bv_signed y)).
+Definition bv_rems {n} (x y : bv n) : bv n := (* SMT: bvsrem *)
+  Z_to_bv n (Z.rem (bv_signed x) (bv_signed y)).
+
+Definition bv_shiftl {n} (x y : bv n) : bv n := (* SMT: bvshl *)
+  Z_to_bv n (Z.shiftl (bv_unsigned x) (bv_unsigned y)).
+Program Definition bv_shiftr {n} (x y : bv n) : bv n := (* SMT: bvlshr *)
+  @BV n (Z.shiftr (bv_unsigned x) (bv_unsigned y)) _.
+Next Obligation.
+  intros n x y. apply bv_wf_in_range. bv_saturate.
+  split; [ apply Z.shiftr_nonneg; lia|].
+  rewrite Z.shiftr_div_pow2; [|lia].
+  apply (Z.le_lt_trans _ (bv_unsigned x)); [|lia].
+  pose proof (Z.pow_pos_nonneg 2 (bv_unsigned y)).
+  apply Z.div_le_upper_bound; [ lia|]. nia.
+Qed.
+Definition bv_ashiftr {n} (x y : bv n) : bv n := (* SMT: bvashr *)
+  Z_to_bv n (Z.shiftr (bv_signed x) (bv_unsigned y)).
+
+Program Definition bv_or {n} (x y : bv n) : bv n := (* SMT: bvor *)
+  @BV n (Z.lor (bv_unsigned x) (bv_unsigned y)) _.
+Next Obligation.
+  intros. eapply bv_wf_bitwise_op; [ apply Z.lor_spec |
+    by intros; eapply Z.lor_nonneg | done | apply bv_is_wf..].
+Qed.
+Program Definition bv_and {n} (x y : bv n) : bv n := (* SMT: bvand *)
+  @BV n (Z.land (bv_unsigned x) (bv_unsigned y)) _.
+Next Obligation.
+  intros. eapply bv_wf_bitwise_op; [ apply Z.land_spec |
+    intros; eapply Z.land_nonneg; by left | done | apply bv_is_wf..].
+Qed.
+Program Definition bv_xor {n} (x y : bv n) : bv n := (* SMT: bvxor *)
+  @BV n (Z.lxor (bv_unsigned x) (bv_unsigned y)) _.
+Next Obligation.
+  intros. eapply bv_wf_bitwise_op; [ apply Z.lxor_spec |
+    intros; eapply Z.lxor_nonneg; naive_solver | done | apply bv_is_wf..].
+Qed.
+Program Definition bv_not {n} (x : bv n) : bv n := (* SMT: bvnot *)
+  Z_to_bv n (Z.lnot (bv_unsigned x)).
+
+(* [bv_zero_extends z b] extends [b] to [z] bits with 0. If [z] is
+smaller than [n], [b] is truncated. Note that [z] gives the resulting
+size instead of the number of bits to add (as SMTLIB does) to avoid a
+type-level [_ + _] *)
+Program Definition bv_zero_extend {n} (z : N) (b : bv n) : bv z := (* SMT: zero_extend *)
+  Z_to_bv z (bv_unsigned b).
+
+Program Definition bv_sign_extend {n} (z : N) (b : bv n) : bv z := (* SMT: sign_extend *)
+  Z_to_bv z (bv_signed b).
+
+(* s is start index and l is length. Note that this is different from
+extract in SMTLIB which uses [extract (inclusive upper bound)
+(inclusive lower bound)]. The version here is phrased in a way that
+makes it impossible to use an upper bound that is lower than the lower
+bound. *)
+Definition bv_extract {n} (s l : N) (b : bv n) : bv l :=
+  Z_to_bv l (bv_unsigned b ≫ Z.of_N s).
+
+(* Note that we should always have n1 + n2 = n, but we use a parameter to avoid a type-level (_ + _) *)
+Program Definition bv_concat n {n1 n2} (b1 : bv n1) (b2 : bv n2) : bv n := (* SMT: concat *)
+  Z_to_bv n (Z.lor (bv_unsigned b1 ≪ Z.of_N n2) (bv_unsigned b2)).
+
+Definition bv_to_little_endian m n (z : Z) : list (bv n) :=
+  (λ b, Z_to_bv n b) <$> Z_to_little_endian m (Z.of_N n) z.
+
+Definition little_endian_to_bv n (bs : list (bv n)) : Z :=
+  little_endian_to_Z (Z.of_N n) (bv_unsigned <$> bs).
+
+(** * Operations on [bv n] and Z *)
+Definition bv_add_Z {n} (x : bv n) (y : Z) : bv n :=
+  Z_to_bv n (Z.add (bv_unsigned x) y).
+Definition bv_sub_Z {n} (x : bv n) (y : Z) : bv n :=
+  Z_to_bv n (Z.sub (bv_unsigned x) y).
+Definition bv_mul_Z {n} (x : bv n) (y : Z) : bv n :=
+  Z_to_bv n (Z.mul (bv_unsigned x) y).
+
+Definition bv_seq {n} (x : bv n) (len : Z) : list (bv n) :=
+  (bv_add_Z x) <$> seqZ 0 len.
+
+(** * Operations on [bv n] and bool *)
+Definition bool_to_bv (n : N) (b : bool) : bv n :=
+  Z_to_bv n (bool_to_Z b).
+
+Definition bv_to_bits {n} (b : bv n) : list bool :=
+  (λ i, Z.testbit (bv_unsigned b) i) <$> seqZ 0 (Z.of_N n).
+
+(** * Notation for [bv] operations *)
+Declare Scope bv_scope.
+Delimit Scope bv_scope with bv.
+Bind Scope bv_scope with bv.
+
+Infix "+" := bv_add : bv_scope.
+Infix "-" := bv_sub : bv_scope.
+Notation "- x" := (bv_opp x) : bv_scope.
+Infix "*" := bv_mul : bv_scope.
+Infix "`divu`" := bv_divu (at level 35) : bv_scope.
+Infix "`modu`" := bv_modu (at level 35) : bv_scope.
+Infix "`divs`" := bv_divs (at level 35) : bv_scope.
+Infix "`quots`" := bv_quots (at level 35) : bv_scope.
+Infix "`mods`" := bv_mods (at level 35) : bv_scope.
+Infix "`rems`" := bv_rems (at level 35) : bv_scope.
+Infix "≪" := bv_shiftl : bv_scope.
+Infix "≫" := bv_shiftr : bv_scope.
+Infix "`ashiftr`" := bv_ashiftr (at level 35) : bv_scope.
+
+Infix "`+Z`" := bv_add_Z (at level 50) : bv_scope.
+Infix "`-Z`" := bv_sub_Z (at level 50) : bv_scope.
+Infix "`*Z`" := bv_mul_Z (at level 40) : bv_scope.
+
+(** This adds number notations into [bv_scope].
+If the number literal is positive or 0, it gets expanded to [BV _ {num} _].
+If the number literal is negative, it gets expanded as [Z_to_bv _ {num}].
+In the negative case, the notation is parsing only and the [Z_to_bv] call will be
+printed explicitly. *)
+Inductive bv_number_notation := BVNumNonNeg (z : Z) | BVNumNeg (z : Z).
+Definition bv_number_notation_to_Z (n : bv_number_notation) : option Z :=
+  match n with
+  | BVNumNonNeg z => Some z
+  (** Don't use the notation for negative numbers for printing. *)
+  | BVNumNeg z => None
+  end.
+Definition Z_to_bv_number_notation (z : Z) :=
+  match z with
+  | Zneg _ => BVNumNeg z
+  | _ => BVNumNonNeg z
+  end.
+
+(** We need to temporarily change the implicit arguments of BV and
+Z_to_bv such that we can pass them to [Number Notation]. *)
+Local Arguments Z_to_bv {_} _.
+Local Arguments BV {_} _ {_}.
+Number Notation bv Z_to_bv_number_notation bv_number_notation_to_Z
+  (via bv_number_notation mapping [[BV] => BVNumNonNeg, [Z_to_bv] => BVNumNeg]) : bv_scope.
+Local Arguments BV _ _ {_}.
+Local Arguments Z_to_bv : clear implicits.
+
+(** * [bv_wrap_simplify]: typeclass-based automation for simplifying [bv_wrap] *)
+(** The [bv_wrap_simplify] tactic removes [bv_wrap] where possible by
+using the fact that [bv_wrap n (bv_warp n z) = bv_wrap n z]. The main
+use case for this tactic is for proving the lemmas about the
+operations of [bv n] below. Users should use the more extensive
+automation provided by [bitvector_auto.v]. *)
+Create HintDb bv_wrap_simplify_db discriminated.
+Global Hint Constants Opaque : bv_wrap_simplify_db.
+Global Hint Variables Opaque : bv_wrap_simplify_db.
+
+Class BvWrapSimplify (n : N) (z z' : Z) := {
+  bv_wrap_simplify_proof : bv_wrap n z = bv_wrap n z';
+}.
+Global Arguments bv_wrap_simplify_proof _ _ _ {_}.
+Global Hint Mode BvWrapSimplify + + - : bv_wrap_simplify_db.
+
+(** Default instance to end search. *)
+Lemma bv_wrap_simplify_id n z :
+  BvWrapSimplify n z z.
+Proof. by constructor. Qed.
+Global Hint Resolve bv_wrap_simplify_id | 1000 : bv_wrap_simplify_db.
+
+(** [bv_wrap_simplify_bv_wrap] performs the actual simplification. *)
+Lemma bv_wrap_simplify_bv_wrap n z z' :
+  BvWrapSimplify n z z' →
+  BvWrapSimplify n (bv_wrap n z) z'.
+Proof. intros [->]. constructor. by rewrite bv_wrap_bv_wrap. Qed.
+Global Hint Resolve bv_wrap_simplify_bv_wrap | 10 : bv_wrap_simplify_db.
+
+(** The rest of the instances propagate [BvWrapSimplify].  *)
+Lemma bv_wrap_simplify_succ n z z' :
+  BvWrapSimplify n z z' →
+  BvWrapSimplify n (Z.succ z) (Z.succ z').
+Proof.
+  intros [Hz]. constructor. by rewrite <-bv_wrap_succ_idemp, Hz, bv_wrap_succ_idemp.
+Qed.
+Global Hint Resolve bv_wrap_simplify_succ | 10 : bv_wrap_simplify_db.
+
+Lemma bv_wrap_simplify_pred n z z' :
+  BvWrapSimplify n z z' →
+  BvWrapSimplify n (Z.pred z) (Z.pred z').
+Proof.
+  intros [Hz]. constructor. by rewrite <-bv_wrap_pred_idemp, Hz, bv_wrap_pred_idemp.
+Qed.
+Global Hint Resolve bv_wrap_simplify_pred | 10 : bv_wrap_simplify_db.
+
+Lemma bv_wrap_simplify_opp n z z' :
+  BvWrapSimplify n z z' →
+  BvWrapSimplify n (- z) (- z').
+Proof.
+  intros [Hz]. constructor. by rewrite <-bv_wrap_opp_idemp, Hz, bv_wrap_opp_idemp.
+Qed.
+Global Hint Resolve bv_wrap_simplify_opp | 10 : bv_wrap_simplify_db.
+
+Lemma bv_wrap_simplify_add n z1 z1' z2 z2' :
+  BvWrapSimplify n z1 z1' →
+  BvWrapSimplify n z2 z2' →
+  BvWrapSimplify n (z1 + z2) (z1' + z2').
+Proof.
+  intros [Hz1] [Hz2]. constructor.
+  by rewrite <-bv_wrap_add_idemp, Hz1, Hz2, bv_wrap_add_idemp.
+Qed.
+Global Hint Resolve bv_wrap_simplify_add | 10 : bv_wrap_simplify_db.
+
+Lemma bv_wrap_simplify_sub n z1 z1' z2 z2' :
+  BvWrapSimplify n z1 z1' →
+  BvWrapSimplify n z2 z2' →
+  BvWrapSimplify n (z1 - z2) (z1' - z2').
+Proof.
+  intros [Hz1] [Hz2]. constructor.
+  by rewrite <-bv_wrap_sub_idemp, Hz1, Hz2, bv_wrap_sub_idemp.
+Qed.
+Global Hint Resolve bv_wrap_simplify_sub | 10 : bv_wrap_simplify_db.
+
+Lemma bv_wrap_simplify_mul n z1 z1' z2 z2' :
+  BvWrapSimplify n z1 z1' →
+  BvWrapSimplify n z2 z2' →
+  BvWrapSimplify n (z1 * z2) (z1' * z2').
+Proof.
+  intros [Hz1] [Hz2]. constructor.
+  by rewrite <-bv_wrap_mul_idemp, Hz1, Hz2, bv_wrap_mul_idemp.
+Qed.
+Global Hint Resolve bv_wrap_simplify_mul | 10 : bv_wrap_simplify_db.
+
+(** [bv_wrap_simplify_left] applies for goals of the form [bv_wrap n z1 = _] and
+ tries to simplify them by removing any [bv_wrap] inside z1. *)
+Ltac bv_wrap_simplify_left :=
+  lazymatch goal with |- bv_wrap _ _ = _ => idtac end;
+  etrans; [ notypeclasses refine (bv_wrap_simplify_proof _ _ _);
+            typeclasses eauto with bv_wrap_simplify_db | ]
+.
+
+(** [bv_wrap_simplify] applies for goals of the form [bv_wrap n z1 = bv_wrap n z2] and
+[bv_swrap n z1 = bv_swrap n z2] and tries to simplify them by removing any [bv_wrap]
+and [bv_swrap] inside z1 and z2. *)
+Ltac bv_wrap_simplify :=
+  unfold bv_signed, bv_swrap;
+  try match goal with | |- _ - _ = _ - _ => f_equal end;
+  bv_wrap_simplify_left;
+  symmetry;
+  bv_wrap_simplify_left;
+  symmetry.
+
+Ltac bv_wrap_simplify_solve :=
+  bv_wrap_simplify; f_equal; lia.
+
+
+(** * Lemmas about [bv n] operations *)
+
+(** ** Unfolding lemmas for the operations. *)
+Section unfolding.
+  Context {n : N}.
+  Implicit Types (b : bv n).
+
+  Lemma bv_0_unsigned :
+    bv_unsigned (bv_0 n) = 0.
+  Proof. done. Qed.
+  Lemma bv_0_signed :
+    bv_signed (bv_0 n) = 0.
+  Proof. unfold bv_0. by rewrite bv_signed_BV, bv_swrap_0. Qed.
+
+  Lemma bv_succ_unsigned b :
+    bv_unsigned (bv_succ b) = bv_wrap n (Z.succ (bv_unsigned b)).
+  Proof. done. Qed.
+  Lemma bv_succ_signed b :
+    bv_signed (bv_succ b) = bv_swrap n (Z.succ (bv_signed b)).
+  Proof. unfold bv_succ. rewrite Z_to_bv_signed. bv_wrap_simplify_solve. Qed.
+
+  Lemma bv_pred_unsigned b :
+    bv_unsigned (bv_pred b) = bv_wrap n (Z.pred (bv_unsigned b)).
+  Proof. done. Qed.
+  Lemma bv_pred_signed b :
+    bv_signed (bv_pred b) = bv_swrap n (Z.pred (bv_signed b)).
+  Proof. unfold bv_pred. rewrite Z_to_bv_signed. bv_wrap_simplify_solve. Qed.
+
+  Lemma bv_add_unsigned b1 b2 :
+    bv_unsigned (b1 + b2) = bv_wrap n (bv_unsigned b1 + bv_unsigned b2).
+  Proof. done. Qed.
+  Lemma bv_add_signed b1 b2 :
+    bv_signed (b1 + b2) = bv_swrap n (bv_signed b1 + bv_signed b2).
+  Proof. unfold bv_add. rewrite Z_to_bv_signed. bv_wrap_simplify_solve. Qed.
+
+  Lemma bv_sub_unsigned b1 b2 :
+    bv_unsigned (b1 - b2) = bv_wrap n (bv_unsigned b1 - bv_unsigned b2).
+  Proof. done. Qed.
+  Lemma bv_sub_signed b1 b2 :
+    bv_signed (b1 - b2) = bv_swrap n (bv_signed b1 - bv_signed b2).
+  Proof. unfold bv_sub. rewrite Z_to_bv_signed. bv_wrap_simplify_solve. Qed.
+
+  Lemma bv_opp_unsigned b :
+    bv_unsigned (- b) = bv_wrap n (- bv_unsigned b).
+  Proof. done. Qed.
+  Lemma bv_opp_signed b :
+    bv_signed (- b) = bv_swrap n (- bv_signed b).
+  Proof. unfold bv_opp. rewrite Z_to_bv_signed. bv_wrap_simplify_solve. Qed.
+
+  Lemma bv_mul_unsigned b1 b2 :
+    bv_unsigned (b1 * b2) = bv_wrap n (bv_unsigned b1 * bv_unsigned b2).
+  Proof. done. Qed.
+  Lemma bv_mul_signed b1 b2 :
+    bv_signed (b1 * b2) = bv_swrap n (bv_signed b1 * bv_signed b2).
+  Proof. unfold bv_mul. rewrite Z_to_bv_signed. bv_wrap_simplify_solve. Qed.
+
+  Lemma bv_divu_unsigned b1 b2 :
+    bv_unsigned (b1 `divu` b2) = bv_unsigned b1 `div` bv_unsigned b2.
+  Proof. done. Qed.
+  Lemma bv_divu_signed b1 b2 :
+    bv_signed (b1 `divu` b2) = bv_swrap n (bv_unsigned b1 `div` bv_unsigned b2).
+  Proof. done. Qed.
+
+  Lemma bv_modu_unsigned b1 b2 :
+    bv_unsigned (b1 `modu` b2) = bv_unsigned b1 `mod` bv_unsigned b2.
+  Proof. done. Qed.
+  Lemma bv_modu_signed b1 b2 :
+    bv_signed (b1 `modu` b2) = bv_swrap n (bv_unsigned b1 `mod` bv_unsigned b2).
+  Proof. done. Qed.
+
+  Lemma bv_divs_unsigned b1 b2 :
+    bv_unsigned (b1 `divs` b2) = bv_wrap n (bv_signed b1 `div` bv_signed b2).
+  Proof. done. Qed.
+  Lemma bv_divs_signed b1 b2 :
+    bv_signed (b1 `divs` b2) = bv_swrap n (bv_signed b1 `div` bv_signed b2).
+  Proof. unfold bv_divs. rewrite Z_to_bv_signed. done. Qed.
+
+  Lemma bv_quots_unsigned b1 b2 :
+    bv_unsigned (b1 `quots` b2) = bv_wrap n (bv_signed b1 `quot` bv_signed b2).
+  Proof. done. Qed.
+  Lemma bv_quots_signed b1 b2 :
+    bv_signed (b1 `quots` b2) = bv_swrap n (bv_signed b1 `quot` bv_signed b2).
+  Proof. unfold bv_quots. rewrite Z_to_bv_signed. done. Qed.
+
+  Lemma bv_mods_unsigned b1 b2 :
+    bv_unsigned (b1 `mods` b2) = bv_wrap n (bv_signed b1 `mod` bv_signed b2).
+  Proof. done. Qed.
+  Lemma bv_mods_signed b1 b2 :
+    bv_signed (b1 `mods` b2) = bv_swrap n (bv_signed b1 `mod` bv_signed b2).
+  Proof. unfold bv_mods. rewrite Z_to_bv_signed. done. Qed.
+
+  Lemma bv_rems_unsigned b1 b2 :
+    bv_unsigned (b1 `rems` b2) = bv_wrap n (bv_signed b1 `rem` bv_signed b2).
+  Proof. done. Qed.
+  Lemma bv_rems_signed b1 b2 :
+    bv_signed (b1 `rems` b2) = bv_swrap n (bv_signed b1 `rem` bv_signed b2).
+  Proof. unfold bv_rems. rewrite Z_to_bv_signed. done. Qed.
+
+  Lemma bv_shiftl_unsigned b1 b2 :
+    bv_unsigned (b1 ≪ b2) = bv_wrap n (bv_unsigned b1 ≪ bv_unsigned b2).
+  Proof. done. Qed.
+  Lemma bv_shiftl_signed b1 b2 :
+    bv_signed (b1 ≪ b2) = bv_swrap n (bv_unsigned b1 ≪ bv_unsigned b2).
+  Proof. unfold bv_shiftl. rewrite Z_to_bv_signed. done. Qed.
+
+  Lemma bv_shiftr_unsigned b1 b2 :
+    bv_unsigned (b1 ≫ b2) = bv_unsigned b1 ≫ bv_unsigned b2.
+  Proof. done. Qed.
+  Lemma bv_shiftr_signed b1 b2 :
+    bv_signed (b1 ≫ b2) = bv_swrap n (bv_unsigned b1 ≫ bv_unsigned b2).
+  Proof. done. Qed.
+
+  Lemma bv_ashiftr_unsigned b1 b2 :
+    bv_unsigned (b1 `ashiftr` b2) = bv_wrap n (bv_signed b1 ≫ bv_unsigned b2).
+  Proof. done. Qed.
+  Lemma bv_ashiftr_signed b1 b2 :
+    bv_signed (b1 `ashiftr` b2) = bv_swrap n (bv_signed b1 ≫ bv_unsigned b2).
+  Proof. unfold bv_ashiftr. rewrite Z_to_bv_signed. done. Qed.
+
+  Lemma bv_or_unsigned b1 b2 :
+    bv_unsigned (bv_or b1 b2) = Z.lor (bv_unsigned b1) (bv_unsigned b2).
+  Proof. done. Qed.
+  Lemma bv_or_signed b1 b2 :
+    bv_signed (bv_or b1 b2) = bv_swrap n (Z.lor (bv_unsigned b1) (bv_unsigned b2)).
+  Proof. done. Qed.
+
+  Lemma bv_and_unsigned b1 b2 :
+    bv_unsigned (bv_and b1 b2) = Z.land (bv_unsigned b1) (bv_unsigned b2).
+  Proof. done. Qed.
+  Lemma bv_and_signed b1 b2 :
+    bv_signed (bv_and b1 b2) = bv_swrap n (Z.land (bv_unsigned b1) (bv_unsigned b2)).
+  Proof. done. Qed.
+
+  Lemma bv_xor_unsigned b1 b2 :
+    bv_unsigned (bv_xor b1 b2) = Z.lxor (bv_unsigned b1) (bv_unsigned b2).
+  Proof. done. Qed.
+  Lemma bv_xor_signed b1 b2 :
+    bv_signed (bv_xor b1 b2) = bv_swrap n (Z.lxor (bv_unsigned b1) (bv_unsigned b2)).
+  Proof. done. Qed.
+
+  Lemma bv_not_unsigned b :
+    bv_unsigned (bv_not b) = bv_wrap n (Z.lnot (bv_unsigned b)).
+  Proof. done. Qed.
+  Lemma bv_not_signed b :
+    bv_signed (bv_not b) = bv_swrap n (Z.lnot (bv_unsigned b)).
+  Proof. unfold bv_not. rewrite Z_to_bv_signed. done. Qed.
+
+  Lemma bv_zero_extend_unsigned' z b :
+    bv_unsigned (bv_zero_extend z b) = bv_wrap z (bv_unsigned b).
+  Proof. done. Qed.
+  (* [bv_zero_extend_unsigned] is the version that we want, but it
+  only holds with a precondition. *)
+  Lemma bv_zero_extend_unsigned z b :
+    (n ≤ z)%N →
+    bv_unsigned (bv_zero_extend z b) = bv_unsigned b.
+  Proof.
+    intros ?. rewrite bv_zero_extend_unsigned', bv_wrap_small; [done|].
+    bv_saturate. pose proof (bv_modulus_le_mono n z). lia.
+  Qed.
+  Lemma bv_zero_extend_signed z b :
+    bv_signed (bv_zero_extend z b) = bv_swrap z (bv_unsigned b).
+  Proof. unfold bv_zero_extend. rewrite Z_to_bv_signed. done. Qed.
+
+  Lemma bv_sign_extend_unsigned z b :
+    bv_unsigned (bv_sign_extend z b) = bv_wrap z (bv_signed b).
+  Proof. done. Qed.
+  Lemma bv_sign_extend_signed' z b :
+    bv_signed (bv_sign_extend z b) = bv_swrap z (bv_signed b).
+  Proof. unfold bv_sign_extend. rewrite Z_to_bv_signed. done. Qed.
+  (* [bv_sign_extend_signed] is the version that we want, but it
+  only holds with a precondition. *)
+  Lemma bv_sign_extend_signed z b :
+    (n ≤ z)%N →
+    bv_signed (bv_sign_extend z b) = bv_signed b.
+  Proof.
+    intros ?. rewrite bv_sign_extend_signed'.
+    destruct (decide (n = 0%N)); subst.
+    { by rewrite bv_signed_N_0, bv_swrap_0. }
+    apply bv_swrap_small. bv_saturate.
+    pose proof bv_half_modulus_le_mono n z. lia.
+  Qed.
+
+  Lemma bv_extract_unsigned s l b :
+    bv_unsigned (bv_extract s l b) = bv_wrap l (bv_unsigned b ≫ Z.of_N s).
+  Proof. done. Qed.
+  Lemma bv_extract_signed s l b :
+    bv_signed (bv_extract s l b) = bv_swrap l (bv_unsigned b ≫ Z.of_N s).
+  Proof. unfold bv_extract. rewrite Z_to_bv_signed. done. Qed.
+
+  Lemma bv_concat_unsigned' m  n2 b1 (b2 : bv n2) :
+    bv_unsigned (bv_concat m b1 b2) = bv_wrap m (Z.lor (bv_unsigned b1 ≪ Z.of_N n2) (bv_unsigned b2)).
+  Proof. done. Qed.
+  (* [bv_concat_unsigned] is the version that we want, but it
+  only holds with a precondition. *)
+  Lemma bv_concat_unsigned m n2 b1 (b2 : bv n2) :
+    (m = n + n2)%N →
+    bv_unsigned (bv_concat m b1 b2) = Z.lor (bv_unsigned b1 ≪ Z.of_N n2) (bv_unsigned b2).
+  Proof.
+    intros ->. rewrite bv_concat_unsigned', bv_wrap_small; [done|].
+    apply Z.bounded_iff_bits_nonneg'; [lia | |].
+    { apply Z.lor_nonneg. bv_saturate. split; [|lia]. apply Z.shiftl_nonneg. lia. }
+    intros k ?. rewrite Z.lor_spec, Z.shiftl_spec; [|lia].
+    apply orb_false_intro; (eapply Z.bounded_iff_bits_nonneg; [..|done]); bv_saturate; try lia.
+    - apply (Z.lt_le_trans _ (bv_modulus n)); [lia|]. apply Z.pow_le_mono_r; lia.
+    - apply (Z.lt_le_trans _ (bv_modulus n2)); [lia|]. apply Z.pow_le_mono_r; lia.
+  Qed.
+  Lemma bv_concat_signed m n2 b1 (b2 : bv n2) :
+    bv_signed (bv_concat m b1 b2) = bv_swrap m (Z.lor (bv_unsigned b1 ≪ Z.of_N n2) (bv_unsigned b2)).
+  Proof. unfold bv_concat. rewrite Z_to_bv_signed. done. Qed.
+
+  Lemma bv_add_Z_unsigned b z :
+    bv_unsigned (b `+Z` z) = bv_wrap n (bv_unsigned b + z).
+  Proof. done. Qed.
+  Lemma bv_add_Z_signed b z :
+    bv_signed (b `+Z` z) = bv_swrap n (bv_signed b + z).
+  Proof. unfold bv_add_Z. rewrite Z_to_bv_signed. bv_wrap_simplify_solve. Qed.
+
+  Lemma bv_sub_Z_unsigned b z :
+    bv_unsigned (b `-Z` z) = bv_wrap n (bv_unsigned b - z).
+  Proof. done. Qed.
+  Lemma bv_sub_Z_signed b z :
+    bv_signed (b `-Z` z) = bv_swrap n (bv_signed b - z).
+  Proof. unfold bv_sub_Z. rewrite Z_to_bv_signed. bv_wrap_simplify_solve. Qed.
+
+  Lemma bv_mul_Z_unsigned b z:
+    bv_unsigned (b `*Z` z) = bv_wrap n (bv_unsigned b * z).
+  Proof. done. Qed.
+  Lemma bv_mul_Z_signed b z :
+    bv_signed (b `*Z` z) = bv_swrap n (bv_signed b * z).
+  Proof. unfold bv_mul_Z. rewrite Z_to_bv_signed. bv_wrap_simplify_solve. Qed.
+End unfolding.
+
+(** ** Properties of bv operations *)
+Section properties.
+  Context {n : N}.
+  Implicit Types (b : bv n).
+  Local Open Scope bv_scope.
+
+  Lemma bv_sub_add_opp b1 b2:
+    b1 - b2 = b1 + - b2.
+  Proof.
+    apply bv_eq. unfold bv_sub, bv_add, bv_opp. rewrite !Z_to_bv_unsigned.
+    bv_wrap_simplify_solve.
+  Qed.
+
+  Global Instance bv_add_assoc : Assoc (=) (@bv_add n).
+  Proof.
+    intros ???. unfold bv_add. apply bv_eq. rewrite !Z_to_bv_unsigned.
+    bv_wrap_simplify_solve.
+  Qed.
+
+  Global Instance bv_mul_assoc : Assoc (=) (@bv_mul n).
+  Proof.
+    intros ???. unfold bv_mul. apply bv_eq. rewrite !Z_to_bv_unsigned.
+    bv_wrap_simplify_solve.
+  Qed.
+
+  Lemma bv_add_0_l b1 b2 :
+    bv_unsigned b1 = 0%Z →
+    b1 + b2 = b2.
+  Proof.
+    intros Hb. apply bv_eq.
+    rewrite bv_add_unsigned, Hb, Z.add_0_l, bv_wrap_small; [done|apply bv_unsigned_in_range].
+  Qed.
+
+  Lemma bv_add_0_r b1 b2 :
+    bv_unsigned b2 = 0%Z →
+    b1 + b2 = b1.
+  Proof.
+    intros Hb. apply bv_eq.
+    rewrite bv_add_unsigned, Hb, Z.add_0_r, bv_wrap_small; [done|apply bv_unsigned_in_range].
+  Qed.
+
+  Lemma bv_add_Z_0 b : b `+Z` 0 = b.
+  Proof.
+    unfold bv_add_Z. rewrite Z.add_0_r.
+    apply bv_eq. apply Z_to_bv_small. apply bv_unsigned_in_range.
+  Qed.
+
+  Lemma bv_add_Z_add_r b m o:
+    b `+Z` (m + o) = (b `+Z` o) `+Z` m.
+  Proof.
+    apply bv_eq. unfold bv_add_Z. rewrite !Z_to_bv_unsigned.
+     bv_wrap_simplify_solve.
+  Qed.
+
+  Lemma bv_add_Z_add_l b m o:
+    b `+Z` (m + o) = (b `+Z` m) `+Z` o.
+  Proof.
+    apply bv_eq. unfold bv_add_Z. rewrite !Z_to_bv_unsigned.
+     bv_wrap_simplify_solve.
+  Qed.
+
+  Lemma bv_add_Z_succ b m:
+    b `+Z` Z.succ m = (b `+Z` 1) `+Z` m.
+  Proof.
+    apply bv_eq. unfold bv_add_Z. rewrite !Z_to_bv_unsigned.
+    bv_wrap_simplify_solve.
+  Qed.
+
+  Lemma bv_add_Z_inj_l b i j:
+    0 ≤ i < bv_modulus n →
+    0 ≤ j < bv_modulus n →
+    b `+Z` i = b `+Z` j ↔ i = j.
+  Proof.
+    intros ??. split; [|naive_solver].
+    intros Heq%bv_eq. rewrite !bv_add_Z_unsigned, !(Z.add_comm (bv_unsigned _)) in Heq.
+    by rewrite <-bv_wrap_add_inj, !bv_wrap_small in Heq.
+  Qed.
+
+  Lemma bv_opp_not b:
+    - b `-Z` 1 = bv_not b.
+  Proof.
+    apply bv_eq.
+    rewrite bv_not_unsigned, bv_sub_Z_unsigned, bv_opp_unsigned, <-Z.opp_lnot.
+    bv_wrap_simplify_solve.
+  Qed.
+
+  Lemma bv_and_comm b1 b2:
+    bv_and b1 b2 = bv_and b2 b1.
+  Proof. apply bv_eq. by rewrite !bv_and_unsigned, Z.land_comm. Qed.
+
+  Lemma bv_or_comm b1 b2:
+    bv_or b1 b2 = bv_or b2 b1.
+  Proof. apply bv_eq. by rewrite !bv_or_unsigned, Z.lor_comm. Qed.
+
+  Lemma bv_or_0_l b1 b2 :
+    bv_unsigned b1 = 0%Z →
+    bv_or b1 b2 = b2.
+  Proof. intros Hb. apply bv_eq. by rewrite bv_or_unsigned, Hb, Z.lor_0_l. Qed.
+
+  Lemma bv_or_0_r b1 b2 :
+    bv_unsigned b2 = 0%Z →
+    bv_or b1 b2 = b1.
+  Proof. intros Hb. apply bv_eq. by rewrite bv_or_unsigned, Hb, Z.lor_0_r. Qed.
+
+  Lemma bv_extract_0_unsigned l b:
+    bv_unsigned (bv_extract 0 l b) = bv_wrap l (bv_unsigned b).
+  Proof. rewrite bv_extract_unsigned, Z.shiftr_0_r. done. Qed.
+
+  Lemma bv_extract_0_bv_add_distr l b1 b2:
+    (l ≤ n)%N →
+    bv_extract 0 l (bv_add b1 b2) = bv_add (bv_extract 0 l b1) (bv_extract 0 l b2).
+  Proof.
+    intros ?.
+    apply bv_eq. rewrite !bv_extract_0_unsigned, !bv_add_unsigned, !bv_extract_0_unsigned.
+    rewrite bv_wrap_bv_wrap by done.
+    bv_wrap_simplify_solve.
+  Qed.
+
+  Lemma bv_concat_0 m n2 b1 (b2 : bv n2) :
+    bv_unsigned b1 = 0%Z →
+    bv_concat m b1 b2 = bv_zero_extend m b2.
+  Proof.
+    intros Hb1. apply bv_eq.
+    by rewrite bv_zero_extend_unsigned', bv_concat_unsigned', Hb1, Z.shiftl_0_l, Z.lor_0_l.
+  Qed.
+
+  Lemma bv_zero_extend_idemp b:
+    bv_zero_extend n b = b.
+  Proof. apply bv_eq. by rewrite bv_zero_extend_unsigned. Qed.
+
+  Lemma bv_sign_extend_idemp b:
+    bv_sign_extend n b = b.
+  Proof. apply bv_eq_signed. by rewrite bv_sign_extend_signed. Qed.
+End properties.
+
+(** ** Lemmas about [bv_to_little] and [bv_of_little] *)
+Section little.
+
+  Lemma bv_to_litte_endian_unsigned m n z:
+    0 ≤ m →
+    bv_unsigned <$> bv_to_little_endian m n z = Z_to_little_endian m (Z.of_N n) z.
+  Proof.
+    intros ?. apply list_eq. intros i. unfold bv_to_little_endian.
+    rewrite list_lookup_fmap, list_lookup_fmap.
+    destruct (Z_to_little_endian m (Z.of_N n) z !! i) eqn: Heq; [simpl |done].
+    rewrite Z_to_bv_small; [done|].
+    eapply (Forall_forall (λ z, _ ≤ z < _)); [ |by eapply elem_of_list_lookup_2].
+    eapply Z_to_little_endian_bound; lia.
+  Qed.
+
+  Lemma bv_to_little_endian_to_bv m n bs:
+    m = Z.of_nat (length bs) →
+    bv_to_little_endian m n (little_endian_to_bv n bs) = bs.
+  Proof.
+    intros ->. apply (inj (fmap bv_unsigned)).
+    rewrite bv_to_litte_endian_unsigned; [|lia].
+    apply Z_to_little_endian_to_Z; [by rewrite fmap_length | lia |].
+    apply Forall_forall. intros ? [?[->?]]%elem_of_list_fmap_2. apply bv_unsigned_in_range.
+  Qed.
+
+  Lemma little_endian_to_bv_to_little_endian m n z:
+    0 ≤ m →
+    little_endian_to_bv n (bv_to_little_endian m n z) = z `mod` 2 ^ (m * Z.of_N n).
+  Proof.
+    intros ?. unfold little_endian_to_bv.
+    rewrite bv_to_litte_endian_unsigned; [|lia].
+    apply little_endian_to_Z_to_little_endian; lia.
+  Qed.
+
+  Lemma bv_to_little_endian_length m n z :
+    0 ≤ m →
+    length (bv_to_little_endian m n z) = Z.to_nat m.
+  Proof.
+    intros ?. unfold bv_to_little_endian. rewrite fmap_length.
+    apply Nat2Z.inj. rewrite Z_to_little_endian_length, ?Z2Nat.id; try lia.
+  Qed.
+
+  Lemma little_endian_to_bv_bound n bs :
+    0 ≤ little_endian_to_bv n bs < 2 ^ (Z.of_nat (length bs) * Z.of_N n).
+  Proof.
+    unfold little_endian_to_bv. rewrite <-(fmap_length bv_unsigned bs).
+    apply little_endian_to_Z_bound; [lia|].
+    apply Forall_forall. intros ? [? [-> ?]]%elem_of_list_fmap.
+    apply bv_unsigned_in_range.
+  Qed.
+
+  Lemma Z_to_bv_little_endian_to_bv_to_little_endian x m n (b : bv x):
+    0 ≤ m →
+    x = (Z.to_N m * n)%N →
+    Z_to_bv x (little_endian_to_bv n (bv_to_little_endian m n (bv_unsigned b))) = b.
+  Proof.
+    intros ? ->. rewrite little_endian_to_bv_to_little_endian, Z.mod_small; [| |lia].
+    - apply bv_eq. rewrite Z_to_bv_small; [done|]. apply bv_unsigned_in_range.
+    - pose proof bv_unsigned_in_range _ b as Hr. unfold bv_modulus in Hr.
+      by rewrite N2Z.inj_mul, Z2N.id in Hr.
+  Qed.
+
+  Lemma bv_to_little_endian_lookup_Some m n z (i : nat) x:
+    0 ≤ m → bv_to_little_endian m n z !! i = Some x ↔
+      Z.of_nat i < m ∧ x = Z_to_bv n (z ≫ (Z.of_nat i * Z.of_N n)).
+  Proof.
+    unfold bv_to_little_endian. intros Hm. rewrite list_lookup_fmap, fmap_Some.
+    split.
+    - intros [?[[??]%Z_to_little_endian_lookup_Some ?]]; [|lia..]; subst. split; [done|].
+      rewrite <-bv_wrap_land. apply bv_eq. by rewrite !Z_to_bv_unsigned, bv_wrap_bv_wrap.
+    - intros [?->]. eexists _. split; [apply Z_to_little_endian_lookup_Some; try done; lia| ].
+      rewrite <-bv_wrap_land. apply bv_eq. by rewrite !Z_to_bv_unsigned, bv_wrap_bv_wrap.
+  Qed.
+
+  Lemma little_endian_to_bv_spec n bs i b:
+    0 ≤ i → n ≠ 0%N →
+    bs !! Z.to_nat (i `div` Z.of_N n) = Some b →
+    Z.testbit (little_endian_to_bv n bs) i = Z.testbit (bv_unsigned b) (i `mod` Z.of_N n).
+  Proof.
+    intros ???. unfold little_endian_to_bv. apply little_endian_to_Z_spec; [lia|lia| |].
+    { apply Forall_fmap. apply Forall_true. intros ?; simpl. apply bv_unsigned_in_range. }
+    rewrite list_lookup_fmap. apply fmap_Some. naive_solver.
+  Qed.
+End little.
+
+(** ** Lemmas about [bv_seq] *)
+Section bv_seq.
+  Context {n : N}.
+  Implicit Types (b : bv n).
+
+  Lemma bv_seq_length b len:
+    length (bv_seq b len) = Z.to_nat len.
+  Proof. unfold bv_seq. by rewrite fmap_length, seqZ_length. Qed.
+
+  Lemma bv_seq_succ b m:
+    0 ≤ m →
+    bv_seq b (Z.succ m) = b :: bv_seq (b `+Z` 1) m.
+  Proof.
+    intros. unfold bv_seq. rewrite seqZ_cons by lia. csimpl.
+    rewrite bv_add_Z_0. f_equal.
+    assert (Z.succ 0 = 1 + 0) as -> by lia.
+    rewrite <-fmap_add_seqZ, <-list_fmap_compose, Z.pred_succ. apply list_fmap_ext.
+    intros i x. simpl. by rewrite bv_add_Z_add_l.
+  Qed.
+
+  Lemma NoDup_bv_seq b z:
+    0 ≤ z ≤ bv_modulus n →
+    NoDup (bv_seq b z).
+  Proof.
+    intros ?. apply NoDup_alt. intros i j b'. unfold bv_seq. rewrite !list_lookup_fmap.
+    intros [?[[??]%lookup_seqZ ?]]%fmap_Some ; simplify_eq.
+    intros [?[[->?]%lookup_seqZ ?%bv_add_Z_inj_l]]%fmap_Some; lia.
+  Qed.
+End bv_seq.
+
+(** ** Lemmas about [bv] and [bool] *)
+Section bv_bool.
+  Implicit Types (b : bool).
+
+  Lemma bool_to_bv_unsigned n b:
+    n ≠ 0%N →
+    bv_unsigned (bool_to_bv n b) = bool_to_Z b.
+  Proof.
+    intros ?. pose proof (bv_modulus_gt_1 n).
+    apply Z_to_bv_small. destruct b; simpl; lia.
+  Qed.
+
+  Lemma bv_extract_bool_to_bv n n2 b:
+    n ≠ 0%N → n2 ≠ 0%N →
+    bv_extract 0 n (bool_to_bv n2 b) = bool_to_bv n b.
+  Proof.
+    intros ??. apply bv_eq. pose proof (bv_modulus_gt_1 n).
+    rewrite bv_extract_unsigned, !bool_to_bv_unsigned, Z.shiftr_0_r by done.
+    rewrite bv_wrap_small; [done|]. destruct b; simpl; lia.
+  Qed.
+
+  Lemma bv_not_bool_to_bv b:
+    bv_not (bool_to_bv 1 b) = bool_to_bv 1 (negb b).
+  Proof. apply bv_eq. by destruct b. Qed.
+
+  Lemma bool_decide_bool_to_bv_0 b:
+    bool_decide (bv_unsigned (bool_to_bv 1 b) = 0) = negb b.
+  Proof. by destruct b. Qed.
+  Lemma bool_decide_bool_to_bv_1 b:
+    bool_decide (bv_unsigned (bool_to_bv 1 b) = 1) = b.
+  Proof. by destruct b. Qed.
+End bv_bool.
+
+Section bv_bits.
+  Context {n : N}.
+  Implicit Types (b : bv n).
+
+  Lemma bv_to_bits_length b : length (bv_to_bits b) = N.to_nat n.
+  Proof. unfold bv_to_bits. rewrite fmap_length, seqZ_length, <-Z_N_nat, N2Z.id. done. Qed.
+
+  Lemma bv_to_bits_lookup_Some b i x:
+    bv_to_bits b !! i = Some x ↔ (i < N.to_nat n)%nat ∧ x = Z.testbit (bv_unsigned b) (Z.of_nat i).
+  Proof.
+    unfold bv_to_bits. rewrite list_lookup_fmap, fmap_Some.
+    split.
+    - intros [?[?%lookup_seqZ?]]. naive_solver lia.
+    - intros [??]. eexists _. split; [|done]. apply lookup_seqZ. lia.
+  Qed.
+
+  Global Instance bv_to_bits_inj : Inj eq eq (@bv_to_bits n).
+  Proof.
+    unfold bv_to_bits. intros x y Hf.
+    apply bv_eq_wrap. apply Z.bits_inj_iff'. intros i Hi.
+    rewrite !bv_wrap_spec; [|lia..]. case_bool_decide; simpl; [|done].
+    eapply list_fmap_inj_1 in Hf; [done|]. apply elem_of_seqZ. lia.
+  Qed.
+End bv_bits.
+
+
+(** * [bvn] *)
+Record bvn := bv_to_bvn {
+  bvn_n : N;
+  bvn_val : bv bvn_n;
+}.
+Global Arguments bv_to_bvn {_} _.
+Add Printing Constructor bvn.
+
+Definition bvn_unsigned (b : bvn) := bv_unsigned (b.(bvn_val)).
+
+Lemma bvn_eq (b1 b2 : bvn) :
+  b1 = b2 ↔ b1.(bvn_n) = b2.(bvn_n) ∧ bvn_unsigned b1 = bvn_unsigned b2.
+Proof. split; [ naive_solver|]. destruct b1, b2; simpl; intros [??]. subst. f_equal. by apply bv_eq. Qed.
+
+Global Program Instance bvn_eq_dec : EqDecision bvn := λ '(@bv_to_bvn n1 b1) '(@bv_to_bvn n2 b2),
+   cast_if_and (decide (n1 = n2)) (decide (bv_unsigned b1 = bv_unsigned b2)).
+(* TODO: The following does not compute to eq_refl*)
+Next Obligation. intros. apply bvn_eq. naive_solver. Qed.
+Next Obligation. intros. intros ?%bvn_eq. naive_solver. Qed.
+Next Obligation. intros. intros ?%bvn_eq. naive_solver. Qed.
+
+Definition bvn_to_bv (n : N) (b : bvn) : option (bv n) :=
+  match decide (b.(bvn_n) = n) with
+  | left eq => Some (eq_rect (bvn_n b) (λ n0 : N, bv n0) (bvn_val b) n eq)
+  | right _ => None
+  end.
+Global Arguments bvn_to_bv !_ !_ /.
+
+Global Coercion bv_to_bvn : bv >-> bvn.
+
+(** * Opaqueness *)
+(** We mark all functions on bitvectors as opaque. *)
+Global Hint Opaque Z_to_bv
+       bv_0 bv_succ bv_pred
+       bv_add bv_sub bv_opp
+       bv_mul bv_divu bv_modu
+       bv_divs bv_quots bv_mods bv_rems
+       bv_shiftl bv_shiftr bv_ashiftr bv_or
+       bv_and bv_xor bv_not bv_zero_extend
+       bv_sign_extend bv_extract bv_concat
+       bv_add_Z bv_sub_Z bv_mul_Z
+       bool_to_bv bv_to_bits : typeclass_instances.
+Global Opaque Z_to_bv
+       bv_0 bv_succ bv_pred
+       bv_add bv_sub bv_opp
+       bv_mul bv_divu bv_modu
+       bv_divs bv_quots bv_mods bv_rems
+       bv_shiftl bv_shiftr bv_ashiftr bv_or
+       bv_and bv_xor bv_not bv_zero_extend
+       bv_sign_extend bv_extract bv_concat
+       bv_add_Z bv_sub_Z bv_mul_Z
+       bool_to_bv bv_to_bits.
diff --git a/stdpp_bitvector/tactics.v b/stdpp_bitvector/tactics.v
index e6a6fa02..ba5fb63a 100644
--- a/stdpp_bitvector/tactics.v
+++ b/stdpp_bitvector/tactics.v
@@ -1,5 +1,5 @@
 (** This file is maintained by Michael Sammler. *)
-From stdpp.bitvector Require Export bitvector.
+From stdpp.bitvector Require Export definitions.
 From stdpp Require Import options.
 
 (** * bitvector tactics *)
diff --git a/stdpp_unstable/bitblast.v b/stdpp_unstable/bitblast.v
index 46d7f804..166fd315 100644
--- a/stdpp_unstable/bitblast.v
+++ b/stdpp_unstable/bitblast.v
@@ -3,7 +3,7 @@ https://gitlab.mpi-sws.org/iris/stdpp/-/issues/141 for details on remaining
 issues before stabilization. This file is maintained by Michael Sammler. *)
 From Coq Require Import ssreflect.
 From Coq.btauto Require Export Btauto.
-From stdpp.bitvector Require Import bitvector.
+From stdpp.bitvector Require Import definitions.
 From stdpp Require Export tactics numbers list.
 From stdpp Require Import options.
 
diff --git a/tests/bitvector.ref b/tests/bitvector_definitions.ref
similarity index 100%
rename from tests/bitvector.ref
rename to tests/bitvector_definitions.ref
diff --git a/tests/bitvector.v b/tests/bitvector_definitions.v
similarity index 87%
rename from tests/bitvector.v
rename to tests/bitvector_definitions.v
index 2384f7d5..f33c3825 100644
--- a/tests/bitvector.v
+++ b/tests/bitvector_definitions.v
@@ -1,5 +1,5 @@
 From stdpp Require Import strings.
-From stdpp.bitvector Require Import bitvector.
+From stdpp.bitvector Require Import definitions.
 
 Check "notation_test".
 Check (BV 10 3 = BV 10 5).
-- 
GitLab