swh:1:snp:a009335c9ad61a15b4ffe398f445dd601942b68c
Raw File
Tip revision: 12569738ed0e90da87b908decdfdcd276c338596 authored by Pierre-Yves Strub on 05 September 2022, 14:53:59 UTC
Merge branch 'main' into deploy-quantum
Tip revision: 1256973
BitEncoding.ec
(* -------------------------------------------------------------------- *)
require import Bool AllCore IntDiv List Ring StdRing StdOrder StdBigop.
require (*--*) FinType Word.
(*---*) import IntID IntOrder Bigint Bigint.BIA.

pragma +implicits.

(* -------------------------------------------------------------------- *)
theory BS2Int.

op bs2int (s : bool list) =
  BIA.bigi predT (fun i => 2^i * b2i (nth false s i)) 0 (size s).

op int2bs (N n : int) =
  mkseq (fun i => (n %/ 2^i) %% 2 <> 0) N.

lemma size_int2bs N n : size (int2bs N n) = max 0 N.
proof. by apply/size_mkseq. qed.

lemma bs2int_nil : bs2int [] = 0.
proof. by rewrite /bs2int BIA.big_geq. qed.

lemma bs2int_cons x s :
  bs2int (x :: s) = b2i x + 2 * bs2int s.
proof.
  rewrite /bs2int /= (addrC 1) Bigint.BIA.big_int_recl ?size_ge0 /= expr0 /=.
  congr => //; rewrite Bigint.BIA.mulr_sumr; apply Bigint.BIA.eq_big_seq.
  move => K /= HK_range; rewrite (@eqz_leq _ 0) -ltzE ltzNge.
  move/mem_range: (HK_range) => [-> _] /=.
  by rewrite mulrA exprS //; move/mem_range: HK_range.
qed.

lemma bs2int_nseq_false N :
  bs2int (nseq N false) = 0.
proof.
  case (0 <= N) => [le0N|/ltzNge/ltzW leN0]; last by rewrite nseq0_le // bs2int_nil.
  elim N le0N => /=; rewrite ?nseq0 ?bs2int_nil // => N le0N.
  by rewrite nseqS // bs2int_cons /b2i => ->.
qed.

lemma nosmt bs2int_rcons b (s : bool list):
  bs2int (rcons s b) = (bs2int s) + 2^(size s) * (b2i b).
proof.
rewrite /bs2int size_rcons BIA.big_int_recr ?size_ge0 //=.
rewrite nth_rcons (@ltrr (size s)) /=; congr.
by apply/BIA.eq_big_int=> i [_ lt_is] /=; rewrite nth_rcons lt_is.
qed.

lemma int2bs0s i : int2bs 0 i = [].
proof. by apply/mkseq0. qed.

lemma int2bs0s_le N i : N <= 0 => int2bs N i = [].
proof. by apply/mkseq0_le. qed.

lemma int2bs0 N : int2bs N 0 = nseq N false.
proof.
apply/(eq_from_nth false); rewrite size_int2bs ?size_nseq //.
case: (N <= 0) => [le0_N i|]; 1: by rewrite ler_maxl ?ler_lt_asym.
move/ltrNge => @/max ^gt0_N ->/= i lt_in.
by rewrite nth_nseq ?nth_mkseq //= div0z mod0z.
qed.

lemma int2bs_nseq_false N n :
  (2 ^ N) %| n =>
  int2bs N n = nseq N false.
proof.
  move => /dvdzP [q ->>]; rewrite /int2bs -mkseq_nseq.
  apply eq_in_mkseq => K /mem_range HK_range /=.
  apply negeqF => /=; rewrite -IntDiv.divzpMr.
  + by apply dvdz_exp2l; move/mem_range: HK_range => [-> /ltzW].
  rewrite -IntDiv.exprD_subz //; first by move/mem_range: HK_range => [-> /ltzW].
  rewrite -dvdzE dvdz_mull; move: (dvdz_exp2l 2 1 (N - K)); rewrite expr1 /= => -> //.
  by apply/ltzS/ltr_subl_addr/ltr_subr_addr; move/mem_range: HK_range.
qed.

lemma nosmt int2bsS N i : 0 <= N =>
  int2bs (N + 1) i = rcons (int2bs N i) ((i %/ 2^N) %% 2 <> 0).
proof. by apply/mkseqS. qed.

lemma int2bs_cons N n :
  0 < N =>
  int2bs N n = (!2 %| n) :: int2bs (N - 1) (n %/ 2).
proof.
  move => lt0N; rewrite /int2bs; move: (subrK N 1) => {1}<-; rewrite mkseqSr /=; first by apply/subr_ge0; move/ltzE: lt0N.
  rewrite expr0 dvdzE /=; apply eq_in_mkseq => K [le0K _]; rewrite /(\o) /=.
  by rewrite -divz_mul // -exprS // (addrC 1).
qed.

