Section parm_sec.
Variable M_in_Z_ : Z_ (2 ^ w).
Variable ptM'cM'' : Z_ w * Z_ w * Z_ (2 ^ w) * Z_ (2 ^ w) * Z_ (2 ^ w).
Variable d_Sir_Si : Z_ (2 ^ w) * Z_ (2 ^ w).
Variable l_in_Z_ : Z_ (2 ^ w).
Variable x_i_in_Z_ : Z_ (2 ^ w).
Variable h : Z.
Let p_in_Z_w := fst (fst (fst (fst ptM'cM''))).
Let t_in_Z_w := snd (fst (fst (fst ptM'cM''))).
Let M'_in_Z_ := snd (fst (fst ptM'cM'')).
Let c_in_Z_ := snd (fst ptM'cM'').
Let M''_in_Z_ := snd ptM'cM''.
Let M := _Z M_in_Z_.
Let p := _Z p_in_Z_w.
Let t := _Z t_in_Z_w.
Let c := _Z c_in_Z_.
Let M'' := _Z M''_in_Z_.
Let M' := _Z M'_in_Z_.
Let l := _Z l_in_Z_.
Let x_i := _Z x_i_in_Z_.
Definition d_Si := fst d_Sir_Si.
Definition r_Si := snd d_Sir_Si.
Hypothesis d_Si_eq_r_Si_eq : P' M p d_Si r_Si h l_in_Z_.
Hypothesis le_1_M : 1 <= M.
Hypothesis le_M_max : M <= 2 ^ (w - 1).
Hypothesis p_M : p = Zlog_sup M.
Hypothesis t_M : t = w - p \/ M = 1.
Hypothesis c_M : c = 2 ^ p.
Hypothesis M''_M : M'' = (2 ^ t * M) mod 2 ^ w.
Hypothesis M'_M : M' = 2 ^ (p + w) / M mod 2 ^ w.
unfold p. apply le_0__Z. Qed.
apply Zlt_le_weak. exact lt02w. Qed.
Local Hint Resolve le_0_w lt02w le02w : a.
Local Hint Resolve le_0_p : a.
rewrite p_M. generalize le_1_M. case M. intro. absurd (2 <= 0); omega. intros q H.
elim (Zlog_sup_correct2 (Zpos q)). omega. omega. intros q H. absurd (2 <= Zneg q). apply Zlt_not_le.
unfold Z.lt. trivial. assumption. Qed.
rewrite p_M. elim (Zle_lt_or_eq 1 M). intro H. left. apply Z.le_trans with (Zlog_sup 2). simpl. omega.
apply Zlog_sup_seq. omega. intro H. right. symmetry. trivial. assumption. Qed.
elim le_1_p; intro H; omega. Qed.
elim lt_0_p; intro H; omega. Qed.
elim (Zle_lt_or_eq 1 M). intro H. rewrite p_M. generalize le_1_M. case M. intro. absurd (2 <= 0); omega.
intros q H0. elim (Zlog_sup_correct2 (Zpos q)). omega. omega. intros q H0. absurd (1 <= Zneg q).
apply Zlt_not_le. unfold Z.lt. trivial. assumption. omega. assumption. Qed.
Local Hint Resolve lt_2pm1_M : a.
Local Hint Resolve le_M_2pp le_1_p lt_0_p le_0_pm1 : a.
elim (Zle_lt_or_eq 1 M). intro H. replace (2 ^ p) with (1 * M + (2 ^ p - M)). apply Zdiv_mult_plus. omega.
cut (M <= 2 ^ p). omega. auto with a. cut (2 ^ p < M + M). omega.
apply Z.lt_le_trans with (2 ^ (p - 1) + 1 + (2 ^ (p - 1) + 1)).
replace (2 ^ (p - 1) + 1 + (2 ^ (p - 1) + 1)) with (2 ^ (p - 1) + 2 ^ (p - 1) + 2). rewrite Z_pow_plus.
replace (p - 1 + 1) with p. omega. ring. elim le_0_pm1. omega. intro H0. absurd (1 < M). omega.
assumption. ring. cut (2 ^ (p - 1) < M). omega. elim lt_2pm1_M. omega. intro H0. absurd (1 < M). omega.
assumption. ring. intro H0. rewrite p_M. rewrite <- H0. trivial. assumption. Qed.
transitivity (2 ^ (p + w) / M / 2 ^ w * 2 ^ w + (2 ^ (p + w) / M) mod 2 ^ w).
apply f_equal2 with (f := Zplus). replace (2 ^ (p + w) / M / 2 ^ w) with 1. ring.
rewrite Zdivdivdiv. rewrite Zmult_comm. rewrite <- Zdivdivdiv. rewrite Zpower_exp; auto with a.
rewrite Z_div_mult. symmetry. unfold p. unfold p_in_Z_w. apply div_M_1_again. apply Z.lt_gt. auto with
a. apply Z.le_ge. auto with a.  apply Z.le_ge. auto with a. auto with
a. omega. omega. auto with a. trivial. apply Zdivmod_split.  auto
with a. Qed.
(* <KE EP TOGETHER - multired_equations *)
Let r'_i := ltw d_Si c_in_Z_ r_Si (minusw r_Si M_in_Z_).
Let s_i := ursh x_i_in_Z_ p_in_Z_w.
Let s'_i := multp2 s_i p_in_Z_w.
Let l'_i := minusw x_i_in_Z_ s'_i.
Let s''_i := ltw r'_i c_in_Z_ s_i (minusw s_i M''_in_Z_).
Let h_i := multp2 r'_i t_in_Z_w.
Let h'_i := plusw h_i s''_i.
Let q_i := uhwm h'_i M'_in_Z_.
Let q'_i := plusw q_i h'_i.
Let y_i := multw q'_i M_in_Z_.
Definition d_i := minusw s'_i y_i.
Definition rrr_i := minusw x_i_in_Z_ y_i.
Definition r_i := ltw l'_i M_in_Z_ rrr_i (minusw rrr_i M_in_Z_).
(* KE EP TOGETHER> *)
Let d0 := d_Si. (* notation *)
Let r0 := r_Si. (* notation *)
(* <KE EP TOGETHER - multired_final *)
Let r'0 := ltw d0 M_in_Z_ r0 (minusw r0 M_in_Z_). (* notation *)
Definition r''0 := ltw r'0 M_in_Z_ r'0 (minusw r'0 M_in_Z_). (* only needed after the loop *)
(* KE EP TOGETHER> *)
Definition r'0' := ltw d0 c_in_Z_ r0 (minusw r0 M_in_Z_). (* needed in loop *)
Definition r''0' := ltw r'0' M_in_Z_ r'0' (minusw r'0' M_in_Z_). (* needed in loop *)
Let h_i' := multp2 r''0' t_in_Z_w.
Let h'_i' := plusw h_i' s_i.
Definition g' := r''0. (* only needed after the loop *)
Let x := h * 2 ^ w + l.
apply lt_0_Zpow. fold (0 + 0). apply Zplus_le_compat; auto with a. Qed.
apply lt_0_Zpow. auto with a. Qed.
Local Hint Resolve lt_0_2pp : a.
clear x_i_in_Z_ x_i s_i s'_i l'_i s''_i h'_i q_i q'_i y_i h'_i'.
cut (0 < w). omega. exact lt_0_w. Qed.
Local Hint Resolve le_0_wm1 lt_0_w : a.
clear x_i_in_Z_ x_i s_i s'_i l'_i s''_i h'_i q_i q'_i y_i h'_i'.
rewrite p_M. apply Z.le_lt_trans with (w - 1). rewrite <- Zlog_sup_pow. apply Zlog_sup_seq.
assumption. auto with a. omega. Qed.
apply Zlt_le_weak. exact lt_p_w. Qed.
cut (p <= w). omega. exact le_p_w. Qed.
Local Hint Resolve lt_p_w le_p_w le_0_wmp : a.
unfold M. apply lt_z__Z. Qed.
apply Zlt_le_weak. exact lt_M_2pw. Qed.
Hint Resolve lt_M_2pw le_M_2pw : a.
unfold P' in d_Si_eq_r_Si_eq. tauto. Qed.
unfold P' in d_Si_eq_r_Si_eq. tauto. Qed.
replace (h mod M * 2 ^ w) with (0 + h mod M * 2 ^ (w - p + p)). rewrite Zpower_exp. rewrite Zmult_assoc.
rewrite Zmod_plus_distr_l with (m := 2 ^ p). rewrite Z_mod_plus. rewrite modred with (x := 0).
replace (0 + l mod 2 ^ p mod M) with (l mod 2 ^ p mod M). rewrite modred with (m := 2 ^ p). exact r_Si_eq_lit.
apply Zmod_le_0_z. omega. apply Z.lt_le_trans with M. apply Zmod_lt_z_m. omega. exact le_M_2pp. ring. omega. apply lt_0_Zpow.
auto with a. apply Z.lt_gt. apply Z.lt_le_trans with M. omega. auto with a. apply lt_0_Zpow. auto with a. cut (p < w). omega.
auto with a. apply Z.le_ge. auto with a. replace (w - p + p) with w. ring. ring. Qed.
replace ((h mod M + M) * 2 ^ w) with (0 + (h mod M + M) * 2 ^ (w - p + p)). rewrite Zpower_exp. rewrite Zmult_assoc.
rewrite Zmod_plus_distr_l with (m := 2 ^ p). rewrite Z_mod_plus. rewrite modred with (x := 0).
replace (0 + l mod 2 ^ p mod M) with (l mod 2 ^ p mod M). rewrite modred with (m := 2 ^ p). exact r_Si_eq_lit.
apply Zmod_le_0_z. omega. apply Z.lt_le_trans with M. apply Zmod_lt_z_m. omega. exact le_M_2pp. ring. omega. apply lt_0_Zpow.
auto with a. apply Z.lt_gt. apply Z.lt_le_trans with M. omega. auto with a. apply lt_0_Zpow. auto with a. cut (p < w). omega.
auto with a. apply Z.le_ge. auto with a. replace (w - p + p) with w. ring. ring. Qed.
rewrite <- Zpower_exp. replace (w - p + p) with w. trivial. ring. cut (p < w). omega. auto with a. apply Z.le_ge. auto with a.
Qed.
intros a b c'. replace w with (w - p + p). rewrite Zpower_exp. do 2 rewrite Zmult_assoc. do 2 rewrite Zplus_comm with (m := b).
rewrite Z_mod_plus. rewrite Z_mod_plus. trivial. apply Z.lt_gt. apply lt_0_Zpow. auto with a. apply Z.lt_gt. apply lt_0_Zpow.
auto with a. cut (p < w). omega. auto with a. apply Z.le_ge. auto with a. ring. Qed.
intro H. unfold r'_i. elim (Zle_or_lt c d_Si); intro H0. rewrite ltw_false. simpl. rewrite r_Si_eq_lit. fold M.
rewrite <- Zmod_minus_distr_l. rewrite H. cut (_Z d_Si = 1). intro H1. rewrite H1. rewrite mod_1_0. replace (1 + 0 - 1) with 0.
apply modred. omega. auto with a. ring. rewrite c_M in H0. rewrite p_M in H0. rewrite H in H0. simpl in H0. cut (d_Si < 2).
omega. elim d_Si_eq; intro H1. rewrite H1. apply Z.lt_trans with M. apply Zmod_lt_z_m. omega. omega. elim H1; intro H2.
rewrite H2. rewrite H. rewrite mod_1_0. omega. elim H2; intro H3. rewrite H3. rewrite H. rewrite mod_1_0. omega. rewrite H3.
rewrite H. rewrite mod_1_0. omega. auto with a. assumption. cut (_Z d_Si = 0). intro H1. rewrite ltw_true. rewrite r_Si_eq_lit.
rewrite H1. rewrite H. rewrite mod_1_0. simpl. apply modred. omega. exact lt02w. rewrite H1. fold c. rewrite c_M.
auto with a. elim d_Si_eq; intro H1. rewrite H1. rewrite H. apply mod_1_0. rewrite c_M in H0. rewrite p_M in H0.
rewrite H in H0. simpl in H0. cut (0 <= d_Si). omega. apply le_0__Z. Qed.
intro H. unfold h_i. simpl. rewrite M1_r'_i_eq. fold t. replace (2 ^ t * 0) with 0. apply modred. omega. auto with a. ring.
assumption. Qed.
intro H. unfold h'_i. rewrite Z_from_plusw. rewrite M1_h_i_eq. unfold s''_i. rewrite ltw_true. unfold s_i. simpl. fold p.
rewrite p_M. rewrite H. simpl. fold x_i. replace x_i with (x_i * 1). rewrite Z_div_mult. transitivity x_i. apply modred.
unfold x_i. apply le_0__Z. unfold x_i. apply lt_z__Z. ring. omega. ring. rewrite M1_r'_i_eq. fold c. rewrite c_M. auto with a.
assumption. assumption. Qed.
intro z. apply Zmod_le_0_z. auto with a. Qed.
intro z. apply Zmod_le_0_z. omega. Qed.
intros a b. rewrite Zpow_2_w_is_2Zpow_2_wm1. apply Zplus_lt_compat. apply Z.lt_le_trans with M. apply Zmod_lt_z_m. omega.
assumption. apply Z.lt_le_trans with (2 ^ p). apply Zmod_lt_z_m. auto with a. apply Zle_pow_le. auto with a. cut (p < w). omega.
auto with a. Qed.
intros a b. replace w with (w - p + p). rewrite Zpower_exp. rewrite Zmult_assoc. rewrite Zdiv_times_plus.
rewrite Zmod_mult_distr_l. rewrite Zmod_plus_distr_l. rewrite Zmod_mult_distr_l with (a := a). rewrite <- Zmod_plus_distr_l.
rewrite <- Zmod_mult_distr_l. rewrite <- Zdiv_times_plus. rewrite <- Zmult_assoc. rewrite <- Zpower_exp.
replace (w - p + p) with w. trivial. ring. cut (p < w). omega. auto with a. apply Z.le_ge. auto with a. auto with a. omega. omega.
omega. omega. omega. auto with a. cut (p < w). omega. auto with a. apply Z.le_ge. auto with a. ring. Qed.
intros a b. replace w with (w - p + p). rewrite Zpower_exp. rewrite Zmult_assoc. rewrite Zplus_comm. rewrite Z_mod_plus. trivial.
apply Z.lt_gt. apply lt_0_Zpow. auto with a. cut (p <= w). omega. auto with a. apply Z.le_ge. auto with a. ring. Qed.
(* KE EP TOGETHER> *)
unfold r'_i. elim d_Si_eq; intro H. left. rewrite ltw_true. rewrite r_Si_eq_a. rewrite H. apply modred. apply Zplus_le_0_compat.
apply le_0_mod_M. apply le_0_2pp. apply lt_plus_M_2p_2w. rewrite H. apply Z.lt_le_trans with M. apply Zmod_lt_z_m. omega.
fold c. rewrite c_M. exact le_M_2pp. elim H; intro H0. elim (Zle_or_lt c d_Si); intro H1. rewrite ltw_false. simpl. left.
rewrite r_Si_eq_a. rewrite H0. fold M. rewrite <- Zmod_minus_distr_l.
replace (((h mod M * 2 ^ w + l) / 2 ^ p * 2 ^ p) mod M + M + (h mod M * 2 ^ w + (l mod 2 ^ p) mod M) mod 2 ^ p - M)
with (((h mod M * 2 ^ w + l) / 2 ^ p * 2 ^ p) mod M + (h mod M * 2 ^ w + (l mod 2 ^ p) mod M) mod 2 ^ p).
apply modred. apply Zplus_le_0_compat. apply le_0_mod_M. apply le_0_2pp. apply lt_plus_M_2p_2w. ring. auto with a. assumption.
rewrite ltw_true. right. rewrite r_Si_eq_a. rewrite <- H0. apply modred. apply Zplus_le_0_compat. apply le_0__Z. apply le_0_2pp.
apply Z.lt_le_trans with (c + 2 ^ p). apply Zplus_lt_compat. assumption. apply Zmod_lt_z_m. auto with a.
rewrite Zpow_2_w_is_2Zpow_2_wm1. apply Zplus_le_compat. rewrite c_M. apply Zle_pow_le. auto with a. cut (p < w). omega.
auto with a. apply Zle_pow_le. auto with a. cut (p < w). omega. auto with a. assumption. elim H0; intro H1. left.
rewrite ltw_true. rewrite r_Si_eq_b. rewrite H1.
transitivity ((((((h mod M + M) * (2 ^ (w - p) * 2 ^ p) + l) / 2 ^ p * 2 ^ p) mod M + M +
((h mod M + M) * (2 ^ (w - p) * 2 ^ p) + l mod 2 ^ p mod M) mod 2 ^ p) mod
(2 ^ (w - p) * 2 ^ p) - M) mod (2 ^ (w - p) * 2 ^ p)). (* workaround *)
rewrite <- p2wp. rewrite <- Zmod_minus_distr_l.
replace ((((h mod M + M) * 2 ^ w + l) / 2 ^ p * 2 ^ p) mod M + M + ((h mod M + M) * 2 ^ w + (l mod 2 ^ p) mod M) mod 2 ^ p - M)
with ((((h mod M + M) * 2 ^ w + l) / 2 ^ p * 2 ^ p) mod M + ((h mod M + M) * 2 ^ w + (l mod 2 ^ p) mod M) mod 2 ^ p).
trivial. ring. exact lt02w. rewrite Zmult_assoc. rewrite mod_over_div. rewrite Zmod_mult_distr_l with (a := h mod M + M).
replace (h mod M + M) with (h mod M + 1 * M). rewrite Z_mod_plus. rewrite <- Zmod_mult_distr_l. rewrite <- mod_over_div.
rewrite <- Zmult_assoc. rewrite <- Zmult_assoc. rewrite <- Zpower_exp. replace (w - p + p) with w.
rewrite mod2p with (c' := h mod M). rewrite <- Zmod_minus_distr_l.
replace (((h mod M * 2 ^ w + l) / 2 ^ p * 2 ^ p) mod M + M + (h mod M * 2 ^ w + (l mod 2 ^ p) mod M) mod 2 ^ p - M)
with (((h mod M * 2 ^ w + l) / 2 ^ p * 2 ^ p) mod M + (h mod M * 2 ^ w + (l mod 2 ^ p) mod M) mod 2 ^ p).
apply modred. apply Zplus_le_0_compat. apply le_0_mod_M. apply le_0_2pp. apply lt_plus_M_2p_2w. ring. auto with a. ring.
cut (p < w). omega. auto with a. apply Z.le_ge. auto with a. auto with a. omega. omega. omega. ring. omega. auto with a.
omega. rewrite H1. apply Z.lt_le_trans with M. apply Zmod_lt_z_m. omega. fold c. rewrite c_M. exact le_M_2pp.
elim (Zle_or_lt c d_Si); intro H2. rewrite ltw_false. simpl. left. rewrite r_Si_eq_b. rewrite H1. fold M.
rewrite <- Zmod_minus_distr_l.
replace ((((h mod M + M) * 2 ^ w + l) / 2 ^ p * 2 ^ p) mod M + M + ((h mod M + M) * 2 ^ w + (l mod 2 ^ p) mod M) mod 2 ^ p - M)
with ((((h mod M + M) * 2 ^ w + l) / 2 ^ p * 2 ^ p) mod M + ((h mod M + M) * 2 ^ w + (l mod 2 ^ p) mod M) mod 2 ^ p).
rewrite modred. rewrite divmultmodM. rewrite Zmod_plus_m. rewrite <- divmultmodM. rewrite r'0plus. rewrite r'0plus. trivial.
omega. apply Zplus_le_0_compat. apply le_0_mod_M. apply le_0_2pp. apply lt_plus_M_2p_2w. ring. auto with a. assumption.
rewrite ltw_true. rewrite r_Si_eq_b. right. rewrite r'0plus. rewrite r'0plus. rewrite modred. rewrite H1. rewrite divmultmodM.
rewrite Zmod_plus_m. rewrite <- divmultmodM. trivial. omega. apply Zplus_le_0_compat. apply le_0__Z. apply le_0_2pp.
apply Z.lt_le_trans with (c + 2 ^ p). apply Zplus_lt_compat. assumption. apply Zmod_lt_z_m. auto with a.
rewrite Zpow_2_w_is_2Zpow_2_wm1. apply Zplus_le_compat. rewrite c_M. apply Zle_pow_le. auto with a. cut (p < w). omega.
auto with a. apply Zle_pow_le. auto with a. cut (p < w). omega. auto with a. assumption. Qed.
elim r'_i_eq; intro H; rewrite H. rewrite Zdiv_a_b_b_Zmod. replace w with (w - p + p). rewrite Zpower_exp.
rewrite Zmult_assoc. rewrite Zmod_km_plus. rewrite Zmod_km_plus. rewrite modred with (x := l mod 2 ^ p mod M).
rewrite <- Zmult_assoc. rewrite <- Zpower_exp. replace (w - p + p) with w.
elim (mod_plus (h mod M * 2 ^ w + l - l mod 2 ^ p) (l mod 2 ^ p) M). intro H0; rewrite H0;
replace (h mod M * 2 ^ w + l - l mod 2 ^ p + l mod 2 ^ p) with (h mod M * 2 ^ w + l). rewrite Zmod_plus_distr_l.
rewrite <- Zmod_mult_distr_l. rewrite <- Zmod_plus_distr_l. tauto. omega. omega. omega. ring.
intro H0; rewrite H0; replace (h mod M * 2 ^ w + l - l mod 2 ^ p + l mod 2 ^ p) with (h mod M * 2 ^ w + l).
rewrite Zmod_plus_distr_l. rewrite <- Zmod_mult_distr_l. rewrite <- Zmod_plus_distr_l. tauto. omega. omega. omega. ring. omega.
ring. cut (p < w). omega. auto with a. apply Z.le_ge. auto with a. apply Zmod_le_0_z. omega. apply Z.lt_le_trans with M.
apply Zmod_lt_z_m. omega. exact le_M_2pp. apply lt_0_Zpow. auto with a. apply lt_0_Zpow. auto with a. cut (p < w). omega.
auto with a. apply Z.le_ge. auto with a. ring. apply lt_0_Zpow. auto with a. right.
cut (((h mod M * 2 ^ w + l) / 2 ^ p * 2 ^ p) mod M + (h mod M * 2 ^ w + (l mod 2 ^ p) mod M) mod 2 ^ p = x mod M \/
((h mod M * 2 ^ w + l) / 2 ^ p * 2 ^ p) mod M + (h mod M * 2 ^ w + (l mod 2 ^ p) mod M) mod 2 ^ p = x mod M + M).
omega.
rewrite Zdiv_a_b_b_Zmod. replace w with (w - p + p). rewrite Zpower_exp.
rewrite Zmult_assoc. rewrite Zmod_km_plus. rewrite Zmod_km_plus. rewrite modred with (x := l mod 2 ^ p mod M).
rewrite <- Zmult_assoc. rewrite <- Zpower_exp. replace (w - p + p) with w.
elim (mod_plus (h mod M * 2 ^ w + l - l mod 2 ^ p) (l mod 2 ^ p) M).
intro H0; rewrite H0; replace (h mod M * 2 ^ w + l - l mod 2 ^ p + l mod 2 ^ p) with (h mod M * 2 ^ w + l).
rewrite Zmod_plus_distr_l. rewrite <- Zmod_mult_distr_l. rewrite <- Zmod_plus_distr_l. tauto. omega. omega. omega. ring.
intro H0; rewrite H0; replace (h mod M * 2 ^ w + l - l mod 2 ^ p + l mod 2 ^ p) with (h mod M * 2 ^ w + l).
rewrite Zmod_plus_distr_l. rewrite <- Zmod_mult_distr_l. rewrite <- Zmod_plus_distr_l. tauto. omega. omega. omega. ring. omega.
ring. cut (p < w). omega. auto with a. apply Z.le_ge. auto with a. apply Zmod_le_0_z. omega. apply Z.lt_le_trans with M.
apply Zmod_lt_z_m. omega. exact le_M_2pp. apply lt_0_Zpow. auto with a. apply lt_0_Zpow. auto with a. cut (p < w). omega.
auto with a. apply Z.le_ge. auto with a. ring. apply lt_0_Zpow. auto with a. Qed.
elim d_Si_eq; intro H. apply Z.lt_trans with M. rewrite H. apply Zmod_lt_z_m. omega. omega. elim H; intro H0. rewrite H0.
cut (((h mod M * 2 ^ w + l) / 2 ^ p * 2 ^ p) mod M < M). omega. apply Zmod_lt_z_m. omega. elim H0; intro H1.
apply Z.lt_trans with M. rewrite H1. apply Zmod_lt_z_m. omega. omega. rewrite H1.
cut ((((h mod M + M) * 2 ^ w + l) / 2 ^ p * 2 ^ p) mod M < M). omega. apply Zmod_lt_z_m. omega. Qed.
clear x_i_in_Z_ x_i s_i s'_i l'_i s''_i h'_i q_i q'_i y_i h'_i'.
unfold r'0. elim d_Si_eq; intro H. left. rewrite ltw_true. unfold r0. rewrite r_Si_eq_lit. rewrite H. trivial. unfold d0.
rewrite H. fold M. apply Zmod_lt_z_m. omega. elim H; intro H0. left. rewrite ltw_false. simpl. unfold r0. rewrite r_Si_eq_lit.
rewrite <- Zmod_minus_distr_l. fold M. rewrite H0. apply f_equal2 with (f := Zmod). ring. trivial. auto with a. fold M.
unfold d0. rewrite H0. cut (0 <= ((h mod M * 2 ^ w + l) / 2 ^ p * 2 ^ p) mod M). omega. apply Zmod_le_0_z. omega. elim H0;
intro H1. right. rewrite ltw_true. unfold r0. rewrite r_Si_eq_lit. rewrite H1. trivial. unfold d0. rewrite H1. fold M.
apply Zmod_lt_z_m. omega. right. rewrite ltw_false. simpl. unfold r0. rewrite r_Si_eq_lit. rewrite <- Zmod_minus_distr_l. fold M.
rewrite H1. apply f_equal2 with (f := Zmod). ring. trivial. auto with a. fold M. unfold d0. rewrite H1.
cut (0 <= (((h mod M + M) * 2 ^ w + l) / 2 ^ p * 2 ^ p) mod M). omega. apply Zmod_le_0_z. omega. Qed.
clear x_i_in_Z_ x_i s_i s'_i l'_i s''_i h'_i q_i q'_i y_i h'_i'.
elim r'0_eq_1; intro H; rewrite H. left. apply modred. apply Zplus_le_0_compat. apply Zmod_le_0_z. omega. apply Zmod_le_0_z.
omega. rewrite Zpow_2_w_is_2Zpow_2_wm1. apply Zplus_lt_compat. apply Z.lt_le_trans with M. apply Zmod_lt_z_m. omega. assumption.
apply Z.lt_le_trans with M. apply Zmod_lt_z_m. omega. assumption. right. apply modred. apply Zplus_le_0_compat. apply Zmod_le_0_z.
omega. apply Zmod_le_0_z. omega. rewrite Zpow_2_w_is_2Zpow_2_wm1. apply Zplus_lt_compat. apply Z.lt_le_trans with M.
apply Zmod_lt_z_m. omega. assumption. apply Z.lt_le_trans with M. apply Zmod_lt_z_m. omega. assumption. Qed.
clear x_i_in_Z_ x_i s_i s'_i l'_i s''_i h'_i q_i q'_i y_i h'_i'.
intro b. rewrite Zmod_plus_distr_l. rewrite Zmod_mult_distr_l. replace (b mod M + M) with (b mod M + 1 * M). rewrite Z_mod_plus.
rewrite modred with (x := b mod M). rewrite <- Zmod_mult_distr_l. rewrite <- Zmod_plus_distr_l. trivial. omega. omega.
apply Zmod_le_0_z. omega. apply Zmod_lt_z_m. omega. omega. ring. omega. omega. Qed.
clear x_i_in_Z_ x_i s_i s'_i l'_i s''_i h'_i q_i q'_i y_i h'_i'.
intro b. replace w with (w - p + p). rewrite Zpower_exp. rewrite Zmult_assoc. rewrite Zplus_comm. rewrite Z_mod_plus. trivial.
apply Z.lt_gt. apply lt_0_Zpow. auto with a. cut (p <= w). omega. apply le_p_w. auto with a. apply Z.le_ge. auto with a. ring. Qed.
clear x_i_in_Z_ x_i s_i s'_i l'_i s''_i h'_i q_i q'_i y_i h'_i'.
elim r'0_eq_2; intro H; rewrite H. left. apply f_equal2 with (f := Zplus). trivial. symmetry. apply f_equal2 with (f := Zmod).
apply r'0_eq_6. trivial. right. apply f_equal2 with (f := Zplus). trivial. symmetry. apply f_equal2 with (f := Zmod).
apply r'0_eq_6. trivial. Qed.
clear x_i_in_Z_ x_i s_i s'_i l'_i s''_i h'_i q_i q'_i y_i h'_i'.
intros a b H. elim (mod_plus (b / 2 ^ p * 2 ^ p) (b mod 2 ^ p) M). intro H0. left. rewrite H. rewrite H0. rewrite Zdivmod_split.
trivial. apply lt_0_Zpow. auto with a. intro H0. right. rewrite H. rewrite H0. rewrite Zdivmod_split. trivial. apply lt_0_Zpow.
auto with a. omega. Qed.
clear x_i_in_Z_ x_i s_i s'_i l'_i s''_i h'_i q_i q'_i y_i h'_i'.
elim r'0_eq_7; intro H; rewrite H. left. apply r'0_eq_8. trivial. right. apply r'0_eq_8. trivial. Qed.
clear x_i_in_Z_ x_i s_i s'_i l'_i s''_i h'_i q_i q'_i y_i h'_i'.
elim r'0_eq_9; intro H. elim H; intro H0; rewrite H0. left. rewrite Zmod_plus_distr_l. rewrite <- Zmod_mult_distr_l.
rewrite <- Zmod_plus_distr_l. trivial. omega. omega. omega. right. rewrite Zmod_plus_distr_l. rewrite <- Zmod_mult_distr_l.
rewrite <- Zmod_plus_distr_l. trivial. omega. omega. omega. elim H; intro H0; rewrite H0. left. rewrite r'0_eq_5. trivial.
rewrite r'0_eq_5. right. trivial. Qed.
(* KE EP TOGETHER> *)
clear x_i_in_Z_ x_i s_i s'_i l'_i s''_i h'_i q_i q'_i y_i h'_i'.
unfold r''0. unfold r''0. (* Proof General beta oddity *) elim r'0_eq; intro H. rewrite ltw_true. trivial. rewrite H. apply Zmod_lt_z_m. fold M. omega. rewrite ltw_false.
simpl. rewrite H. fold M. replace (x mod M + M - M) with (x mod M). apply modred.
apply Zmod_le_0_z. omega. apply Z.lt_trans with M. apply Zmod_lt_z_m. omega. unfold M. apply lt_z__Z. ring. rewrite H. fold M.
cut (0 <= x mod M). omega. apply Zmod_le_0_z. omega. Qed.
unfold r'_i. elim (Zle_or_lt c d_Si); intro H. rewrite ltw_false. simpl. rewrite r_Si_eq_lit. rewrite <- Zmod_minus_distr_l.
fold M. rewrite modred. cut (d_Si < 2 ^ p + M). cut (l mod 2 ^ p mod M < M). omega. apply Zmod_lt_z_m. omega.
apply Z.lt_le_trans with (M + M). exact d_Si_small. apply Zplus_le_compat. exact le_M_2pp. omega.
cut (0 <= d_Si - M). cut (0 <= (l mod 2 ^ p) mod M). omega. apply Zmod_le_0_z. omega. cut (M <= d_Si). omega.
apply Z.le_trans with (2 ^ p). exact le_M_2pp. omega. cut ((l mod 2 ^ p) mod M < M). cut (d_Si < 2 ^ w). omega. apply lt_z__Z.
apply Zmod_lt_z_m. omega. exact lt02w. assumption. rewrite ltw_true. rewrite r_Si_eq_lit. rewrite modred.
apply Zplus_lt_compat. apply Z.lt_le_trans with c. assumption. omega. apply Zmod_lt_z_m. omega. apply Zplus_le_0_compat.
apply le_0__Z. apply Zmod_le_0_z. omega. rewrite Zpow_2_w_is_2Zpow_2_wm1. apply Z.lt_le_trans with (c + M). apply Zplus_lt_compat.
assumption. apply Zmod_lt_z_m. omega. apply Zplus_le_compat. rewrite c_M. apply Zle_pow_le. auto with a. cut (p < w). omega.
auto with a. omega. assumption. Qed.
Let r'a := ltw r'_i c_in_Z_ r'_i (minusw r'_i M_in_Z_).
unfold r'a. elim (Zle_or_lt c r'_i); intro H. rewrite ltw_false. simpl. fold M. rewrite modred. cut (r'_i < 2 ^ p + M).
omega. exact r'_small. cut (M <= r'_i). omega. apply Z.le_trans with c. rewrite c_M. exact le_M_2pp. omega.
cut (r'_i < 2 ^ w). cut (0 < M). omega. omega. apply lt_z__Z. assumption. rewrite ltw_true. rewrite <- c_M. assumption.
assumption. Qed.
unfold r'a. elim (Zle_or_lt c r'_i); intro H. rewrite ltw_false. elim r'_i_eq'_i; intro H0. rewrite H0 in H.
absurd (c <= x mod M). apply Zlt_not_le. apply Z.lt_le_trans with M. apply Zmod_lt_z_m. omega. rewrite c_M. exact le_M_2pp.
assumption. simpl. fold M. elim H0; intro H1. rewrite H1. replace (x mod M + M - M) with (x mod M). rewrite modred. tauto.
apply Zmod_le_0_z. omega. apply Z.lt_trans with M. apply Zmod_lt_z_m. omega. unfold M. apply lt_z__Z. ring. rewrite H1.
replace (x mod M + M + M - M) with (x mod M + M). rewrite modred. tauto. apply Zplus_le_0_compat. apply Zmod_le_0_z. omega.
omega. rewrite Zpow_2_w_is_2Zpow_2_wm1. apply Zplus_lt_le_compat. apply Z.lt_le_trans with M. apply Zmod_lt_z_m. omega.
assumption. assumption. ring. assumption. rewrite ltw_true. elim r'_i_eq'_i; intro H0. tauto. elim H0; intro H1.
tauto. rewrite H1 in H. absurd (x mod M + M + M <= c). apply Zlt_not_le. cut (c < M + M). cut (0 <= x mod M). omega.
apply Zmod_le_0_z. omega. elim (Zle_lt_or_eq 0 p). intro H2. apply Z.lt_le_trans with (2 ^ (p - 1) + 1 + (2 ^ (p - 1) + 1)).
replace (2 ^ (p - 1) + 1 + (2 ^ (p - 1) + 1)) with (2 ^ 1 * 2 ^ (p - 1) + 2). rewrite <- Zpower_exp.
replace (1 + (p - 1)) with p. omega. ring. omega. omega. ring. apply Zplus_le_compat. elim lt_2pm1_M; intro H3. omega.
rewrite H3 in p_M. simpl in p_M. rewrite p_M in H2. absurd (0 < 0); omega. elim lt_2pm1_M; intro H3. omega.
rewrite H3 in p_M. simpl in p_M. rewrite p_M in H2. absurd (0 < 0); omega. intro H2. rewrite c_M. rewrite <- H2. simpl. omega.
unfold p. apply le_0__Z. omega. assumption. Qed.
Let x' := r'a * 2 ^ w + x_i.
unfold x'. rewrite Zmod_km_plus. symmetry. unfold x_i. apply modred. apply le_0__Z. apply lt_z__Z. exact lt02w. Qed.
(* KE EP TOGETHER> *)
simpl. fold l. fold p. fold x_i. rewrite x_i_eq. trivial. Qed.
(* KE EP TOGETHER> *)
unfold s'_i. rewrite Z_from_multp2. fold p. rewrite s_i_eq. rewrite Zmult_comm. rewrite modred.
replace (x' mod 2 ^ p) with (x' mod 2 ^ w mod 2 ^ p). apply Zdiv_a_b_b_Zmod. auto with a.
replace (2 ^ w) with (2 ^ (w - p) * 2 ^ p). apply Zmod_prod_Zmod. apply lt_0_Zpow. auto with a.
auto with a. rewrite <- Zpower_exp. apply f_equal2 with (f := Zpower). trivial. ring. apply Z.le_ge.
auto with a. apply Z.le_ge. auto with a. fold (0 * 0). apply Zmult_le_compat. apply Zle_0_div.
apply Zmod_le_0_z. auto with a. auto with a. apply Zlt_le_weak. auto with a. omega. omega.
apply Z.le_lt_trans with (x' mod 2 ^ w). rewrite Zmult_comm. apply Z_mult_div_ge. apply Z.lt_gt.
auto with a. apply Zmod_lt_z_m. auto with a. Qed.
unfold x'. fold (0 + 0). apply Zplus_le_compat; auto with a. apply Zmult_le_0_compat. apply le_0__Z.
auto with a. unfold x_i. apply le_0__Z. Qed.
Local Hint Resolve le_0_x' : a.
unfold x'. apply Z.le_lt_trans with ((2 ^ p - 1) * 2 ^ w + (2 ^ w - 1)). apply Zplus_le_compat. apply Zmult_le_compat.
cut (r'a < 2 ^ p). omega. exact r'a_small. omega. apply le_0__Z. apply Zlt_le_weak. exact lt02w. cut (x_i < 2 ^ w). omega.
unfold x_i. apply lt_z__Z. rewrite Zmult_minus_distr_r. rewrite <- Zpower_exp. omega. apply Z.le_ge. auto with a. apply Z.le_ge.
auto with a. Qed.
cut (x' < 2 ^ (p + w)). omega. exact lt_x'_x'max. Qed.
Local Hint Resolve lt_x'_x'max le_x'_x'max : a.
elim t_M; intro Ht. right. unfold h'_i. simpl. fold t. rewrite <- Zmod_plus_distr_l. unfold s''_i. unfold x'. unfold r'a.
unfold s_i. elim (Zle_or_lt c r'_i); intro H. rewrite ltw_false. rewrite ltw_false. simpl. fold p. fold x_i. fold p.
fold M''. fold M. transitivity ((((r'_i - M) mod 2 ^ w * 2 ^ (w - p + p) + x_i) / 2 ^ p) mod 2 ^ w). rewrite Zpower_exp.
rewrite Zmult_assoc. rewrite Zdiv_times_plus. rewrite M''_M. rewrite <- Zmod_minus_distr_r. rewrite Zplus_comm.
rewrite Zmod_plus_distr_l with (b := x_i / 2 ^ p). rewrite <- Zmod_mult_distr_l. rewrite <- Zmod_plus_distr_l.
rewrite <- Zmod_plus_distr_l. rewrite Zmult_minus_distr_r. rewrite Ht. apply f_equal2 with (f := Zmod). ring. trivial.
exact lt02w. exact lt02w. exact lt02w. exact lt02w. exact lt02w. apply lt_0_Zpow. auto with a. cut (p < w). omega. auto with a.
apply Z.le_ge. auto with a. replace (w - p + p) with w. trivial. ring. assumption. assumption. rewrite ltw_true. rewrite ltw_true.
rewrite Zmult_comm. (* replace w with (w - p + p). *)  (* Does not work... *)
transitivity (((r'_i * 2 ^ (w - p + p) + x_i) / 2 ^ p) mod 2 ^ w). (* ... *)
rewrite Zpower_exp. rewrite Zmult_assoc. rewrite Zdiv_times_plus. rewrite Ht. simpl. fold x_i. fold p. trivial. apply lt_0_Zpow.
auto with a. cut (p < w). omega. auto with a. apply Z.le_ge. auto with a. replace (w - p + p) with w. trivial. ring. assumption.
assumption. auto with a. tauto. Qed.
elim h'_i_eq_mod; intro H. tauto. right. rewrite H. apply modred. apply Zle_0_div. exact le_0_x'. apply lt_0_Zpow.
auto with a. apply Z.le_lt_trans with ((2 ^ (p + w) - 1) / 2 ^ p). apply Zdiv_le. apply lt_0_Zpow. auto with a.
exact le_x'_x'max. rewrite Zplus_comm. rewrite Zpower_exp. rewrite Zdiv_mult_minus. omega. apply lt_0_Zpow. auto with a.
omega. cut (0 < 2 ^ p). omega. apply lt_0_Zpow. auto with a. apply Z.le_ge. auto with a. apply Z.le_ge. auto with a. Qed.
(* KE EP TOGETHER> *)
elim M1_r'a; intro H. tauto. right. unfold q_i. rewrite uhwm_eq. rewrite H. fold M'. rewrite M'_M. trivial. Qed.
(* KE EP TOGETHER> *)
elim (Zle_lt_or_eq 1 M). intro H. elim q_i_eq_ch; intro H0. absurd (1 < M); omega. assumption. intro H. unfold q_i.
rewrite uhwm_eq. fold M'. rewrite M'_M. rewrite <- H. replace (2 ^ (p + w)) with (2 ^ (p + w) * 1). rewrite Z_div_mult.
rewrite Zpower_exp. rewrite Zmult_comm with (m := 2 ^ w). rewrite Zmod_mult_distr_l. rewrite Z_mod_same.
replace (0 * 2 ^ p) with 0. rewrite modred. replace (_Z h'_i * 0) with 0. replace (x' / 2 ^ p * 0) with 0. trivial. ring. ring.
omega. exact lt02w. ring. apply Z.lt_gt. exact lt02w. exact lt02w. apply Z.le_ge. auto with a. apply Z.le_ge. auto with a. omega.
ring. omega. Qed.
elim M1_r'a; intro H. tauto. right. unfold q'_i. rewrite Z_from_plusw. rewrite q_i_eq. rewrite H.
rewrite Zplus_comm. rewrite <- Zdiv_times_plus. rewrite <- Zmult_plus_distr_r. rewrite large_M'_eq. trivial. auto with a. Qed.
elim q'_i_eq_pre_ch; intro H. unfold q'_i. rewrite Z_from_plusw. rewrite q_i_eq. rewrite M1_h'_i_eq. rewrite p_M. rewrite H.
simpl. do 2 rewrite div_1_id. rewrite Z_div_mult. rewrite Z_mod_same. replace (x' * 0) with 0. rewrite Zdiv_small_0. simpl.
unfold x'. symmetry. apply Zmod_km_plus. auto with a. omega. auto with a. ring. apply Z.lt_gt. auto with a. apply Z.lt_gt.
auto with a. assumption. assumption. Qed.
intros z a b m H H0. elim H0; intro H1; rewrite H1; tauto. Qed.
(* KE EP TOGETHER> *)
rewrite q'_i_eq_pre. apply mod_ab_mod. auto with a.
cut (x' / 2 ^ p * (2 ^ p * 2 ^ w / M) / 2 ^ w = x' / 2 ^ p * 2 ^ p / M - 1 \/
x' / 2 ^ p * (2 ^ p * 2 ^ w / M) / 2 ^ w = x' / 2 ^ p * 2 ^ p / M).
rewrite Zpower_exp. tauto. apply Z.le_ge. auto with a. apply Z.le_ge. auto with a.
cut (x' / 2 ^ p * 2 ^ p / M - 1 <= x' / 2 ^ p * (2 ^ p * 2 ^ w / M) / 2 ^ w).
cut (x' / 2 ^ p * (2 ^ p * 2 ^ w / M) / 2 ^ w <= x' / 2 ^ p * 2 ^ p / M). intros H H0.
elim (Zle_lt_or_eq (x' / 2 ^ p * (2 ^ p * 2 ^ w / M) / 2 ^ w) (x' / 2 ^ p * 2 ^ p / M)). intro H1. left.
omega. intro H1. right. omega. assumption. apply approx_0_one. omega. auto with a. auto with a.
auto with a. apply approx_m1_one. auto with a. auto with a. omega. auto with a.
cut (x' < 2 ^ p * 2 ^ w). cut (0 < 2 ^ p). omega. auto with a. rewrite <- Zpower_exp. auto with a.
apply Z.le_ge. auto with a. apply Z.le_ge. auto with a. Qed.
(* KE EP TOGETHER> *)
unfold y_i. rewrite Z_from_multw. fold M. elim q'_i_eq; intro H; rewrite H. left.
rewrite <- Zmod_mult_distr_l. trivial. auto with a. right. rewrite <- Zmod_mult_distr_l.
apply f_equal2 with (f := Zmod). ring. trivial. auto with a. Qed.

(* <KE EP TOGETHER - d_i_eq *)
Lemma d_i_eq :
_Z d_i = x' / 2 ^ p * 2 ^ p mod M \/
_Z d_i = x' / 2 ^ p * 2 ^ p mod M + M.