aboutsummaryrefslogtreecommitdiffstats
path: root/src/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/common')
-rw-r--r--src/common/IntegerExtra.v328
-rw-r--r--src/common/Maps.v31
-rw-r--r--src/common/Monad.v19
-rw-r--r--src/common/Statemonad.v18
-rw-r--r--src/common/Vericertlib.v35
-rw-r--r--src/common/ZExtra.v266
6 files changed, 666 insertions, 31 deletions
diff --git a/src/common/IntegerExtra.v b/src/common/IntegerExtra.v
index c9b5dbd..2b6cded 100644
--- a/src/common/IntegerExtra.v
+++ b/src/common/IntegerExtra.v
@@ -1,11 +1,30 @@
-Require Import BinInt.
-Require Import Lia.
-Require Import ZBinary.
-
-From bbv Require Import ZLib.
-From compcert Require Import Integers Coqlib.
-
-Require Import Vericertlib.
+(*
+ * Vericert: Verified high-level synthesis.
+ * Copyright (C) 2020-2021 Yann Herklotz <yann@yannherklotz.com>
+ * 2020 James Pollard <j@mes.dev>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *)
+
+Require Import Coq.ZArith.BinInt.
+Require Import Coq.micromega.Lia.
+Require Import Coq.Numbers.Integer.Binary.ZBinary.
+
+Require Import compcert.lib.Coqlib.
+Require Import compcert.lib.Integers.
+
+Require Import vericert.common.Vericertlib.
Local Open Scope Z_scope.
@@ -319,7 +338,7 @@ Module IntExtra.
assert (zwordsize = 4 * Byte.zwordsize) by reflexivity.
fold (testbit (shru n (repr Byte.zwordsize)) i). rewrite bits_shru.
change (unsigned (repr Byte.zwordsize)) with Byte.zwordsize.
- apply zlt_true. omega. omega.
+ apply zlt_true. lia. lia.
Qed.
Lemma bits_byte3:
@@ -329,7 +348,7 @@ Module IntExtra.
assert (zwordsize = 4 * Byte.zwordsize) by reflexivity.
fold (testbit (shru n (repr (2 * Byte.zwordsize))) i). rewrite bits_shru.
change (unsigned (repr (2 * Byte.zwordsize))) with (2 * Byte.zwordsize).
- apply zlt_true. omega. omega.
+ apply zlt_true. lia. lia.
Qed.
Lemma bits_byte4:
@@ -339,7 +358,7 @@ Module IntExtra.
assert (zwordsize = 4 * Byte.zwordsize) by reflexivity.
fold (testbit (shru n (repr (3 * Byte.zwordsize))) i). rewrite bits_shru.
change (unsigned (repr (3 * Byte.zwordsize))) with (3 * Byte.zwordsize).
- apply zlt_true. omega. omega.
+ apply zlt_true. lia. lia.
Qed.
Lemma bits_ofwords:
@@ -362,4 +381,291 @@ Module IntExtra.
rewrite testbit_repr; auto.
Abort.
+ Lemma div_divs_equiv :
+ forall x y,
+ signed x >= 0 ->
+ signed y >= 0 ->
+ divs x y = divu x y.
+ Proof.
+ unfold divs, divu.
+ intros.
+ rewrite !signed_eq_unsigned;
+ try rewrite Zquot.Zquot_Zdiv_pos; try reflexivity;
+ lazymatch goal with
+ | |- unsigned _ <= max_signed =>
+ solve [rewrite <- signed_positive; assumption]
+ | |- 0 <= unsigned _ => solve [apply unsigned_range_2]
+ end.
+ Qed.
+
+ Lemma neg_signed' :
+ forall x : int,
+ unsigned x <> 2147483648 ->
+ signed (neg x) = - signed x.
+ Proof.
+ intros x Hhalf.
+ Transparent repr.
+ unfold signed.
+ simpl.
+ rewrite Z_mod_modulus_eq.
+ replace modulus with 4294967296; auto.
+ replace half_modulus with 2147483648; auto.
+ repeat match goal with | |- context[if ?x then _ else _] => destruct x end.
+ - destruct (Z.eq_dec (unsigned x) 0).
+ + rewrite e. auto.
+ + pose proof (Z.mod_opp_l_nz (unsigned x) 4294967296).
+ assert (4294967296 <> 0) by lia.
+ apply H in H0.
+ rewrite H0 in l.
+ pose proof (Z.mod_small (unsigned x) 4294967296).
+ assert (0 <= unsigned x < 4294967296).
+ pose proof (unsigned_range_2 x). lia.
+ apply H1 in H2. rewrite H2 in l. lia.
+ rewrite Z.mod_small. assumption.
+ pose proof (unsigned_range_2 x). lia.
+ - destruct (Z.eq_dec (unsigned x) 0).
+ + lia.
+ + rewrite Z.mod_opp_l_nz; try lia.
+ rewrite Z.opp_sub_distr.
+ rewrite Z.mod_small. lia.
+ pose proof (unsigned_range_2 x).
+ simplify; lia.
+ rewrite Z.mod_small. assumption.
+ pose proof (unsigned_range_2 x).
+ simplify; lia.
+ - destruct (Z.eq_dec (unsigned x) 0).
+ + rewrite e in *. rewrite Z.opp_0 in *. rewrite Zmod_0_l in g. lia.
+ + rewrite Z.mod_opp_l_nz; try lia.
+ rewrite Z.mod_small. lia.
+ pose proof (unsigned_range_2 x). lia.
+ rewrite Z.mod_small. assumption.
+ pose proof (unsigned_range_2 x). lia.
+ - destruct (Z.eq_dec (unsigned x) 0).
+ + lia.
+ + rewrite Z.mod_opp_l_nz in g; try lia.
+ rewrite Z.mod_small in g.
+ assert (unsigned x < 2147483648) by lia. lia.
+ pose proof (unsigned_range_2 x).
+ replace max_unsigned with 4294967295 in * by auto. lia.
+ rewrite Z.mod_small. assumption.
+ pose proof (unsigned_range_2 x).
+ replace max_unsigned with 4294967295 in * by auto. lia.
+ Qed.
+
+ Lemma neg_divs_distr_l :
+ forall x y,
+ unsigned x <> 2147483648 ->
+ neg (divs x y) = divs (neg x) y.
+ Proof.
+ intros x y Hhalf. unfold divs, neg.
+ set (x' := signed x). set (y' := signed y).
+ apply eqm_samerepr.
+ apply eqm_trans with (- (Z.quot x' y')).
+ auto with ints.
+ replace (- (Z.quot x' y')) with (Z.quot (- x') y')
+ by (rewrite Zquot.Zquot_opp_l; auto).
+ unfold x'.
+ rewrite <- neg_signed'.
+ auto with ints.
+ assumption.
+ Qed.
+
+ Lemma neg_signed :
+ forall x : int,
+ unsigned x <> 2147483648 ->
+ signed x < 0 ->
+ signed (neg x) >= 0.
+ Proof.
+ intros.
+ rewrite neg_signed'. lia.
+ assumption.
+ Qed.
+
+ Lemma shl_signed_positive :
+ forall y,
+ unsigned y <= 30 ->
+ signed (shl one y) >= 0.
+ Proof.
+ intros.
+ unfold signed, shl.
+ destruct (zlt (unsigned (repr (Z.shiftl (unsigned one) (unsigned y)))) half_modulus).
+ - rewrite unsigned_repr.
+ + rewrite Z.shiftl_1_l.
+ apply Z.le_ge. apply Z.pow_nonneg. lia.
+ + rewrite Z.shiftl_1_l. split.
+ apply Z.pow_nonneg. lia.
+ simplify.
+ replace (4294967295) with (2 ^ 32 - 1); try lia.
+ transitivity (2 ^ 31); try lia.
+ apply Z.pow_le_mono_r; lia.
+ - simplify. rewrite Z.shiftl_1_l in g.
+ unfold half_modulus, modulus, wordsize,
+ Wordsize_32.wordsize in *. unfold two_power_nat in *. simplify.
+ unfold Z_mod_modulus in *.
+ destruct (2 ^ unsigned y) eqn:?.
+ apply Z.ge_le in g. exfalso.
+ replace (4294967296 / 2) with (2147483648) in g; auto.
+ rewrite Z.shiftl_1_l. rewrite Heqz.
+ unfold wordsize in *. unfold Wordsize_32.wordsize in *.
+ rewrite Zbits.P_mod_two_p_eq in *.
+ replace (4294967296 / 2) with (2147483648) in g; auto.
+ rewrite <- Heqz in g.
+ rewrite Z.mod_small in g.
+ replace (2147483648) with (2 ^ 31) in g.
+ pose proof (Z.pow_le_mono_r 2 (unsigned y) 30).
+ apply Z.ge_le in g.
+ assert (0 < 2) by lia. apply H0 in H1. lia. assumption. lia.
+ split. lia. rewrite two_power_nat_equiv.
+ apply Z.pow_lt_mono_r; lia.
+
+ pose proof (Zlt_neg_0 p).
+ pose proof (Z.pow_nonneg 2 (unsigned y)). rewrite <- Heqz in H0.
+ lia.
+ Qed.
+
+ Lemma is_power2_shl :
+ forall y,
+ unsigned y <= 30 ->
+ is_power2 (shl one y) = Some y.
+ Proof.
+ intros.
+ unfold is_power2, shl.
+ destruct (Zbits.Z_is_power2 (unsigned (repr (Z.shiftl (unsigned one) (unsigned y))))) eqn:?.
+ - simplify.
+ rewrite Z_mod_modulus_eq in Heqo.
+ rewrite Z.mod_small in Heqo. rewrite Z.shiftl_1_l in Heqo.
+ rewrite <- two_p_correct in Heqo.
+ rewrite Zbits.Z_is_power2_complete in Heqo. inv Heqo.
+ rewrite repr_unsigned. auto.
+ pose proof (unsigned_range_2 y). lia.
+ rewrite Z.shiftl_1_l. unfold modulus, wordsize, Wordsize_32.wordsize.
+ rewrite two_power_nat_equiv.
+ split. apply Z.pow_nonneg. lia.
+ apply Z.pow_lt_mono_r; lia.
+ - simplify.
+ rewrite Z_mod_modulus_eq in Heqo.
+ rewrite Z.mod_small in Heqo. rewrite Z.shiftl_1_l in Heqo.
+ rewrite <- two_p_correct in Heqo.
+ rewrite Zbits.Z_is_power2_complete in Heqo. discriminate.
+ pose proof (unsigned_range_2 y). lia.
+ rewrite Z.shiftl_1_l. unfold modulus, wordsize, Wordsize_32.wordsize.
+ rewrite two_power_nat_equiv.
+ split. apply Z.pow_nonneg. lia.
+ apply Z.pow_lt_mono_r; lia.
+ Qed.
+
+ Definition shrx_alt (x y : int) : int :=
+ if zlt (signed x) 0
+ then neg (shru (neg x) y)
+ else shru x y.
+
+ Lemma shrx_shrx_alt_equiv_ne :
+ forall x y,
+ unsigned x <> 2147483648 ->
+ unsigned y <= 30 ->
+ shrx x y = shrx_alt x y.
+ Proof.
+ intros x y Hhalf H.
+ unfold shrx, shrx_alt, lt.
+ destruct (Z_ge_lt_dec (signed x) 0);
+ [rewrite zlt_false | rewrite zlt_true];
+
+ repeat lazymatch goal with
+ | |- is_power2 _ = Some _ => apply is_power2_shl
+ | |- signed (shl one _) >= 0 => apply shl_signed_positive
+ | |- signed (neg _) >= 0 => apply neg_signed
+ | |- divs _ _ = divu _ _ => apply div_divs_equiv
+ | |- divs ?x (shl one ?y) = neg (shru (neg ?x) ?y) =>
+ rewrite <- neg_involutive at 1; rewrite neg_divs_distr_l;
+ try assumption; f_equal
+ | |- divs ?x (shl one ?y) = shru ?x ?y =>
+ let H := fresh "H" in
+ pose proof (divu_pow2 x (shl one y) y) as H;
+ rewrite <- H
+ end; try assumption.
+ Qed.
+
+ Lemma shrx_shrx_alt_equiv_eq :
+ forall x y,
+ unsigned x = 2147483648 ->
+ unsigned y <= 30 ->
+ shrx x y = shrx_alt x y.
+ Proof.
+ intros.
+ repeat unfold shrx, shrx_alt, signed, divs, neg.
+ replace half_modulus with 2147483648 by auto.
+ replace modulus with 4294967296 by auto.
+ simplify.
+ rewrite !Z_mod_modulus_eq.
+ rewrite !H.
+ simplify.
+ assert (Hshl: Z.shiftl 1 (unsigned y) mod 4294967296 = Z.shiftl 1 (unsigned y)).
+ { apply Z.mod_small.
+ rewrite Z.shiftl_1_l.
+ split.
+ apply Z.pow_nonneg. lia.
+ replace 4294967296 with (2^32) by auto.
+ apply Z.le_lt_trans with (m := 2 ^ 31); try lia.
+ apply Z.pow_le_mono_r; lia.
+ }
+ rewrite !Hshl.
+ f_equal.
+ assert ((Z.shiftl 1 (unsigned y)) < 2147483648).
+ rewrite Z.shiftl_1_l.
+ replace 2147483648 with (2^31) by auto.
+ apply Z.le_lt_trans with (m := 2 ^ 30); try lia.
+ apply Z.pow_le_mono_r; lia.
+ destruct (zlt (Z.shiftl 1 (unsigned y)) 2147483648); try lia.
+ replace (-2147483648 mod 4294967296) with 2147483648 by auto.
+ assert (Hmodeq : Z.shiftr 2147483648 (unsigned y) mod 4294967296
+ = Z.shiftr 2147483648 (unsigned y)).
+ { apply Z.mod_small. split.
+ apply Z.shiftr_nonneg. lia.
+ rewrite Z.shiftr_div_pow2.
+ replace 4294967296 with (Z.succ 4294967295); auto.
+ apply Zle_lt_succ.
+ replace 4294967295 with (4294967295 * (2 ^ unsigned y) / (2 ^ unsigned y)).
+ 2: {
+ apply Z.div_mul.
+ pose proof (Z.pow_pos_nonneg 2 (unsigned y)).
+ apply not_eq_sym.
+ apply Z.le_neq. apply H2; try lia.
+ apply unsigned_range_2.
+ }
+
+ apply Z.div_le_mono.
+ apply Z.pow_pos_nonneg. lia.
+ apply unsigned_range_2.
+ transitivity 4294967295; try lia.
+ apply Z.le_mul_diag_r; try lia.
+ replace 1 with (Z.succ 0) by auto.
+ apply Z.le_succ_l.
+ apply Z.pow_pos_nonneg; try lia.
+ apply unsigned_range_2. apply unsigned_range_2.
+ }
+ rewrite !Hmodeq.
+ replace (-2147483648) with (Z.opp 2147483648) by auto.
+ rewrite Zquot.Zquot_opp_l.
+ f_equal.
+ rewrite Zquot.Zquot_Zdiv_pos.
+ rewrite Z.shiftr_div_pow2.
+ rewrite Z.shiftl_1_l. auto.
+ apply unsigned_range_2.
+ lia.
+ rewrite Z.shiftl_1_l.
+ apply Z.lt_le_incl.
+ apply Z.pow_pos_nonneg; try lia.
+ apply unsigned_range_2.
+ Qed.
+
+ Theorem shrx_shrx_alt_equiv :
+ forall x y,
+ unsigned y <= 30 ->
+ shrx x y = shrx_alt x y.
+ Proof.
+ intros.
+ destruct (Z.eq_dec (unsigned x) 2147483648);
+ [ apply shrx_shrx_alt_equiv_eq | apply shrx_shrx_alt_equiv_ne]; auto.
+ Qed.
+
End IntExtra.
diff --git a/src/common/Maps.v b/src/common/Maps.v
index 7a45259..2db5114 100644
--- a/src/common/Maps.v
+++ b/src/common/Maps.v
@@ -1,11 +1,32 @@
-From vericert Require Import Vericertlib.
-
-From compcert Require Export Maps.
-From compcert Require Import Errors.
-Import PTree.
+(*
+ * Vericert: Verified high-level synthesis.
+ * Copyright (C) 2020 Yann Herklotz <yann@yannherklotz.com>
+ * 2020 James Pollard <j@mes.dev>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *)
Set Implicit Arguments.
+Require Export compcert.lib.Maps.
+
+Require Import compcert.common.Errors.
+
+Require Import vericert.common.Vericertlib.
+
+Import PTree.
+
Local Open Scope error_monad_scope.
(** Instance of traverse for [PTree] and [Errors]. This should maybe be generalised
diff --git a/src/common/Monad.v b/src/common/Monad.v
index c42f4a3..68233b1 100644
--- a/src/common/Monad.v
+++ b/src/common/Monad.v
@@ -1,3 +1,22 @@
+(*
+ * Vericert: Verified high-level synthesis.
+ * Copyright (C) 2020 Yann Herklotz <yann@yannherklotz.com>
+ * 2021 Michalis Pardalos <mpardalos@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *)
+
From Coq Require Import BinNums Lists.List.
From compcert Require Import Maps.
diff --git a/src/common/Statemonad.v b/src/common/Statemonad.v
index 2eada2f..16dcbbf 100644
--- a/src/common/Statemonad.v
+++ b/src/common/Statemonad.v
@@ -1,3 +1,21 @@
+(*
+ * Vericert: Verified high-level synthesis.
+ * Copyright (C) 2020 Yann Herklotz <yann@yannherklotz.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *)
+
From compcert Require Errors.
From vericert Require Import Monad.
From Coq Require Import Lists.List.
diff --git a/src/common/Vericertlib.v b/src/common/Vericertlib.v
index 8b56c7d..a0d2af1 100644
--- a/src/common/Vericertlib.v
+++ b/src/common/Vericertlib.v
@@ -1,6 +1,7 @@
(*
* Vericert: Verified high-level synthesis.
- * Copyright (C) 2019-2020 Yann Herklotz <yann@yannherklotz.com>
+ * Copyright (C) 2019-2021 Yann Herklotz <yann@yannherklotz.com>
+ * 2020 James Pollard <j@mes.dev>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -16,21 +17,23 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*)
-From Coq Require Export
- String
- ZArith
- Znumtheory
- List
- Bool.
+Set Implicit Arguments.
-Require Import Lia.
+Require Export Coq.Bool.Bool.
+Require Export Coq.Lists.List.
+Require Export Coq.Strings.String.
+Require Export Coq.ZArith.ZArith.
+Require Export Coq.ZArith.Znumtheory.
+Require Import Coq.micromega.Lia.
-From vericert Require Import Show.
+Require Export compcert.lib.Coqlib.
+Require Import compcert.lib.Integers.
+
+Require Export vericert.common.VericertTactics.
+Require Import vericert.common.Show.
(* Depend on CompCert for the basic library, as they declare and prove some
useful theorems. *)
-From compcert.lib Require Export Coqlib.
-From compcert Require Import Integers.
Local Open Scope Z_scope.
@@ -70,7 +73,12 @@ Ltac solve_by_invert := solve_by_inverts 1.
Ltac invert x := inversion x; subst; clear x.
Ltac destruct_match :=
- match goal with | [ |- context[match ?x with | _ => _ end ] ] => destruct x end.
+ match goal with
+ | [ |- context[match ?x with | _ => _ end ] ] => destruct x eqn:?
+ | [ H: context[match ?x with | _ => _ end] |- _ ] => destruct x eqn:?
+ end.
+
+Ltac auto_destruct x := destruct x eqn:?; simpl in *; try discriminate; try congruence.
Ltac nicify_hypotheses :=
repeat match goal with
@@ -180,7 +188,8 @@ Ltac liapp :=
| _ => idtac
end.
-Ltac crush := simplify; try discriminate; try congruence; try lia; liapp; try assumption.
+Ltac crush := simplify; try discriminate; try congruence; try lia; liapp;
+ try assumption; try (solve [auto]).
Global Opaque Nat.div.
Global Opaque Z.mul.
diff --git a/src/common/ZExtra.v b/src/common/ZExtra.v
index 519ee7c..62ca54d 100644
--- a/src/common/ZExtra.v
+++ b/src/common/ZExtra.v
@@ -1,8 +1,270 @@
-Require Import ZArith.
-Require Import Lia.
+(*
+ * Vericert: Verified high-level synthesis.
+ * Copyright (C) 2020 Yann Herklotz <yann@yannherklotz.com>
+ * 2020 James Pollard <j@mes.dev>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *)
+
+Require Import Coq.ZArith.BinInt.
+Require Import Coq.micromega.Lia.
+Require Import Coq.ZArith.ZArith.
Local Open Scope Z_scope.
+Module ZLib.
+
+Lemma mod2_cases: forall (n: Z), n mod 2 = 0 \/ n mod 2 = 1.
+Proof.
+ intros. pose proof (Z.mod_pos_bound n 2). lia.
+Qed.
+
+Lemma div_mul_undo: forall a b,
+ b <> 0 ->
+ a mod b = 0 ->
+ a / b * b = a.
+Proof.
+ intros.
+ pose proof Z.div_mul_cancel_l as A. specialize (A a 1 b).
+ replace (b * 1) with b in A by lia.
+ rewrite Z.div_1_r in A.
+ rewrite Z.mul_comm.
+ rewrite <- Z.divide_div_mul_exact; try assumption.
+ - apply A; congruence.
+ - apply Z.mod_divide; assumption.
+Qed.
+
+Lemma mod_0_r: forall (m: Z),
+ m mod 0 = 0.
+Proof.
+ intros. destruct m; reflexivity.
+Qed.
+
+Lemma sub_mod_0: forall (a b m: Z),
+ a mod m = 0 ->
+ b mod m = 0 ->
+ (a - b) mod m = 0.
+Proof.
+ intros *. intros E1 E2.
+ rewrite Zminus_mod.
+ rewrite E1. rewrite E2.
+ reflexivity.
+Qed.
+
+Lemma add_mod_0: forall a b m : Z,
+ a mod m = 0 ->
+ b mod m = 0 ->
+ (a + b) mod m = 0.
+Proof.
+ intros *. intros E1 E2.
+ rewrite Zplus_mod.
+ rewrite E1. rewrite E2.
+ reflexivity.
+Qed.
+
+Lemma Z_mod_mult': forall a b : Z,
+ (a * b) mod a = 0.
+Proof.
+ intros. rewrite Z.mul_comm. apply Z_mod_mult.
+Qed.
+
+Lemma mod_add_r: forall a b,
+ b <> 0 ->
+ (a + b) mod b = a mod b.
+Proof.
+ intros. rewrite <- Z.add_mod_idemp_r by lia.
+ rewrite Z.mod_same by lia.
+ rewrite Z.add_0_r.
+ reflexivity.
+Qed.
+
+Lemma mod_pow2_same_cases: forall a n,
+ a mod 2 ^ n = a ->
+ 2 ^ n = 0 /\ a = 0 \/ 0 <= a < 2 ^ n.
+Proof.
+ intros.
+ assert (n < 0 \/ 0 <= n) as C by lia. destruct C as [C | C].
+ - left. rewrite (Z.pow_neg_r 2 n C) in *. rewrite mod_0_r in H. auto.
+ - right.
+ rewrite <- H. apply Z.mod_pos_bound.
+ apply Z.pow_pos_nonneg; lia.
+Qed.
+
+Lemma mod_pow2_same_bounds: forall a n,
+ a mod 2 ^ n = a ->
+ 0 <= n ->
+ 0 <= a < 2 ^ n.
+Proof.
+ intros. rewrite <- H. apply Z.mod_pos_bound.
+ apply Z.pow_pos_nonneg; lia.
+Qed.
+
+Lemma pow2_nonneg: forall n,
+ 0 <= 2 ^ n.
+Proof.
+ intros. apply Z.pow_nonneg. lia.
+Qed.
+
+Lemma pow2_pos: forall n,
+ 0 <= n ->
+ 0 < 2 ^ n.
+Proof.
+ intros. apply Z.pow_pos_nonneg; lia.
+Qed.
+
+Lemma pow2_times2: forall i,
+ 0 < i ->
+ 2 ^ i = 2 * 2 ^ (i - 1).
+Proof.
+ intros.
+ rewrite <- Z.pow_succ_r by lia.
+ f_equal.
+ lia.
+Qed.
+
+Lemma pow2_div2: forall i,
+ 0 <= i ->
+ 2 ^ (i - 1) = 2 ^ i / 2.
+Proof.
+ intros.
+ assert (i = 0 \/ 0 < i) as C by lia. destruct C as [C | C].
+ - subst. reflexivity.
+ - rewrite Z.pow_sub_r by lia.
+ reflexivity.
+Qed.
+
+Lemma div_mul_undo_le: forall a b,
+ 0 <= a ->
+ 0 < b ->
+ a / b * b <= a.
+Proof.
+ intros.
+ pose proof (Zmod_eq_full a b) as P.
+ pose proof (Z.mod_bound_pos a b) as Q.
+ lia.
+Qed.
+
+Lemma testbit_true_nonneg: forall a i,
+ 0 <= a ->
+ 0 <= i ->
+ Z.testbit a i = true ->
+ 2 ^ i <= a.
+Proof.
+ intros.
+ apply Z.testbit_true in H1; [|assumption].
+ pose proof (pow2_pos i H0).
+ eapply Z.le_trans; [| apply (div_mul_undo_le a (2 ^ i)); lia].
+ replace (2 ^ i) with (1 * 2 ^ i) at 1 by lia.
+ apply Z.mul_le_mono_nonneg_r; [lia|].
+ pose proof (Z.div_pos a (2 ^ i)).
+ assert (a / 2 ^ i <> 0); [|lia].
+ intro E. rewrite E in H1. cbv in H1. discriminate H1.
+Qed.
+
+Lemma range_div_pos: forall a b c d,
+ 0 < d ->
+ a <= b <= c ->
+ a / d <= b / d <= c / d.
+Proof.
+ intuition idtac.
+ - apply (Z.div_le_mono _ _ _ H H1).
+ - apply (Z.div_le_mono _ _ _ H H2).
+Qed.
+
+Lemma testbit_true_nonneg': forall a i,
+ 0 <= i ->
+ 2 ^ i <= a < 2 ^ (i + 1) ->
+ Z.testbit a i = true.
+Proof.
+ intros.
+ apply Z.testbit_true; [assumption|].
+ destruct H0 as [A B].
+ pose proof (pow2_pos i H) as Q.
+ apply (Z.div_le_mono _ _ _ Q) in A.
+ rewrite Z_div_same in A by lia.
+ pose proof (Z.div_lt_upper_bound a (2 ^ i) 2 Q) as P.
+ rewrite Z.mul_comm in P.
+ replace i with (i + 1 - 1) in P by lia.
+ rewrite <- pow2_times2 in P by lia.
+ specialize (P B).
+ replace (i + 1 - 1) with i in P by lia.
+ replace (a / 2 ^ i) with 1 by lia.
+ reflexivity.
+Qed.
+
+Lemma testbit_false_nonneg: forall a i,
+ 0 <= a < 2 ^ i ->
+ 0 < i ->
+ Z.testbit a (i - 1) = false ->
+ a < 2 ^ (i - 1).
+Proof.
+ intros.
+ assert (2 ^ (i - 1) <= a < 2 ^ i \/ a < 2 ^ (i - 1)) as C by lia.
+ destruct C as [C | C]; [exfalso|assumption].
+ assert (Z.testbit a (i - 1) = true); [|congruence].
+ replace i with (i - 1 + 1) in C at 2 by lia.
+ apply testbit_true_nonneg'; lia.
+Qed.
+
+Lemma signed_bounds_to_sz_pos: forall sz n,
+ - 2 ^ (sz - 1) <= n < 2 ^ (sz - 1) ->
+ 0 < sz.
+Proof.
+ intros.
+ assert (0 < sz \/ sz - 1 < 0) as C by lia.
+ destruct C as [C | C]; [assumption|exfalso].
+ rewrite Z.pow_neg_r in H by assumption.
+ lia.
+Qed.
+
+Lemma two_digits_encoding_inj_lo: forall base a b c d: Z,
+ 0 <= b < base ->
+ 0 <= d < base ->
+ base * a + b = base * c + d ->
+ b = d.
+Proof.
+ intros.
+ pose proof Z.mod_unique as P.
+ specialize P with (b := base) (q := c) (r := d).
+ specialize P with (2 := H1).
+ rewrite P by lia.
+ rewrite <- Z.add_mod_idemp_l by lia.
+ rewrite Z.mul_comm.
+ rewrite Z.mod_mul by lia.
+ rewrite Z.add_0_l.
+ rewrite Z.mod_small by lia.
+ reflexivity.
+Qed.
+
+Lemma two_digits_encoding_inj_hi: forall base a b c d: Z,
+ 0 <= b < base ->
+ 0 <= d < base ->
+ base * a + b = base * c + d ->
+ a = c.
+Proof.
+ intros. nia.
+Qed.
+
+Lemma Z_to_nat_neg: forall (n: Z),
+ n < 0 ->
+ Z.to_nat n = 0%nat.
+Proof.
+ intros. destruct n; (lia||reflexivity).
+Qed.
+
+End ZLib.
+
Module ZExtra.
Lemma mod_0_bounds :