lemma int2bs_rcons N n :
  0 < N =>
  int2bs N n = rcons (int2bs (N - 1) n) ((n %/ 2 ^ (N - 1)) %% 2 <> 0).
proof. by move => lt0N; rewrite -int2bsS // subr_ge0; move: lt0N; rewrite ltzE. qed.

lemma int2bs_mod N n :
  int2bs N (n %% 2 ^ N) = int2bs N n.
proof.
  apply eq_in_mkseq => K /mem_range HK_range /=.
  rewrite IntDiv.modz_pow2_div; first by move/mem_range: HK_range => [-> /=]; apply ltzW.
  rewrite modz_dvd //; move: (dvdz_exp2l 2 1 (N - K)); rewrite expr1 => /= -> //.
  by apply/ler_subr_addl/ltzE; move/mem_range: HK_range.
qed.

lemma int2bs_nseq_true N :
  int2bs N (2 ^ N - 1) = nseq N true.
proof.
  rewrite /int2bs -mkseq_nseq.
  apply eq_in_mkseq => K /mem_range HK_range /=.
  rewrite divzDl.
  + by apply dvdz_exp2l; move/mem_range: HK_range => [-> /ltzW].
  rewrite -IntDiv.exprD_subz //; first by move/mem_range: HK_range => [-> /ltzW].
  rewrite dvdz_modzDl.
  + move: (dvdz_exp2l 2 1 (N - K)); rewrite expr1 /= => -> //.
    by rewrite ler_subr_addr addrC -ltzE; case/mem_range: HK_range.
  move: (subrK (-1) (2 ^ K)) => <-; rewrite addrAC (addrC (-1)).
  rewrite divzDr; [by rewrite -{2}(mul1r (2 ^ K)) -mulNr dvdz_mull dvdzz|].
  rewrite divz_small; [by rewrite normrX ltzE //= subr_ge0 -ltzS ltr_addr expr_gt0|].
  by rewrite -{1}(mul1r (2 ^ K)) -mulNr mulzK //=; apply/gtr_eqF/expr_gt0.
qed.

lemma nosmt bs2int_eq N i j: 0 <= i => 0 <= j => i %% 2^N = j %% 2^N =>
  int2bs N i = int2bs N j.
proof.
move=> ge0_i ge0_j eq; apply/eq_in_mkseq=> k [ge0_k lt_kN] /=.
move/(congr1 (fun z => z %/ 2^k)): eq => /=.
have ge0_N: 0 <= N by apply/(ler_trans _ ge0_k)/ltrW.
rewrite !modz_pow2_div // ?ge0_k ?ltrW //.
move/(congr1 (fun z => z %% 2^1))=> /=; rewrite !modz_dvd_pow /=;
  first 2 by (rewrite ler_subr_addr lez_add1r).
by rewrite !expr1 => ->.
qed.

lemma bs2int_ge0 s : 0 <= bs2int s.
proof.
elim/last_ind: s => /= [|s b ih]; 1: by rewrite bs2int_nil.
by rewrite bs2int_rcons addr_ge0 ?ih mulr_ge0 ?b2i_ge0 ltrW expr_gt0.
qed.

lemma bs2int_le2Xs s : bs2int s < 2^(size s).
proof.
elim/last_ind: s=> /= [|s b ih]; first by rewrite bs2int_nil expr0.
rewrite bs2int_rcons size_rcons exprS ?size_ge0.
(have {2}->: 2=1+1 by done); rewrite mulrDl mul1r ltr_le_add //.
by rewrite ler_pimulr ?b2i_le1 ltrW expr_gt0.
qed.

(* -------------------------------------------------------------------- *)
lemma bs2intK s : int2bs (size s) (bs2int s) = s.
proof.
elim/last_ind: s=> /= [|s b ih]; 1: by rewrite bs2int_nil int2bs0s.
rewrite bs2int_rcons size_rcons int2bsS ?size_ge0.
rewrite -(@int2bs_mod (size s)).
rewrite mulrC modzMDr int2bs_mod; congr.
have gt0_2Xs: 0 < 2  ^ (size s) by rewrite expr_gt0.
rewrite divzMDr 1:gtr_eqF // divz_small /=.
  by rewrite bs2int_ge0 gtr0_norm ?bs2int_le2Xs.
by rewrite mod2_b2i b2i_eq0.
qed.

(* -------------------------------------------------------------------- *)
lemma int2bsK N i : 0 <= N => 0 <= i < 2^N => bs2int (int2bs N i) = i.
proof.
move=> ge0N; elim: N ge0N i=> /= [|n ge0_n ih] i.
  rewrite expr0 ltz1 -eqr_le => <-; rewrite int2bs0.
  by rewrite nseq0_le // bs2int_nil.
case=> [ge0_i lt_i2XSn]; rewrite int2bsS // bs2int_rcons.
rewrite -{1}int2bs_mod // ih //=.
  by rewrite modz_ge0 ?gtr_eqF ?ltz_pmod ?expr_gt0.
rewrite size_int2bs ler_maxr // eq_sym {1}(@divz_eq i (2^n)).
rewrite addrC mulrC; do 2! congr; move: lt_i2XSn.
rewrite exprS // -ltz_divLR ?expr_gt0 // => lt.
rewrite -{1}(@modz_small (i %/ 2^n) 2) ?ger0_norm ?b2i_mod2 //.
by rewrite lt /= divz_ge0 ?expr_gt0.
qed.

(* -------------------------------------------------------------------- *)
lemma int2bs_cat K N n :
  0 <= K <= N =>
  int2bs N n = (int2bs K n) ++ (int2bs (N - K) (n %/ (2 ^ K))).
proof.
  move => [le0K leKN]; rewrite /int2bs.
  rewrite -{1}(@subrK N K) (@addrC _ K).
  rewrite mkseq_add ?subr_ge0 //.
  congr; apply eq_in_mkseq => I /= [le0I _].
  by rewrite exprD_nneg // IntDiv.divz_mul // expr_ge0.
qed.

lemma int2bs1 N :
  0 < N =>
  int2bs N 1 = true :: nseq (N - 1) false.
proof. by move => lt0N; rewrite int2bs_cons // dvdzE /= int2bs_nseq_false. qed.

lemma int2bs_cat_nseq_true_false K N :
  0 <= K <= N =>
  int2bs N (2 ^ K - 1) = nseq K true ++ nseq (N - K) false.
proof.
  move => le2; move: (int2bs_cat _ _ (2 ^ K - 1) le2) => ->.
  rewrite int2bs_nseq_true divz_small; [by rewrite normrX ltzE //= subr_ge0 -ltzS ltr_addr expr_gt0|].
  by rewrite int2bs0.
qed.

lemma int2bs_mulr_pow2 K N n :
  0 <= K <= N =>
  int2bs N (2 ^ K * n) = nseq K false ++ int2bs (N - K) n.
proof.
  move => [le0K leKN]; rewrite /int2bs.
  rewrite -{1}(@subrK N K) (@addrC _ K).
  move: (subrK N K) => {1}<-.
  move: (addrC (N - K) K) => ->.
  rewrite mkseq_add //; first by apply/subr_ge0/ler_subl_addl => /=; apply/subr_ge0.
  rewrite -/int2bs.
  rewrite -(@int2bs_nseq_false _ (2 ^ K * n)).
  + by apply/dvdz_mulr/dvdzz.
  congr; rewrite // (addrC K) -(addrA N) /=.
  apply eq_in_mkseq => I /= /mem_range HI_range.
  rewrite exprD_nneg //; first by move/mem_range: HI_range.
  by rewrite divzMpl //; apply expr_gt0.
qed.

lemma int2bs_divr_pow2 K N n :
  int2bs N (n %/ 2 ^ K) = drop `|K| (int2bs (N + `|K|) n).
