Section GaussIntegers.
(** First we define a predicate for the algebraic numbers which are gauss integers. *)
Definition gaussInteger := [qualify a x | ('Re x \in Cint) && ('Im x \in Cint)].
(**  There follows the boilerplate to use the proof GI_subring in order to canonically provide a subring structure to the predicate gaussInteger.  *)
Fact GI_key : pred_key gaussInteger. Proof. by []. Qed.
Canonical GI_keyed := KeyedQualifier GI_key.
Canonical GI_opprPred := OpprPred GI_subring.
Canonical GI_addrPred := AddrPred GI_subring.
Canonical GI_mulrPred := MulrPred GI_subring.
Canonical GI_zmodPred := ZmodPred GI_subring.
Canonical GI_semiringPred := SemiringPred GI_subring.
Canonical GI_smulrPred := SmulrPred GI_subring.
Canonical GI_subringPred := SubringPred GI_subring.
(**  Finally, we define the type of Gauss Integer, as a sigma type of algebraic numbers. We soon prove that this is in fact a sub type.  *)
Record GI := GIof {
algGI : algC;
algGIP : algGI \is a gaussInteger }.
(** We make the defining property of GI a Hint *)
Hint Resolve algGIP : core.
(**  We provide the subtype property.  - This makes it possible to use the generic operator "val" to get an algC from a Gauss Integer.  *)
Canonical GI_subType := [subType for algGI].
Fact unitGIP (x y : GI) : y * x = 1 -> unitGI x.
Fact unitGI_out : {in [predC unitGI], invGI =1 id}.
Definition GI_comUnitRingMixin :=
ComUnitRingMixin mulGIr unitGIP unitGI_out.
Canonical GI_unitRingType := UnitRingType GI GI_comUnitRingMixin.
Canonical GI_comUnitRingType := [comUnitRingType of GI].
Fact GI_idomainAxiom (x y : GI) : x * y = 0 -> (x == 0) || (y == 0).
Canonical GI_idomainType :=
Eval hnf in IdomainType GI GI_idomainAxiom.
Fact divGI_subproof (x y : int) : x%:~R + 'i * y%:~R \is a gaussInteger.
Definition modGI (x y : GI) : GI := x - (x %/ y)%GI * y.
Notation " x %% y " := (modGI x y) : GI_scope.
Fact iGI_proof : 'i \is a gaussInteger.
Fixpoint gcdGI_rec (n : nat) (xx  yy : GI) {struct n} :=
let rr := modGI xx yy in
if rr == 0 then yy else
if n is n1.+1 then gcdGI_rec n1 yy rr else rr.
Definition gcdGI x y :=
let: (x1, y1) := if ('N x < 'N y)%N then (y, x) else (x, y) in
if x1 == 0 then y1 else
gcdGI_rec ('N x1) x1 y1.
Definition eqGI x y :=  (dvdGI x y) && (dvdGI y x).
Infix "%=" := eqGI : GI_scope.
Fixpoint egcdGI_rec n (x y : GI) :=
if y == 0 then (1, 0) else
if n is n1.+1 then
let: (u, v) := egcdGI_rec n1 y (modGI x y) in
(v, u - v * (x %/ y)%GI)
else (1, 0).
Definition egcdGI (x y : GI) :=
if ('N y <= 'N x)%N then
egcdGI_rec ('N x) x y else
let e := egcdGI_rec ('N y) y x in (e.2, e.1).

Lemma mulGI_gcdl x y z : gcdGI y z * x %= gcdGI (y * x) (z * x).