(**************************************************************)
(*   Copyright Dominique Larchey-Wendling [*]                 *)
(*                                                            *)
(*                             [*] Affiliation LORIA -- CNRS  *)
(**************************************************************)
(*      This file is distributed under the terms of the       *)
(*         CeCILL v2 FREE SOFTWARE LICENSE AGREEMENT          *)
(**************************************************************)
(** * Arithmetic libraries for Matiyasevich's theorems *)
(** ** Modular arithmetic *)
Set Implicit Arguments.
Section le_pirr.
(* a dependent induction principle for le *)
Scheme le_indd := Induction for le Sort Prop.
Fact le_pirr : forall x y (H1 H2 : x <= y), H1 = H2.
End le_pirr.
Fact lt_pirr x y (H1 H2 : x < y) : H1 = H2.
Fact Z_coprime u v : (exists a b, a * Z.of_nat u + b*Z.of_nat v = 1) -> is_gcd u v 1%nat.
End Z_coprime.
Section Zp.
Variable (p : nat) (Hp : p <> 0).
Definition Z_Zp := { x | x < p }.
Implicit Type x y : Z_Zp.
Fact Zp_inj : forall x y, proj1_sig x = proj1_sig y -> x = y.
Fact Zp_plus_comm : forall x y,  x ⊕ y = y ⊕ x.
Fact Zp_plus_assoc : forall x y z, x ⊕ (y ⊕ z) = x ⊕ y ⊕ z.
Fact Zp_minus : forall x, x ⊕ ∸ x = Zp.
Fact Zp_mult_one : forall x, Op ⊗ x = x.
Fact Zp_mult_comm : forall x y, x ⊗ y = y ⊗ x.
Fact Zp_mult_one_r x : x ⊗ Op = x.
Fact Zp_mult_plus_distr_l : forall x y z, x ⊗ (y ⊕ z) = x ⊗ y ⊕ x ⊗ z.
Fact Zp_mult_plus_distr_r x y z : (y ⊕ z) ⊗ x = y ⊗ x ⊕ z ⊗ x.
(* Invertible elements are some which are relatively prime to x *)
Fact Zp_mult_invert : forall x, is_gcd p (proj1_sig x) 1 -> { i | i ⊗ x = Op }.
Fact Zp_mult_revert : forall x i, i ⊗ x = Op -> is_gcd p (proj1_sig x) 1.
Definition Zp_invertible x := exists i, i ⊗ x = Op.
Fact Zp_invertible_spec x : Zp_invertible x <-> is_gcd p (proj1_sig x) 1.
Fact Zp_prime_invert : prime p -> forall x, x <> Zp -> { i | i ⊗ x = Op }.
(* So we have the commutative ring structure and the field structure for p prime  These follow form (non-commutative) ring axioms  *)
Add Ring Zp_ring : Zp_is_ring.
Fact Zp_plus_monoid : monoid_theory Zp_plus Zp_zero.
Hint Resolve Zp_one_invertible Zp_opp_invertible Zp_mult_invertible.
Fact Zp_expo_invertible n x : Zp_invertible x -> Zp_invertible (mscal Zp_mult Op n x).
(* ⊕  ⊗  ∸ *)
Fact Zp_opp_zero : ∸ Zp = Zp.
Fact Zp_plus_zero_r x : x ⊕ Zp = x.
Fact Zp_zero_is_one : Zp = Op <-> p = 1.
Fact Zp_eq_dec : forall x y : Z_Zp, { x = y } + { x <> y }.
(* We define a morphism for nat to Z_Zp *)
Section nat2Zp.
Definition nat2Zp (u : nat) : Z_Zp.
Fact nat2Zp_one : 〚1〛= Op.
Fact nat2Zp_invertible n : Zp_invertible 〚n〛 <-> is_gcd p n 1.
Fact nat2Zp_p :〚p〛= Zp.
Fact nat2Zp_inj u v : 〚u〛=〚v〛 <-> rem u p = rem v p.
Fact nat2Zp_surj u : { x | x < p /\ u = 〚x〛 }.
Fact nat2Zp_plus u v : 〚u+v〛=〚u〛⊕〚v〛.
Fact nat2Zp_minus u v : v <= u -> 〚u-v〛=〚u〛⊕ ∸〚v〛.
Fact nat2Zp_minus_one : 〚p-1〛= ∸Op.
Fact nat2Zp_mult u v :〚u*v〛=〚u〛⊗〚v〛.
Fact nat2Zp_expo n u : 〚mscal mult 1%nat n u〛 = mscal Zp_mult Zp_one n 〚u〛.
Fact nat2Zp_divides u v :〚u〛=〚v〛-> divides p u -> divides p v.
Fact divides_nat2Zp u : divides p u <-> 〚u〛= Zp.
End nat2Zp.
Arguments nat2Zp u /.
Definition Zp_list := map nat2Zp (list_an 0 p).
Fact Zp_list_spec x : In x Zp_list.
Fact Zp_list_length : length Zp_list = p.
Fact Zp_prime_integral x y : prime p -> x ⊗ y = Zp -> x = Zp \/ y = Zp.
Fact Zp_prime_square_eq_square x y : prime p -> x⊗x = y⊗y <-> x = y \/ x = ∸y.
Fact Zp_prime_square_one x : prime p -> x⊗x = Op <-> x = Op \/ x = ∸Op.
(* We define a morphism for Z to Z_Zp *)
Section Z2Zp.
Open Scope Z_scope.
Implicit Types u v w : Z.
Definition Z2Zp u : Z_Zp.
Fact Z2Zp_of_nat n : 〘Z.of_nat n〙 = 〚n〛.
Fact Z2Zp_zero : 〘0〙= Zp.
Fact Z2Zp_neg u : u <= 0 -> 〘u〙 = ∸〘-u〙.
(* ⊕  ⊗  ∸ *)
Section Z2Zp_plus.
Let Z2Zp_plus_loc u v : u <= 0 -> 0 <= v -> -u <= v -> 〘u+v〙=〘u〙⊕〘v〙.
Let Z2Zp_plus_loc' u v : u <= 0 -> 0 <= v -> v <= -u -> 〘u+v〙=〘u〙⊕〘v〙.
Fact Z2Zp_plus u v : 〘u+v〙=〘u〙⊕〘v〙.
End Z2Zp_plus.
Fact Z2Zp_opp u :〘-u〙= ∸〘u〙.
Fact Z2Zp_minus u v : 〘u-v〙 = 〘u〙⊕ ∸〘v〙.
Section Z2Zp_mult.
Let Z2Zp_mult_loc u v : 0 <= u -> 0 <= v -> 〘u*v〙=〘u〙⊗〘v〙.
Fact Z2Zp_mult u v : 〘u*v〙=〘u〙⊗〘v〙.
End Z2Zp_mult.
Fact Z2Zp_one : 〘1〙 = Op.
Section Z2Zp_inj.
Let Z2Zp_inj_loc u v : v <= u ->〘u〙=〘v〙-> exists i, u-v = i*Z.of_nat p.
Fact Z2Zp_inj u v :〘u〙=〘v〙<-> exists i, u-v = i*Z.of_nat p.
End Z2Zp_inj.
Fact nat2Zp_choose : forall x, x = Zp \/ x = Op \/ x = ∸ Op \/ exists m,  (1 < m < p-1)%nat /\〚m〛= x.
Section prime.
Hypothesis Hp' : prime p.
Fact nat2Zp_invertible_prime n : (0 < n < p)%nat -> Zp_invertible 〚n〛.
Let Hp'' : (2 <= p)%nat.
Fact Zp_invertible_factorial n : (n < p)%nat -> Zp_invertible 〚fact n〛.
Section inv.
Let Zp_invert_full x : { i | (x = Zp -> i = Zp) /\ (x <> Zp -> i ⊗ x = Op) }.
Definition Zp_invert x := proj1_sig (Zp_invert_full x).
Fact Zp_invert_spec1 : Zp_invert Zp = Zp.
Fact Zp_invert_opp x : inv (∸x) = ∸(inv x).
Fact Zp_invert_one : inv Op = Op.
Fact Zp_invert_minus_one : inv (∸ Op) = ∸ Op.
Fact Zp_invert_involutive x : inv (inv x) = x.
Fact Zp_invert_not_fix n : (1 < n < p-1)%nat -> inv〚n〛<>〚n〛.
Fact Zp_invert_stable n : (1 < n < p-1)%nat -> exists m, (1 < m < p-1)%nat /\〚m〛= inv〚n〛.
Definition Zp_lprod := fold_right Zp_mult Zp_one.
Fact Zp_lprod_nil : Zp_lprod nil = Op.

Theorem Zp_mult_autoinv l : ~ list_has_dup l -> (forall x, In x l -> x <> Zp /\ inv x <> x /\ In (inv x) l) -> Zp_lprod l = Op.
Fact Zp_lprod_fact n : 〚fact (S n)〛= Zp_lprod (map nat2Zp (list_an 2 n)).