proof.
  rewrite pow_normr; case (0 <= N) => [le0N|/ltzNge/ltzW leN0]; last first.
  + by rewrite drop_oversize; [rewrite size_int2bs ler_maxrP normr_ge0 ler_naddl|rewrite int2bs0s_le].
  rewrite (@int2bs_cat `|K| (N + `|K|)) ?normr_ge0 ?lez_addr //= drop_size_cat -?addrA //=.
  by rewrite size_int2bs ler_maxr // normr_ge0.
qed.

lemma int2bs_pow2 K N :
  K \in range 0 N =>
  int2bs N (2 ^ K) = nseq K false ++ true :: nseq (N - 1 - K) false.
proof.
  move => HK_range; move: (int2bs_mulr_pow2 K N 1) => /= ->.
  + by split; move/mem_range: HK_range => // [_ ltKN] _; apply ltzW.
  rewrite int2bs1; first by apply subr_gt0; move/mem_range:HK_range.
  by rewrite addrAC.
qed.

(*-----------------------------------------------------------------------------*)
lemma bs2int_cat s1 s2 :
  bs2int (s1 ++ s2) = bs2int s1 + (2 ^ (size s1)) * bs2int s2.
proof.
  elim/last_ind: s2 => [|s2 b]; first by rewrite cats0 bs2int_nil.
  rewrite -rcons_cat !bs2int_rcons mulrDr addrA mulrA size_cat => ->.
  by rewrite exprD_nneg ?size_ge0.
qed.

lemma bs2int_range s :
  bs2int s \in range 0 (2 ^ size s).
proof. by apply/mem_range; split; [apply bs2int_ge0|move => _; apply bs2int_le2Xs]. qed.

lemma bs2int1 N :
  bs2int (true :: nseq N false) = 1.
proof. by rewrite bs2int_cons bs2int_nseq_false /b2i. qed.

lemma bs2int_nseq_true N :
  0 <= N =>
  bs2int (nseq N true) = 2 ^ N - 1.
proof.
  move => le0N; rewrite -int2bs_nseq_true int2bsK //.
  by rewrite ltzE //= subr_ge0 -ltzS ltr_addr expr_gt0.
