Fixpoint countLeadingZerosWord_nat ni: word ni -> nat :=
match ni return word ni -> nat with
| 0 => fun _ => 0
| S m => fun e =>
if (weq (split2 m 1 (nat_cast (fun n => word n) (eq_sym (Nat.add_1_r m)) e)) WO~0)
then 1 + @countLeadingZerosWord_nat m (split1 m 1 (nat_cast (fun n => word n) (eq_sym (Nat.add_1_r m)) e))
else 0
end.

(* Specifications: *)
Lemma countLeadingZerosWord_nat_correct ni:
forall no (w: word ni),
ni < pow2 no ->
#(countLeadingZerosWord no w) = countLeadingZerosWord_nat w.
Lemma countLeadingZerosWord_nat_le_len ni (w: word ni):
countLeadingZerosWord_nat w <= ni.
Lemma countLeadingZerosWord_enough_size ni no no' (pf: ni < pow2 no) (pf': ni < pow2 no'): forall (w: word ni),
#(countLeadingZerosWord no w) =
#(countLeadingZerosWord no' w).
(* Usually this kind of lemmas would need a guarantee that "(wordToN a mod wordToN b)%N" does not overflow, but fortunately this can never overflow. And also, we don't need to prevent b from being 0. *)
Lemma wordToN_mod: forall sz (a b: word sz),
wordToN (a ^% b) = (wordToN a mod wordToN b)%N.
Lemma wordToNat_mod: forall sz (a b: word sz),
b <> $0 ->
#(a ^% b) = #a mod #b.
Lemma wlshift_mul_pow2: forall sz n (a: word sz),
wlshift a n = a ^* $ (pow2 n).
Lemma wlshift_mul_Zpow2: forall sz n (a: word sz),
(0 <= n)%Z ->
wlshift a (Z.to_nat n) = a ^* ZToWord sz (2 ^ n).
Lemma wlshift_distr_plus: forall sz n (a b: word sz),
wlshift (a ^+ b) n = wlshift a n ^+ wlshift b n.
Lemma wlshift'_distr_plus: forall sz n (a b: word sz),
wlshift' (a ^+ b) n = wlshift' a n ^+ wlshift' b n.
Lemma wlshift_iter: forall sz n1 n2 (a: word sz),
wlshift (wlshift a n1) n2 = wlshift a (n1 + n2).
Lemma wlshift'_iter: forall sz n1 n2 (a: word sz),
wlshift' (wlshift' a n1) n2 = wlshift' a (n1 + n2).
Lemma wlshift_zero: forall sz n, wlshift $0 n = natToWord sz 0.
Lemma wlshift'_zero: forall sz n, wlshift' $0 n = natToWord sz 0.
Lemma sext_natToWord_nat_cast: forall sz2 sz1 sz n (e: sz1 + sz2 = sz),
2 * n < pow2 sz1 ->
nat_cast word e (sext (natToWord sz1 n) sz2) = natToWord sz n.
Lemma sext_neg_natToWord_nat_cast: forall sz2 sz1 sz n (e: sz1 + sz2 = sz),
2 * n < pow2 sz1 ->
nat_cast word e (sext (wneg (natToWord sz1 n)) sz2) = wneg (natToWord sz n).
Lemma sext0: forall sz0 sz (v: word sz) (e: sz0 = 0),
sext v sz0 = nat_cast word (eq_ind_r (fun sz0 : nat => sz = sz + sz0) (plus_n_O sz) e) v.
Lemma wordToN_wordToZ: forall (sz : nat) (w : word sz),
wordToN w = Z.to_N (wordToZ w + Z.of_N (if wmsb w false then Npow2 sz else 0%N)).
Lemma uwordToZ_ZToWord_0: forall (sz : nat) (z : Z),
(0 <= z < Z.of_N (Npow2 sz))%Z ->
uwordToZ (ZToWord sz z) = z.
Lemma uwordToZ_ZToWord: forall (sz : nat) (z : Z),
(0 <= z < 2 ^ (Z.of_nat sz))%Z ->
uwordToZ (ZToWord sz z) = z.
Lemma NToWord_Z_to_N: forall sz n,
(0 <= n)%Z ->
NToWord sz (Z.to_N n) = ZToWord sz n.
Lemma uwordToZ_ZToWord_k: forall (sz : nat) (n : Z),
(0 <= n)%Z ->
exists k, uwordToZ (ZToWord sz n) = (n - k * 2 ^ Z.of_nat sz)%Z /\ (k * 2 ^ Z.of_nat sz <= n)%Z.
Lemma Zpow2_pos: forall n, (2 ^ Z.of_nat n > 0)%Z.
Lemma uwordToZ_bound: forall sz (a: word sz),
(0 <= uwordToZ a < 2 ^ Z.of_nat sz)%Z.
Lemma uwordToZ_ZToWord_mod: forall (sz : nat) (z : Z),
(0 <= z)%Z ->
uwordToZ (ZToWord sz z) = (z mod 2 ^ (Z.of_nat sz))%Z.
Lemma ZToWord_uwordToZ: forall sz (a: word sz),
ZToWord sz (uwordToZ a) = a.
Lemma wordToN_neq_0: forall sz (b : word sz),
b <> $0 ->
wordToN b <> 0%N.
(* These counterexamples will hopefully be found by users who use commands such as "Search ((_ ^+ _) ^% _)" *)
Lemma wmod_plus_distr_does_not_hold: ~ forall sz (a b m: word sz),
m <> $0 ->
(a ^+ b) ^% m = ((a ^% m) ^+ (b ^% m)) ^% m.
Lemma wmul_mod_distr_does_not_hold: ~ forall sz (a b n: word sz),
n <> $0 ->
(a ^* b) ^% n = ((a ^% n) ^* (b ^% n)) ^% n.
Lemma Nmod_0_r: forall a : N, (a mod 0)%N = a.
Lemma wordToN_0: forall sz,
wordToN (natToWord sz 0) = 0%N.
Lemma NToWord_0: forall sz,
NToWord sz 0 = $ (0).
Lemma wmod_0_r: forall sz (a: word sz), a ^% $0 = a.
Lemma wordToN_NToWord_eqn: forall sz (n : N),
wordToN (NToWord sz n) = (n mod Npow2 sz)%N.
Lemma Nminus_mod_idemp_r: forall a b n : N,
(n <> 0)%N ->
(b <= a)%N ->
((a - b mod n) mod n)%N = ((a - b) mod n)%N.
Lemma drop_sub_N: forall sz (n k : N),
(k * Npow2 sz <= n)%N ->
NToWord sz (n - k * Npow2 sz) = NToWord sz n.
Lemma wmod_divides: forall sz (a b: word sz),
a ^% b = $0 ->
exists k, a = b ^* k.
Lemma wmod_divides_other_direction_does_not_hold: ~ forall sz (a b: word sz),
b <> $0 ->
(exists k, a = b ^* k) ->
a ^% b = $0.
Lemma wmod_mul_does_not_hold: ~ forall sz (a b: word sz),
b <> $0 ->
(a ^* b) ^% b = $0.
Lemma wmult_plus_distr_l: forall (sz : nat) (x y z : word sz),
z ^* (x ^+ y) = z ^* x ^+ z ^* y.
Lemma wmod_same: forall sz (a: word sz), a ^% a = $0.
Lemma wmod_0_l: forall sz (m: word sz),
$0 ^% m = $0.
Lemma wmod_plus_distr: forall sz (a b m: word sz),
(exists k, (wordToN m * k)%N = Npow2 sz) ->
(a ^+ b) ^% m = ((a ^% m) ^+ (b ^% m)) ^% m.
Lemma wmod_mul: forall sz (a b: word sz),
(exists k, (wordToN b * k)%N = Npow2 sz) ->
(a ^* b) ^% b = $0.
Lemma combine_zero_general sz1 sz2:
forall (w: word sz1) (b: word sz2), (combine w b = $ 0 -> w = $ 0 /\ b = $ 0)%word.
Lemma combine_lt sz1 sz2:
forall (w1 w2: word sz1) (b1 b2: word sz2), (combine w1 b1 < combine w2 b2 ->
b1 <= b2)%word.
Lemma split2_le sz1 sz2:
forall (w1 w2: word (sz1 + sz2)), (w1 <= w2 ->
split2 _ _ w1 <= split2 _ _ w2)%word.
Lemma word1_neq': forall w : word 1, w <> WO~1 -> w = WO~0.
Lemma combine_ge sz1 sz2 (x1 y1: word sz1) (x2 y2: word sz2):
(combine x1 x2 <= combine y1 y2 ->
x2 < y2 \/ (x2 = y2 /\ x1 <= y1))%word.