(* This file is generated by Why3's Coq-realize driver *)
(* Beware! Only edit allowed sections below    *)
Require Import BuiltIn.
Require BuiltIn.

Require Import ClassicalEpsilon.
Require Import ZArith.
Require Import BV_Gen.

(* range_pred and modulus can have arbitrary values in this realization,
   provided the range is inhabited *)

Definition modulus : BV_Gen.t.
Admitted.

Definition range_pred : BV_Gen.t -> Prop.
Admitted.

Definition range_inhabited : {x | range_pred x /\ ult x modulus}.
Admitted.

Definition range_pred_dec: forall x: BV_Gen.t, {range_pred x} + {~range_pred x}.
intro r.
destruct (excluded_middle_informative (range_pred r)) as [P | P];
[left | right]; auto.
Qed.

Lemma modulus_pos: (to_uint (modulus) > 0)%Z.
destruct (Z_dec (to_uint modulus) 0) as [[P | P] | P];
[contradict P | | ]; unfold to_uint; auto with zarith.
destruct range_inhabited as [x [_ Q]]; contradict Q.
unfold ult; rewrite P; unfold to_uint.
auto with zarith.
Qed.

Lemma of_int_to_uint: forall (r:t), (of_int (to_uint r) = r).
intros; unfold of_int, to_uint.
rewrite Nat2Z.id;
rewrite nat_to_bvec_bvec_to_nat; auto.
Qed.

(* Why3 goal *)
Definition t : Type.
exact (sig (fun x => range_pred x /\ (ult x modulus))).
Defined.

(* Why3 goal *)
Definition rep_type : Type.
exact BV_Gen.t.
Defined.

(* Why3 goal *)
Definition to_rep : t -> rep_type.
intros [r _]; exact r.
Defined.

(* Why3 goal *)
Definition of_rep : rep_type -> t.
intros r.
destruct (range_pred_dec (urem r modulus)) as [P | P].
apply (exist (fun x => range_pred x /\ (ult x modulus))
        (urem r modulus)); split; auto.
unfold ult.
rewrite to_uint_urem.
rewrite (mod1_is_mod _ _ modulus_pos).
apply Z_mod_lt; exact modulus_pos.
apply range_inhabited.
Defined.

(* Why3 goal *)
Definition in_range : rep_type -> Prop.
exact range_pred.
Defined.

(* Why3 goal *)
Lemma inversion_axiom : forall (x:t), ((of_rep (to_rep x)) = x).
intros [r [P H]].
unfold to_rep, of_rep, ult; unfold ult in H.
destruct (range_pred_dec (urem r modulus)) as [Q | Q].
assert (forall (K : (to_uint (urem r modulus) < to_uint modulus)%Z) (Q : range_pred (urem r modulus)),
exist (fun x : BV_Gen.t => range_pred x /\ (to_uint x < to_uint modulus)%Z) (urem r modulus) (conj Q K) =
exist (fun x : BV_Gen.t => range_pred x /\ (to_uint x < to_uint modulus)%Z) r (conj P H));
[|auto].
clear Q; unfold urem.
rewrite (mod1_is_mod _ _ modulus_pos).
destruct (to_uint_bounds r) as [N _].
rewrite (Zmod_small _ _ (conj N H)).
rewrite of_int_to_uint.
intros K Q.
rewrite (proof_irrelevance _ (conj Q K) (conj P H)).
auto.
contradict Q; unfold urem.
rewrite (mod1_is_mod _ _ modulus_pos).
destruct (to_uint_bounds r) as [N _].
rewrite (Zmod_small _ _ (conj N H)).
rewrite of_int_to_uint.
auto.
Qed.

(* Why3 goal *)
Lemma range_axiom : forall (x:t), in_range (to_rep x).
intros [r [P H]]; unfold to_rep; auto.
Qed.

(* Why3 goal *)
Definition urem : rep_type -> rep_type -> rep_type.
exact BV_Gen.urem.
Defined.

(* Why3 goal *)
Definition attr__ATTRIBUTE_MODULUS : rep_type.
exact modulus.
Defined.

(* Why3 goal *)
Definition rep_to_int : rep_type -> Numbers.BinNums.Z.
exact to_uint.
Defined.

(* Why3 goal *)
Definition in_range_int : Numbers.BinNums.Z -> Prop.
intros r.
exact (in_range (of_int (r))).
Defined.

(* Why3 goal *)
Lemma coerce_axiom :
  forall (x:rep_type),
  let y := urem x attr__ATTRIBUTE_MODULUS in
  in_range y -> ((to_rep (of_rep x)) = y).
unfold urem, attr__ATTRIBUTE_MODULUS, BV_Gen.urem; intros r.
rewrite (mod1_is_mod _ _ modulus_pos).
intros H.
unfold to_rep, of_rep.
destruct (range_pred_dec (BV_Gen.urem r modulus)) as [Q | Q];
[ | contradict Q];
unfold BV_Gen.urem; rewrite (mod1_is_mod _ _ modulus_pos); auto.
Qed.

(* Why3 assumption *)
Definition to_int (x:t) : Numbers.BinNums.Z := rep_to_int (to_rep x).

(* Why3 goal *)
Lemma range_int_axiom : forall (x:t), in_range_int (to_int x).
intros [r [P H]]; unfold in_range_int, to_int, rep_to_int, to_rep.
rewrite of_int_to_uint; auto.
Qed.