qed.

lemma bs2int_cat_nseq_true_false K N :
  0 <= K <= N =>
  bs2int (nseq K true ++ nseq (N - K) false) = 2 ^ K - 1.
proof.
  move => le2; rewrite -int2bs_cat_nseq_true_false // int2bsK //.
  + by move: le2 => [? ?]; apply/(ler_trans K).
  by rewrite ltzE //= subr_ge0 -ltzS ltr_addr expr_gt0 //= ler_weexpn2l.
qed.

lemma bs2int_mulr_pow2 N s :
  bs2int (nseq N false ++ s) = 2 ^ max 0 N * bs2int s.
proof. by rewrite bs2int_cat bs2int_nseq_false size_nseq. qed.

lemma bs2int_pow2 K N :
  bs2int (nseq K false ++ true :: nseq N false) = 2 ^ max 0 K.
proof. by rewrite bs2int_mulr_pow2 bs2int1. qed.

end BS2Int.


(* -------------------------------------------------------------------- *)
theory BitReverse.

import BS2Int.

op bsrev N n = bs2int (rev (int2bs N n)).

lemma int2bs_bsrev N n :
  int2bs N (bsrev N n) = rev (int2bs N n).
proof.
  rewrite /bsrev; move: (bs2intK (rev (int2bs N n))).
  rewrite size_rev size_int2bs /max.
  by case (0 < N) => // /lezNgt ?; rewrite !int2bs0s_le.
qed.

lemma bsrev_bs2int s :
  bsrev (size s) (bs2int s) = bs2int (rev s).
proof. by rewrite /bsrev bs2intK. qed.

lemma bsrev_neg N n :
  N <= 0 =>
  bsrev N n = 0.
proof. by rewrite /bsrev => leN0; move: (size_int2bs N n); rewrite ler_maxl // size_eq0 => ->; rewrite rev_nil bs2int_nil. qed.

lemma bsrev_cons N n :
  0 < N =>
  bsrev N n = 2 ^ (N - 1) * b2i (!2 %| n) + bsrev (N - 1) (n %/ 2).
proof.
  move => lt0N; rewrite /bsrev int2bs_cons // rev_cons -cats1 bs2int_cat size_rev size_int2bs ler_maxr; first by rewrite -ltzS.
  by rewrite bs2int_cons; move: (bs2int_nseq_false 0); rewrite nseq0 => -> /=; rewrite addrC.
qed.

lemma bsrev_ge0 N n :
  0 <= bsrev N n.
proof. by rewrite /bsrev bs2int_ge0. qed.

lemma bsrev_lt_pow2 N n :
  bsrev N n < 2 ^ N.
proof.
  case (0 <= N) => [le0N|/ltrNge /ltzW leN0]; last by rewrite bsrev_neg // expr_gt0.
  rewrite /bsrev; move: (bs2int_le2Xs (rev (int2bs N n))).
  by rewrite size_rev size_int2bs ler_maxr.
qed.

lemma bsrev_range N n :
  bsrev N n \in range 0 (2 ^ N).
proof. by rewrite mem_range bsrev_ge0 bsrev_lt_pow2. qed.

lemma bsrev_cat K N n :
  0 <= K <= N =>
  bsrev N n = bsrev (N - K) (n %/ 2 ^ K) + 2 ^ (N - K) * bsrev K n.
proof. by move => [le0K leKN]; rewrite /bsrev (@int2bs_cat K) // rev_cat bs2int_cat size_rev size_int2bs ler_maxr // subr_ge0. qed.

lemma bsrev_mod N n :
  bsrev N (n %% 2 ^ N) = bsrev N n.
proof. by rewrite /bsrev int2bs_mod. qed.

lemma bsrev_involutive N n :
  0 <= N =>
  n \in range 0 (2 ^ N) =>
  bsrev N (bsrev N n) = n.
proof.
  move => le0N Hn_range; rewrite /bsrev.
  move: (bs2intK (rev (int2bs N n))).
  rewrite size_rev size_int2bs ler_maxr // => ->.
  by rewrite revK int2bsK // -mem_range.
qed.

lemma bsrev_injective N n1 n2 :
  0 <= N =>
  n1 \in range 0 (2 ^ N) =>
  n2 \in range 0 (2 ^ N) =>
  bsrev N n1 = bsrev N n2 =>
  n1 = n2.
proof. by move => le0N Hn1_range Hn2_range eq_bsrev; rewrite -(@bsrev_involutive N n1) // -(@bsrev_involutive N n2) // eq_bsrev. qed.

lemma bsrev_bijective N n1 n2 :
  0 <= N =>
  n1 \in range 0 (2 ^ N) =>
  n2 \in range 0 (2 ^ N) =>
  bsrev N n1 = bsrev N n2 <=>
  n1 = n2.
