(* Copyright 2001, 2009 Luc Rutten  This file is part of ModRed.  ModRed is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.  ModRed 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 Lesser General Public License for more details.  You should have received a copy of the GNU Lesser General Public License along with ModRed.  If not, see <http://www.gnu.org/licenses/>. *)
(* Preparations for fast modular reduction lemmas. *)
(* Coq 8.2 *)
Open Scope Z_scope.
Definition in_Z_ (m x : Z) := 0 <= x < m.
Definition Z_ (m : Z) := sig (in_Z_ m).
Coercion Z_from_Z_ (m : Z) (x : Z_ m) := proj1_sig (P := in_Z_ m) x.
Notation _Z := (Z_from_Z_ _).
Definition in_Zpls (x : Z) := 0 < x.
Definition Zpls := sig in_Zpls.
Coercion Zpls_ (x : Zpls) := proj1_sig (P := in_Zpls) x.
intros n i. case i. unfold in_Z_. simpl. tauto. Qed.
intros n i. case i. unfold in_Z_. simpl. tauto. Qed.
intros x m H. elim (Z_mod_lt x m); omega. Qed.
intros x m H. elim (Z_mod_lt x m); omega. Qed.
intros x m H H0. cut (m * (x / m) + x mod m < m). intro H1. cut (m * (x / m) < m).
intro H2. elim (Zle_or_lt 0 (x / m)); intro H3. elim (Zle_lt_or_eq 0 (x / m)). intro H4.
cut (m * 1 <= m * (x / m)). intro H5. absurd (m * (x / m) < m). omega. assumption.
apply Zmult_le_compat_l. omega. omega. omega. assumption. absurd (x / m < 0). apply Zle_not_lt.
apply Z.ge_le. apply Z_div_ge0; omega. assumption. cut (0 <= x mod m). omega. elim (Z_mod_lt x m). omega.
omega. rewrite <- Z_div_mod_eq. assumption. omega. Qed.
intros a m H H0. apply Z.ge_le. apply Z_div_ge0. omega. omega. Qed.
intros. apply Z.ge_le. apply Z_div_ge. omega. omega. Qed.
intros x m H. replace (x + m) with (x + 1 * m). apply Z_mod_plus. omega. ring. Qed.
intros x m H. replace (x - m) with (x + -1 * m). apply Z_mod_plus. omega. ring. Qed.
intros. rewrite Zplus_comm. apply Z_mod_plus. omega. Qed.
intros. unfold Zminus. rewrite <- Zopp_mult_distr_l_reverse. apply Z_mod_plus. omega. Qed.
intros. rewrite Zmult_comm. rewrite <- Z_div_mod_eq. trivial. omega. Qed.
intros a b m H. replace (a + b) with (a / m * m + a mod m + b). rewrite Zplus_comm.
rewrite Zplus_comm with (m := a mod m). rewrite Zplus_assoc. rewrite Z_mod_plus.
rewrite Zplus_comm. trivial. omega. rewrite Zdivmod_split. trivial. assumption. Qed.
intros a b m H. replace (a * b) with ((a / m * m + a mod m) * b).
rewrite Zmult_plus_distr_l. rewrite Zmod_plus_distr_l.
replace (a / m * m * b) with (0 + a / m * b * m). rewrite Z_mod_plus. trivial. omega.
ring. assumption. rewrite Zdivmod_split. trivial. assumption. Qed.
unfold Zminus. intros. apply Zmod_plus_distr_l. assumption. Qed.
intros a b m H. unfold Zminus. replace (- b) with (b * -1). do 2 rewrite Zplus_comm with (n := a).
rewrite Zmod_plus_distr_l. rewrite Zmod_mult_distr_l. rewrite <- Zmod_plus_distr_l.
apply f_equal2 with (f := Zmod). ring. trivial. assumption. assumption. assumption. ring. Qed.
intros x m H H0. rewrite <- Zdivmod_split with (x := x) (m := m).
replace ((x / m * m + x mod m) mod m) with (x mod m).
rewrite Zdiv_small_0; auto. rewrite Zdivmod_split. trivial. omega. omega. Qed.
intros a b H. cut (a / b * b + a mod b = a). omega. apply Zdivmod_split. assumption. Qed.
intros. rewrite Zdiv_a_b_b_Zmod. ring. assumption. Qed.
intros x y z H H0. rewrite <- Zminus_Zdiv_Zmult with (a := x). rewrite Zmult_assoc.
apply Zmod_minus_km. assumption. fold (0 * 0). apply Zmult_lt_0_compat; omega. Qed.

Lemma Zdiv_mult_plus : forall a b m : Z, 0 < m -> 0 <= b -> b < m -> (a * m + b) / m = a.