(************************************************************************)
(*         *      The Rocq Prover / The Rocq Development Team           *)
(*  v      *         Copyright INRIA, CNRS and contributors             *)
(* <O___,, * (see version control and CREDITS file for authors & dates) *)
(*   \VV/  **************************************************************)
(*    //   *    This file is distributed under the terms of the         *)
(*         *     GNU Lesser General Public License Version 2.1          *)
(*         *     (see LICENSE file for the text of the license)         *)
(************************************************************************)
(* Instances of [ZifyClasses] for emulating the existing zify. Each instance is registered using a Add 'class' 'name_of_instance'. *)
Declare ML Module "rocq-runtime.plugins.zify".
Local Open Scope Z_scope.
Ltac refl :=
abstract (intros ; match goal with
| |- context[@inj _ _ ?X] => unfold X, inj
end ; reflexivity).
#[global]
Instance Inj_Z_Z : InjTyp Z Z :=
mkinj _ _ (fun x => x) (fun x =>  True ) (fun _ => I).
Add Zify InjTyp Inj_Z_Z.
(** Support for nat *)
#[global]
Instance Inj_nat_Z : InjTyp nat Z :=
mkinj _ _ Z.of_nat (fun x =>  0 <= x ) Nat2Z.is_nonneg.
Add Zify InjTyp Inj_nat_Z.
(* zify_nat_rel *)
#[global]
Instance Op_ge : BinRel ge :=
{ TR := Z.ge; TRInj := Nat2Z.inj_ge }.
Add Zify BinRel Op_ge.
#[global]
Instance Op_lt : BinRel lt :=
{ TR := Z.lt; TRInj := Nat2Z.inj_lt }.
Add Zify BinRel Op_lt.
#[global]
Instance Op_Nat_lt : BinRel Nat.lt := Op_lt.
Add Zify BinRel Op_Nat_lt.
#[global]
Instance Op_gt : BinRel gt :=
{ TR := Z.gt; TRInj := Nat2Z.inj_gt }.
Add Zify BinRel Op_gt.
#[global]
Instance Op_le : BinRel le :=
{ TR := Z.le; TRInj := Nat2Z.inj_le }.
Add Zify BinRel Op_le.
#[global]
Instance Op_Nat_le : BinRel Nat.le := Op_le.
Add Zify BinRel Op_Nat_le.
#[global]
Instance Op_eq_nat : BinRel (@eq nat) :=
{ TR := @eq Z ; TRInj x y := iff_sym (Nat2Z.inj_iff x y) }.
Add Zify BinRel Op_eq_nat.
#[global]
Instance Op_Nat_eq : BinRel (Nat.eq) := Op_eq_nat.
Add Zify BinRel Op_Nat_eq.
(* zify_nat_op *)
#[global]
Instance Op_plus : BinOp Nat.add :=
{ TBOp := Z.add; TBOpInj := Nat2Z.inj_add }.
Add Zify BinOp Op_plus.
#[global]
Instance Op_sub : BinOp Nat.sub :=
{ TBOp n m := Z.max 0 (n - m) ; TBOpInj := Nat2Z.inj_sub_max }.
Add Zify BinOp Op_sub.
#[global]
Instance Op_mul : BinOp Nat.mul :=
{ TBOp := Z.mul ; TBOpInj := Nat2Z.inj_mul }.
Add Zify BinOp Op_mul.
#[global]
Instance Op_min : BinOp Nat.min :=
{ TBOp := Z.min ; TBOpInj := Nat2Z.inj_min }.
Add Zify BinOp Op_min.
#[global]
Instance Op_max : BinOp Nat.max :=
{ TBOp := Z.max ; TBOpInj := Nat2Z.inj_max }.
Add Zify BinOp Op_max.
#[global]
Instance Op_pred : UnOp Nat.pred :=
{ TUOp n := Z.max 0 (n - 1) ; TUOpInj := Nat2Z.inj_pred_max }.
Add Zify UnOp Op_pred.
#[global]
Instance Op_S : UnOp S :=
{ TUOp x := Z.add x 1 ; TUOpInj := Nat2Z.inj_succ }.
Add Zify UnOp Op_S.
#[global]
Instance Op_O : CstOp O :=
{ TCst := Z0 ; TCstInj := eq_refl (Z.of_nat 0) }.
Add Zify CstOp Op_O.
#[global]
Instance Op_Z_abs_nat : UnOp  Z.abs_nat :=
{ TUOp := Z.abs ; TUOpInj := Zabs2Nat.id_abs }.
Add Zify UnOp Op_Z_abs_nat.
#[global]
Instance Op_nat_div2 : UnOp Nat.div2 :=
{ TUOp x := x / 2 ;
TUOpInj x := ltac:(now rewrite Nat2Z.inj_div2, Z.div2_div) }.
Add Zify UnOp Op_nat_div2.
#[global]
Instance Op_nat_double : UnOp Nat.double :=
{| TUOp := Z.mul 2 ; TUOpInj := Nat2Z.inj_double |}.
Add Zify UnOp Op_nat_double.
(** Support for positive *)
#[global]
Instance Inj_pos_Z : InjTyp positive Z :=
{ inj := Zpos ; pred x := 0 < x ; cstr := Pos2Z.pos_is_pos }.
Add Zify InjTyp Inj_pos_Z.
#[global]
Instance Op_pos_to_nat : UnOp  Pos.to_nat :=
{TUOp x := x ; TUOpInj := positive_nat_Z}.
Add Zify UnOp Op_pos_to_nat.
#[global]
Instance Inj_N_Z : InjTyp N Z :=
mkinj _ _ Z.of_N (fun x =>  0 <= x ) N2Z.is_nonneg.
Add Zify InjTyp Inj_N_Z.
#[global]
Instance Op_N_to_nat : UnOp  N.to_nat :=
{ TUOp x := x ; TUOpInj := N_nat_Z }.
Add Zify UnOp Op_N_to_nat.
(* zify_positive_rel *)
#[global]
Instance Op_pos_ge : BinRel Pos.ge :=
{ TR := Z.ge; TRInj x y := iff_refl (Z.pos x >= Z.pos y) }.
Add Zify BinRel Op_pos_ge.
#[global]
Instance Op_pos_lt : BinRel Pos.lt :=
{ TR := Z.lt; TRInj x y := iff_refl (Z.pos x < Z.pos y) }.
Add Zify BinRel Op_pos_lt.
#[global]
Instance Op_pos_gt : BinRel Pos.gt :=
{ TR := Z.gt; TRInj x y := iff_refl (Z.pos x > Z.pos y) }.
Add Zify BinRel Op_pos_gt.
#[global]
Instance Op_pos_le : BinRel Pos.le :=
{ TR := Z.le; TRInj x y := iff_refl (Z.pos x <= Z.pos y) }.
Add Zify BinRel Op_pos_le.

Lemma eq_pos_inj x y : x = y <-> Z.pos x = Z.pos y.
#[global]
Instance Op_eq_pos : BinRel (@eq positive) :=
{ TR := @eq Z ; TRInj := eq_pos_inj }.