proof. by move => le0n Hn1_range Hn2_range; split => [|-> //]; apply bsrev_injective. qed.

lemma bsrev0 N :
  bsrev N 0 = 0.
proof. by rewrite /bsrev int2bs0 rev_nseq bs2int_nseq_false. qed.

lemma bsrev1 N :
  0 < N =>
  bsrev N 1 = 2 ^ (N - 1).
proof.
  move => lt0N; rewrite /bsrev int2bs1 // rev_cons bs2int_rcons rev_nseq.
  by rewrite size_nseq bs2int_nseq_false /b2i /= ler_maxr // subr_ge0; move/ltzE: lt0N.
qed.

lemma bsrev_mulr_pow2 K N n :
  0 <= K <= N =>
  bsrev N (2 ^ K * n) = bsrev N n %/ 2 ^ K.
proof.
  move => [le0K leKN]; rewrite /bsrev int2bs_mulr_pow2 // rev_cat rev_nseq bs2int_cat bs2int_nseq_false /=.
  rewrite (@int2bs_cat (N - K) N); first by split; [rewrite subr_ge0|move => _; rewrite ler_subl_addr ler_addl].
  rewrite rev_cat bs2int_cat size_rev size_int2bs opprD addrA /= ler_maxr // (mulrC (exp _ _)%IntDiv) divzMDr.
  + by apply/gtr_eqF/expr_gt0.
  by rewrite divz_small // -mem_range normrX_nat // bsrev_range.
qed.

lemma bsrev_divr_pow2 K N n :
  0 <= K <= N =>
  bsrev N (n %/ 2 ^ K) = (bsrev (N + K) n) %% (2 ^ N).
proof.
  move => [le0K leKN]; rewrite /bsrev int2bs_divr_pow2 ger0_norm // /int2bs drop_mkseq.
  + by split => // _; rewrite ler_addr (lez_trans K).
  rewrite -addrA /= addrC mkseq_add //; first by apply/(lez_trans K).
  rewrite rev_cat bs2int_cat size_rev size_mkseq ler_maxr //; first by apply/(lez_trans K).
  rewrite mulrC modzMDr modz_small //.
  rewrite -mem_range normrX_nat; first by apply/(lez_trans K).
  move: (bs2int_range (rev (mkseq (fun (i : int) => (fun (i0 : int) => n %/ 2 ^ i0 %% 2 <> 0) (K + i)) N))).
  by rewrite size_rev size_mkseq ler_maxr //; apply/(lez_trans K).
qed.

lemma bsrev_pow2 K N :
  K \in range 0 N =>
  bsrev N (2 ^ K) = 2 ^ (N - 1 - K).
proof.
  move => HK_range; move: (bsrev_mulr_pow2 K N 1) => /= ->; first by rewrite (@ltzW K); move/mem_range: HK_range.
  rewrite bsrev1; first by apply/(ler_lt_trans K); move/mem_range: HK_range.
  by rewrite -exprD_subz //= -(@ltzS K); move/mem_range: HK_range.
qed.

lemma bsrev_range_dvdz K N n :
  0 <= K <= N =>
  n \in range 0 (2 ^ (N - K)) =>
  2 ^ K %| bsrev N n.
proof.
  move => [le0K leKN] Hn_range; rewrite (@bsrev_cat (N - K)) //.
  + by rewrite subr_ge0 leKN /= ler_subl_addl -ler_subl_addr.
  rewrite opprD addrA /= divz_small; first by rewrite -mem_range normrX_nat // subr_ge0.
  by rewrite bsrev0 /= dvdz_mulr dvdzz.
qed.

lemma bsrev_dvdz_range K N n :
  0 <= K <= N =>
  (2 ^ (N - K)) %| n =>
  bsrev N n \in range 0 (2 ^ K).
proof.
  move => [le0K leKN] dvdz_n; rewrite (@bsrev_cat (N - K)).
  + by rewrite subr_ge0 leKN /= ler_subl_addl -ler_subl_addr.
  rewrite opprD addrA /=; move/dvdzP: dvdz_n => [q ->>].
  rewrite (mulrC q) bsrev_mulr_pow2.
  + by rewrite subr_ge0 leKN.
  rewrite (@divz_small (bsrev _ _)) /=; last by apply bsrev_range.
  by rewrite -mem_range normrX_nat ?subr_ge0 // bsrev_range.
qed.

lemma bsrev_add K N m n :
  0 <= K <= N =>
  m \in range 0 (2 ^ K) =>
  bsrev N (m + 2 ^ K * n) = bsrev N m + bsrev N n %/ 2 ^ K.
proof.
  move => [le0K leKN] Hm_range; rewrite (@bsrev_cat K) //.
  rewrite (mulrC (exp _ _)) divzMDr; first by apply/gtr_eqF/expr_gt0.
  rewrite divz_small /=; first by rewrite -mem_range normrX_nat.
  rewrite -(@bsrev_mod K) modzMDr bsrev_mod (addrC (bsrev _ _)); congr.
  + rewrite (@bsrev_cat K N) // divz_small; last by rewrite bsrev0.
    by rewrite -mem_range normrX_nat.
  rewrite (@bsrev_cat (N - K) N).
  + by split; [rewrite subr_ge0|move => _; rewrite ler_subl_addr ler_addl].
  rewrite !opprD addrA /= (mulrC (exp _ _)) divzMDr.
  + by apply/gtr_eqF/expr_gt0.
  by rewrite divz_small // -mem_range normrX_nat // bsrev_range.
qed.

lemma bsrev2_le M N n :
  0 <= M <= N =>
  bsrev M (bsrev N n) = n %% 2 ^ N %/ 2 ^ (N - M).
proof.
  move => [le0M leMN]; rewrite -(@bsrev_mod N) /bsrev (@int2bs_cat (N - M) N).
  + by rewrite subr_ge0 ler_subl_addl -ler_subl_addr.
  rewrite opprD addrA /= rev_cat bs2int_cat size_rev size_int2bs ler_maxr //.
  rewrite -{1}int2bs_mod mulrC modzMDr int2bs_mod.
  move: (bsrev_involutive M (n %% 2 ^ N %/ 2 ^ (N - M)) _ _) => //.
  rewrite range_div_range /=; first by apply/expr_gt0.
  rewrite -exprD_nneg //; first by apply/subr_ge0.
  rewrite addrA addrAC /=; move: (mem_range_mod n (2 ^ N)).
  by rewrite normrX_nat; [apply/(lez_trans M)|move => -> //; apply/gtr_eqF/expr_gt0].
qed.

lemma bsrev2_ge M N n :
  0 <= N <= M =>
  bsrev M (bsrev N n) = 2 ^ (M - N) * (n %% 2 ^ N).
proof.
  move => [le0N leNM]; rewrite -(@bsrev_mod N) /bsrev (@int2bs_cat N M) //.
  rewrite divz_small; first by rewrite -mem_range normrX_nat // bsrev_range.
  rewrite int2bs0 rev_cat rev_nseq bs2int_cat size_nseq ler_maxr ?subr_ge0 //.
  rewrite bs2int_nseq_false /=; congr; move: (bsrev_involutive N (n %% 2 ^ N) _ _) => //.
  by move: (mem_range_mod n (2 ^ N)); rewrite normrX_nat; [apply/(lez_trans N)|move => -> //; apply/gtr_eqF/expr_gt0].
qed.

lemma bsrev_range_pow2_perm_eq K N :
  0 <= K <= N =>
  perm_eq
    (map (bsrev N)             (range 0 (2 ^ K)))
    (map (( * ) (2 ^ (N - K))) (range 0 (2 ^ K))).
proof.
  move => [le0K leKN]; rewrite perm_eqP_pred1 => x.
  rewrite !count_uniq_mem.
  + rewrite map_inj_in_uniq ?range_uniq // => {x} x y Hx_range Hy_range.
    apply/bsrev_injective; first last.
    - by move: Hx_range; apply/mem_range_incl => //; apply/ler_weexpn2l.
    - by move: Hy_range; apply/mem_range_incl => //; apply/ler_weexpn2l.
    by apply/(lez_trans K).
  + rewrite map_inj_in_uniq ?range_uniq // => {x} x y Hx_range Hy_range.
    by apply/mulfI/gtr_eqF/expr_gt0.
  congr; rewrite eq_iff !mapP; split => -[y [Hy_range ->>]].
  + exists (bsrev N y %/ (2 ^ (N - K))).
    rewrite mulrC divzK /=.
    - apply/bsrev_range_dvdz; last by rewrite opprD addrA.
      by rewrite subr_ge0 leKN /= ger_addl oppz_le0.
    rewrite range_div_range ?expr_gt0 //= -exprD_nneg ?subr_ge0 //.
    by rewrite addrA addrAC /= bsrev_range.
  exists (bsrev N y %/ (2 ^ (N - K))).
  rewrite -bsrev_mulr_pow2 ?bsrev_involutive /=.
  + by rewrite subr_ge0 leKN /= ger_addl oppz_le0.
  + by apply/(lez_trans K).
  + rewrite mem_range_mull ?expr_gt0 //=.
    rewrite -(@mulN1r (exp _ _)%Int) -divzpMr.
    - by rewrite dvdz_exp2l subr_ge0 leKN /= ger_addl oppz_le0.
    rewrite mulN1r opprK -exprD_subz // ?opprD ?addrA //=.
    by rewrite subr_ge0 leKN /= ger_addl oppz_le0.
  by rewrite bsrev_dvdz_range // dvdz_mulr dvdzz.
qed.

lemma bsrev_mul_range_pow2_perm_eq K M N :
  0 <= K =>
  0 <= M =>
  K + M <= N =>
  perm_eq
    (map (bsrev N \o (( * ) (2 ^ M))) (range 0 (2 ^ K)))
    (map (( * ) (2 ^ (N - K - M)))    (range 0 (2 ^ K))).
proof.
  move => le0K le0M le_N.
  move: (eq_in_map (bsrev N \o ( * ) (2 ^ M)) (transpose (%/) (2 ^ M) \o (bsrev N)) (range 0 (2 ^ K))).
  move => [Heq_map _]; move: Heq_map => -> => [x Hx_range|]; last rewrite map_comp.
  + by rewrite /(\o) bsrev_mulr_pow2; first rewrite le0M /= (lez_trans (K + M)) // ler_addr.
  move: (eq_in_map (( * ) (2 ^ (N - K - M))) ((transpose (fun (m d : int) => m %/ d) (2 ^ M)) \o (( * ) (2 ^ (N - K)))) (range 0 (2 ^ K))).
  move => [Heq_map _]; move: Heq_map => -> => [x Hx_range|].
  + rewrite /(\o) /= -!(mulrC x) -divzpMr.
    - by rewrite dvdz_exp2l le0M /= ler_subr_addl.
    by rewrite -exprD_subz // le0M /= ler_subr_addl.
  rewrite (@map_comp (transpose _ _) (( * )%Int _)) perm_eq_map bsrev_range_pow2_perm_eq.
  by rewrite le0K /= (lez_trans (K + M)) // ler_addl.
qed.

lemma bsrev_cat_nseq_true_false K N :
  0 <= K <= N =>
  bsrev N (2 ^ K - 1) = 2 ^ N - 2 ^ (N - K).
proof.
  move => [? ?]; rewrite /bsrev int2bs_cat_nseq_true_false //.
  rewrite rev_cat !rev_nseq bs2int_mulr_pow2 bs2int_nseq_true // ler_maxr.
  + by rewrite subr_ge0.
  rewrite mulrDr -exprD_nneg //=.
  + by rewrite subr_ge0.
  by rewrite -addrA /= mulrN.
qed.

end BitReverse.


(* -------------------------------------------------------------------- *)
theory BitChunking.
op chunk r (bs : 'a list) =
  mkseq (fun i => take r (drop (r * i)%Int bs)) (size bs %/ r).

lemma nosmt chunk_le0 r (s : 'a list) : r <= 0 => chunk r s = [].
proof.
move/ler_eqVlt=> [->|gt0_r] @/chunk; 1: by rewrite mkseq0.
rewrite mkseq0_le // -oppr_ge0 -divzN.
by rewrite divz_ge0 ?size_ge0 oppr_gt0.
qed.

lemma nosmt nth_flatten x0 n (bs : 'a list list) i :
     all (fun s => size s = n) bs
  => nth x0 (flatten bs) i = nth x0 (nth [] bs (i %/ n)) (i %% n).
proof.
case: (n <= 0) => [ge0_n|/ltrNge gt0_n] /allP /= eqz.
  have bsE: bs = nseq (size bs) [].
    elim: bs eqz => /= [|b bs ih eqz]; 1: by rewrite nseq0.
    rewrite addrC nseqS ?size_ge0 -ih /=.
      by move=> x bsx; apply/eqz; rewrite bsx.
    by rewrite -size_eq0 -leqn0 ?size_ge0 eqz.
  rewrite {2}bsE nth_nseq_if if_same /=.
  rewrite bsE; elim/natind: (size bs)=> [m le0_m|m ge0_m ih];
    by rewrite ?nseqS // nseq0_le // flatten_nil.
case: (i < 0)=> [lt0_i|/lerNgt ge0_i].
  rewrite nth_neg // (@nth_neg []) // ltrNge.
  by rewrite divz_ge0 // -ltrNge.
elim: bs i ge0_i eqz => [|b bs ih] i ge0_i eqz /=.
  by rewrite flatten_nil.
have /(_ b) /= := eqz; rewrite flatten_cons nth_cat => ->.
have <-: (i < n) <=> (i %/ n = 0) by rewrite -divz_eq0 // ge0_i.
case: (i < n) => [lt_in|/lerNgt le_ni]; 2: rewrite ih ?subr_ge0 //.
+ by rewrite modz_small // ge0_i ltr_normr ?lt_in.
+ by move=> x bx; have := eqz x; apply; rewrite /= bx.
rewrite -mulN1r modzMDr; congr.
case: (n = 0)=> [^zn ->/=|nz_n]; 2: by rewrite divzMDr 1?addrC.
rewrite eq_sym nth_neg ?oppr_lt0 // => {ih}; move: eqz.
by case: bs => // c bs /(_ c) /=; rewrite zn size_eq0 => ->.
qed.

lemma size_chunk r (bs : 'a list) : 0 < r =>
  size (chunk r bs) = size bs %/ r.
proof.
move=> gt0_r; rewrite size_mkseq ler_maxr //.
by rewrite divz_ge0 ?gt0_r ?size_ge0.
qed.

lemma in_chunk_size r (bs : 'a list) b: 0 < r =>
  mem (chunk r bs) b => size b = r.
proof.
move=> gt0_r /mapP [i] [] /mem_iota /= [ge0_i ^lt_is +] ->.
rewrite ltzE -(@ler_pmul2r r) 1:gt0_r divzE mulrDl mul1r.
rewrite -ler_subr_addr addrAC => /ler_trans/(_ (size bs - r) _).
  by rewrite ler_naddr // oppr_le0 modz_ge0 gtr_eqF ?gt0_r.
rewrite (mulrC i) ler_subr_addl -ler_subr_addr => ler.
have ge0_r: 0 <= r by apply/ltrW/gt0_r.
rewrite size_take ?ge0_r size_drop // 1:mulr_ge0 ?ge0_r //.
rewrite ler_maxr 1:subr_ge0 1:-subr_ge0 1:(ler_trans r) ?ge0_r //.
by move/ler_eqVlt: ler=> [<-|->].
qed.

lemma size_flatten_chunk r (bs : 'a list) : 0 < r =>
  size (flatten (chunk r bs)) = (size bs) %/ r * r.
proof.
move=> gt0_r; rewrite size_flatten sumzE big_map.
pose F := fun (x : 'a list) => r; rewrite predT_comp /(\o) /= big_seq.
rewrite (@eq_bigr _ _ F) /F /=; first by move=> i; apply/in_chunk_size.
rewrite -(@big_seq _ (chunk r bs)) big_constz count_predT.
by rewrite size_chunk // mulrC.
qed.

lemma chunkK r (bs : 'a list) : 0 < r =>
  r %| size bs => flatten (chunk r bs) = bs.
proof.
move=> gt0_r dvd_d_bs; apply/(eq_from_nth witness)=> [|i].
  by rewrite size_flatten_chunk // divzK.
rewrite size_flatten_chunk ?divzK // => -[ge0_i lt_ibs].
rewrite (@nth_flatten witness r); 1: apply/allP=> s.
by move/(@in_chunk_size _ _ _ gt0_r).
rewrite nth_mkseq /= 1:divz_ge0 ?ge0_i ?ltz_divRL ?gt0_r //=.
  by apply/(@ler_lt_trans i)=> //; rewrite lez_floor gtr_eqF ?gt0_r.
rewrite nth_take ?ltz_pmod 1:ltrW ?gt0_r nth_drop; last 2 first.
  by rewrite modz_ge0 ?gtr_eqF ?gt0_r. by rewrite (@mulrC r) -divz_eq.
by rewrite mulr_ge0 1:ltrW ?gt0_r // divz_ge0 // gt0_r.
qed.

lemma flattenK r (bs : 'a list list) : 0 < r =>
  (forall b, mem bs b => size b = r) => chunk r (flatten bs) = bs.
proof.
move=> gt0_r; elim: bs => [|x xs ih] h.
  by rewrite flatten_nil /chunk div0z mkseq0.
rewrite flatten_cons /chunk size_cat h // -{3}(mul1r r).
rewrite divzMDl ?gtr_eqF // mkseq_add //=.
  by rewrite divz_ge0 1:gt0_r size_ge0.
rewrite mkseq1 /= drop0 take_cat h //= take0 cats0 /= -{3}ih.
  by move=> b xsb; apply/h; right.
apply/eq_in_mkseq=> i /=; rewrite mulrDr mulr1 drop_cat (@h x) //.
case=> [ge0_i lti]; rewrite ltrNge ler_paddr // 1:mulr_ge0 1:ltrW //.
by rewrite addrAC addrN.
qed.

lemma chunk_cat r (xs ys : 'a list) :
  r %| size xs => chunk r (xs ++ ys) = chunk r xs ++ chunk r ys.
proof.
case: (r <= 0) => [/chunk_le0<:'a> h|/ltrNge lt0_r]; 1: by rewrite !h.
case/dvdzP=> q @/chunk szxs; rewrite size_cat szxs divzMDl ?gtr_eqF //.
rewrite mulzK ?gtr_eqF // mkseq_add ?divz_ge0 ?size_ge0 //=.
  by rewrite -(@ler_pmul2r r) //= -szxs size_ge0.
congr; last first; apply/eq_in_mkseq=> i /=.
  case=> [ge0_i lt_i] /=; rewrite drop_cat szxs (mulrC r) ltr_pmul2r //.
  by rewrite  gtr_addl ltrNge ge0_i /= mulrDl addrAC subrr /= mulrC.
case=> [ge0_i lt_iq]; rewrite drop_cat szxs (mulrC r).
rewrite ltr_pmul2r ?lt_iq //= take_cat; pose s := drop _ _.
suff /ler_eqVlt[->|->//]: r <= size s; 1: rewrite ltrr /=.
  by rewrite take0 take_size cats0.
rewrite size_drop ?mulr_ge0 // 1:ltrW // szxs -mulrBl.
rewrite ler_maxr ?mulr_ge0 1,2:ltrW ?subr_gt0 ?ler_pmull //.
by rewrite ler_subr_addl -ltzE.
qed.
end BitChunking.
back to top