(********************************************************************)
(*                                                                  *)
(*  The Why3 Verification Platform   /   The Why3 Development Team  *)
(*  Copyright 2010-2018   --   Inria - CNRS - Paris-Sud University  *)
(*                                                                  *)
(*  This software is distributed under the terms of the GNU Lesser  *)
(*  General Public License version 2.1, with the special exception  *)
(*  on linking described in file LICENSE.                           *)
(*                                                                  *)
(********************************************************************)

(* This file is generated by Why3's Coq-realize driver *)
(* Beware! Only edit allowed sections below    *)
Require Import BuiltIn.
Require Reals.Rbasic_fun.
Require Reals.R_sqrt.
Require BuiltIn.
Require int.Int.
Require real.Real.
Require real.RealInfix.
Require real.Abs.
Require real.FromInt.
Require real.Truncate.
Require real.Square.
Require bv.Pow2int.
Require ieee_float.RoundingMode.

Require Import Flocq.Core.Fcore.
Require Import Flocq.Appli.Fappli_IEEE.
Require Import Flocq.Calc.Fcalc_bracket.
Require Import Flocq.Calc.Fcalc_round.
Require Import Flocq.Prop.Fprop_plus_error.
Require Import ZArith.
Require Import Fourier.
Import real.Truncate.
Import ieee_float.RoundingMode.

(* extra arguments _ below are needed for Coq prior to 8.6
   keep them until support for Coq 8.5 is dropped *)

Arguments B754_zero {prec} {emax} _ .
Arguments B754_infinity {prec} {emax} _.
Arguments B754_nan {prec} {emax} _ _ .
Arguments B754_finite {prec} {emax} _ _ _ _ .

Definition mode_to_IEEE : mode -> Fappli_IEEE.mode.
  exact (fun m =>
           match m with
           | RNE => mode_NE
           | RNA => mode_NA
           | RTP => mode_UP
           | RTN => mode_DN
           | RTZ => mode_ZR
           end).
Defined.

Coercion mode_to_IEEE : mode >-> Fappli_IEEE.mode.

Section GenericFloat.

Variable eb_pos sb_pos : positive.

(* Why3 goal *)
Definition eb: Z.
Proof.
  exact (Z.pos eb_pos).
Defined.

(* Why3 goal *)
Definition sb: Z.
Proof.
  exact (Z.pos sb_pos).
Defined.

Hypothesis Heb : Zlt_bool 1 eb = true.
Hypothesis Hsbb : Zlt_bool 1 sb = true.

(* Why3 goal *)
Lemma eb_gt_1 : (1%Z < eb)%Z.
Proof.
  rewrite Zlt_is_lt_bool.
  apply Heb.
Qed.

(* Why3 goal *)
Lemma sb_gt_1 : (1%Z < sb)%Z.
Proof.
  rewrite Zlt_is_lt_bool.
  apply Hsbb.
Qed.

(* power of infinities *)
Definition emax : Z.
Proof.
  exact (radix2 ^ (eb - 1))%Z.
Defined.

(* Why3 goal *)
Definition t : Type.
Proof.
  exact (binary_float sb emax).
Defined.

(* Why3 goal *)
Definition zeroF: t.
Proof.
  exact (B754_zero false).
Defined.

Definition emin := (3 - emax - sb)%Z.
Notation fexp := (FLT_exp emin sb).

Lemma Hsb : Zlt_bool 0 sb = true.
Proof.
  auto with zarith.
Qed.

Lemma Hsb': (0 < sb)%Z.
Proof.
  unfold sb; auto with zarith.
Qed.

Hypothesis Hemax : Zlt_bool sb emax = true.

Lemma Hemax': (sb < emax)%Z.
Proof.
  rewrite Zlt_is_lt_bool.
  apply Hemax.
Qed.

Instance Hsb'' : Prec_gt_0 sb := Hsb'.

Lemma fexp_Valid : Valid_exp fexp.
Proof.
  apply (fexp_correct _ _ Hsb'').
Qed.

Definition r_to_fp rnd x : binary_float sb emax :=
  let r := round radix2 fexp (round_mode rnd) x in
  let m := Ztrunc (scaled_mantissa radix2 fexp r) in
  let e := canonic_exp radix2 fexp r in
  binary_normalize sb emax Hsb' Hemax' rnd m e false.

Theorem r_to_fp_correct :
  forall rnd x,
  let r := round radix2 fexp (round_mode rnd) x in
  (Rabs r < bpow radix2 emax)%R ->
  is_finite sb emax (r_to_fp rnd x) = true /\
  B2R sb emax (r_to_fp rnd x) = r.
Proof with auto with typeclass_instances.
intros rnd x r Bx.
unfold r_to_fp. fold r.
generalize (binary_normalize_correct sb emax Hsb' Hemax' rnd (Ztrunc (scaled_mantissa radix2 fexp r)) (canonic_exp radix2 fexp r) false).
unfold r.
elim generic_format_round...
fold emin r.
rewrite round_generic...
rewrite Rlt_bool_true with (1 := Bx).
now split.
apply generic_format_round...
Qed.

Theorem r_to_fp_format :
  forall rnd x,
  FLT_format radix2 emin sb x ->
  (Rabs x < bpow radix2 emax)%R ->
  B2R sb emax (r_to_fp rnd x) = x.
Proof with auto with typeclass_instances.
intros rnd x Fx Bx.
assert (Gx: generic_format radix2 fexp x).
apply generic_format_FLT.
apply Fx.
pattern x at 2 ; rewrite <- round_generic with (rnd := round_mode rnd) (2 := Gx)...
refine (proj2 (r_to_fp_correct _ _ _)).
rewrite round_generic...
Qed.

Lemma max_eb_bounded : bounded sb emax (2 ^ sb_pos - 1) (emax - sb) = true.
Proof.
  assert (0 <= sb - 1)%Z.
  apply Zlt_0_le_0_pred, Hsb'.
  unfold bounded; apply Bool.andb_true_iff; split.
  unfold canonic_mantissa, FLT_exp.
  apply Zeq_bool_true.
  rewrite Fcore_digits.Zpos_digits2_pos.
  rewrite (Fcore_digits.Zdigits_unique radix2 _ sb).
  assert (sb + (emax - sb) - sb = emax - sb)%Z by omega; rewrite H0.
  apply Zmax_left.
  assert (1 < emax)%Z.
  apply Z.le_lt_trans with (m := sb).
  change (Z.succ 0 <= sb)%Z; apply Zgt_le_succ; easy.
  apply Hemax'.
  auto with zarith.
  rewrite Z.abs_eq by auto with zarith.
  change ((2 ^ (sb - 1) <= Z.pos (2 ^ sb_pos - 1) < 2 ^ sb)%Z).
  assert (Z.pos (2 ^ sb_pos - 1) = 2 ^ sb -1)%Z.
  rewrite Pos2Z.inj_sub, Pos2Z.inj_pow.
  fold sb; reflexivity.
  apply Pos.pow_gt_1; easy.
  rewrite H0.
  split; auto with zarith.
  assert (sb = Z.succ (sb - 1)) by auto with zarith.
  rewrite H1 at 2.
  rewrite Z.pow_succ_r by trivial.
  assert (1 <= 2 ^ (sb - 1))%Z.
  apply Z.lt_pred_le, (Zpower_gt_0 radix2 (sb - 1)); trivial.
  omega.
  apply Zle_bool_true; auto with zarith.
Qed.

Definition max_value: t.
Proof.
  exact (B754_finite false (2 ^ sb_pos - 1) (emax - sb) max_eb_bounded).
Defined.

Definition One_Nan_pl: nan_pl sb.
Proof.
  unfold nan_pl, Fcore_digits.digits2_pos.
  exists 1%positive.
  apply Hsbb.
Qed.

Definition One_Nan: t := B754_nan false One_Nan_pl.

Definition nan_bf: binary_float sb emax -> binary_float sb emax -> bool * nan_pl sb.
Proof.
  exact (fun b nan => (true,One_Nan_pl)).
Qed.

(* Why3 goal *)
Definition add: ieee_float.RoundingMode.mode -> t -> t -> t.
Proof.
  exact (Bplus sb emax _ Hemax' nan_bf).
Defined.

(* Why3 goal *)
Definition sub: ieee_float.RoundingMode.mode -> t -> t -> t.
Proof.
  exact (Bminus sb emax _ Hemax' nan_bf).
Defined.

(* Why3 goal *)
Definition mul: ieee_float.RoundingMode.mode -> t -> t -> t.
Proof.
  exact (Bmult sb emax _ Hemax' nan_bf).
Defined.

(* Why3 goal *)
Definition div: ieee_float.RoundingMode.mode -> t -> t -> t.
Proof.
  exact (Bdiv sb emax _ Hemax' nan_bf).
Defined.

(* Why3 goal *)
Definition abs: t -> t.
Proof.
  exact (Babs sb emax pair).
Defined.

(* Why3 goal *)
Definition neg: t -> t.
Proof.
  exact (Bopp sb emax pair).
Defined.

(* Why3 goal *)
Definition fma: ieee_float.RoundingMode.mode -> t -> t -> t -> t.
Admitted.

(* Why3 goal *)
Definition sqrt: ieee_float.RoundingMode.mode -> t -> t.
Proof.
  exact (Bsqrt sb emax Hsb' Hemax' (fun b => (true,One_Nan_pl))).
Defined.

Definition z_to_fp m x := binary_normalize sb emax Hsb' Hemax' (mode_to_IEEE m) x 0 false.

Definition fp_to_z: mode -> t -> Z.
Proof.
  exact (fun m x => match m with
         | RNA => ZnearestA (B2R sb emax x)
         | RNE => ZnearestE (B2R sb emax x)
         | RTP => Zceil (B2R sb emax x)
         | RTN => Zfloor (B2R sb emax x)
         | RTZ => Ztrunc (B2R sb emax x)
         end).
Defined.

(* Why3 goal *)
Definition roundToIntegral: ieee_float.RoundingMode.mode -> t -> t.
Proof.
  exact (fun m x =>
           match x with
             | B754_zero b => B754_zero b
             | B754_infinity b => B754_infinity b
             | B754_nan b n => B754_nan b n
             | B754_finite b ma e _ =>
               let x_int := fp_to_z m x in
               match Z.eq_dec x_int 0%Z with
               | left _ => B754_zero b
               | right _ => z_to_fp RTZ x_int
               end
           end).
Defined.

(* Why3 goal *)
Definition min: t -> t -> t.
Proof.
  exact (fun x y => match Bcompare _ _ x y with
           | Some Lt => x
           | Some Gt | Some Eq => y
           | None => One_Nan
         end).
Defined.

(* Why3 goal *)
Definition max: t -> t -> t.
Proof.
  exact (fun x y => match Bcompare _ _ x y with
           | Some Lt => y
           | Some Gt | Some Eq => x
           | None => One_Nan
         end).
Defined.

(* Why3 goal *)
Definition le: t -> t -> Prop.
Proof.
  exact (fun a b => Bcompare _ _ a b = Some Lt \/ Bcompare _ _ a b = Some Eq).
Defined.

Hint Unfold le.

(* Why3 goal *)
Definition lt: t -> t -> Prop.
Proof.
  exact (fun a b => Bcompare _ _ a b = Some Lt).
Defined.

Hint Unfold lt.

Lemma gt_bcompare : forall {x y}, lt y x <-> Bcompare _ _ x y = Some Gt.
Proof.
  intros x y.
  rewrite Bcompare_swap.
  unfold lt.
  destruct Bcompare.
  destruct c; simpl; split; intro h; inversion h; auto.
  split; intro h; inversion h; auto.
Qed.

Lemma ge_bcompare : forall {x y}, le y x <-> Bcompare _ _ x y = Some Gt \/ Bcompare _ _ x y = Some Eq.
Proof.
  intros x y.
  unfold le.
  rewrite <-gt_bcompare.
  assert (Bcompare _ _ y x = Some Eq <-> Bcompare _ _ x y = Some Eq).
  rewrite Bcompare_swap.
  destruct Bcompare; try (split; now auto).
  destruct c; simpl; split; intro h; inversion h; auto.
  rewrite H; reflexivity.
Qed.

(* Why3 goal *)
Definition eq: t -> t -> Prop.
Proof.
  exact (fun a b => Bcompare _ _ a b = Some Eq).
Defined.

Hint Unfold eq.

Lemma le_correct: forall x y, le x y <-> lt x y \/ eq x y.
Proof.
  intros x y; split; intro h; destruct h; auto.
Qed.

Lemma lt_le: forall {x y}, lt x y -> le x y.
Proof.
  intros x y.
  rewrite le_correct; auto.
Qed.

Lemma eq_zero_iff: forall {x}, eq zeroF x <-> x = zeroF \/ x = neg zeroF.
Proof.
  intro; unfold eq; destruct x ; destruct b; simpl.
  split; auto.
  split; auto.
  split; [easy| intro h; destruct h; easy].
  split; [easy| intro h; destruct h; easy].
  split; [easy| intro h; destruct h; easy].
  split; [easy| intro h; destruct h; easy].
  split; [easy| intro h; destruct h; easy].
  split; [easy| intro h; destruct h; easy].
Qed.

(* Why3 goal *)
Definition is_normal: t -> Prop.
Proof.
  exact (fun x => match x with
                    | B754_zero _ => True
                    | B754_finite _ _ e _ => (emin < e)%Z
                    | _ => False
        end).
Defined.

(* Why3 goal *)
Definition is_subnormal: t -> Prop.
Proof.
  exact (fun x => match x with
                    | B754_finite _ _ e _ => (emin = e)%Z
                    | _ => False
        end).
Defined.

(* Why3 goal *)
Definition is_zero: t -> Prop.
Proof.
  exact (fun x => eq zeroF x).
Defined.

Lemma zero_is_zero: forall {b}, is_zero (B754_zero b).
Proof.
  easy.
Qed.

Lemma is_zero_B754_zero :
  forall x, is_zero x <-> exists b, x = B754_zero b.
Proof.
  intro.
  unfold is_zero; rewrite eq_zero_iff.
  unfold zeroF.
  simpl neg.
  split; intro h; destruct h; auto; try easy.
  exists false; assumption.
  exists true; assumption.
  destruct x0;[right|left];auto.
Qed.

Lemma zero_or_not : forall x, ~ is_zero x \/ is_zero x.
Proof.
  destruct x ; destruct b; try (right; easy); left; simpl; try easy.
Qed.

(* Why3 goal *)
Definition is_infinite: t -> Prop.
Proof.
  exact (fun x => match x with
                  | B754_infinity _ => True
                  | _ => False
                  end).
Defined.

Coercion is_true : bool >-> Sortclass.

Lemma eq_infinite_dist: forall {x y}, eq x y -> is_infinite x -> is_infinite y.
Proof.
  intros; destruct x, y; try easy; destruct b,b0; easy.
Qed.

(* Why3 goal *)
Definition is_nan: t -> Prop.
Proof.
  exact (is_nan sb emax).
Defined.

Lemma is_infinite_not_nan: forall {x}, is_infinite x -> ~ is_nan x.
Proof.
  intro x; destruct x; easy.
Qed.

Lemma le_infinity: forall {x}, ~ is_nan x -> le x (B754_infinity false).
Proof.
  intros.
  unfold le.
  destruct x; try easy.
  + left; easy.
  + destruct b; unfold Bcompare; [left|right]; easy.
  + contradict H; easy.
  + destruct b; left; easy.
Qed.

Lemma is_nan_dec: forall x, {is_nan x} + {~ is_nan x}.
Proof.
  intro; destruct x; compute; intuition.
Qed.

Lemma eq_not_nan_refl: forall {x : t}, ~ is_nan x -> eq x x.
Proof.
  intros; destruct x; auto.
  destruct b; auto.
  destruct H; easy.
  destruct b; unfold eq; simpl; rewrite Z.compare_refl, Pcompare_refl; easy.
Qed.

Lemma eq_not_nan: forall {x y}, eq x y -> ~ is_nan x /\ ~ is_nan y.
Proof.
  intros; destruct x; destruct y; try easy; intuition; destruct b; easy.
Qed.

Lemma lt_not_nan: forall {x y}, lt x y -> ~ is_nan x /\ ~ is_nan y.
Proof.
  intros; destruct x, y; try easy; intuition; destruct b; easy.
Qed.

Lemma le_or_lt_or_nan: forall x y, le x y \/ lt y x \/ is_nan x \/ is_nan y.
Proof.
  unfold is_nan.
  destruct x, y; auto; try easy;
    try (destruct b, b0; auto; easy).
  unfold le, lt.
  set (x:=(B754_finite b m e e0)).
  set (y:=(B754_finite b0 m0 e1 e2)).
  rewrite (Bcompare_correct sb emax x y), (Bcompare_correct sb emax y x); auto.
  destruct (Rtotal_order (B2R sb emax x) (B2R sb emax y)) as [h|[h|h]].
  rewrite (Rcompare_Lt _ _ h), (Rcompare_Gt _ _ h); auto.
  rewrite Rcompare_Eq; auto.
  rewrite (Rcompare_Lt _ _ (Rfourier_gt_to_lt _ _  h)), (Rcompare_Gt _ _ (Rfourier_gt_to_lt _ _ h)); auto.
Qed.

(* Why3 goal *)
Definition is_positive: t -> Prop.
Proof.
  exact (fun x => match x with
                    | B754_zero false => True
                    | B754_finite false _ e _ => True
                    | B754_infinity false => True
                    | _ => False
        end).
Defined.

Hint Unfold is_positive.

Lemma is_positive_Bsign: forall x, ~ is_nan x -> (is_positive x <-> Bsign sb emax x = false).
Proof.
  split.
  destruct x ; destruct b; try easy.
  destruct x ; destruct b; try easy.
  contradict H; easy.
Qed.

Lemma is_positive_correct: forall x, is_positive x <-> lt zeroF x \/ x = B754_zero false.
Proof.
  split.
  intro h; destruct x ; destruct b; auto; easy.
  intro h; destruct h; destruct x ; destruct b; easy.
Qed.

(* Why3 goal *)
Definition is_negative: t -> Prop.
Proof.
  exact (fun x => match x with
                    | B754_zero true => True
                    | B754_finite true _ e _ => True
                    | B754_infinity true => True
                    | _ => False
        end).
Defined.

Hint Unfold is_negative.

Lemma is_negative_Bsign: forall x, ~ is_nan x -> (is_negative x <-> Bsign sb emax x = true).
Proof.
  split.
  destruct x ; destruct b; easy.
  destruct x ; destruct b; easy.
Qed.

Lemma is_negative_correct: forall x, is_negative x <-> lt x zeroF \/ x = B754_zero true.
Proof.
  split.
  intro h; destruct x ; destruct b; auto; easy.
  intro h; destruct h; destruct x ; destruct b; easy.
Qed.

(* Why3 goal *)
Definition is_finite: t -> Prop.
  exact (is_finite sb emax).
Defined.

Lemma not_nan: forall x, ~ (is_nan x) -> {is_finite x} + {is_infinite x}.
Proof.
  unfold is_nan, is_finite.
  intro x; destruct x; simpl; intro; auto.
Qed.

Lemma is_finite_not_nan: forall {x}, is_finite x -> ~ is_nan x.
Proof.
  intro x; destruct x; easy.
Qed.

Lemma Finite_Infinite_Nan_dec: forall x:t, {is_finite x} + {is_infinite x} + {is_nan x}.
Proof.
  intro x; destruct x; simpl.
  left; left; easy.
  left; right; easy.
  right; easy.
  left; left; easy.
Qed.

Lemma eq_finite_dist: forall {x y}, eq x y -> is_finite x -> is_finite y.
Proof.
  intros; destruct x, y; try easy; destruct b,b0; easy.
Qed.

Lemma bounded_floats :
  forall x:t, (is_finite x) ->
              Bcompare sb emax (abs x) max_value = Some Lt
           \/ Bcompare sb emax (abs x) max_value = Some Eq.
Proof.
  destruct x; try easy; auto; (* simpl; *) intros.
  destruct (andb_prop _ _ e0) as (H1,H2).
  generalize (Zeq_bool_eq _ _ H1); clear H1; intro H1.
  generalize (Zle_bool_imp_le _ _ H2); clear H2; intro H2.
  destruct (Z_le_lt_eq_dec _ _ H2); clear H2.
  simpl; rewrite (Zcompare_Lt _ _ l); auto.
  simpl; rewrite (Zcompare_Eq _ _ e1).
  unfold FLT_exp in H1.
  rewrite Fcore_digits.Zpos_digits2_pos in H1.
  assert (3 - emax - sb <= Fcore_digits.Zdigits radix2 (Z.pos m) + e - sb)%Z.
  rewrite e1.
  pose sb_gt_1. pose Hemax'.
  assert (Z.pos m <> 0)%Z. pose (Pos2Z.is_pos m); auto with zarith.
  pose (Fcore_digits.Zdigits_gt_0 radix2 (Z.pos m) H0).
  auto with zarith.
  rewrite <-Z.max_l_iff in H0.
  rewrite H0 in H1; clear H0.
  assert (Fcore_digits.Zdigits radix2 (Z.pos m) = sb) by auto with zarith; clear H1.
  pose (Fcore_digits.Zdigits_correct radix2 (Z.pos m)).
  rewrite H0 in a.
  replace (Z.abs (Z.pos m)) with (Z.pos m) in a by auto with zarith.
  destruct a. clear H1.
  assert (Z.pos m = radix2 ^ sb - 1 \/ Z.pos m < radix2 ^ sb - 1)%Z by omega.
  destruct H1.
  right.
  replace (radix2 ^ sb)%Z with (Z.pos 2 ^ Z.pos sb_pos)%Z in H1 by auto.
  rewrite <-Pos2Z.inj_pow, <-Pos2Z.inj_sub, <-Zpos_eq_iff in H1.
  rewrite H1.
  rewrite Pcompare_refl; reflexivity.
  apply Pos.pow_gt_1; easy.
  left.
  rewrite nat_of_P_lt_Lt_compare_complement_morphism. reflexivity.
  apply Pos2Nat.inj_lt.
  replace (radix2 ^ sb)%Z with (Z.pos 2 ^ Z.pos sb_pos)%Z in H1 by auto.
  rewrite <-Pos2Z.inj_pow, <-Pos2Z.inj_sub in H1.
  auto with zarith.
  apply Pos.pow_gt_1; easy.
Qed.

Lemma bounded_floats_le: forall x, is_finite x -> le (abs x) max_value.
Proof.
  apply bounded_floats.
Qed.

Lemma is_finite_B: forall (x:t),
    is_finite x ->
    exists b, x = B754_zero b \/ exists b m e p, x = B754_finite b m e p.
Proof.
  intro x.
  destruct x; try easy; intros.
  exists b; auto.
  exists b; right.
  exists b, m, e, e0; trivial.
Qed.

(* Why3 assumption *)
Definition is_plus_infinity (x:t): Prop := (is_infinite x) /\ (is_positive
  x).

(* Why3 assumption *)
Definition is_minus_infinity (x:t): Prop := (is_infinite x) /\ (is_negative
  x).

(* Why3 assumption *)
Definition is_plus_zero (x:t): Prop := (is_zero x) /\ (is_positive x).

(* Why3 assumption *)
Definition is_minus_zero (x:t): Prop := (is_zero x) /\ (is_negative x).

(* Why3 assumption *)
Definition is_not_nan (x:t): Prop := (is_finite x) \/ (is_infinite x).

(* Why3 goal *)
Lemma is_not_nan1 : forall (x:t), (is_not_nan x) <-> ~ (is_nan x).
Proof.
  unfold is_not_nan; split; intro H.
  destruct H; [apply is_finite_not_nan|apply is_infinite_not_nan];trivial.
  destruct (not_nan _ H); auto.
Qed.

(* Why3 goal *)
Lemma is_not_finite : forall (x:t), (~ (is_finite x)) <-> ((is_infinite x) \/
  (is_nan x)).
Proof.
intros x.
destruct x; split; intro h; try easy.
contradict h; easy.
destruct h as [h|h]; contradict h; easy.
left; easy.
right; easy.
contradict h; easy.
destruct h as [h|h]; contradict h; easy.
Qed.

(* Why3 goal *)
Definition to_real: t -> R.
Proof.
  exact (B2R sb emax).
Defined.

Lemma Some_ext: forall {T} (a b:T), Some a = Some b <-> a = b.
Proof.
  intros; split; intro H;[inversion H|rewrite H]; reflexivity.
Qed.

Lemma to_real_eq: forall {x:t} {y:t}, is_finite x -> is_finite y ->
                                      (eq x y <-> to_real x = to_real y).
Proof.
  intros x y h1 h2.
  unfold eq.
  rewrite (Bcompare_correct _ _ x y h1 h2), Some_ext.
  split; intro H; [apply (Rcompare_Eq_inv _ _ H)|
                   apply (Rcompare_Eq _ _ H)].
Qed.

Lemma to_real_eq_alt: forall {x y}, eq x y -> to_real x = to_real y.
Proof.
  destruct x, y; destruct b, b0; unfold eq, Bcompare; try easy.
  - destruct (Z_dec e e1) as [[s|s]|s].
    + rewrite Zcompare_Lt by auto; intro; easy.
    + apply Z.gt_lt in s.
      rewrite Zcompare_Gt by auto; intro; easy.
    + rewrite Zcompare_Eq by auto; intro.
      inversion H.
      rewrite <-ZC4 in H1.
      apply Pcompare_Eq_eq in H1.
      destruct m; simpl; subst; auto.
  - destruct (Z_dec e e1) as [[s|s]|s].
    + rewrite Zcompare_Lt by auto; intro; easy.
    + apply Z.gt_lt in s.
      rewrite Zcompare_Gt by auto; intro; easy.
    + rewrite Zcompare_Eq by auto; intro.
      inversion H.
      apply Pcompare_Eq_eq in H1.
      destruct m; simpl; subst; auto.
Qed.

Lemma le_to_real: forall (x:t) (y:t), is_finite x -> is_finite y ->
  (le x y <-> (to_real x <= to_real y)).
Proof.
intros x y h1 h2.
unfold le.
rewrite (Bcompare_correct _ _ x y h1 h2), Some_ext, Some_ext.
split; intro H.
destruct H; [apply Rlt_le; apply (Rcompare_Lt_inv _ _ H)|
             apply Req_le; apply (Rcompare_Eq_inv _ _ H)].
destruct H; [left; apply (Rcompare_Lt _ _ H)|
             right; apply (Rcompare_Eq _ _ H)].
Qed.

(* Why3 goal *)
Lemma zeroF_is_positive : (is_positive zeroF).
Proof.
easy.
Qed.

(* Why3 goal *)
Lemma zeroF_is_zero : (is_zero zeroF).
Proof.
  apply eq_refl; easy.
Qed.

Lemma zeroF_to_real : ((to_real zeroF) = 0%R).
Proof.
easy.
Qed.

Lemma B754_zero_to_real: forall {b}, to_real (B754_zero b) = 0.
Proof.
  intro b.
  rewrite <-(to_real_eq_alt zero_is_zero).
  apply zeroF_to_real.
Qed.

(* Why3 goal *)
Lemma zero_to_real : forall (x:t), (is_zero x) <-> ((is_finite x) /\
  ((to_real x) = 0%R)).
Proof.
  unfold is_zero.
  assert (is_finite zeroF) by easy.
  intros x; split; intro H0.
  assert (is_finite x) by apply (eq_finite_dist H0 H).
  split; auto.
  symmetry.
  rewrite <-(to_real_eq H H1); auto.
  destruct H0.
  rewrite to_real_eq; auto.
Qed.

(* Why3 goal *)
Notation of_int := z_to_fp.

(* Why3 goal *)
Notation to_int := fp_to_z.

Lemma to_int_zeroF: forall m, to_int m zeroF = 0%Z.
Proof.
  intro m.
  destruct (valid_rnd_round_mode m) as (_,b).
  pose proof (b 0%Z) as H; simpl in H.
  destruct m; simpl; auto.
Qed.

(* add to theory ? *)
Lemma to_int_eq: forall {m x y}, eq x y -> to_int m x = to_int m y.
Proof.
  destruct x, y; destruct b, b0; unfold eq, Bcompare; try easy.
  - destruct (Z_dec e e1) as [[s|s]|s].
    + rewrite Zcompare_Lt by auto; intro; easy.
    + apply Z.gt_lt in s.
      rewrite Zcompare_Gt by auto; intro; easy.
    + rewrite Zcompare_Eq by auto; intro.
      inversion H.
      rewrite <-ZC4 in H1.
      apply Pcompare_Eq_eq in H1.
      destruct m; simpl; subst; auto.
  - destruct (Z_dec e e1) as [[s|s]|s].
    + rewrite Zcompare_Lt by auto; intro; easy.
    + apply Z.gt_lt in s.
      rewrite Zcompare_Gt by auto; intro; easy.
    + rewrite Zcompare_Eq by auto; intro.
      inversion H.
      apply Pcompare_Eq_eq in H1.
      destruct m; simpl; subst; auto.
Qed.

Lemma to_int_B754_zero: forall {b m}, to_int m (B754_zero b) = 0%Z.
Proof.
  intros b m.
  rewrite <-(to_int_eq zero_is_zero).
  apply to_int_zeroF.
Qed.

(* add to theory ? *)
Lemma to_int_le: forall {x y m}, is_finite x -> is_finite y -> le x y -> (to_int m x <= to_int m y)%Z.
Proof.
  intros.
  destruct m;

  [destruct (valid_rnd_round_mode mode_NE)|
   destruct (valid_rnd_NA)|
   destruct (valid_rnd_UP)|
   destruct (valid_rnd_DN)|
   destruct (valid_rnd_ZR)];
  apply Zrnd_le;
  apply le_to_real; auto.
Qed.

(* Why3 goal *)
Lemma zero_of_int : forall (m:ieee_float.RoundingMode.mode),
  (zeroF = (of_int m 0%Z)).
Proof.
auto.
Qed.

(* Why3 goal *)
Definition round: ieee_float.RoundingMode.mode -> R -> R.
Proof.
  exact (fun m => round radix2 fexp (round_mode m)).
Defined.

(* Why3 goal *)
Definition max_real: R.
Proof.
  exact ((1 - bpow radix2 (- sb)) * bpow radix2 emax).
Defined.

Lemma max_real_is_F2R: @F2R radix2 {| Fnum := Z.pos (2 ^ sb_pos - 1); Fexp := emax - sb |} = max_real.
Proof.
  unfold F2R.
  unfold Fnum, Fexp.
  rewrite Pos2Z.inj_sub, Pos2Z.inj_pow, Z2R_minus.
  fold sb.
  simpl (Z2R 1).
  change 2%Z with (radix_val radix2).
  rewrite Z2R_Zpower by easy.
  rewrite Int.infix_mn_def, bpow_plus.
  rewrite Rmult_comm, Rmult_assoc, Rmult_comm, Rmult_minus_distr_l.
  rewrite <-bpow_plus.
  replace (- sb + sb)%Z with 0%Z by auto with zarith.
  replace (bpow radix2 0) with 1 by auto.
  rewrite Rmult_1_r.
  reflexivity.
  apply Pos.pow_gt_1; easy.
Qed.

Lemma min_real_is_F2R: @F2R radix2 {| Fnum := Z.neg (2 ^ sb_pos - 1); Fexp := emax - sb |} = - max_real.
Proof.
  rewrite <-max_real_is_F2R.
  rewrite <-Fcalc_ops.F2R_opp.
  unfold Fcalc_ops.Fopp.
  reflexivity.
Qed.

Lemma max_value_to_real: to_real max_value = max_real.
Proof.
  unfold B2R; simpl.
  apply max_real_is_F2R.
Qed.

Lemma max_real_alt : max_real = bpow radix2 emax - bpow radix2 (emax - sb).
Proof.
  unfold max_real.
  rewrite Rmult_minus_distr_r, Rmult_1_l.
  rewrite <-bpow_plus.
  replace (- sb + emax)%Z with (emax - sb)%Z by auto with zarith.
  reflexivity.
Qed.

Lemma max_real_lt_bpow_radix2_emax: max_real < bpow radix2 emax.
Proof.
  rewrite max_real_alt.
  assert (0 < bpow radix2 (emax - sb)) by apply bpow_gt_0.
  fourier.
Qed.

Lemma max_real_ge_6: 6 <= max_real.
Proof.
  rewrite max_real_alt.
  assert (2 <= sb)%Z by (pose proof sb_gt_1; auto with zarith).
  assert (sb + 1 <= emax)%Z by (pose proof Hemax'; auto with zarith).
  assert (3 <= emax)%Z by auto with zarith.
  assert (8 <= bpow radix2 emax).
  assert (8 = bpow radix2 3) by easy.
  rewrite H2.
  apply bpow_le; assumption.
  change (6 <= bpow radix2 emax - bpow radix2 (emax + (- sb))).
  rewrite bpow_plus.
  assert (bpow radix2 (- sb) <= /4).
  assert (/4 = bpow radix2 (-2)) by easy.
  rewrite H3.
  apply bpow_le.
  rewrite <-(Z.opp_le_mono 2 sb); assumption.
  assert (bpow radix2 emax * bpow radix2 (-sb) <= bpow radix2 emax * / 4).
  apply Rfourier_le; auto; fourier.
  fourier.
Qed.

Lemma max_real_generic_format: generic_format radix2 fexp max_real.
Proof.
  rewrite <-max_value_to_real.
  apply generic_format_B2R.
Qed.

(* Why3 goal *)
Definition max_int: Z.
Proof.
  exact (2 ^ emax - 2 ^ (emax - sb))%Z.
Defined.

(* Why3 goal *)
Lemma max_int1 : (max_int = ((bv.Pow2int.pow2 (bv.Pow2int.pow2 (eb - 1%Z)%Z)) - (bv.Pow2int.pow2 ((bv.Pow2int.pow2 (eb - 1%Z)%Z) - sb)%Z))%Z).
Proof.
  rewrite two_p_equiv, two_p_equiv, two_p_equiv.
  now unfold max_int, emax.
Qed.

(* Why3 goal *)
Lemma max_real_int : (max_real = (BuiltIn.IZR max_int)).
Proof.
  unfold max_int.
  rewrite <-Z2R_IZR, Z2R_minus.
  change 2%Z with (radix_val radix2).
  rewrite Z2R_Zpower, Z2R_Zpower by (pose Hsb'; pose Hemax'; auto with zarith).
  apply max_real_alt.
Qed.

(* Why3 assumption *)
Definition in_range (x:R): Prop := ((-max_real)%R <= x)%R /\
  (x <= max_real)%R.

(* Why3 assumption *)
Definition in_int_range (i:Z): Prop := ((-max_int)%Z <= i)%Z /\
  (i <= max_int)%Z.

Lemma in_range_bpow_radix2_emax: forall x, in_range x -> Rabs x < bpow radix2 emax.
Proof.
  unfold in_range; intros.
  apply Rle_lt_trans with (r2:= max_real).
  apply Abs.Abs_le; apply H.
  apply max_real_lt_bpow_radix2_emax.
Qed.

Lemma is_finite_abs : forall x:t, is_finite x -> is_finite (abs x).
Proof.
  destruct x; try easy.
Qed.

(* Why3 goal *)
Lemma is_finite1 : forall (x:t), (is_finite x) -> (in_range (to_real x)).
Proof.
  intros x h1.
  apply Rabs_le_inv.
  rewrite <-max_value_to_real.
  apply Rcompare_not_Lt_inv.
  pose (is_finite_abs x h1).
  pose (bounded_floats x h1).
  rewrite Bcompare_correct in o; try easy.
  unfold abs in o.
  rewrite B2R_Babs in o.
  unfold to_real.
  intro H0.
  destruct o as [H|H]; inversion H as (H1);
  rewrite Rcompare_sym in H1; apply CompOpp_iff in H1;
  simpl in H0; rewrite H0 in H1;
  inversion H1.
Qed.

Lemma Rabs_round_max_real_emax:
  forall {m} {x}, Rabs (round m x) <= max_real <-> Rabs (round m x) < bpow radix2 emax.
Proof.
  intros m x; split; intro h.
  - apply Rle_lt_trans with (r2 := max_real).
    apply h.
    apply max_real_lt_bpow_radix2_emax.
  - destruct (r_to_fp_correct m x h).
    unfold round.
    rewrite <-H0, Abs.Abs_le.
    apply (is_finite1 _ H).
Qed.

(* Why3 assumption *)
Definition no_overflow (m:ieee_float.RoundingMode.mode) (x:R): Prop :=
  (in_range (round m x)).

Lemma no_overflow_Rabs_round_max_real: forall {m} {x}, no_overflow m x <-> Rabs (round m x) <= max_real.
Proof.
  intro x.
  split; intro h; apply Abs.Abs_le; easy.
Qed.

Lemma no_overflow_Rabs_round_emax: forall {m} {x}, no_overflow m x <-> Rabs (round m x) < bpow radix2 emax.
Proof.
  intros m x.
  apply (iff_trans no_overflow_Rabs_round_max_real Rabs_round_max_real_emax).
Qed.

Lemma IZR_alt: forall {x}, @F2R radix2 {| Fnum := x; Fexp := 0 |} = IZR x.
Proof.
  intros; unfold F2R, Fnum, Fexp, FLT_exp.
  assert (bpow radix2 0 = 1) as bpow_0 by easy.
  rewrite bpow_0, Rmult_1_r, Z2R_IZR.
  reflexivity.
Qed.

Lemma of_int_correct :
  forall {m} {x},
    no_overflow m (IZR x) ->
      to_real (of_int m x) = round m (IZR x) /\ is_finite (of_int m x) /\
      Bsign sb emax (of_int m x) =
      match (x ?= 0)%Z with
      | Eq => false
      | Lt => true
      | Gt => false
      end.
Proof.
  intros m x h1.
  generalize (binary_normalize_correct sb emax Hsb' Hemax' m x 0 false).
  rewrite Rlt_bool_true.
  - intro; destruct H; destruct H0.
    rewrite IZR_alt in H1.
    split.
    rewrite IZR_alt in H; auto.
    split; auto.
    rewrite <-Rcompare_Z2R.
    rewrite Z2R_IZR; auto.

  - apply no_overflow_Rabs_round_emax.
    rewrite IZR_alt; apply h1.
Qed.

(* Why3 goal *)
Lemma Bounded_real_no_overflow : forall (m:ieee_float.RoundingMode.mode)
  (x:R), (in_range x) -> (no_overflow m x).
Proof.
intros m x h1.
rewrite no_overflow_Rabs_round_max_real.
apply abs_round_le_generic.
apply fexp_Valid.
apply valid_rnd_round_mode.
apply max_real_generic_format.
rewrite Abs.Abs_le; easy.
Qed.

(* Why3 goal *)
Lemma Round_monotonic : forall (m:ieee_float.RoundingMode.mode) (x:R) (y:R),
  (x <= y)%R -> ((round m x) <= (round m y))%R.
Proof.
  intros m x y h1.
  apply round_le.
  apply fexp_Valid.
  apply valid_rnd_round_mode.
  apply h1.
Qed.

Lemma round_lt : forall {x y} {m:mode}, round m x < round m y -> x < y.
Proof.
  intros x y m h.
  case (Rlt_dec x y); auto.
  intro.
  apply Rnot_lt_le in n.
  apply (Round_monotonic m) in n.
  apply RIneq.Rle_not_lt in n.
  contradict h; assumption.
Qed.

(* Why3 goal *)
Lemma Round_idempotent : forall (m1:ieee_float.RoundingMode.mode)
  (m2:ieee_float.RoundingMode.mode) (x:R), ((round m1 (round m2
  x)) = (round m2 x)).
Proof with auto with typeclass_instances.
intros m1 m2 x.
apply round_generic...
apply generic_format_round...
Qed.

(* Why3 goal *)
Lemma Round_to_real : forall (m:ieee_float.RoundingMode.mode) (x:t),
  (is_finite x) -> ((round m (to_real x)) = (to_real x)).
Proof with auto with typeclass_instances.
intros m x h.
apply round_generic...
apply generic_format_B2R.
Qed.

(* Why3 goal *)
Lemma Round_down_le : forall (x:R), ((round ieee_float.RoundingMode.RTN
  x) <= x)%R.
Proof with auto with typeclass_instances.
intros x.
apply round_DN_pt...
Qed.

(* Why3 goal *)
Lemma Round_up_ge : forall (x:R), (x <= (round ieee_float.RoundingMode.RTP
  x))%R.
Proof with auto with typeclass_instances.
intros x.
apply round_UP_pt...
Qed.

(* Why3 goal *)
Lemma Round_down_neg : forall (x:R), ((round ieee_float.RoundingMode.RTN
  (-x)%R) = (-(round ieee_float.RoundingMode.RTP x))%R).
Proof.
intros x.
apply round_opp.
Qed.

(* Why3 goal *)
Lemma Round_up_neg : forall (x:R), ((round ieee_float.RoundingMode.RTP
  (-x)%R) = (-(round ieee_float.RoundingMode.RTN x))%R).
Proof.
intros x.
pattern x at 2 ; rewrite <- Ropp_involutive.
rewrite Round_down_neg.
now rewrite Ropp_involutive.
Qed.

(* Why3 goal *)
Definition pow2sb: Z.
Proof.
  exact (Zpower 2 sb).
Defined.

(* Why3 goal *)
Lemma pow2sb1 : (pow2sb = (bv.Pow2int.pow2 sb)).
Proof.
  now rewrite two_p_equiv.
Qed.

(* Why3 assumption *)
Definition in_safe_int_range (i:Z): Prop := ((-pow2sb)%Z <= i)%Z /\
  (i <= pow2sb)%Z.

Lemma max_rep_int_bounded: bounded sb emax (shift_pos (sb_pos - 1) 1) 1 = true.
Proof.
  unfold bounded.
  apply Bool.andb_true_iff; split.
  unfold canonic_mantissa.
  apply Zeq_bool_true.
  rewrite Fcore_digits.Zpos_digits2_pos, shift_pos_correct.
  rewrite Zmult_1_r, Z.pow_pos_fold.
  rewrite Fcore_digits.Zdigits_Zpower by easy.
  rewrite Pos2Z.inj_sub by exact sb_gt_1.
  fold sb.
  unfold FLT_exp.
  replace (sb - 1 + 1 + 1 - sb)%Z with 1%Z by ring.
  apply Zmax_l.
  pose sb_gt_1; pose Hemax'; omega.
  apply Zle_bool_true.
  pose Hemax'; pose Hsbb; omega.
Qed.

Definition Bmax_rep_int: t.
Proof.
  exact (B754_finite false _ _ max_rep_int_bounded).
Defined.

Lemma IZR_pow2sb: IZR pow2sb = bpow radix2 sb.
Proof.
  unfold pow2sb. simpl.
  rewrite Z2R_IZR; reflexivity.
Qed.

Lemma Bmax_rep_int_to_real: to_real Bmax_rep_int = IZR pow2sb.
Proof.
  rewrite IZR_pow2sb.
  unfold B2R; simpl.
  rewrite shift_pos_correct.
  rewrite Z.pow_pos_fold.
  unfold F2R.
  unfold Fnum, Fexp.
  rewrite Zmult_1_r.
  change 2%Z with (radix_val radix2).
  rewrite Z2R_Zpower by easy.
  rewrite <-bpow_plus.
  rewrite Pos2Z.inj_sub by exact sb_gt_1.
  replace (Z.pos sb_pos - 1 + 1)%Z with (Z.pos sb_pos).
  reflexivity.
  ring.
Qed.

Lemma pow2sb_lt_max_int: (pow2sb <= max_int)%Z.
Proof.
  apply le_IZR.
  rewrite <-max_real_int, <-Bmax_rep_int_to_real.
  now apply is_finite1.
Qed.

Lemma rep_int_in_range:
  forall i, (- pow2sb <= i <= pow2sb)%Z ->
            in_range (IZR i).
Proof.
  intros.
  rewrite <-Z.abs_le in H.
  pose (IZR_le _ _ H).
  rewrite <-Rabs_Zabs in r.
  unfold in_range.
  apply Rabs_le_inv.
  apply Rle_trans with (r2 := IZR pow2sb); auto.

  rewrite <-Bmax_rep_int_to_real, <-max_value_to_real.
  apply le_to_real; try easy.

  assert (is_finite Bmax_rep_int) by easy.
  pose (bounded_floats _ H0).
  rewrite le_correct.
  easy.
Qed.

(* Why3 goal *)
Lemma Exact_rounding_for_integers : forall (m:ieee_float.RoundingMode.mode)
  (i:Z), (in_safe_int_range i) -> ((round m
  (BuiltIn.IZR i)) = (BuiltIn.IZR i)).
Proof with auto with typeclass_instances.
intros m z Hz.
apply round_generic...
assert (Zabs z <= pow2sb)%Z.
apply Z.abs_le with (1:=Hz).
destruct (Zle_lt_or_eq _ _ H) as [Bz|Bz] ; clear H Hz.
apply generic_format_FLT.
exists (Float radix2 z 0).
unfold F2R ; simpl.
split.
rewrite Z2R_IZR.
now rewrite Rmult_1_r.
split. easy.
unfold emin; generalize Hsb' Hemax'; omega.
unfold pow2sb in Bz.
change 2%Z with (radix_val radix2) in Bz.
apply generic_format_abs_inv.
rewrite  <- Z2R_IZR, <- Z2R_abs, Bz, Z2R_Zpower.
apply generic_format_bpow.
unfold FLT_exp, emin.
clear Bz; generalize Hsb' Hemax'; zify.
omega.
apply Zlt_le_weak.
apply Hsb'.
Qed.

Lemma in_safe_int_range_no_overflow : forall m {i}, in_safe_int_range i -> no_overflow m (IZR i).
Proof.
  intros m i h.
  apply Bounded_real_no_overflow.
  unfold in_safe_int_range in h.
  unfold in_range.
  rewrite max_real_int, <-FromInt.Neg, <-Z2R_IZR, <-Z2R_IZR, <-Z2R_IZR.
  pose proof pow2sb_lt_max_int.
  split; apply Z2R_le; auto with zarith.
Qed.

(* Why3 assumption *)
Definition same_sign (x:t) (y:t): Prop := ((is_positive x) /\ (is_positive
  y)) \/ ((is_negative x) /\ (is_negative y)).

Hint Unfold same_sign.

Lemma same_sign_refl: forall {x}, ~ is_nan x -> same_sign x x.
Proof.
  unfold is_nan, same_sign.
  intros x h.
  destruct x; try easy; try (destruct b; now auto).
  now contradict h.
Qed.

(* Why3 assumption *)
Definition diff_sign (x:t) (y:t): Prop := ((is_positive x) /\ (is_negative
  y)) \/ ((is_negative x) /\ (is_positive y)).

Hint Unfold same_sign.

(* Why3 goal *)
Lemma feq_eq : forall (x:t) (y:t), (is_finite x) -> ((is_finite y) ->
  ((~ (is_zero x)) -> ((eq x y) -> (x = y)))).
Proof.
intros x y h1 h2 h3 h4.
destruct x, y; try easy.

destruct b, b0; try easy.
destruct b, b0; try easy.

revert h3 h4.
case (Bool.bool_dec b b0); intro h; [rewrite h;clear h|destruct b, b0; try easy; destruct h;reflexivity].
intros h3 h4. clear h1.

case (Z_dec e e1); intro.

- unfold eq in h4; simpl in h4.
  destruct s.
  rewrite (Zcompare_Lt _ _ l) in h4.
  destruct b, b0; try easy.
  rewrite (Zcompare_Gt _ _ (Z.gt_lt _ _ g)) in h4.
  destruct b, b0; try easy.

- destruct e3.
  case (Pos.eq_dec m m0); intro.
  + destruct e1.
    assert (forall x y: bool, x = y \/ x <> y) by
        (intros; case (Bool.bool_dec x y); auto).
    destruct (Eqdep_dec.eq_proofs_unicity H e0 e2).
    reflexivity.

  + unfold eq in h4; simpl in h4.
    destruct b0.
    * rewrite Z.compare_refl, Some_ext, Pos.compare_cont_antisym in h4.
      rewrite (Pos.compare_eq _ _ h4) in n.
      destruct n; reflexivity.

    * rewrite Z.compare_refl, Some_ext in h4.
      rewrite (Pos.compare_eq _ _ h4) in n.
      destruct n; reflexivity.
Qed.

Lemma to_real_refl: forall {x:t} {y:t}, is_finite x -> is_finite y ->
                                        to_real x = to_real y -> same_sign x y ->
                                        x = y.
Proof.
  intros x y h1 h2 h3 h4.
  destruct x, y; try easy.
  + destruct b, b0, h4; easy.
  + symmetry in h3.
    apply F2R_eq_0_reg in h3.
    destruct b0; contradict h3; easy.
  + apply F2R_eq_0_reg in h3.
    destruct b; contradict h3; easy.
  + apply feq_eq; auto.
    rewrite eq_zero_iff; intro.
    destruct H; easy.
    apply to_real_eq; auto.
Qed.

(* Why3 goal *)
Lemma eq_feq : forall (x:t) (y:t), (is_finite x) -> ((is_finite y) ->
  ((x = y) -> (eq x y))).
Proof.
intros x y h1 h2 h3.
rewrite h3.
apply (eq_not_nan_refl (is_finite_not_nan h2)).
Qed.

(* Why3 goal *)
Lemma eq_refl : forall (x:t), (is_finite x) -> (eq x x).
Proof.
intros x h1.
apply (eq_not_nan_refl (is_finite_not_nan h1)).
Qed.

(* Why3 goal *)
Lemma eq_sym : forall (x:t) (y:t), (eq x y) -> (eq y x).
Proof.
intros x y.
unfold eq; intro h; rewrite Bcompare_swap, h; easy.
Qed.

(* Why3 goal *)
Lemma eq_trans : forall (x:t) (y:t) (z:t), (eq x y) -> ((eq y z) -> (eq x
  z)).
Proof.
  intros x y z h1 h2.
  destruct x, y, z; auto; destruct b, b0, b1; auto; try easy;
  apply to_real_eq in h1; try (split;easy);
  apply to_real_eq in h2; try (split;easy);
  apply to_real_eq; try (split;easy);
  apply (eq_trans h1 h2).
Qed.

(* Why3 goal *)
Lemma eq_zero : (eq zeroF (neg zeroF)).
Proof.
easy.
Qed.

(* Why3 goal *)
Lemma eq_to_real_finite : forall (x:t) (y:t), ((is_finite x) /\ (is_finite
  y)) -> ((eq x y) <-> ((to_real x) = (to_real y))).
Proof.
intros x y (h1,h2).
apply (to_real_eq h1 h2).
Qed.

(* Why3 goal *)
Lemma eq_special : forall (x:t) (y:t), (eq x y) -> ((is_not_nan x) /\
  ((is_not_nan y) /\ (((is_finite x) /\ (is_finite y)) \/ ((is_infinite x) /\
  ((is_infinite y) /\ (same_sign x y)))))).
Proof.
  intros x y h1.
  rewrite is_not_nan1, is_not_nan1.
  destruct (eq_not_nan h1) as (h2,h3).
  split; [auto|split;auto].
  destruct (not_nan _ h2); [left|right].
  - split; [apply i|apply (eq_finite_dist h1 i)].
  - split; [apply i|split;[apply (eq_infinite_dist h1 i)|]].
    destruct x, y; destruct b, b0; auto; easy.
Qed.

(* Why3 goal *)
Lemma lt_finite : forall (x:t) (y:t), ((is_finite x) /\ (is_finite y)) ->
  ((lt x y) <-> ((to_real x) < (to_real y))%R).
Proof.
intros x y (h1,h2).
unfold lt.
rewrite (Bcompare_correct _ _ x y h1 h2), Some_ext.
split; intro H; [apply (Rcompare_Lt_inv _ _ H)|
                 apply (Rcompare_Lt _ _ H)].
Qed.

(* Why3 goal *)
Lemma le_finite : forall (x:t) (y:t), ((is_finite x) /\ (is_finite y)) ->
  ((le x y) <-> ((to_real x) <= (to_real y))%R).
Proof.
intros x y (h1,h2).
unfold le.
rewrite (Bcompare_correct _ _ x y h1 h2), Some_ext, Some_ext.
split; intro H.
destruct H; [apply Rlt_le; apply (Rcompare_Lt_inv _ _ H)|
             apply Req_le; apply (Rcompare_Eq_inv _ _ H)].
destruct H; [left; apply (Rcompare_Lt _ _ H)|
             right; apply (Rcompare_Eq _ _ H)].
Qed.

Lemma lt_eq_trans : forall {x y z:t}, lt x y -> eq y z -> lt x z.
Proof.
  intros x y z h1 h2.
  destruct x, y, z; try easy; try (destruct b, b0, b1; easy).
  set (x:=B754_finite b m e e0);
    set (y:=B754_finite b0 m0 e1 e2);
    set (z:=B754_finite b1 m1 e3 e4); fold x y z in h1, h2.
  pose proof (Bcompare_correct sb emax x y).
  pose proof (Bcompare_correct sb emax y z).
  pose proof (Bcompare_correct sb emax x z).
  unfold lt in h1.
  rewrite H in h1 by easy; clear H.
  unfold eq in h2.
  rewrite H0 in h2 by easy; clear H0.
  unfold lt.
  rewrite H1 by easy; clear H1.
  apply f_equal, Rcompare_Lt.
  apply Rlt_le_trans with (B2R sb emax (B754_finite b0 m0 e1 e2)).
  + apply Rcompare_Lt_inv.
    now injection h1.
  + apply Rcompare_not_Gt_inv.
    inversion h1; inversion h2.
    destruct Rcompare; try easy.
    destruct Rcompare; try easy;
    simpl; rewrite H1; discriminate.
Qed.

Lemma eq_lt_trans : forall {x y z:t}, eq x y -> lt y z -> lt x z.
Proof.
  intros x y z h1 h2.
  destruct x, y, z; try easy; try (destruct b, b0, b1; easy).
  set (x:=B754_finite b m e e0);
    set (y:=B754_finite b0 m0 e1 e2);
    set (z:=B754_finite b1 m1 e3 e4); fold x y z in h1, h2.
  pose proof (Bcompare_correct sb emax x y).
  pose proof (Bcompare_correct sb emax y z).
  pose proof (Bcompare_correct sb emax x z).
  unfold eq in h1.
  unfold lt in h2.
  rewrite H in h1 by easy; clear H.
  rewrite H0 in h2 by easy; clear H0.
  unfold lt.
  rewrite H1 by easy; clear H1.
  apply f_equal, Rcompare_Lt.
  apply Rle_lt_trans with (B2R sb emax (B754_finite b0 m0 e1 e2)).
  + apply Rcompare_not_Gt_inv.
    inversion h1; inversion h2.
    destruct Rcompare; try easy;
    simpl; rewrite H0; discriminate.
  + apply Rcompare_Lt_inv.
    now injection h2.
Qed.

Lemma lt_lt_trans : forall {x y z:t}, lt x y -> lt y z -> lt x z.
Proof.
  intros x y z h1 h2.
  destruct x, y, z; try easy; try (destruct b, b0, b1; easy).
  set (x:=B754_finite b m e e0);
    set (y:=B754_finite b0 m0 e1 e2);
    set (z:=B754_finite b1 m1 e3 e4); fold x y z in h1, h2.
  pose proof (Bcompare_correct sb emax x y).
  pose proof (Bcompare_correct sb emax y z).
  pose proof (Bcompare_correct sb emax x z).
  unfold lt in h1, h2.
  rewrite H in h1 by easy; clear H.
  rewrite H0 in h2 by easy; clear H0.
  unfold lt.
  rewrite H1 by easy; clear H1.
  apply f_equal, Rcompare_Lt.
  apply Rlt_trans with (B2R sb emax (B754_finite b0 m0 e1 e2)).
  + apply Rcompare_Lt_inv.
    now injection h1.
  + apply Rcompare_Lt_inv.
    now injection h2.
Qed.

(* Why3 goal *)
Lemma le_lt_trans : forall (x:t) (y:t) (z:t), ((le x y) /\ (lt y z)) -> (lt x
  z).
Proof.
  intros x y z (h,h1).
  destruct h as [h|h].
  apply (lt_lt_trans h h1).
  apply (eq_lt_trans h h1).
Qed.

(* Why3 goal *)
Lemma lt_le_trans : forall (x:t) (y:t) (z:t), ((lt x y) /\ (le y z)) -> (lt x
  z).
Proof.
  intros x y z (h1,h2).
  destruct h2 as [h2|h2].
  apply (lt_lt_trans h1 h2).
  apply (lt_eq_trans h1 h2).
Qed.

(* Why3 goal *)
Lemma le_ge_asym : forall (x:t) (y:t), ((le x y) /\ (le y x)) -> (eq x y).
Proof.
intros x y.
unfold le, eq.
destruct x, y; intros (h,h1); auto; try (destruct b,b0,h,h1; easy).
set (x:=B754_finite b m e e0);
  set (y:=B754_finite b0 m0 e1 e2);
  fold x y in h, h1.
pose proof (Bcompare_correct sb emax x y).
pose proof (Bcompare_correct sb emax y x).
rewrite H in h by easy.
rewrite H0 in h1 by easy.
rewrite H by easy.
f_equal.
apply Rcompare_Eq.
apply Rle_antisym.
+ apply Rcompare_not_Gt_inv.
  destruct Rcompare.
  easy.
  easy.
  destruct h as [h|h]; inversion h.
+ apply Rcompare_not_Gt_inv.
  destruct (Rcompare (B2R sb emax y) (B2R sb emax x)).
  easy.
  easy.
  destruct h1 as [h1|h1]; inversion h1.
Qed.

Lemma Some_ext_op: forall {T} (a b:T), Some a <> Some b <-> a <> b.
Proof.
  intros; split; intro H.
  intro h.
  apply (H (f_equal _ h)).
  intro h; injection h; auto.
Qed.

(* Why3 goal *)
Lemma not_lt_ge : forall (x:t) (y:t), ((~ (lt x y)) /\ ((is_not_nan x) /\
  (is_not_nan y))) -> (le y x).
Proof.
  intros x y.
  rewrite is_not_nan1; rewrite is_not_nan1.
  unfold is_nan.
  destruct x, y; intros (h,(h1,h2)); auto; try (destruct b, b0; auto; destruct h1; auto; easy).
  set (x:=B754_finite b m e e0);
    set (y:=B754_finite b0 m0 e1 e2);
    fold x y in h, h1, h2.
  pose proof (Bcompare_correct sb emax x y).
  pose proof (Bcompare_correct sb emax y x).
  try easy;
  unfold le; unfold lt in h.
  rewrite H in h by easy.
  rewrite H0 by easy.
  rewrite Some_ext_op in h.
  destruct (Rcompare_not_Lt_inv _ _ h);
    [left;f_equal; apply Rcompare_Lt; assumption|
     right;f_equal; apply Rcompare_Eq; assumption].
Qed.

(* Why3 goal *)
Lemma not_gt_le : forall (x:t) (y:t), ((~ (lt y x)) /\ ((is_not_nan x) /\
  (is_not_nan y))) -> (le x y).
Proof.
  intros x y.
  rewrite is_not_nan1; rewrite is_not_nan1.
  unfold is_nan.
  destruct x, y; intros (h,(h1,h2)); auto; try (destruct b, b0; auto; destruct h1; auto; easy).
  set (x:=B754_finite b m e e0);
    set (y:=B754_finite b0 m0 e1 e2);
    fold x y in h, h1, h2.
  pose proof (Bcompare_correct sb emax x y).
  pose proof (Bcompare_correct sb emax y x).
  unfold le; unfold lt in h.
  rewrite H0 in h by easy.
  rewrite H by easy.
  rewrite Some_ext_op in h.
  destruct (Rcompare_not_Lt_inv _ _ h);
    [left;f_equal; apply Rcompare_Lt; assumption|
     right;f_equal; apply Rcompare_Eq; assumption].
Qed.

(* Why3 goal *)
Lemma le_special : forall (x:t) (y:t), (le x y) -> (((is_finite x) /\
  (is_finite y)) \/ (((is_minus_infinity x) /\ (is_not_nan y)) \/
  ((is_not_nan x) /\ (is_plus_infinity y)))).
Proof.
  intros x y h.
  rewrite is_not_nan1; rewrite is_not_nan1.
  unfold le in h.
  unfold is_nan, is_finite.
  destruct x, y; auto; try easy; try (destruct b, b0; auto; destruct h; auto; easy).
  - right; right. destruct b0, h; split; easy.
  - right; left. destruct b, h; split; easy.
  - right. destruct b, b0, h; try easy.
    + left; split; easy.
    + left; split; easy.
    + right; split; easy.
  - right; left. destruct b, h; split; easy.
  - right; right. destruct b, b0, h; split; easy.
Qed.

(* Why3 goal *)
Lemma lt_special : forall (x:t) (y:t), (lt x y) -> (((is_finite x) /\
  (is_finite y)) \/ (((is_minus_infinity x) /\ ((is_not_nan y) /\
  ~ (is_minus_infinity y))) \/ ((is_not_nan x) /\ ((~ (is_plus_infinity
  x)) /\ (is_plus_infinity y))))).
Proof.
  intros x y h.
  rewrite is_not_nan1; rewrite is_not_nan1.
  unfold lt in h.
  unfold is_nan, is_finite.
  unfold is_plus_infinity, is_minus_infinity.
  destruct x, y; auto; try easy; try (destruct b, b0; easy).

  - right; right. destruct b0; split; try split; easy.
  - right; left. destruct b; split; try split; easy.
  - right; right. destruct b, b0; split; try split; easy.
  - right; left. destruct b; split; try split; easy.
  - right; right. destruct b, b0; split; try split; easy.
Qed.

(* Why3 goal *)
Lemma lt_lt_finite : forall (x:t) (y:t) (z:t), (lt x y) -> ((lt y z) ->
  (is_finite y)).
Proof.
intros x y z h1 h2.
destruct x, y, z; destruct b, b0, b1; easy.
Qed.

(* Why3 goal *)
Lemma positive_to_real : forall (x:t), (is_finite x) -> ((is_positive x) ->
  (0%R <= (to_real x))%R).
Proof.
  intros x h1 h2.
  assert (is_finite zeroF) as zero_is_finite by easy.
  rewrite <-zeroF_to_real; apply (le_to_real _ _ zero_is_finite h1).
  generalize (is_positive_correct x); intro H; destruct H, H; auto.
  rewrite H; auto.
Qed.

Lemma non_zero_positive_to_real : forall {x:t},
    is_finite x -> ~ is_zero x -> is_positive x ->
     0 < to_real x.
Proof.
  intros x h1 h2 h3.
  rewrite <-zeroF_to_real; apply lt_finite.
  split; easy.
  destruct x; try easy.
  contradict h2; easy.
  simpl in h3.
  destruct b; simpl; easy.
Qed.

Lemma is_positive_to_int: forall {x} {m:mode}, is_finite x -> is_positive x -> (0 <= to_int m x)%Z.
Proof.
  intros x m h h1.
  rewrite <-(to_int_zeroF m).
  destruct (valid_rnd_round_mode m) as (h2,_).
  destruct m; simpl in h2; apply h2; now apply positive_to_real.
Qed.

(* Why3 goal *)
Lemma to_real_positive : forall (x:t), (is_finite x) ->
  ((0%R < (to_real x))%R -> (is_positive x)).
Proof.
  intros x h1 h2.
  assert (is_finite zeroF) as zero_is_finite by easy.
  rewrite <-zeroF_to_real, <-(lt_finite _ _ (conj zero_is_finite h1)) in h2.
  generalize (is_positive_correct x); intro H; destruct H; auto.
Qed.

(* Why3 goal *)
Lemma negative_to_real : forall (x:t), (is_finite x) -> ((is_negative x) ->
  ((to_real x) <= 0%R)%R).
Proof.
  intros x h1 h2.
  assert (is_finite zeroF) as zero_is_finite by easy.
  rewrite <-zeroF_to_real; apply (le_to_real _ _ h1 zero_is_finite).
  generalize (is_negative_correct x); intro H; destruct H, H; auto.
  rewrite H; auto.
Qed.

Lemma non_zero_negative_to_real : forall {x:t},
    is_finite x -> ~ is_zero x -> is_negative x ->
     to_real x < 0.
Proof.
  intros x h1 h2 h3.
  rewrite <-zeroF_to_real; apply lt_finite.
  split; easy.
  destruct x; try easy.
  contradict h2; easy.
  simpl in h3.
  destruct b; simpl; easy.
Qed.

Lemma is_negative_to_int: forall {x} {m:mode}, is_finite x -> is_negative x -> (to_int m x <= 0)%Z.
Proof.
  intros x m h h1.
  rewrite <-(to_int_zeroF m).
  destruct (valid_rnd_round_mode m) as (h2,_).
  destruct m; simpl in h2; apply h2; now apply negative_to_real.
Qed.

(* Why3 goal *)
Lemma to_real_negative : forall (x:t), (is_finite x) ->
  (((to_real x) < 0%R)%R -> (is_negative x)).
Proof.
  intros x h1 h2.
  assert (is_finite zeroF) as zero_is_finite by easy.
  rewrite <-zeroF_to_real, <-(lt_finite _ _ (conj h1 zero_is_finite)) in h2.
  generalize (is_negative_correct x); intro H; destruct H; auto.
Qed.

(* Why3 goal *)
Lemma negative_xor_positive : forall (x:t), ~ ((is_positive x) /\
  (is_negative x)).
Proof.
intros x.
destruct x ; destruct b; easy.
Qed.

(* Why3 goal *)
Lemma negative_or_positive : forall (x:t), (is_not_nan x) -> ((is_positive
  x) \/ (is_negative x)).
Proof.
intros x h1.
destruct x ; destruct b; simpl; auto; now elim h1.
Qed.

(* Why3 goal *)
Lemma diff_sign_trans : forall (x:t) (y:t) (z:t), ((diff_sign x y) /\
  (diff_sign y z)) -> (same_sign x z).
Proof.
  unfold diff_sign, same_sign.
  intros x y z (h1,h2).
  pose proof (negative_xor_positive y) as H.
  destruct h1 as [(h,h1)|(h,h1)], h2 as [(h2,h3)|(h2,h3)].
  - contradict H; split; easy.
  - left; split; easy.
  - right; split; easy.
  - contradict H; split; easy.
Qed.

(* Why3 goal *)
Lemma diff_sign_product : forall (x:t) (y:t), ((is_finite x) /\ ((is_finite
  y) /\ (((to_real x) * (to_real y))%R < 0%R)%R)) -> (diff_sign x y).
Proof.
intros x y (h1,(h2,h3)).
unfold diff_sign.
case (Rcase_abs (to_real y)); intro; [left|right].
- split; [apply to_real_positive; try easy|apply to_real_negative; easy].
  apply Rmult_lt_reg_r with (r := (-to_real y)); try fourier.
  rewrite Rmult_0_l, Ropp_mult_distr_r_reverse.
  apply Ropp_0_gt_lt_contravar.
  fourier.
- destruct r.
  + split; [apply to_real_negative; try easy|apply to_real_positive; easy].
    apply Rmult_lt_reg_r with (r := (to_real y)); try fourier.
  + assert false;[|easy].
    rewrite H in h3; fourier.
Qed.

(* Why3 goal *)
Lemma same_sign_product : forall (x:t) (y:t), ((is_finite x) /\ ((is_finite
  y) /\ (same_sign x y))) -> (0%R <= ((to_real x) * (to_real y))%R)%R.
Proof.
intros x y (h1,(h2,h3)).
unfold same_sign in h3.
destruct h3 as [(h3,h4)|(h3,h4)].
- apply (positive_to_real _ h1) in h3.
  apply (positive_to_real _ h2) in h4.
  apply Rmult_le_pos; auto.
- apply (negative_to_real _ h1) in h3.
  apply (negative_to_real _ h2) in h4.
  rewrite <-Rmult_opp_opp.
  apply Rmult_le_pos; fourier.
Qed.

(* Why3 assumption *)
Definition product_sign (z:t) (x:t) (y:t): Prop := ((same_sign x y) ->
  (is_positive z)) /\ ((diff_sign x y) -> (is_negative z)).

(* Why3 assumption *)
Definition overflow_value (m:ieee_float.RoundingMode.mode) (x:t): Prop :=
  match m with
  | ieee_float.RoundingMode.RTN => ((is_positive x) -> ((is_finite x) /\
      ((to_real x) = max_real))) /\ ((~ (is_positive x)) -> (is_infinite x))
  | ieee_float.RoundingMode.RTP => ((is_positive x) -> (is_infinite x)) /\
      ((~ (is_positive x)) -> ((is_finite x) /\
      ((to_real x) = (-max_real)%R)))
  | ieee_float.RoundingMode.RTZ => ((is_positive x) -> ((is_finite x) /\
      ((to_real x) = max_real))) /\ ((~ (is_positive x)) -> ((is_finite x) /\
      ((to_real x) = (-max_real)%R)))
  | (ieee_float.RoundingMode.RNA|ieee_float.RoundingMode.RNE) => (is_infinite
      x)
  end.

(* Why3 assumption *)
Definition sign_zero_result (m:ieee_float.RoundingMode.mode) (x:t): Prop :=
  (is_zero x) ->
  match m with
  | ieee_float.RoundingMode.RTN => (is_negative x)
  | _ => (is_positive x)
  end.

(* Why3 goal *)
Lemma add_finite : forall (m:ieee_float.RoundingMode.mode) (x:t) (y:t),
  (is_finite x) -> ((is_finite y) -> ((no_overflow m
  ((to_real x) + (to_real y))%R) -> ((is_finite (add m x y)) /\
  ((to_real (add m x y)) = (round m ((to_real x) + (to_real y))%R))))).
Proof.
intros m x y h1 h2 h3.
generalize (Bplus_correct sb emax Hsb'' Hemax' nan_bf m x y h1 h2); rewrite Rlt_bool_true.
intro; split; easy.
apply (in_range_bpow_radix2_emax _ h3).
Qed.

(* Why3 goal *)
Lemma add_finite_rev : forall (m:ieee_float.RoundingMode.mode) (x:t) (y:t),
  (is_finite (add m x y)) -> ((is_finite x) /\ (is_finite y)).
Proof.
intros m x y h1.
destruct x, y; try easy; destruct b, b0; easy.
Qed.

(* Why3 goal *)
Lemma add_finite_rev_n : forall (m:ieee_float.RoundingMode.mode) (x:t) (y:t),
  (ieee_float.RoundingMode.to_nearest m) -> ((is_finite (add m x y)) ->
  ((no_overflow m ((to_real x) + (to_real y))%R) /\ ((to_real (add m x
  y)) = (round m ((to_real x) + (to_real y))%R)))).
Proof.
intros m x y h1 h2.
destruct (add_finite_rev m x y h2).
assert (no_overflow m (to_real x + to_real y)).
2: split; [easy|apply add_finite; easy].
pose proof max_real_ge_6.
destruct x, y; try easy; try (rewrite B754_zero_to_real).
apply Bounded_real_no_overflow; rewrite (@B754_zero_to_real b0); split; fourier.
apply Bounded_real_no_overflow; rewrite Rplus_0_l; apply is_finite1; auto.
apply Bounded_real_no_overflow; rewrite Rplus_0_r; apply is_finite1; auto.
set (x := B754_finite b m0 e e0).
set (y := B754_finite b0 m1 e1 e2).
fold x y in h2, H, H0.
destruct (Rlt_le_dec (Rabs (round m (to_real x + to_real y))) (bpow radix2 emax)).
rewrite no_overflow_Rabs_round_emax; assumption.
pose proof (Bplus_correct sb emax Hsb'' Hemax' nan_bf m x y H H0).
change (Bplus sb emax Hsb'' Hemax' nan_bf m) with (add m) in H2.
rewrite Rlt_bool_false in H2; auto.
destruct m, h1; try easy.
destruct (add RNE x y); easy.
destruct (add RNA x y); easy.
Qed.

(* Why3 goal *)
Lemma sub_finite : forall (m:ieee_float.RoundingMode.mode) (x:t) (y:t),
  (is_finite x) -> ((is_finite y) -> ((no_overflow m
  ((to_real x) - (to_real y))%R) -> ((is_finite (sub m x y)) /\
  ((to_real (sub m x y)) = (round m ((to_real x) - (to_real y))%R))))).
Proof.
intros m x y h1 h2 h3.
generalize (Bminus_correct sb emax Hsb'' Hemax' nan_bf m x y h1 h2); rewrite Rlt_bool_true.
intro; split; easy.
apply (in_range_bpow_radix2_emax _ h3).
Qed.

(* Why3 goal *)
Lemma sub_finite_rev : forall (m:ieee_float.RoundingMode.mode) (x:t) (y:t),
  (is_finite (sub m x y)) -> ((is_finite x) /\ (is_finite y)).
Proof.
  intros m x y h1.
  destruct x, y; try easy; destruct b, b0; easy.
Qed.

(* Why3 goal *)
Lemma sub_finite_rev_n : forall (m:ieee_float.RoundingMode.mode) (x:t) (y:t),
  (ieee_float.RoundingMode.to_nearest m) -> ((is_finite (sub m x y)) ->
  ((no_overflow m ((to_real x) - (to_real y))%R) /\ ((to_real (sub m x
  y)) = (round m ((to_real x) - (to_real y))%R)))).
Proof.
intros m x y h1 h2.
destruct (sub_finite_rev m x y h2).
assert (no_overflow m (to_real x - to_real y)).
2: split; [easy|apply sub_finite; easy].
pose proof max_real_ge_6.
destruct x, y; try easy; try (rewrite B754_zero_to_real).
apply Bounded_real_no_overflow; rewrite (@B754_zero_to_real b0); split; fourier.
apply Bounded_real_no_overflow.
unfold to_real.
rewrite Rminus_0_l, <-(B2R_Bopp _ _ pair).
apply is_finite1, is_finite_Bopp.
apply Bounded_real_no_overflow; rewrite Rminus_0_r; apply is_finite1; auto.
set (x := B754_finite b m0 e e0).
set (y := B754_finite b0 m1 e1 e2).
fold x y in h2, H, H0.
destruct (Rlt_le_dec (Rabs (round m (to_real x - to_real y))) (bpow radix2 emax)).
rewrite no_overflow_Rabs_round_emax; assumption.
pose proof (Bminus_correct sb emax Hsb'' Hemax' nan_bf m x y H H0).
change (Bminus sb emax Hsb'' Hemax' nan_bf m) with (sub m) in H2.
rewrite Rlt_bool_false in H2; auto.
destruct m, h1; try easy.
destruct (sub RNE x y); easy.
destruct (sub RNA x y); easy.
Qed.

(* Why3 goal *)
Lemma mul_finite : forall (m:ieee_float.RoundingMode.mode) (x:t) (y:t),
  (is_finite x) -> ((is_finite y) -> ((no_overflow m
  ((to_real x) * (to_real y))%R) -> ((is_finite (mul m x y)) /\
  ((to_real (mul m x y)) = (round m ((to_real x) * (to_real y))%R))))).
Proof.
intros m x y h1 h2 h3.
generalize (Bmult_correct sb emax Hsb'' Hemax' nan_bf m x y); rewrite Rlt_bool_true, h1, h2.
intro; split; easy.
apply (in_range_bpow_radix2_emax _ h3).
Qed.

(* Why3 goal *)
Lemma mul_finite_rev : forall (m:ieee_float.RoundingMode.mode) (x:t) (y:t),
  (is_finite (mul m x y)) -> ((is_finite x) /\ (is_finite y)).
Proof.
  intros m x y h1.
  destruct x, y; try easy; destruct b, b0; easy.
Qed.

(* Why3 goal *)
Lemma mul_finite_rev_n : forall (m:ieee_float.RoundingMode.mode) (x:t) (y:t),
  (ieee_float.RoundingMode.to_nearest m) -> ((is_finite (mul m x y)) ->
  ((no_overflow m ((to_real x) * (to_real y))%R) /\ ((to_real (mul m x
  y)) = (round m ((to_real x) * (to_real y))%R)))).
Proof.
intros m x y h1 h2.
destruct (mul_finite_rev m x y h2).
assert (no_overflow m (to_real x * to_real y)).
2: split; [easy|apply mul_finite; easy].
pose proof max_real_ge_6.
destruct x, y; try easy; try (rewrite B754_zero_to_real).
apply Bounded_real_no_overflow; rewrite (@B754_zero_to_real b0); split; fourier.
unfold no_overflow; rewrite Rmult_0_l, <-zeroF_to_real, Round_to_real, zeroF_to_real; auto; split; fourier.
unfold no_overflow; rewrite Rmult_0_r, <-zeroF_to_real, Round_to_real, zeroF_to_real; auto; split; fourier.
set (x := B754_finite b m0 e e0).
set (y := B754_finite b0 m1 e1 e2).
fold x y in h2, H, H0.
destruct (Rlt_le_dec (Rabs (round m (to_real x * to_real y))) (bpow radix2 emax)).
rewrite no_overflow_Rabs_round_emax; assumption.
pose proof (Bmult_correct sb emax Hsb'' Hemax' nan_bf m x y).
change (Bmult sb emax Hsb'' Hemax' nan_bf m) with (mul m) in H2.
rewrite Rlt_bool_false in H2; auto.
destruct m, h1; try easy.
destruct (mul RNE x y); easy.
destruct (mul RNA x y); easy.
Qed.

(* Why3 goal *)
Lemma div_finite : forall (m:ieee_float.RoundingMode.mode) (x:t) (y:t),
  (is_finite x) -> ((is_finite y) -> ((~ (is_zero y)) -> ((no_overflow m
  ((to_real x) / (to_real y))%R) -> ((is_finite (div m x y)) /\
  ((to_real (div m x y)) = (round m ((to_real x) / (to_real y))%R)))))).
Proof.
  intros m x y h1 h2 h3 h4.
  assert (is_finite zeroF) as zero_is_finite by easy.
  rewrite (to_real_eq zero_is_finite h2) in h3.
  apply not_eq_sym in h3.
  generalize (Bdiv_correct sb emax Hsb'' Hemax' nan_bf m x y h3); rewrite Rlt_bool_true, h1.
  intro; split; easy.
  apply (in_range_bpow_radix2_emax _ h4).
Qed.

(* Why3 goal *)
Lemma div_finite_rev : forall (m:ieee_float.RoundingMode.mode) (x:t) (y:t),
  (is_finite (div m x y)) -> (((is_finite x) /\ ((is_finite y) /\ ~ (is_zero
  y))) \/ ((is_finite x) /\ ((is_infinite y) /\ ((to_real (div m x
  y)) = 0%R)))).
Proof.
  intros m x y h1.
  destruct x, y; try easy.
  right; destruct b, b0; easy.
  left; destruct b, b0; split; try split; easy.
  right; destruct b, b0; split; try split; easy.
  left; destruct b, b0; split; try split; easy.
Qed.

(* Why3 goal *)
Lemma div_finite_rev_n : forall (m:ieee_float.RoundingMode.mode) (x:t) (y:t),
  (ieee_float.RoundingMode.to_nearest m) -> ((is_finite (div m x y)) ->
  ((is_finite y) -> ((no_overflow m ((to_real x) / (to_real y))%R) /\
  ((to_real (div m x y)) = (round m ((to_real x) / (to_real y))%R))))).
Proof.
  intros m x y h1 h2 h3.
  destruct (div_finite_rev m x y h2) as [(h4,(h5,h6))|(h4,(h5,h6))].
  + assert (no_overflow m (to_real x / to_real y)).
    2: split; [easy|apply div_finite; easy].
    pose proof max_real_ge_6.
    destruct x, y; try easy; try (rewrite B754_zero_to_real).
    rewrite Real.infix_sl_def, Rmult_0_l.
    apply Bounded_real_no_overflow; split; fourier.
    set (x := B754_finite b m0 e e0).
    set (y := B754_finite b0 m1 e1 e2).
    fold x y in h2, h3,h4,h5,h6.
    destruct (Rlt_le_dec (Rabs (round m (to_real x / to_real y))) (bpow radix2 emax)).
    rewrite no_overflow_Rabs_round_emax; assumption.
    assert (to_real y <> 0) by (rewrite zero_to_real in h6; auto).
    pose proof (Bdiv_correct sb emax Hsb'' Hemax' nan_bf m x y H0).
    change (Bdiv sb emax Hsb'' Hemax' nan_bf m) with (div m) in H1.
    rewrite Rlt_bool_false in H1; auto.
    destruct m, h1; try easy.
    destruct (div RNE x y); easy.
    destruct (div RNA x y); easy.
  + destruct y; easy.
Qed.

(* Why3 goal *)
Lemma neg_finite : forall (x:t), (is_finite x) -> ((is_finite (neg x)) /\
  ((to_real (neg x)) = (-(to_real x))%R)).
Proof.
intros x h1.
split.
unfold neg, is_finite.
rewrite is_finite_Bopp; apply h1.
apply B2R_Bopp.
Qed.

(* Why3 goal *)
Lemma neg_finite_rev : forall (x:t), (is_finite (neg x)) -> ((is_finite x) /\
  ((to_real (neg x)) = (-(to_real x))%R)).
Proof.
  intros x h1.
  assert (is_finite x) by (destruct x; easy).
  split; [easy| apply neg_finite; auto].
Qed.

(* Why3 goal *)
Lemma abs_finite : forall (x:t), (is_finite x) -> ((is_finite (abs x)) /\
  (((to_real (abs x)) = (Reals.Rbasic_fun.Rabs (to_real x))) /\ (is_positive
  (abs x)))).
Proof.
  intros x h1.
  pose proof (is_finite_abs x h1).
  split;[assumption|split].
  apply B2R_Babs.
  pose proof (is_finite_not_nan h1).
  pose proof (Bsign_Babs sb emax pair x).
  apply is_positive_Bsign.
  apply (is_finite_not_nan H).
  apply H1.
  now apply Bool.not_true_is_false.
Qed.

(* add to theory ? *)
Lemma abs_le: forall {x y}, le (neg y) x /\ le x y -> le (abs x) y.
Proof.
  intros x y.
  destruct (Finite_Infinite_Nan_dec y) as [[hy|hy]|hy].
  + destruct (Finite_Infinite_Nan_dec x) as [[hx|hx]|hx].

    * pose proof (is_finite_abs _ hx).
      destruct (neg_finite _ hy).
      rewrite (le_to_real (abs x) y) by auto.
      rewrite (le_to_real (neg y) x) by auto.
      rewrite le_to_real by auto.
      rewrite H1 by auto.
      destruct (abs_finite _ hx) as (_,(u,_)).
      rewrite u.
      apply Rabs_le.

    * intros (a,b).
      destruct (le_special _ _ b) as [H|[H|H]].
      - destruct x; easy.
      - pose proof (le_special _ _ a).
        unfold is_minus_infinity, is_plus_infinity in H0.
        destruct H0, H0, x, y; auto; try easy; destruct b0, b1; auto.
      - destruct y; unfold is_plus_infinity in H; easy.

    * intros (a,b).
      destruct (le_special _ _ b) as [H|[H|H]];
        unfold is_minus_infinity in H;
        destruct x; easy.

  + intros (a,b).

    destruct (Finite_Infinite_Nan_dec x) as [[hx|hx]|hx].
    * destruct (le_special _ _ b) as [H|[H|H]].
      - destruct y; easy.
      - unfold is_minus_infinity in H; destruct x; easy.
      - destruct y, H as (H,(_,H')); destruct b0; try easy.
        assert (~ is_nan x) by (rewrite <-is_not_nan1; assumption).
        apply le_infinity.
        destruct x; easy.
    * destruct x, y; try easy.
      unfold le, Bcompare.
      destruct b1; [|right; reflexivity].
      destruct b0; easy.
    * destruct x; unfold le, Bcompare in b; easy.

  + intros (_,a).
    destruct y; unfold le, Bcompare in a; try easy.
    destruct x as [b0|b0|b0|b0]; destruct b0; easy.
Qed.

(* Why3 goal *)
Lemma abs_finite_rev : forall (x:t), (is_finite (abs x)) -> ((is_finite x) /\
  ((to_real (abs x)) = (Reals.Rbasic_fun.Rabs (to_real x)))).
Proof.
intros x h1.
assert (is_finite x) by (destruct x; easy).
split; [easy| apply abs_finite; auto].
Qed.

(* Why3 goal *)
Lemma abs_universal : forall (x:t), ~ (is_negative (abs x)).
Proof.
destruct x; easy.
Qed.

(* Why3 goal *)
Lemma fma_finite : forall (m:ieee_float.RoundingMode.mode) (x:t) (y:t) (z:t),
  (is_finite x) -> ((is_finite y) -> ((is_finite z) -> ((no_overflow m
  (((to_real x) * (to_real y))%R + (to_real z))%R) -> ((is_finite (fma m x y
  z)) /\ ((to_real (fma m x y z)) = (round m
  (((to_real x) * (to_real y))%R + (to_real z))%R)))))).
Proof.
intros m x y z h1 h2 h3 h4.
Admitted.

(* Why3 goal *)
Lemma fma_finite_rev : forall (m:ieee_float.RoundingMode.mode) (x:t) (y:t)
  (z:t), (is_finite (fma m x y z)) -> ((is_finite x) /\ ((is_finite y) /\
  (is_finite z))).
Proof.
intros m x y z h1.
Admitted.

(* Why3 goal *)
Lemma fma_finite_rev_n : forall (m:ieee_float.RoundingMode.mode) (x:t) (y:t)
  (z:t), (ieee_float.RoundingMode.to_nearest m) -> ((is_finite (fma m x y
  z)) -> ((no_overflow m (((to_real x) * (to_real y))%R + (to_real z))%R) /\
  ((to_real (fma m x y z)) = (round m
  (((to_real x) * (to_real y))%R + (to_real z))%R)))).
Proof.
intros m x y z h1 h2.
Admitted.

(* Why3 goal *)
Lemma sqrt_finite : forall (m:ieee_float.RoundingMode.mode) (x:t), (is_finite
  x) -> ((0%R <= (to_real x))%R -> ((is_finite (sqrt m x)) /\
  ((to_real (sqrt m x)) = (round m (Reals.R_sqrt.sqrt (to_real x)))))).
Proof.
  intros m x h1 h2.
  destruct (Bsqrt_correct sb emax Hsb' Hemax' (fun b => (true,One_Nan_pl)) m x) as (g,(g1,g2)).
  split; auto.
  destruct x ; destruct b; try easy.
  contradict h2.
  apply Rlt_not_le, non_zero_negative_to_real; easy.
Qed.

(* Why3 goal *)
Lemma sqrt_finite_rev : forall (m:ieee_float.RoundingMode.mode) (x:t),
  (is_finite (sqrt m x)) -> ((is_finite x) /\ ((0%R <= (to_real x))%R /\
  ((to_real (sqrt m x)) = (round m (Reals.R_sqrt.sqrt (to_real x)))))).
Proof.
  intros m x h1.
  assert (is_finite x) by (destruct x ; destruct b; easy).
  split; [easy|].
  assert (0 <= to_real x).
  { destruct x ; destruct b; try easy.
    rewrite (@B754_zero_to_real true); fourier.
    rewrite (@B754_zero_to_real false); fourier.
    assert (is_positive (B754_finite false m0 e e0)) by easy.
    apply positive_to_real; auto. }
  split; [easy| apply sqrt_finite; auto].
Qed.

(* Why3 assumption *)
Definition same_sign_real (x:t) (r:R): Prop := ((is_positive x) /\
  (0%R < r)%R) \/ ((is_negative x) /\ (r < 0%R)%R).

Lemma sign_FF_overflow : forall m b,
    sign_FF (binary_overflow sb emax m b) = b.
Proof.
  intros m b.
  unfold binary_overflow.
  destruct (overflow_to_inf m b); auto.
Qed.

Lemma sign_FF_B2FF : forall x, sign_FF (B2FF sb emax x) = Bsign sb emax x.
Proof.
  destruct x; try easy.
  destruct n; easy.
Qed.

(* Why3 goal *)
Lemma add_special : forall (m:ieee_float.RoundingMode.mode) (x:t) (y:t),
  let r := (add m x y) in ((((is_nan x) \/ (is_nan y)) -> (is_nan r)) /\
  ((((is_finite x) /\ (is_infinite y)) -> ((is_infinite r) /\ (same_sign r
  y))) /\ ((((is_infinite x) /\ (is_finite y)) -> ((is_infinite r) /\
  (same_sign r x))) /\ ((((is_infinite x) /\ ((is_infinite y) /\ (same_sign x
  y))) -> ((is_infinite r) /\ (same_sign r x))) /\ ((((is_infinite x) /\
  ((is_infinite y) /\ (diff_sign x y))) -> (is_nan r)) /\ ((((is_finite x) /\
  ((is_finite y) /\ ~ (no_overflow m ((to_real x) + (to_real y))%R))) ->
  ((same_sign_real r ((to_real x) + (to_real y))%R) /\ (overflow_value m
  r))) /\ (((is_finite x) /\ (is_finite y)) -> (((same_sign x y) ->
  (same_sign r x)) /\ ((~ (same_sign x y)) -> (sign_zero_result m
  r)))))))))).
Proof.
intros m x y r.
  unfold same_sign, diff_sign, same_sign, same_sign_real.
  split;[intro h|split;[intros (h,h1)|split;[intros (h,h1)|split;[intros (h,(h1,h2))|split;[intros (h,(h1,h2))|split;[intros (h,(h1,h2))|split;[intro h|intro h]]]]]]].
  - destruct x, y; destruct b, b0, h; easy.
  - destruct x, y; destruct b0; auto; easy.
  - destruct x, y; destruct b; auto; easy.
  - destruct x, y; destruct b, b0; destruct h2 as [(h2,h3)|(h2,h3)]; auto; easy.
  - destruct x, y; destruct b, b0; destruct h2 as [(h2,h3)|(h2,h3)]; auto; easy.
  - rewrite no_overflow_Rabs_round_max_real, Rabs_round_max_real_emax in h2.
    apply Rnot_lt_le in h2.
    pose proof (Bplus_correct sb emax Hsb' Hemax' nan_bf m x y h h1).
    rewrite Rlt_bool_false in H by auto.
    destruct H.
    split.

    + destruct x, y; try easy.
      * destruct b, b0, m; simpl in H; easy.
      * rewrite B754_zero_to_real at 1 2.
        rewrite Rplus_0_l.
        rewrite <-(@B754_zero_to_real false) at 1 2.
        destruct b0;[right;split;[easy|]|left;split;[easy|]]; apply lt_finite; easy.
      * rewrite B754_zero_to_real at 1 2.
        rewrite Rplus_0_r.
        rewrite <-(@B754_zero_to_real false) at 1 2.
        destruct b;[right;split;[easy|]|left;split;[easy|]]; apply lt_finite; easy.
      * change (Bplus sb emax Hsb' Hemax' nan_bf m (B754_finite b m0 e e0) (B754_finite b0 m1 e1 e2)) with r in H.
        pose proof (sign_FF_overflow m (Bsign sb emax (B754_finite b m0 e e0))).
        rewrite <-H in H1.
        rewrite sign_FF_B2FF in H1.
        simpl Bsign at 2 in H1.
        assert (~is_nan r).
        { simpl binary_overflow in H.
          unfold binary_overflow in H.
          destruct overflow_to_inf in H;
            destruct r; try easy;
              destruct n; easy. }

        simpl in H0. {
        destruct b;rewrite <-H0;[right|left]; split.

        - rewrite <-is_negative_Bsign in H1; auto.
        - assert (to_real (B754_finite true m0 e e0) < 0) by
              (apply non_zero_negative_to_real; easy).
          assert (to_real (B754_finite true m1 e1 e2) < 0) by
              (apply non_zero_negative_to_real; easy).
          fourier.
        - rewrite <-is_positive_Bsign in H1; auto.
        - assert (0 < to_real (B754_finite false m0 e e0)) by
              (apply non_zero_positive_to_real; easy).
          assert (0 < to_real (B754_finite false m1 e1 e2)) by
              (apply non_zero_positive_to_real; easy).
          fourier. }

    + change (Bplus sb emax Hsb' Hemax' nan_bf m x y) with r in H.
      assert (H':=H).
      apply (f_equal sign_FF) in H'.
      rewrite sign_FF_B2FF, sign_FF_overflow in H'.
      destruct m; simpl;
        unfold binary_overflow in H;
        simpl in H.
      * destruct r; try easy; destruct n; easy.
      * destruct r; try easy; destruct n; easy.
      * assert (Bsign sb emax x = true \/ Bsign sb emax x = false) by
            (destruct x ; destruct b; simpl; auto). {
        destruct H1; rewrite H1 in H', H0, H; simpl in H.
        - split.
          assert (~is_nan r) by
              (destruct r; try easy; destruct n; easy).
          rewrite is_positive_Bsign, H'; easy.
          intro; split.
          destruct r; try easy; destruct n; easy.
          replace (Z.pow_pos 2 sb_pos - 1)%Z with (Z.pos (2 ^ sb_pos - 1)) in H.
          + unfold to_real. rewrite <-min_real_is_F2R, <-FF2R_B2FF, H; auto.
          + rewrite Pos2Z.inj_sub, Pos2Z.inj_pow_pos; auto.
          apply Pos.pow_gt_1.
          zify; auto with zarith.
        - split.
          destruct r; easy.
          intro.
          assert (~is_nan r) by
              (destruct r; try easy; destruct n; easy).
          rewrite is_positive_Bsign in H2; auto; easy. }
      * assert (Bsign sb emax x = true \/ Bsign sb emax x = false) by
            (destruct x ; destruct b; simpl; auto). {
        destruct H1; rewrite H1 in H', H0, H; simpl in H.
        - split.
          intro.
          assert (~is_nan r) by
              (destruct r; try easy; destruct n; easy).
          rewrite is_positive_Bsign in H2; auto. rewrite H' in H2; easy.
          destruct r; try easy; destruct n; easy.
        - split.
          intro; split.
          destruct r; easy.
          replace (Z.pow_pos 2 sb_pos - 1)%Z with (Z.pos (2 ^ sb_pos - 1)) in H.
          + unfold to_real. rewrite <-max_real_is_F2R, <-FF2R_B2FF, H; auto.
          + rewrite Pos2Z.inj_sub, Pos2Z.inj_pow_pos; auto.
            apply Pos.pow_gt_1.
            zify; auto with zarith.
          + assert (~is_nan r) by
                (destruct r; try easy; destruct n; easy).
            rewrite is_positive_Bsign; easy. }
      * assert (Bsign sb emax x = true \/ Bsign sb emax x = false) by
            (destruct x ; destruct b; simpl; auto).
        assert (~is_nan r) by
            (destruct H1, r; try easy; destruct n; easy).
        rewrite is_positive_Bsign; auto. {
        destruct H1; rewrite H1 in H', H0, H; simpl in H.
        - split.
          rewrite H'; easy.
          intro; split.
          destruct r; try easy; destruct n; easy.
          replace (Z.pow_pos 2 sb_pos - 1)%Z with (Z.pos (2 ^ sb_pos - 1)) in H.
          + unfold to_real. rewrite <-min_real_is_F2R, <-FF2R_B2FF, H; auto.
          + rewrite Pos2Z.inj_sub, Pos2Z.inj_pow_pos; auto.
            apply Pos.pow_gt_1.
            zify; auto with zarith.
        - split.
          + intro; split.
            destruct r; try easy; destruct n; easy.
            replace (Z.pow_pos 2 sb_pos - 1)%Z with (Z.pos (2 ^ sb_pos - 1)) in H.
            * unfold to_real. rewrite <-max_real_is_F2R, <-FF2R_B2FF, H; auto.
            *  rewrite Pos2Z.inj_sub, Pos2Z.inj_pow_pos; auto.
               apply Pos.pow_gt_1.
               zify; auto with zarith.
           + easy. }
  - destruct H.
    pose proof (Bplus_correct sb emax Hsb' Hemax' nan_bf m x y H H0).
    destruct (Rlt_le_dec (Rabs (round m (to_real x + to_real y))) (bpow radix2 emax)).
    + rewrite Rlt_bool_true in H1; auto.
      destruct H1 as (h1,(h2,h3)).
      change (Bplus sb emax Hsb' Hemax' nan_bf m x y) with r in h1, h2, h3.
      assert (~is_nan r) by (destruct r; easy).
      destruct h as [(h,h')|(h,h')];[left|right];split;auto.
      * rewrite is_positive_Bsign; auto.
        destruct y; destruct b, x; destruct b; try easy.
        rewrite Rcompare_Gt in h3; auto.
        assert (0 < B2R sb emax (B754_finite false m0 e e0)) by
            (apply non_zero_positive_to_real; easy).
        assert (0 < B2R sb emax (B754_finite false m1 e1 e2)) by
            (apply non_zero_positive_to_real; easy).
        fourier.
      * rewrite is_negative_Bsign; auto.
        destruct y; destruct b,x; destruct b; try easy.
        rewrite Rcompare_Lt in h3; auto.
        assert (B2R sb emax (B754_finite true  m0 e e0) < 0) by
            (apply non_zero_negative_to_real; easy).
        assert (B2R sb emax (B754_finite true m1 e1 e2) < 0) by
            (apply non_zero_negative_to_real; easy).
        fourier.
    + rewrite Rlt_bool_false in H1; auto.
      destruct H1 as (h1,h2).
      change (Bplus sb emax Hsb' Hemax' nan_bf m x y) with r in h1.
      destruct (is_nan_dec r).
      destruct x; destruct b,y; destruct b; try easy; destruct m;
        unfold binary_overflow in h1; simpl in h1;
          destruct r; try easy; destruct n; easy.

      rewrite is_positive_Bsign; auto.
      rewrite is_negative_Bsign; auto.
      apply (f_equal sign_FF) in h1.
      rewrite sign_FF_B2FF, sign_FF_overflow in h1.
      assert (~is_nan x) by (destruct x; easy).
      rewrite is_positive_Bsign in h; auto.
      rewrite is_negative_Bsign in h; auto.
      destruct h as [(h,h')|(h,h')];rewrite h in h1, h2;[left|right];split;auto.
      rewrite is_positive_Bsign; auto.
      rewrite is_negative_Bsign; auto.
  - intuition.
    unfold sign_zero_result; intro.
    unfold is_zero in H2.
    rewrite eq_zero_iff in H2.
    pose proof (Bplus_correct sb emax Hsb' Hemax' nan_bf m x y H0 H1).
    destruct (Rlt_dec (Rabs (round m (to_real x + to_real y))) (bpow radix2 emax)) as [r0|r0];
      [rewrite Rlt_bool_true in H4; auto; clear r0|
       rewrite Rlt_bool_false in H4].
    + destruct H4 as (h1,(h2,h)).

      assert (to_real x + to_real y = 0).
      { apply (round_plus_eq_zero radix2 fexp (round_mode m)).
        apply generic_format_B2R.
        apply generic_format_B2R.
        destruct H2 as [H2|H2]; apply (f_equal to_real) in H2.
        rewrite zeroF_to_real in H2.
        rewrite <-H2; auto.
        assert (is_finite zeroF) as zero_is_finite by easy.
        destruct (neg_finite zeroF zero_is_finite).
        rewrite H5, zeroF_to_real, Ropp_0 in H2.
        rewrite <-H2; auto. }

      rewrite Rcompare_Eq in h; auto.
      replace (add m x y) with r in h by auto.

      assert (~is_nan r) by (destruct H2, r; easy).

      destruct x ; destruct b; destruct y ; destruct b;
        try (simpl in H; contradict H; now auto);
        try (destruct m; simpl in r; easy);
        simpl in h;
        destruct m; try (rewrite is_positive_Bsign; auto);
          rewrite is_negative_Bsign; auto.

    + destruct H4.
      unfold binary_overflow in H4.
      change (Bplus sb emax Hsb' Hemax' nan_bf m x y) with r in H4.
      destruct H2 as [H2|H2]; rewrite H2 in H4; simpl in H4;
        destruct overflow_to_inf in H4; easy.

    + apply Rnot_lt_le; assumption.
Qed.

Lemma no_overflow_or_not: forall m x, no_overflow m x \/ ~ no_overflow m x.
Proof.
  intros m x.
  rewrite no_overflow_Rabs_round_emax.
  destruct (Rlt_dec (Rabs (round m x)) (bpow radix2 emax));[left|right]; assumption.
Qed.

Lemma add_finite_rev_n' : forall (m:mode) (x:t) (y:t),
    is_finite (add m x y) ->
    (no_overflow m ((to_real x) + (to_real y))%R
     /\ to_real (add m x y) = round m ((to_real x) + (to_real y))%R)
    \/ to_real (add m x y) = max_real \/ to_real (add m x y) =-  max_real .
Proof.
intros m x y h1.
destruct (add_finite_rev m x y h1).

destruct (no_overflow_or_not m (to_real x + to_real y));[left|right].

split; [auto|apply add_finite; easy].
destruct (add_special m x y) as (_,(_,(_,(_,(_,(h,_)))))).
assert (is_finite x /\ is_finite y /\ ~ no_overflow m (to_real x + to_real y)) by auto.
apply h in H2; clear h.
destruct H2.
destruct (add m x y); destruct b, m; simpl in *; try easy;
  try (destruct H3; destruct H4; now auto);
  destruct H3, H3; auto.
Qed.

(* Why3 goal *)
Lemma sub_special : forall (m:ieee_float.RoundingMode.mode) (x:t) (y:t),
  let r := (sub m x y) in ((((is_nan x) \/ (is_nan y)) -> (is_nan r)) /\
  ((((is_finite x) /\ (is_infinite y)) -> ((is_infinite r) /\ (diff_sign r
  y))) /\ ((((is_infinite x) /\ (is_finite y)) -> ((is_infinite r) /\
  (same_sign r x))) /\ ((((is_infinite x) /\ ((is_infinite y) /\ (same_sign x
  y))) -> (is_nan r)) /\ ((((is_infinite x) /\ ((is_infinite y) /\ (diff_sign
  x y))) -> ((is_infinite r) /\ (same_sign r x))) /\ ((((is_finite x) /\
  ((is_finite y) /\ ~ (no_overflow m ((to_real x) - (to_real y))%R))) ->
  ((same_sign_real r ((to_real x) - (to_real y))%R) /\ (overflow_value m
  r))) /\ (((is_finite x) /\ (is_finite y)) -> (((diff_sign x y) ->
  (same_sign r x)) /\ ((~ (diff_sign x y)) -> (sign_zero_result m
  r)))))))))).
Proof.
  intros m x y r.
  unfold same_sign, diff_sign, same_sign, same_sign_real.
  split;[intro h|split;[intros (h,h1)|split;[intros (h,h1)|split;[intros (h,(h1,h2))|split;[intros (h,(h1,h2))|split;[intros (h,(h1,h2))|split;[intro h|intro h]]]]]]].
  - destruct x, y; destruct b, b0; destruct h; easy.
  - destruct x, y; destruct b, b0; auto; easy.
  - destruct x, y; destruct b, b0; auto; easy.
  - destruct x, y; destruct b, b0; destruct h2 as [(h2,h3)|(h2,h3)]; auto; easy.
  - destruct x, y; destruct b, b0; destruct h2 as [(h2,h3)|(h2,h3)]; auto; easy.
  - rewrite no_overflow_Rabs_round_max_real, Rabs_round_max_real_emax in h2.
    apply Rnot_lt_le in h2.
    pose proof (Bminus_correct sb emax Hsb' Hemax' nan_bf m x y h h1).
    rewrite Rlt_bool_false in H by auto.
    destruct H.
    split.

    + destruct x, y; try easy.
      * destruct b, b0, m; simpl in H; easy.
      * rewrite B754_zero_to_real at 1 2.
        rewrite Rminus_0_l.
        rewrite <-(@B754_zero_to_real false) at 1 2.
        destruct (neg_finite (B754_finite b0 m0 e e0)) as (_,h3); auto.
        rewrite <-h3.
        destruct b0;[left;split;[easy|]|right;split;[easy|]]; apply lt_finite; easy.
      * rewrite B754_zero_to_real at 1 2.
        rewrite Rminus_0_r.
        rewrite <-(@B754_zero_to_real false) at 1 2.
        destruct b;[right;split;[easy|]|left;split;[easy|]]; apply lt_finite; easy.
      * change (Bminus sb emax Hsb' Hemax' nan_bf  m (B754_finite b m0 e e0) (B754_finite b0 m1 e1 e2)) with r in H.
        pose proof (sign_FF_overflow m (Bsign sb emax (B754_finite b m0 e e0))).
        rewrite <-H in H1.
        rewrite sign_FF_B2FF in H1.
        simpl Bsign at 2 in H1.
        assert (~is_nan r).
        { simpl binary_overflow in H.
          unfold binary_overflow in H.
          destruct overflow_to_inf in H;
            destruct r; try easy;
              destruct n; easy. }

        simpl in H0.
        apply Bool.negb_sym in H0. {

        destruct b; simpl in H0;rewrite H0 in *;[right|left];split.

        - rewrite <-is_negative_Bsign in H1; auto.
        - assert (to_real (B754_finite true m0 e e0) < 0) by
              (apply non_zero_negative_to_real; easy).
          assert (0 < to_real (B754_finite false m1 e1 e2)) by
              (apply non_zero_positive_to_real; easy).
          fourier.
        - rewrite <-is_positive_Bsign in H1; auto.
        - assert (0 < to_real (B754_finite false m0 e e0)) by
              (apply non_zero_positive_to_real; easy).
          assert (to_real (B754_finite true m1 e1 e2) < 0) by
              (apply non_zero_negative_to_real; easy).
          fourier. }

    + change (Bminus sb emax Hsb' Hemax' nan_bf m x y) with r in H.
      assert (H':=H).
      apply (f_equal sign_FF) in H'.
      rewrite sign_FF_B2FF, sign_FF_overflow in H'.
      destruct m; simpl;
        unfold binary_overflow in H;
        simpl in H.
      * destruct r; try easy; destruct n; easy.
      * destruct r; try easy; destruct n; easy.
      * assert (Bsign sb emax x = true \/ Bsign sb emax x = false) by
            (destruct x ; destruct b; simpl; auto). {
        destruct H1; rewrite H1 in H', H0, H; simpl in H.
        - split.
          assert (~is_nan r) by
              (destruct r; try easy; destruct n; easy).
          rewrite is_positive_Bsign, H'; easy.
          intro; split.
          destruct r; try easy; destruct n; easy.
          replace (Z.pow_pos 2 sb_pos - 1)%Z with (Z.pos (2 ^ sb_pos - 1)) in H.
          + unfold to_real. rewrite <-min_real_is_F2R, <-FF2R_B2FF, H; auto.
          + rewrite Pos2Z.inj_sub, Pos2Z.inj_pow_pos; auto.
            apply Pos.pow_gt_1.
            zify; auto with zarith.
        - split.
          destruct r; easy.
          intro.
          assert (~is_nan r) by
              (destruct r; try easy; destruct n; easy).
          rewrite is_positive_Bsign in H2; auto; easy. }
      * assert (Bsign sb emax x = true \/ Bsign sb emax x = false) by
            (destruct x ; destruct b; simpl; auto). {
        destruct H1; rewrite H1 in H', H0, H; simpl in H.
        - split.
          intro.
          assert (~is_nan r) by
              (destruct r; try easy; destruct n; easy).
          rewrite is_positive_Bsign in H2; auto. rewrite H' in H2; easy.
          destruct r; try easy; destruct n; easy.
        - split.
          intro; split.
          destruct r; easy.
          replace (Z.pow_pos 2 sb_pos - 1)%Z with (Z.pos (2 ^ sb_pos - 1)) in H.
          + unfold to_real. rewrite <-max_real_is_F2R, <-FF2R_B2FF, H; auto.
          + rewrite Pos2Z.inj_sub, Pos2Z.inj_pow_pos; auto.
              apply Pos.pow_gt_1.
              zify; auto with zarith.
          + assert (~is_nan r) by
                (destruct r; try easy; destruct n; easy).
            rewrite is_positive_Bsign; easy. }
      * assert (Bsign sb emax x = true \/ Bsign sb emax x = false) by
            (destruct x ; destruct b; simpl; auto).
        assert (~is_nan r) by
            (destruct H1, r; try easy; destruct n; easy).
        rewrite is_positive_Bsign; auto. {
        destruct H1; rewrite H1 in H', H0, H; simpl in H.
        - split.
          rewrite H'; easy.
          intro; split.
          destruct r; try easy; destruct n; easy.
          replace (Z.pow_pos 2 sb_pos - 1)%Z with (Z.pos (2 ^ sb_pos - 1)) in H.
          + unfold to_real. rewrite <-min_real_is_F2R, <-FF2R_B2FF, H; auto.
          + rewrite Pos2Z.inj_sub, Pos2Z.inj_pow_pos; auto.
            apply Pos.pow_gt_1.
            zify; auto with zarith.
        - split.
          + intro; split.
            destruct r; try easy; destruct n; easy.
            replace (Z.pow_pos 2 sb_pos - 1)%Z with (Z.pos (2 ^ sb_pos - 1)) in H.
            * unfold to_real. rewrite <-max_real_is_F2R, <-FF2R_B2FF, H; auto.
            *  rewrite Pos2Z.inj_sub, Pos2Z.inj_pow_pos; auto.
               apply Pos.pow_gt_1.
               zify; auto with zarith.
          + easy. }
  - destruct H.
    pose proof (Bminus_correct sb emax Hsb' Hemax' nan_bf m x y H H0).
    destruct (Rlt_le_dec (Rabs (round m (to_real x - to_real y))) (bpow radix2 emax)).
    + rewrite Rlt_bool_true in H1; auto.
      destruct H1 as (h1,(h2,h3)).
      change (Bminus sb emax Hsb' Hemax' nan_bf m x y) with r in h1, h2, h3.
      assert (~is_nan r) by (destruct r; easy).
      destruct h as [(h,h')|(h,h')];[left|right];split;auto.
      * rewrite is_positive_Bsign; auto.
        destruct y; destruct b,x; destruct b; try easy.
        rewrite Rcompare_Gt in h3; auto.
        assert (B2R sb emax (B754_finite true m0 e e0) < 0) by
            (apply non_zero_negative_to_real; easy).
        assert (0 < B2R sb emax (B754_finite false m1 e1 e2)) by
            (apply non_zero_positive_to_real; easy).
        fourier.
      * rewrite is_negative_Bsign; auto.
        destruct y; destruct b,x; destruct b; try easy.
        rewrite Rcompare_Lt in h3; auto.
        assert (0 < B2R sb emax (B754_finite false m0 e e0)) by
            (apply non_zero_positive_to_real; easy).
        assert (B2R sb emax (B754_finite true m1 e1 e2) < 0) by
            (apply non_zero_negative_to_real; easy).
        fourier.
    + rewrite Rlt_bool_false in H1; auto.
      destruct H1 as (h1,h2).
      change (Bminus sb emax Hsb' Hemax' nan_bf m x y) with r in h1.
      destruct (is_nan_dec r).
      destruct x; destruct b,y; destruct b; try easy; destruct m;
        unfold binary_overflow in h1; simpl in h1;
          destruct r; try easy; destruct n; easy.

      rewrite is_positive_Bsign; auto.
      rewrite is_negative_Bsign; auto.
      apply (f_equal sign_FF) in h1.
      rewrite sign_FF_B2FF, sign_FF_overflow in h1.
      assert (~is_nan x) by (destruct x; easy).
      assert (~is_nan y) by (destruct y; easy).
      rewrite is_positive_Bsign in h,h; auto.
      rewrite is_negative_Bsign in h,h; auto.
      destruct h as [(h,h')|(h,h')];rewrite h in h1, h2;[left|right];split;auto.
      rewrite is_positive_Bsign; auto.
      rewrite is_negative_Bsign; auto.
  - intuition.
    unfold sign_zero_result; intro.
    unfold is_zero in H2.
    rewrite eq_zero_iff in H2.
    pose proof (Bminus_correct sb emax Hsb' Hemax' nan_bf m x y H0 H1).
    destruct (Rlt_dec (Rabs (round m (to_real x - to_real y))) (bpow radix2 emax)) as [r0|r0];
      [rewrite Rlt_bool_true in H4; auto; clear r0|
       rewrite Rlt_bool_false in H4].
    + destruct H4 as (h1,(h2,h)).

      assert (to_real x - to_real y = 0).
      { apply (round_plus_eq_zero radix2 fexp (round_mode m)).
        apply generic_format_B2R.
        destruct (neg_finite y) as (_,h3); auto; rewrite <-h3.
        apply generic_format_B2R.
        destruct H2 as [H2|H2]; apply (f_equal to_real) in H2.
        rewrite zeroF_to_real in H2.
        rewrite <-H2; auto.
        assert (is_finite zeroF) as zero_is_finite by easy.
        destruct (neg_finite zeroF zero_is_finite).
        rewrite H5, zeroF_to_real, Ropp_0 in H2.
        rewrite <-H2; auto. }

      rewrite Rcompare_Eq in h; auto.
      change (Bminus sb emax Hsb' Hemax' nan_bf m x y) with r in h.

      assert (~is_nan r) by (destruct H2, r; easy).

      destruct x ; destruct b; destruct y ; destruct b;
        try (simpl in H; contradict H; now auto);
        try (destruct m; simpl in r; easy);
        simpl in h;
        destruct m; try (rewrite is_positive_Bsign; auto);
          rewrite is_negative_Bsign; auto.

    + destruct H4.
      unfold binary_overflow in H4.
      change (Bminus sb emax Hsb' Hemax' nan_bf m x y) with r in H4.
      destruct H2 as [H2|H2]; rewrite H2 in H4; simpl in H4;
        destruct overflow_to_inf in H4; easy.

    + apply Rnot_lt_le; assumption.
Qed.

Lemma sub_finite_rev_n' : forall (m:mode) (x:t) (y:t),
    is_finite (sub m x y) ->
    (no_overflow m ((to_real x) - (to_real y))%R
     /\ to_real (sub m x y) = round m ((to_real x) - (to_real y))%R)
    \/ to_real (sub m x y) = max_real \/ to_real (sub m x y) =-  max_real .
Proof.
intros m x y h1.
destruct (sub_finite_rev m x y h1).
destruct (no_overflow_or_not m (to_real x - to_real y));[left|right].
split; [auto|apply sub_finite; easy].
destruct (sub_special m x y) as (_,(_,(_,(_,(_,(h,_)))))).
assert (is_finite x /\ is_finite y /\ ~ no_overflow m (to_real x - to_real y)) by auto.
apply h in H2; clear h.
destruct H2.
destruct (sub m x y); destruct b, m; simpl in *; try easy;
  try (destruct H3; destruct H4; now auto);
  destruct H3, H3; auto.
Qed.

(* Why3 goal *)
Lemma mul_special : forall (m:ieee_float.RoundingMode.mode) (x:t) (y:t),
  let r := (mul m x y) in ((((is_nan x) \/ (is_nan y)) -> (is_nan r)) /\
  ((((is_zero x) /\ (is_infinite y)) -> (is_nan r)) /\ ((((is_finite x) /\
  ((is_infinite y) /\ ~ (is_zero x))) -> (is_infinite r)) /\ ((((is_infinite
  x) /\ (is_zero y)) -> (is_nan r)) /\ ((((is_infinite x) /\ ((is_finite
  y) /\ ~ (is_zero y))) -> (is_infinite r)) /\ ((((is_infinite x) /\
  (is_infinite y)) -> (is_infinite r)) /\ ((((is_finite x) /\ ((is_finite
  y) /\ ~ (no_overflow m ((to_real x) * (to_real y))%R))) -> (overflow_value
  m r)) /\ ((~ (is_nan r)) -> (product_sign r x y))))))))).
Proof.
  intros m x y r.
  unfold product_sign, same_sign, diff_sign.
  intuition.
  - destruct x; easy.
  - destruct x, y; easy.
  - destruct x, y; destruct b; easy.
  - destruct x, y; destruct b; try easy; contradict H2; easy.
  - destruct x, y; destruct b0; easy.
  - destruct x, y; destruct b0; try easy; contradict H2; easy.
  - destruct x, y; easy.
  - rewrite no_overflow_Rabs_round_max_real, Rabs_round_max_real_emax in H2.
    apply Rnot_lt_le in H2.
    pose proof (Bmult_correct sb emax Hsb' Hemax' nan_bf m x y).
    rewrite Rlt_bool_false in H1 by auto.
    change (Bmult sb emax Hsb' Hemax' nan_bf m x y) with r in H1.
    assert (H1':=H1).
    apply (f_equal sign_FF) in H1'.
    rewrite sign_FF_B2FF, sign_FF_overflow in H1'.
    destruct m; simpl;
      unfold binary_overflow in H1;
      simpl in H1.
    * destruct r; try easy; destruct n; easy.
    * destruct r; try easy; destruct n; easy.
    * assert (Bsign sb emax x = true \/ Bsign sb emax x = false) by
          (destruct x ; destruct b; simpl; auto).
      assert (Bsign sb emax y = true \/ Bsign sb emax y = false) by
          (destruct y; destruct b; simpl; auto). {
        destruct H3, H4; rewrite H3, H4 in H1', H1; simpl in H1, H1'.
        - split.
          destruct r; easy.
          assert (~is_nan r) by
              (destruct r; try easy; destruct n; easy).
          rewrite is_positive_Bsign, H1'; intro h; contradict h; easy.
        - split.
          assert (~is_nan r) by
              (destruct r; try easy; destruct n; easy).
          rewrite is_positive_Bsign, H1'; intro h; contradict h; easy.
          intro; split.
          destruct r; try easy; destruct n; easy.
          replace (Z.pow_pos 2 sb_pos - 1)%Z with (Z.pos (2 ^ sb_pos - 1)) in H1.
          + unfold to_real. rewrite <-min_real_is_F2R, <-FF2R_B2FF, H1; auto.
          + rewrite Pos2Z.inj_sub, Pos2Z.inj_pow_pos; auto.
            apply Pos.pow_gt_1.
            zify; auto with zarith.
        - split.
          assert (~is_nan r) by
              (destruct r; try easy; destruct n; easy).
          rewrite is_positive_Bsign, H1'; intro h; contradict h; easy.
          intro; split.
          destruct r; try easy; destruct n; easy.
          replace (Z.pow_pos 2 sb_pos - 1)%Z with (Z.pos (2 ^ sb_pos - 1)) in H1.
          + unfold to_real. rewrite <-min_real_is_F2R, <-FF2R_B2FF, H1; auto.
          + rewrite Pos2Z.inj_sub, Pos2Z.inj_pow_pos; auto.
            apply Pos.pow_gt_1.
            zify; auto with zarith.
        - split.
          destruct r; easy.
          assert (~is_nan r) by
              (destruct r; try easy; destruct n; easy).
          rewrite is_positive_Bsign, H1'; intro h; contradict h; easy. }
    * assert (Bsign sb emax x = true \/ Bsign sb emax x = false) by
          (destruct x ; destruct b; simpl; auto).
      assert (Bsign sb emax y = true \/ Bsign sb emax y = false) by
          (destruct y; destruct b; simpl; auto). {
      destruct H3, H4; rewrite H3, H4 in H1', H1; simpl in H1, H1'.
      - split.
        { intro; split.
          destruct r; try easy; destruct n; easy.
          replace (Z.pow_pos 2 sb_pos - 1)%Z with (Z.pos (2 ^ sb_pos - 1)) in H1.
          + unfold to_real. rewrite <-max_real_is_F2R, <-FF2R_B2FF, H1; auto.
          + rewrite Pos2Z.inj_sub, Pos2Z.inj_pow_pos; auto.
            apply Pos.pow_gt_1.
            zify; auto with zarith. }
        assert (~is_nan r) by
            (destruct r; try easy; destruct n; easy).
        rewrite is_positive_Bsign, H1'; intro h; contradict h; easy.
      - split.
        assert (~is_nan r) by
            (destruct r; try easy; destruct n; easy).
        rewrite is_positive_Bsign, H1'; intro h; contradict h; easy.
        destruct r; try easy; destruct n; easy.
      - split.
        assert (~is_nan r) by
            (destruct r; try easy; destruct n; easy).
        rewrite is_positive_Bsign, H1'; intro h; contradict h; easy.
        destruct r; try easy; destruct n; easy.
      - split.
        { intro; split.
          destruct r; try easy; destruct n; easy.
          replace (Z.pow_pos 2 sb_pos - 1)%Z with (Z.pos (2 ^ sb_pos - 1)) in H1.
          + unfold to_real. rewrite <-max_real_is_F2R, <-FF2R_B2FF, H1; auto.
          + rewrite Pos2Z.inj_sub, Pos2Z.inj_pow_pos; auto.
            apply Pos.pow_gt_1.
            zify; auto with zarith. }
        assert (~is_nan r) by
            (destruct r; try easy; destruct n; easy).
        rewrite is_positive_Bsign, H1'; intro h; contradict h; easy. }
    * assert (Bsign sb emax x = true \/ Bsign sb emax x = false) by
          (destruct x ; destruct b; simpl; auto).
      assert (Bsign sb emax y = true \/ Bsign sb emax y = false) by
          (destruct y; destruct b; simpl; auto).
      replace (Z.pow_pos 2 sb_pos - 1)%Z with (Z.pos (2 ^ sb_pos - 1)) in H1.
      { unfold to_real. rewrite <-min_real_is_F2R,<-max_real_is_F2R, <-FF2R_B2FF, H1; auto.
        destruct H3, H4; rewrite H3, H4 in *; simpl in *.
        - split.
          intro; split; [destruct r; easy|auto].
          assert (~is_nan r) by
              (destruct r; try easy; destruct n; easy).
          rewrite is_positive_Bsign, H1'; intro h; contradict h; easy.
        - split.
          assert (~is_nan r) by
              (destruct r; try easy; destruct n; easy).
          rewrite is_positive_Bsign, H1'; intro h; contradict h; easy.
          intro; split; [destruct r; try easy; destruct n; easy|auto].
        - split.
          assert (~is_nan r) by
              (destruct r; try easy; destruct n; easy).
          rewrite is_positive_Bsign, H1'; intro h; contradict h; easy.
          intro; split; [destruct r; try easy; destruct n; easy|auto].
        - split.
          intro; split; [destruct r; easy|auto].
          assert (~is_nan r) by
              (destruct r; try easy; destruct n; easy).
          rewrite is_positive_Bsign, H1'; intro h; contradict h; easy. }
      rewrite Pos2Z.inj_sub, Pos2Z.inj_pow_pos; auto.
      apply Pos.pow_gt_1.
      zify; auto with zarith.
  - pose proof (Bmult_correct sb emax Hsb' Hemax' nan_bf m x y).
    destruct (Rlt_le_dec (Rabs (round m (to_real x * to_real y))) (bpow radix2 emax));
    [rewrite Rlt_bool_true in H1; auto| rewrite Rlt_bool_false in H1; auto].
    + destruct H1 as (h1, (h2, h3)).
      rewrite is_positive_Bsign; auto.
      unfold r, mul, Hsb''.
      rewrite h3.
      destruct x; destruct b,y; destruct b; easy.
      now apply Bool.not_true_is_false.
    + apply (f_equal sign_FF) in H1.
      rewrite sign_FF_B2FF, sign_FF_overflow in H1.
      rewrite is_positive_Bsign; auto.
      destruct x; destruct b,y; destruct b; easy.
  - pose proof (Bmult_correct sb emax Hsb' Hemax' nan_bf m x y).
    destruct (Rlt_le_dec (Rabs (round m (to_real x * to_real y))) (bpow radix2 emax));
    [rewrite Rlt_bool_true in H1; auto| rewrite Rlt_bool_false in H1; auto].
    + destruct H1 as (h1, (h2, h3)).
      rewrite is_positive_Bsign; auto.
      unfold r, mul, Hsb''.
      rewrite h3.
      destruct x; destruct b,y; destruct b; easy.
      now apply Bool.not_true_is_false.
    + apply (f_equal sign_FF) in H1.
      rewrite sign_FF_B2FF, sign_FF_overflow in H1.
      rewrite is_positive_Bsign; auto.
      destruct x; destruct b,y; destruct b; easy.
  - pose proof (Bmult_correct sb emax Hsb' Hemax' nan_bf m x y).
    destruct (Rlt_le_dec (Rabs (round m (to_real x * to_real y))) (bpow radix2 emax));
    [rewrite Rlt_bool_true in H1; auto| rewrite Rlt_bool_false in H1; auto].
    + destruct H1 as (h1, (h2, h3)).
      rewrite is_negative_Bsign; auto.
      unfold r, mul, Hsb''.
      rewrite h3.
      destruct x; destruct b,y; destruct b; auto.
      now apply Bool.not_true_is_false.
    + apply (f_equal sign_FF) in H1.
      rewrite sign_FF_B2FF, sign_FF_overflow in H1.
      rewrite is_negative_Bsign; auto.
      destruct x; destruct b,y; destruct b; auto.
  - pose proof (Bmult_correct sb emax Hsb' Hemax' nan_bf m x y).
    destruct (Rlt_le_dec (Rabs (round m (to_real x * to_real y))) (bpow radix2 emax));
    [rewrite Rlt_bool_true in H1; auto| rewrite Rlt_bool_false in H1; auto].
    + destruct H1 as (h1, (h2, h3)).
      rewrite is_negative_Bsign; auto.
      unfold r, mul, Hsb''.
      rewrite h3.
      destruct x; destruct b,y; destruct b; auto.
      now apply Bool.not_true_is_false.
    + apply (f_equal sign_FF) in H1.
      rewrite sign_FF_B2FF, sign_FF_overflow in H1.
      rewrite is_negative_Bsign; auto.
      destruct x; destruct b,y; destruct b; auto.
Qed.

Lemma mul_finite_rev_n' : forall (m:mode) (x:t) (y:t),
    is_finite (mul m x y) ->
    (no_overflow m ((to_real x) * (to_real y))%R
     /\ to_real (mul m x y) = round m ((to_real x) * (to_real y))%R)
    \/ to_real (mul m x y) = max_real \/ to_real (mul m x y) =-  max_real .
Proof.
intros m x y h1.
destruct (mul_finite_rev m x y h1).
destruct (no_overflow_or_not m (to_real x * to_real y));[left|right].
split; [auto|apply mul_finite; easy].
destruct (mul_special m x y) as (_,(_,(_,(_,(_,(_,(h,_))))))).
assert (is_finite x /\ is_finite y /\ ~ no_overflow m (to_real x * to_real y)) by auto.
apply h in H2; clear h.
destruct (mul m x y); destruct b, m; simpl in *; try easy;
  try (destruct H2; destruct H3; now auto);
  destruct H2, H2; auto.
Qed.

(* Why3 goal *)
Lemma div_special : forall (m:ieee_float.RoundingMode.mode) (x:t) (y:t),
  let r := (div m x y) in ((((is_nan x) \/ (is_nan y)) -> (is_nan r)) /\
  ((((is_finite x) /\ (is_infinite y)) -> (is_zero r)) /\ ((((is_infinite
  x) /\ (is_finite y)) -> (is_infinite r)) /\ ((((is_infinite x) /\
  (is_infinite y)) -> (is_nan r)) /\ ((((is_finite x) /\ ((is_finite y) /\
  ((~ (is_zero y)) /\ ~ (no_overflow m ((to_real x) / (to_real y))%R)))) ->
  (overflow_value m r)) /\ ((((is_finite x) /\ ((is_zero y) /\ ~ (is_zero
  x))) -> (is_infinite r)) /\ ((((is_zero x) /\ (is_zero y)) -> (is_nan
  r)) /\ ((~ (is_nan r)) -> (product_sign r x y))))))))).
Proof.
  intros m x y r.
  unfold product_sign, same_sign, diff_sign.
  intuition.
  - destruct x; easy.
  - destruct x, y; easy.
  - destruct x, y; destruct b; easy.
  - destruct x, y; destruct b; try easy; contradict H2; easy.
  - destruct x, y; destruct b0; easy.
  - assert (to_real y <> 0) by (rewrite zero_to_real in H1; auto).
    pose proof (Bdiv_correct sb emax Hsb' Hemax' nan_bf m x y H2).
    rewrite Rlt_bool_false in H4.
    + unfold binary_overflow in H4.
      change (Bdiv sb emax Hsb' Hemax' nan_bf m x y) with r in H4; auto.
      destruct m; simpl in *.
      * destruct r; try easy.
        destruct b, n in H4; easy.
      * destruct r; try easy.
        destruct b, n in H4; easy.
      * destruct (xorb (Bsign sb emax x) (Bsign sb emax y)); simpl in H4. {
          split; intro.
          - destruct r; destruct b; easy.
          - split; [destruct r; try easy; destruct b, n in H4; easy|].
            apply (f_equal (FF2R radix2)) in H4.
            rewrite FF2R_B2FF in H4.
            simpl in H4.
            replace (Z.pow_pos 2 sb_pos - 1)%Z with (Z.pos (2 ^ sb_pos - 1)) in H4 by
                (rewrite Pos2Z.inj_sub, Pos2Z.inj_pow_pos; auto;
                 apply Pos.pow_gt_1;
                 zify; auto with zarith).
            rewrite <-min_real_is_F2R; assumption. }
        split; intro; [destruct r; easy|].
        destruct r; try easy.
        destruct b; try easy.
        simpl in H5; contradict H5; auto.
        destruct n; easy.
      * destruct (xorb (Bsign sb emax x) (Bsign sb emax y)); simpl in H4. {
        split; intro.
        - destruct r; destruct b; easy.
        - destruct r; try easy.
           destruct n; easy. }
        { split; intro.
          + split;[destruct r; easy|].
            apply (f_equal (FF2R radix2)) in H4.
            rewrite FF2R_B2FF in H4.
            simpl in H4.
            replace (Z.pow_pos 2 sb_pos - 1)%Z with (Z.pos (2 ^ sb_pos - 1)) in H4 by
                (rewrite Pos2Z.inj_sub, Pos2Z.inj_pow_pos; auto;
                 apply Pos.pow_gt_1;
                 zify; auto with zarith).
            rewrite <-max_real_is_F2R; assumption.
          + destruct r; try easy; try (destruct n; easy).
            destruct b; try easy.
            simpl in H5; contradict H5; auto. }
      * { destruct (xorb (Bsign sb emax x) (Bsign sb emax y)); simpl in H4;
        split; intro.
          - destruct r; destruct b; easy.
          - split;[destruct r; destruct b; try easy; destruct n; easy|].
            apply (f_equal (FF2R radix2)) in H4.
            rewrite FF2R_B2FF in H4.
            simpl in H4.
            replace (Z.pow_pos 2 sb_pos - 1)%Z with (Z.pos (2 ^ sb_pos - 1)) in H4 by
                (rewrite Pos2Z.inj_sub, Pos2Z.inj_pow_pos; auto;
                 apply Pos.pow_gt_1;
                 zify; auto with zarith).
            rewrite <-min_real_is_F2R; assumption.
          - split; [destruct r; try easy| ].
            apply (f_equal (FF2R radix2)) in H4.
            rewrite FF2R_B2FF in H4.
            simpl in H4.
            replace (Z.pow_pos 2 sb_pos - 1)%Z with (Z.pos (2 ^ sb_pos - 1)) in H4 by
                (rewrite Pos2Z.inj_sub, Pos2Z.inj_pow_pos; auto;
                 apply Pos.pow_gt_1;
                 zify; auto with zarith).
            rewrite <-max_real_is_F2R; assumption.
          - contradict H5.
            destruct r; destruct b; try easy; destruct n; easy. }
    + clear H4.
      rewrite no_overflow_Rabs_round_emax in H3.
      apply Rge_le, Rfourier_not_lt_ge; assumption.
  - destruct x, y; try easy.
    rewrite is_zero_B754_zero in H2; destruct H2; exists b; reflexivity.
    rewrite is_zero_B754_zero in H2; destruct H2; exists b; reflexivity.
    rewrite is_zero_B754_zero in H; destruct H; inversion H.
    rewrite is_zero_B754_zero in H; destruct H; inversion H.
  - rewrite is_zero_B754_zero in H0; destruct H0.
    rewrite is_zero_B754_zero in H1; destruct H1.
    destruct x, y; easy.
  - destruct x ; destruct b; destruct y ; destruct b; try easy.
    now elim H.
    now elim H.
    set (x:=B754_finite false m0 e e0);
      set (y:=B754_finite false m1 e1 e2); fold x y in r, H0, H2.
    assert (to_real y <> 0).
    { assert (0 < to_real y) by
          (apply non_zero_positive_to_real; easy).
      apply not_eq_sym, Rlt_not_eq; fourier. }
    pose proof (Bdiv_correct sb emax Hsb' Hemax' nan_bf m x y H1).
    destruct (Rlt_le_dec (Rabs (round m (to_real x / to_real y))) (bpow radix2 emax));
      [rewrite Rlt_bool_true in H3|rewrite Rlt_bool_false in H3];auto.
    + destruct H3 as (_,(_,h)).
      rewrite is_positive_Bsign; auto.
      unfold r, div, Hsb''.
      rewrite h.
      easy.
      now apply Bool.not_true_is_false.
    + apply (f_equal sign_FF) in H3.
      rewrite sign_FF_B2FF, sign_FF_overflow in H3.
      rewrite is_positive_Bsign; auto.
  - destruct x ; destruct b; destruct y ; destruct b; try easy.
    now elim H.
    now elim H.
    set (x:=B754_finite true m0 e e0);
      set (y:=B754_finite true m1 e1 e2); fold x y in r, H0, H2.
    assert (to_real y <> 0).
    { assert (to_real y < 0) by
          (apply non_zero_negative_to_real; easy).
      apply Rlt_not_eq; fourier. }
    pose proof (Bdiv_correct sb emax Hsb' Hemax' nan_bf m x y H1).
    destruct (Rlt_le_dec (Rabs (round m (to_real x / to_real y))) (bpow radix2 emax));
      [rewrite Rlt_bool_true in H3|rewrite Rlt_bool_false in H3];auto.
    + destruct H3 as (_,(_,h)).
      rewrite is_positive_Bsign; auto.
      unfold r, div, Hsb''.
      rewrite h.
      easy.
      now apply Bool.not_true_is_false.
    + apply (f_equal sign_FF) in H3.
      rewrite sign_FF_B2FF, sign_FF_overflow in H3.
      rewrite is_positive_Bsign; auto.
  - destruct x ; destruct b; destruct y ; destruct b; try easy.
    now elim H.
    now elim H.
    set (x:=B754_finite false m0 e e0);
      set (y:=B754_finite true m1 e1 e2); fold x y in r, H0, H2.
    assert (to_real y <> 0).
    { assert (to_real y < 0) by
          (apply non_zero_negative_to_real; easy).
      apply Rlt_not_eq; fourier. }
    pose proof (Bdiv_correct sb emax Hsb' Hemax' nan_bf m x y H1).
    destruct (Rlt_le_dec (Rabs (round m (to_real x / to_real y))) (bpow radix2 emax));
      [rewrite Rlt_bool_true in H3|rewrite Rlt_bool_false in H3];auto.
    + destruct H3 as (_,(_,h)).
      rewrite is_negative_Bsign; auto.
      unfold r, div, Hsb''.
      rewrite h.
      easy.
      now apply Bool.not_true_is_false.
    + apply (f_equal sign_FF) in H3.
      rewrite sign_FF_B2FF, sign_FF_overflow in H3.
      rewrite is_negative_Bsign; auto.
  - destruct x ; destruct b; destruct y ; destruct b; try easy.
    now elim H.
    now elim H.
    set (x:=B754_finite true m0 e e0);
      set (y:=B754_finite false m1 e1 e2); fold x y in r, H0, H2.
    assert (to_real y <> 0).
    { assert (0 < to_real y) by
          (apply non_zero_positive_to_real; easy).
      apply not_eq_sym, Rlt_not_eq; fourier. }
    pose proof (Bdiv_correct sb emax Hsb' Hemax' nan_bf m x y H1).
    destruct (Rlt_le_dec (Rabs (round m (to_real x / to_real y))) (bpow radix2 emax));
      [rewrite Rlt_bool_true in H3|rewrite Rlt_bool_false in H3];auto.
    + destruct H3 as (_,(_,h)).
      rewrite is_negative_Bsign; auto.
      unfold r, div, Hsb''.
      rewrite h.
      easy.
      now apply Bool.not_true_is_false.
    + apply (f_equal sign_FF) in H3.
      rewrite sign_FF_B2FF, sign_FF_overflow in H3.
      rewrite is_negative_Bsign; auto.
Qed.

(* Why3 goal *)
Lemma neg_special : forall (x:t), ((is_nan x) -> (is_nan (neg x))) /\
  (((is_infinite x) -> (is_infinite (neg x))) /\ ((~ (is_nan x)) ->
  (diff_sign x (neg x)))).
Proof.
  intros x.
  split; [|split]; intro.
  - destruct x; easy.
  - destruct x; easy.
  - unfold diff_sign.
    destruct x.
    destruct b;[right|left]; easy.
    destruct b;[right|left]; easy.
    contradict H; easy.
    destruct b;[right|left]; easy.
Qed.

(* Why3 goal *)
Lemma abs_special : forall (x:t), ((is_nan x) -> (is_nan (abs x))) /\
  (((is_infinite x) -> (is_infinite (abs x))) /\ ((~ (is_nan x)) ->
  (is_positive (abs x)))).
Proof.
  intros x.
  split;[|split];intro.
  - destruct x; easy.
  - destruct x; easy.
  - destruct x; try easy.
    contradict H; easy.
Qed.

(* add to theory ? *)
Lemma abs_le_inv: forall {x y}, le (abs x) y -> le (neg y) x /\ le x y.
Proof.
  intros x y.
  destruct (Finite_Infinite_Nan_dec y) as [[hy|hy]|hy].
  + destruct (Finite_Infinite_Nan_dec x) as [[hx|hx]|hx].

    * pose proof (is_finite_abs _ hx).
      destruct (neg_finite _ hy).
      rewrite (le_to_real (abs x) y) by auto.
      rewrite (le_to_real (neg y) x) by auto.
      rewrite le_to_real by auto.
      rewrite H1 by auto.
      destruct (abs_finite _ hx) as (_,(u,_)).
      rewrite u.
      apply Rabs_le_inv.

    * intros a.
      pose proof (abs_special x) as (_,(b,_)); pose proof (b hx); clear b.
      destruct (le_special _ _ a) as [H1|[H1|H1]].
      - destruct x; easy.
      - unfold is_minus_infinity in H1.
        destruct H1, H1, x, y; try easy; destruct b0, b1; easy.
      - destruct y; unfold is_plus_infinity in H1; easy.

    * intros a.
      pose proof (abs_special x) as (b,_); pose proof (b hx); clear b.
      destruct (le_special _ _ a) as [H1|[H1|H1]];
        unfold is_minus_infinity, is_not_nan in H1;
        destruct (abs x), H1; try easy.
      destruct H0; easy.

  + intros a.

    destruct (Finite_Infinite_Nan_dec x) as [[hx|hx]|hx].
    * pose proof (is_finite_abs _ hx).
      destruct (le_special _ _ a) as [H1|[H1|H1]].
      - destruct y; easy.
      - unfold is_minus_infinity in H1; destruct x; easy.
      - destruct y, H1; try easy.
        unfold is_plus_infinity in H1; destruct b; try easy.
        destruct x ; destruct b; split; easy.
    * unfold Bcompare.
      destruct x, y; destruct b, b0; simpl; split; auto; easy.
    * destruct x; unfold le, Bcompare in a; try easy.
      destruct a as [a|a]; contradict a; easy.
  + intros a.
    destruct y; unfold le, Bcompare in a; try easy.
    destruct x; destruct b0; split; easy.
Qed.

(* Why3 goal *)
Lemma fma_special : forall (m:ieee_float.RoundingMode.mode) (x:t) (y:t)
  (z:t), let r := (fma m x y z) in ((((is_nan x) \/ ((is_nan y) \/ (is_nan
  z))) -> (is_nan r)) /\ ((((is_zero x) /\ (is_infinite y)) -> (is_nan r)) /\
  ((((is_infinite x) /\ (is_zero y)) -> (is_nan r)) /\ ((((is_finite x) /\
  ((~ (is_zero x)) /\ ((is_infinite y) /\ (is_finite z)))) -> ((is_infinite
  r) /\ (product_sign r x y))) /\ ((((is_finite x) /\ ((~ (is_zero x)) /\
  ((is_infinite y) /\ (is_infinite z)))) -> (((product_sign z x y) ->
  ((is_infinite r) /\ (same_sign r z))) /\ ((~ (product_sign z x y)) ->
  (is_nan r)))) /\ ((((is_infinite x) /\ ((is_finite y) /\ ((~ (is_zero
  y)) /\ (is_finite z)))) -> ((is_infinite r) /\ (product_sign r x y))) /\
  ((((is_infinite x) /\ ((is_finite y) /\ ((~ (is_zero y)) /\ (is_infinite
  z)))) -> (((product_sign z x y) -> ((is_infinite r) /\ (same_sign r z))) /\
  ((~ (product_sign z x y)) -> (is_nan r)))) /\ ((((is_infinite x) /\
  ((is_infinite y) /\ (is_finite z))) -> ((is_infinite r) /\ (product_sign r
  x y))) /\ ((((is_finite x) /\ ((is_finite y) /\ (is_infinite z))) ->
  ((is_infinite r) /\ (same_sign r z))) /\ ((((is_infinite x) /\
  ((is_infinite y) /\ (is_infinite z))) -> (((product_sign z x y) ->
  ((is_infinite r) /\ (same_sign r z))) /\ ((~ (product_sign z x y)) ->
  (is_nan r)))) /\ ((((is_finite x) /\ ((is_finite y) /\ ((is_finite z) /\
  ~ (no_overflow m (((to_real x) * (to_real y))%R + (to_real z))%R)))) ->
  ((same_sign_real r (((to_real x) * (to_real y))%R + (to_real z))%R) /\
  (overflow_value m r))) /\ (((is_finite x) /\ ((is_finite y) /\ (is_finite
  z))) -> (((product_sign z x y) -> (same_sign r z)) /\ ((~ (product_sign z x
  y)) -> (((((to_real x) * (to_real y))%R + (to_real z))%R = 0%R) ->
  (((m = ieee_float.RoundingMode.RTN) -> (is_negative r)) /\
  ((~ (m = ieee_float.RoundingMode.RTN)) -> (is_positive r)))))))))))))))))).
Proof.
intros m x y z r.
Admitted.

Lemma fma_finite_rev_n' : forall (m:mode) (x:t) (y:t) (z:t),
    is_finite (fma m x y z) ->
    (no_overflow m (to_real x * to_real y + to_real z)%R
     /\ to_real (fma m x y z) = round m (to_real x * to_real y + to_real z)%R)
    \/ to_real (fma m x y z) = max_real \/ to_real (fma m x y z) =-  max_real .
Proof.
intros m x y z h1.
destruct (fma_finite_rev m x y z h1) as (H,(H0,H1)).

destruct (no_overflow_or_not m (to_real x * to_real y + to_real z));[left|right].

split; [auto|apply fma_finite; easy].
destruct (fma_special m x y z) as (_,(_,(_,(_,(_,(_,(_,(_,(_,(_,(h,_))))))))))).
assert (is_finite x /\ is_finite y /\ is_finite z /\ ~ no_overflow m (to_real x * to_real y + to_real z)) by auto.
apply h in H3; clear h.
destruct (fma m x y z); destruct b, m; simpl in *; try easy;
  try (destruct H3; destruct H4; destruct H4; now auto);
  destruct H3; destruct H4; destruct H5; auto.
Qed.

(* Why3 goal *)
Lemma sqrt_special : forall (m:ieee_float.RoundingMode.mode) (x:t), let r :=
  (sqrt m x) in (((is_nan x) -> (is_nan r)) /\ (((is_plus_infinity x) ->
  (is_plus_infinity r)) /\ (((is_minus_infinity x) -> (is_nan r)) /\
  ((((is_finite x) /\ ((to_real x) < 0%R)%R) -> (is_nan r)) /\ (((is_zero
  x) -> (same_sign r x)) /\ (((is_finite x) /\ (0%R < (to_real x))%R) ->
  (is_positive r))))))).
Proof.
  intros m x r.
  unfold sqrt in r.
  intuition.
  - destruct x; easy.
  - unfold is_plus_infinity in *.
    destruct x ; destruct b; easy.
  - unfold is_minus_infinity in H.
    destruct x ; destruct b; easy.
  - destruct x ; destruct b; try easy.
    rewrite B754_zero_to_real in H1; fourier.
    rewrite B754_zero_to_real in H1; fourier.
    apply (to_real_negative _ H0) in H1.
    rewrite is_negative_Bsign in H1 by easy.
    easy.
  - unfold same_sign.
    rewrite is_zero_B754_zero in H; destruct H.
    destruct x; try easy.
    destruct b;[right|left];split;auto.
    simpl in r; unfold r; auto.
    simpl in r; unfold r; auto.
  - destruct (Bsqrt_correct sb emax Hsb' Hemax' (fun b => (true,One_Nan_pl)) m x) as (_,(h1,h2));
      fold r in h1, h2.
    destruct x; try easy.
    rewrite B754_zero_to_real in H1 at 1; fourier.
    apply (to_real_positive _ H0) in H1.
    rewrite is_positive_Bsign in H1 by easy.
    simpl in H1.
    destruct b; try easy.
    assert (not (is_nan r)) by (destruct r; easy).
    rewrite (is_positive_Bsign _ H).
    apply h2.
    now apply Bool.not_true_is_false.
Qed.

Lemma in_int_range_no_overflow : forall m {i}, in_int_range i -> no_overflow m (IZR i).
Proof.
  intros m i h.
  apply Bounded_real_no_overflow.
  unfold in_int_range in h.
  unfold in_range.
  rewrite max_real_int, <-FromInt.Neg, <-Z2R_IZR, <-Z2R_IZR, <-Z2R_IZR.
  pose proof pow2sb_lt_max_int.
  split; apply Z2R_le; auto with zarith.
Qed.

(* Why3 goal *)
Lemma of_int_add_exact : forall (m:ieee_float.RoundingMode.mode)
  (n:ieee_float.RoundingMode.mode) (i:Z) (j:Z), (in_safe_int_range i) ->
  ((in_safe_int_range j) -> ((in_safe_int_range (i + j)%Z) -> (eq (of_int m
  (i + j)%Z) (add n (of_int m i) (of_int m j))))).
Proof.
  intros m n i j h1 h2 h3.
  assert (h1':= in_safe_int_range_no_overflow m h1).
  assert (h2':= in_safe_int_range_no_overflow m h2).
  assert (h3':= in_safe_int_range_no_overflow n h3).
  assert (h3'':= in_safe_int_range_no_overflow m h3).
  assert (is_finite (of_int m i)) by (apply of_int_correct; assumption).
  assert (is_finite (of_int m j)) by (apply of_int_correct; assumption).
  assert (is_finite (of_int n (i+j)%Z)) by (apply of_int_correct; assumption).
  assert (is_finite (of_int m (i+j)%Z)) by (apply of_int_correct; assumption).
  destruct (of_int_correct h1') as (f,_).
  destruct (of_int_correct h2') as (g,_).
  destruct (add_finite n (of_int m i) (of_int m j)); auto.
  simpl; rewrite f, g, Exact_rounding_for_integers, Exact_rounding_for_integers; auto.
  rewrite <-plus_IZR; assumption.
  destruct (of_int_correct h3'') as (h,_).
  rewrite to_real_eq, H4, f, g, h; auto.
  repeat rewrite Exact_rounding_for_integers; auto.
  rewrite <-plus_IZR, Exact_rounding_for_integers; auto; reflexivity.
Qed.

(* Why3 goal *)
Lemma of_int_sub_exact : forall (m:ieee_float.RoundingMode.mode)
  (n:ieee_float.RoundingMode.mode) (i:Z) (j:Z), (in_safe_int_range i) ->
  ((in_safe_int_range j) -> ((in_safe_int_range (i - j)%Z) -> (eq (of_int m
  (i - j)%Z) (sub n (of_int m i) (of_int m j))))).
Proof.
  intros m n i j h1 h2 h3.
  assert (h1':= in_safe_int_range_no_overflow m h1).
  assert (h2':= in_safe_int_range_no_overflow m h2).
  assert (h3':= in_safe_int_range_no_overflow n h3).
  assert (h3'':= in_safe_int_range_no_overflow m h3).
  assert (is_finite (of_int m i)) by (apply of_int_correct; assumption).
  assert (is_finite (of_int m j)) by (apply of_int_correct; assumption).
  assert (is_finite (of_int n (i-j)%Z)) by (apply of_int_correct; assumption).
  assert (is_finite (of_int m (i-j)%Z)) by (apply of_int_correct; assumption).
  destruct (of_int_correct h1') as (f,_).
  destruct (of_int_correct h2') as (g,_).
  destruct (sub_finite n (of_int m i) (of_int m j)); auto.
  simpl; rewrite f, g, Exact_rounding_for_integers, Exact_rounding_for_integers; auto.
  rewrite <-minus_IZR; assumption.
  destruct (of_int_correct h3'') as (h,_).
  rewrite to_real_eq, H4, f, g, h; auto.
  repeat rewrite Exact_rounding_for_integers; auto.
  rewrite <-minus_IZR, Exact_rounding_for_integers; auto; reflexivity.
Qed.

(* Why3 goal *)
Lemma of_int_mul_exact : forall (m:ieee_float.RoundingMode.mode)
  (n:ieee_float.RoundingMode.mode) (i:Z) (j:Z), (in_safe_int_range i) ->
  ((in_safe_int_range j) -> ((in_safe_int_range (i * j)%Z) -> (eq (of_int m
  (i * j)%Z) (mul n (of_int m i) (of_int m j))))).
Proof.
  intros m n i j h1 h2 h3.
  assert (h1':= in_safe_int_range_no_overflow m h1).
  assert (h2':= in_safe_int_range_no_overflow m h2).
  assert (h3':= in_safe_int_range_no_overflow n h3).
  assert (h3'':= in_safe_int_range_no_overflow m h3).
  assert (is_finite (of_int m i)) by (apply of_int_correct; assumption).
  assert (is_finite (of_int m j)) by (apply of_int_correct; assumption).
  assert (is_finite (of_int n (i*j)%Z)) by (apply of_int_correct; assumption).
  assert (is_finite (of_int m (i*j)%Z)) by (apply of_int_correct; assumption).
  destruct (of_int_correct h1') as (f,_).
  destruct (of_int_correct h2') as (g,_).
  destruct (mul_finite n (of_int m i) (of_int m j)); auto.
  simpl; rewrite f, g,
         Exact_rounding_for_integers, Exact_rounding_for_integers; auto.
  rewrite <-mult_IZR; assumption.
  destruct (of_int_correct h3'') as (h,_).
  rewrite to_real_eq, H4, f, g, h; auto.
  repeat rewrite Exact_rounding_for_integers; auto.
  rewrite <-mult_IZR, Exact_rounding_for_integers; auto; reflexivity.
Qed.

(* Why3 goal *)
Lemma Min_r : forall (x:t) (y:t), (le y x) -> (eq (min x y) y).
Proof.
intros x y h1.
destruct h1;
pose (Bcompare_swap _ _ y x);
rewrite H in e;
unfold min; rewrite e; simpl.
apply (eq_not_nan_refl (proj1 (lt_not_nan H))).
apply (eq_not_nan_refl (proj1 (eq_not_nan H))).
Qed.

(* Why3 goal *)
Lemma Min_l : forall (x:t) (y:t), (le x y) -> (eq (min x y) x).
Proof.
intros x y h1.
destruct h1; unfold min; rewrite H; simpl.
apply (eq_not_nan_refl (proj1 (lt_not_nan H))).
pose (Bcompare_swap _ _ x y); rewrite H in e; apply e.
Qed.

(* Why3 goal *)
Lemma Max_r : forall (x:t) (y:t), (le y x) -> (eq (max x y) x).
Proof.
intros x y h1.
destruct h1;
pose (Bcompare_swap _ _ y x);
rewrite H in e;
unfold max; rewrite e; simpl.
apply (eq_not_nan_refl (proj2 (lt_not_nan H))).
apply (eq_not_nan_refl (proj2 (eq_not_nan H))).
Qed.

(* Why3 goal *)
Lemma Max_l : forall (x:t) (y:t), (le x y) -> (eq (max x y) y).
Proof.
intros x y h1.
destruct h1; unfold max; rewrite H; simpl.
apply (eq_not_nan_refl (proj2 (lt_not_nan H))).
apply H.
Qed.

Definition is_intR: R -> Prop.
Proof.
 exact (fun x => x = Z2R (Zfloor x)).
Defined.

Lemma is_intR_equiv: forall {x}, (exists i:int, x = Z2R i) -> is_intR x.
Proof.
  unfold is_intR.
  intros x h; destruct h.
  rewrite H, Zfloor_Z2R; reflexivity.
Qed.

Lemma is_intR_Z2R: forall i, is_intR (Z2R i).
Proof.
  intro i; apply is_intR_equiv; exists i; reflexivity.
Qed.

Lemma is_intR_FIX_format: forall {x}, is_intR x <-> FIX_format radix2 0%Z x.
Proof.
  intro x; unfold FIX_format; split; intro.
  exists ({| Fnum := Zfloor x; Fexp := 0 |}); simpl; split; auto.
  unfold F2R, Fnum, Fexp; simpl.
  rewrite Rmult_1_r; assumption.
  destruct H, H.
  unfold F2R in H.
  rewrite H0 in H; simpl in H.
  rewrite Rmult_1_r in H.
  apply is_intR_equiv.
  exists (Fnum x0); assumption.
Qed.

Lemma is_intR_round: forall {x m}, is_intR x -> is_intR (round m x).
Proof.
  intros x m h.
  rewrite is_intR_FIX_format.
  apply FIX_format_generic.
  apply generic_round_generic.
  apply FIX_exp_valid.
  apply fexp_Valid.
  apply valid_rnd_round_mode.
  apply generic_format_FIX.
  rewrite <-is_intR_FIX_format; assumption.
Qed.

(* Why3 goal *)
Definition is_int: t -> Prop.
Proof.
 exact (fun x => is_finite x /\ is_intR (to_real x)).
Defined.

Hint Unfold is_int.

Lemma is_int_or_not: forall (x:t), is_int x \/ ~ is_int x.
Proof.
  intro x.
  destruct x.
  - left.
    split.
    easy.
    rewrite B754_zero_to_real.
    unfold is_intR.
    change 0 with (Z2R 0%Z) at 2.
    rewrite Zfloor_Z2R; auto.
  - right; intro h; destruct h; easy.
  - right; intro h; destruct h; easy.
  - set (x := (B754_finite b m e e0)).
    unfold is_int, is_intR.
    destruct (Req_dec (to_real x) (Z2R (floor (to_real x)))).
    left.
    rewrite H, Zfloor_Z2R; easy.
    right.
    intro h; destruct h; auto.
Qed.

Lemma Bmax_rep_int_is_int: is_int Bmax_rep_int.
Proof.
  split. easy.
  rewrite Bmax_rep_int_to_real.
  rewrite <-Z2R_IZR; apply is_intR_Z2R.
Qed.

Lemma B754_zero_is_int : forall {b}, (is_int (B754_zero b)).
Proof.
  split. easy.
  rewrite B754_zero_to_real.
  unfold is_intR.
  change 0%R with (Z2R 0).
  now rewrite Zfloor_Z2R.
Qed.

Lemma max_value_is_int : is_int max_value.
Proof.
  split. easy.
  unfold is_intR.
  rewrite max_value_to_real, max_real_int.
  rewrite Floor_int.
  rewrite Z2R_IZR.
  reflexivity.
Qed.

(* TODO: add to theory *)
Lemma neg_is_int : forall {x}, is_int x -> is_int (neg x).
Proof.
  intros x (h,g).
  destruct (neg_finite _ h).
  split; auto.
  rewrite H0.
  rewrite g.
  unfold is_intR.
  rewrite <-Z2R_opp at 2.
  rewrite Zfloor_Z2R.
  symmetry; apply Z2R_opp.
Qed.

(* Why3 goal *)
Lemma zeroF_is_int : (is_int zeroF).
Proof.
  apply B754_zero_is_int.
Qed.

(* Why3 goal *)
Lemma of_int_is_int : forall (m:ieee_float.RoundingMode.mode) (x:Z),
  (in_int_range x) -> (is_int (of_int m x)).
Proof.
  intros m x (h1,h2).
  assert (no_overflow m (IZR x)) as h3.
  apply Bounded_real_no_overflow.
  unfold in_range.
  rewrite max_real_int.
  rewrite <-opp_IZR.
  split; apply IZR_le; assumption.
  destruct (of_int_correct h3) as (h4,(h5,_)).
  split; auto.
  rewrite h4.
  apply is_intR_round.
  rewrite <-Z2R_IZR.
  apply is_intR_Z2R.
Qed.

Lemma int_to_real_ : forall {m:mode} {x:t}, (is_int x) ->
  ((to_real x) = (BuiltIn.IZR (to_int m x))).
Proof.
intros m x h1.
destruct h1.
assert (is_intR (to_real x)) by assumption.
unfold is_intR in H0.
unfold to_int.
rewrite Znearest_imp with (n := (floor (to_real x))).
fold (to_real x).
case m; rewrite <-Z2R_IZR;
  [auto|
   destruct valid_rnd_NA|
   destruct valid_rnd_UP|
   destruct valid_rnd_DN|
   destruct valid_rnd_ZR]; rewrite H0;
rewrite Zrnd_Z2R; easy.
apply Rabs_lt.
rewrite Rminus_diag_eq.
split; fourier.
assumption.
Qed.

(* TODO: add to theory? *)
Lemma neg_int_to_int : forall {x} {m:mode}, is_int x -> to_int m (neg x) = (- (to_int m x))%Z.
Proof.
  intros x m h.
  apply eq_IZR.
  rewrite FromInt.Neg, <-(int_to_real_ h), <-(int_to_real_ (neg_is_int h)).
  destruct h.
  apply neg_finite; auto.
Qed.

Lemma Bmax_rep_int_to_int: forall {m}, to_int m Bmax_rep_int = pow2sb.
Proof.
  intro m.
  apply eq_IZR.
  rewrite <-int_to_real_.
  apply Bmax_rep_int_to_real.
  apply Bmax_rep_int_is_int.
Qed.

Lemma pow2sb_finite : forall (m:mode), is_finite (of_int m pow2sb).
Proof.
  intro.
  apply of_int_correct.
  apply Bounded_real_no_overflow.
  apply rep_int_in_range.
  split; auto with zarith.
  unfold pow2sb.
  assert (0 <= 2 ^ sb)%Z by (apply Z.pow_nonneg; auto with zarith).
  auto with zarith.
Qed.

Lemma Bmax_rep_int_of_int: forall {m:mode}, Bmax_rep_int = of_int m pow2sb.
Proof.
  intro m.
  pose proof pow2sb_finite.
  apply feq_eq; try easy.
  rewrite to_real_eq; try easy.

  assert (- pow2sb <= pow2sb <= pow2sb)%Z.
  split; auto with zarith.
  assert (0 < pow2sb)%Z.
  apply (Z.pow_pos_nonneg 2 sb); auto with zarith.
  apply Z.lt_le_incl, Hsb'.
  auto with zarith.

  destruct (@of_int_correct m pow2sb) as (f,_); auto.
  apply Bounded_real_no_overflow.
  apply rep_int_in_range; auto.

  rewrite f, Bmax_rep_int_to_real.
  symmetry.
  apply Exact_rounding_for_integers; auto.
Qed.

(* Why3 goal *)
Lemma big_float_is_int : forall (m:ieee_float.RoundingMode.mode) (i:t),
  (is_finite i) -> (((le i (neg (of_int m pow2sb))) \/ (le (of_int m pow2sb)
  i)) -> (is_int i)).
Proof.
  intros m i h1 h2.
  destruct i; try easy.
  apply B754_zero_is_int.
  split; auto.
  rewrite <-Bmax_rep_int_of_int in h2.
  unfold to_real, B2R, F2R,Fnum,Fexp.

  assert (1 <= e)%Z.
  { unfold le in h2; simpl Bcompare in h2.
    destruct b.
    destruct (Z_lt_le_dec e 1%Z); auto.
    rewrite Zcompare_Lt in h2; auto.
    destruct h2 as [[h2|h2]|[h2|h2]]; contradict h2; easy.
    destruct h2 as [[h2|h2]|h2]; try (contradict h2; easy).
    destruct e.
    destruct h2 as [h2|h2]; contradict h2; easy.
    pose proof (Pos2Z.is_pos p).
    auto with zarith.
    destruct h2 as [h2|h2]; contradict h2; easy. }

  rewrite <-Z2R_Zpower by auto with zarith.
  rewrite <-Z2R_mult.
  apply is_intR_Z2R.
Qed.

Lemma max_value_to_int : forall m, to_int m max_value = max_int.
Proof.
  intro m.
  apply eq_IZR.
  rewrite <-max_real_int, <-int_to_real_, max_value_to_real.
  reflexivity.
  unfold is_int.

  apply max_value_is_int.
Qed.

Lemma neg_to_int_max_value : forall {m}, (- to_int m max_value)%Z = to_int m (neg max_value).
Proof.
  intro m.
  destruct (valid_rnd_round_mode m).
  pose proof max_value_is_int as H.
  pose proof (neg_is_int H).
  apply eq_IZR.
  rewrite Ropp_Ropp_IZR.
  rewrite <-int_to_real_, <-int_to_real_  by auto.
  now destruct (neg_finite max_value) as (_,h).
Qed.

Lemma is_finite_range_to_int : forall {x} m, (is_finite x) -> (in_range (IZR (to_int m x))).
Proof.
  intros x m h1.
  apply Rabs_le_inv.
  rewrite Abs.Abs_le, max_real_int, <-opp_IZR, <-(max_value_to_int m), neg_to_int_max_value.

  pose proof (bounded_floats_le _ h1).
  pose proof (abs_le_inv H) as (H1,H2).
  now split; apply IZR_le; apply to_int_le.
Qed.

Lemma is_finite_to_int: forall {x} (m1 m2:mode), is_finite x -> no_overflow m1 (IZR (to_int m2 x)).
Proof.
  intros x m1 m2 h;
  apply (Bounded_real_no_overflow m1 _ (is_finite_range_to_int m2 h)).
Qed.

Lemma is_finite_to_int2: forall {x} m, is_finite x -> (- max_int <= to_int m x <= max_int)%Z.
Proof.
  intros x m h.
  rewrite <-(@max_value_to_int m).
  rewrite (@neg_to_int_max_value m).
  pose proof (bounded_floats_le _ h).
  apply abs_le_inv in H.
  destruct H.
  now split; apply to_int_le.
Qed.

Lemma roundToIntegral_finite: forall m {x}, is_finite x -> is_finite (roundToIntegral m x).
Proof.
  intros m x h; destruct x; auto.
  unfold roundToIntegral.
  destruct Z.eq_dec; auto.
  apply of_int_correct.
  apply (is_finite_to_int RTZ m h).
Qed.

(* Why3 goal *)
Lemma roundToIntegral_is_int : forall (m:ieee_float.RoundingMode.mode) (x:t),
  (is_finite x) -> (is_int (roundToIntegral m x)).
Proof.
  intros m x h1.
  destruct x; try easy.
  + apply zeroF_is_int.
  + simpl.
    destruct Z.eq_dec.
    split;[apply h1|].
    simpl; unfold is_intR.
    change 0 with (Z2R 0).
    now rewrite Zfloor_Z2R.
    change (is_int (of_int RTZ (to_int m (B754_finite b m0 e e0)))).
    apply of_int_is_int.
    apply is_finite_to_int2.
    assumption.
Qed.

(* Why3 goal *)
Lemma eq_is_int : forall (x:t) (y:t), (eq x y) -> ((is_int x) -> (is_int y)).
Proof.
  intros x y h1 (h2,h3).
  unfold is_int.
  split; [apply (eq_finite_dist h1); auto|].
  unfold is_intR in *.
  apply to_real_eq_alt in h1.
  rewrite h1 in *; assumption.
Qed.

(* Why3 goal *)
Lemma add_int : forall (x:t) (y:t) (m:ieee_float.RoundingMode.mode), (is_int
  x) -> ((is_int y) -> ((is_finite (add m x y)) -> (is_int (add m x y)))).
Proof.
intros x y m (h1,h1') (h2,h2') h3.
destruct (add_finite_rev_n' m x y h3).
+ destruct H.
  unfold is_int.
  split; auto.
  rewrite H0.
  apply is_intR_round.
  apply is_intR_equiv.
  exists (floor (to_real x) + floor (to_real y))%Z.
  unfold is_intR in h1', h2'; rewrite h1', h2' at 1.
  symmetry; apply Z2R_plus.
+ split; auto.
  rewrite max_real_int, <-Z2R_IZR in H.
  destruct H; apply is_intR_equiv.
  exists max_int; assumption.
  exists (- max_int)%Z.
  rewrite Z2R_opp; assumption.
Qed.

(* Why3 goal *)
Lemma sub_int : forall (x:t) (y:t) (m:ieee_float.RoundingMode.mode), (is_int
  x) -> ((is_int y) -> ((is_finite (sub m x y)) -> (is_int (sub m x y)))).
Proof.
intros x y m (h1,h1') (h2,h2') h3.
destruct (sub_finite_rev_n' m x y h3).
+ destruct H.
  unfold is_int.
  split; auto.
  rewrite H0.
  apply is_intR_round.
  apply is_intR_equiv.
  exists (floor (to_real x) - floor (to_real y))%Z.
  unfold is_intR in h1', h2'; rewrite h1', h2' at 1.
  symmetry; apply Z2R_minus.
+ split; auto.
  rewrite max_real_int, <-Z2R_IZR in H.
  destruct H; apply is_intR_equiv.
  exists max_int; assumption.
  exists (- max_int)%Z.
  rewrite Z2R_opp; assumption.
Qed.

(* Why3 goal *)
Lemma mul_int : forall (x:t) (y:t) (m:ieee_float.RoundingMode.mode), (is_int
  x) -> ((is_int y) -> ((is_finite (mul m x y)) -> (is_int (mul m x y)))).
Proof.
intros x y m (h1,h1') (h2,h2') h3.
destruct (mul_finite_rev_n' m x y h3).
+ destruct H.
  unfold is_int.
  split; auto.
  rewrite H0.
  apply is_intR_round.
  apply is_intR_equiv.
  exists (floor (to_real x) * floor (to_real y))%Z.
  unfold is_intR in h1', h2'; rewrite h1', h2' at 1.
  symmetry; apply Z2R_mult.
+ split; auto.
  rewrite max_real_int, <-Z2R_IZR in H.
  destruct H; apply is_intR_equiv.
  exists max_int; assumption.
  exists (- max_int)%Z.
  rewrite Z2R_opp; assumption.
Qed.

(* Why3 goal *)
Lemma fma_int : forall (x:t) (y:t) (z:t) (m:ieee_float.RoundingMode.mode),
  (is_int x) -> ((is_int y) -> ((is_int z) -> ((is_finite (fma m x y z)) ->
  (is_int (fma m x y z))))).
Proof.
intros x y z m (h1,h1') (h2,h2') (h3,h3') h4.
destruct (fma_finite_rev_n' m x y z h4).
+ destruct H.
  unfold is_int.
  split; auto.
  rewrite H0.
  apply is_intR_round.
  apply is_intR_equiv.
  exists (floor (to_real x) * floor (to_real y) + floor (to_real z))%Z.
  unfold is_intR in h1', h2', h3'; rewrite h1', h2', h3' at 1.
  rewrite Z2R_plus, Z2R_mult; reflexivity.
+ split; auto.
  rewrite max_real_int, <-Z2R_IZR in H.
  destruct H; apply is_intR_equiv.
  exists max_int; assumption.
  exists (- max_int)%Z.
  rewrite Z2R_opp; assumption.
Qed.

(* Why3 goal *)
Lemma neg_int : forall (x:t), (is_int x) -> (is_int (neg x)).
Proof.
intros x (h1,h2).
destruct (neg_finite x h1).
split; try easy.
unfold is_intR in *.
rewrite H0.
rewrite h2 at 2.
rewrite <-Z2R_opp, Zfloor_Z2R, Z2R_opp.
apply f_equal, h2.
Qed.

(* Why3 goal *)
Lemma abs_int : forall (x:t), (is_int x) -> (is_int (abs x)).
Proof.
intros x (h1,h2).
destruct (abs_finite x h1) as (h3,(h4,h5)).
split; try easy.
unfold is_intR in *.
rewrite h4.
rewrite h2 at 2.
rewrite <-Z2R_abs, Zfloor_Z2R, Z2R_abs.
apply f_equal, h2.
Qed.

(* Why3 goal *)
Lemma is_int_of_int : forall (x:t) (m:ieee_float.RoundingMode.mode)
  (m':ieee_float.RoundingMode.mode), (is_int x) -> (eq x (of_int m' (to_int m
  x))).
Proof.
  intros x m m' h1.
  assert (h1':=h1).
  destruct h1 as (h1,h2).
  assert (no_overflow m' (IZR (to_int m x))) by
      (apply is_finite_to_int; auto).
  destruct (of_int_correct H) as (h3,(h4,h5)); auto.
  rewrite to_real_eq; auto.
  rewrite h3, <-int_to_real_; auto.
  symmetry; apply Round_to_real; auto.
Qed.

(* Why3 goal *)
Lemma is_int_to_int : forall (m:ieee_float.RoundingMode.mode) (x:t), (is_int
  x) -> (in_int_range (to_int m x)).
Proof.
intros m x h1.
pose proof (@int_to_real_ m x h1).
unfold in_int_range.
destruct h1.
apply is_finite1 in H0.
unfold in_range in H0.
destruct H0.
split; apply le_IZR; try rewrite FromInt.Neg; rewrite <-max_real_int, <-H; auto.
Qed.

(* Why3 goal *)
Lemma is_int_is_finite : forall (x:t), (is_int x) -> (is_finite x).
Proof.
intros x (h,_); assumption.
Qed.

(* Why3 goal *)
Lemma int_to_real : forall (m:ieee_float.RoundingMode.mode) (x:t), (is_int
  x) -> ((to_real x) = (BuiltIn.IZR (to_int m x))).
Proof.
intros m x.
apply int_to_real_.
Qed.

Lemma of_int_to_real : forall (m:mode) (x:Z), (no_overflow m
  (BuiltIn.IZR x)) -> ((to_real (of_int m x)) = (round m
  (BuiltIn.IZR x))).
Proof.
  intros m x h1.
  apply (of_int_correct h1).
Qed.

(* TODO: add to theory ? *)
Lemma to_real_roundToIntegral: forall {x} m, is_finite x -> to_real (roundToIntegral m x) = Z2R (to_int m x).
Proof.
  intros x m h.
  unfold roundToIntegral.
  destruct Z.eq_dec.

  destruct x; try easy.
  rewrite B754_zero_to_real, e; auto.
  rewrite B754_zero_to_real, e; auto.

  destruct x; try easy.
  contradict n.
  apply to_int_B754_zero.

  set (x:= B754_finite b m0 e e0); fold x in h, h.
  change (to_real (of_int RTZ (to_int m x)) = Z2R (to_int m x)).

  assert (is_finite Bmax_rep_int) by auto.
  destruct (neg_finite _ H) as (H0,_).

  assert (((le (neg Bmax_rep_int) x) /\ (le x Bmax_rep_int))
       \/ ((le x (neg Bmax_rep_int)) \/ (le Bmax_rep_int x))) as h1.
  destruct (le_or_lt_or_nan x Bmax_rep_int) as [h1|[h1|[h1|h1]]].
  + destruct (le_or_lt_or_nan (neg Bmax_rep_int) x) as [h2|[h2|[h2|h2]]].
    - left; split; easy.
    - right; left; apply lt_le; auto.
    - destruct (neg Bmax_rep_int); easy.
    - destruct x; easy.
  + right; right; apply lt_le; auto.
  + destruct x; easy.
  + destruct Bmax_rep_int; easy.

  + destruct h1.
    - destruct H1.
      apply (@to_int_le _ _ m H0 h) in H1.
      apply (@to_int_le _ _ m h H) in H2.
      rewrite (neg_int_to_int Bmax_rep_int_is_int) in H1.
      rewrite Bmax_rep_int_to_int in H1, H2.
      rewrite of_int_to_real.
      rewrite Exact_rounding_for_integers; auto.
      rewrite Z2R_IZR; reflexivity.
      unfold in_safe_int_range; auto.
      apply Bounded_real_no_overflow.
      apply rep_int_in_range; auto.
    - rewrite (@Bmax_rep_int_of_int m) in H1.
      pose proof (big_float_is_int m _ h H1).
      rewrite of_int_to_real.
      rewrite <-int_to_real; auto.
      rewrite Round_to_real; auto.
      rewrite Z2R_IZR.
      apply int_to_real; auto.
      apply Bounded_real_no_overflow.
      apply is_finite_range_to_int; auto.
Qed.

(* Why3 goal *)
Lemma truncate_int : forall (m:ieee_float.RoundingMode.mode) (i:t), (is_int
  i) -> (eq (roundToIntegral m i) i).
Proof.
  intros m i (h1,h2).
  pose proof (roundToIntegral_finite m h1).
  rewrite eq_to_real_finite; auto.
  rewrite to_real_roundToIntegral; auto.
  rewrite Z2R_IZR.
  symmetry.
  apply int_to_real; auto.
Qed.

(* Why3 goal *)
Lemma truncate_neg : forall (x:t), (is_finite x) -> ((is_negative x) ->
  ((roundToIntegral ieee_float.RoundingMode.RTZ
  x) = (roundToIntegral ieee_float.RoundingMode.RTP x))).
Proof.
  intros x h1 h2.
  destruct x; try easy.
  simpl roundToIntegral.
  pose proof (negative_to_real _ h1 h2).
  pose proof (Ztrunc_ceil _ H).
  simpl in H0.
  rewrite H0.
  reflexivity.
Qed.

(* Why3 goal *)
Lemma truncate_pos : forall (x:t), (is_finite x) -> ((is_positive x) ->
  ((roundToIntegral ieee_float.RoundingMode.RTZ
  x) = (roundToIntegral ieee_float.RoundingMode.RTN x))).
Proof.
  intros x h1 h2.
  destruct x; try easy.
  simpl roundToIntegral.
  pose proof (positive_to_real _ h1 h2).
  pose proof (Ztrunc_floor _ H).
  simpl in H0.
  rewrite H0.
  reflexivity.
Qed.

(* Why3 goal *)
Lemma ceil_le : forall (x:t), (is_finite x) -> (le x
  (roundToIntegral ieee_float.RoundingMode.RTP x)).
Proof.
intros x h1.
pose proof (roundToIntegral_finite RTP h1).
rewrite le_to_real; auto.
rewrite to_real_roundToIntegral; auto.
simpl.
apply Zceil_ub.
Qed.

(* Why3 goal *)
Lemma ceil_lest : forall (x:t) (y:t), ((le x y) /\ (is_int y)) -> (le
  (roundToIntegral ieee_float.RoundingMode.RTP x) y).
Proof.
intros x y (h1,h2).
destruct (le_special _ _ h1) as [h|[h|h]]; try easy.
- destruct h.
  pose proof (roundToIntegral_finite RTP H).
  rewrite le_to_real; auto.
  rewrite to_real_roundToIntegral; auto.
  simpl.
  rewrite (int_to_real RTP _ h2), <-Z2R_IZR.
  apply Z2R_le, Zceil_glb.
  rewrite Z2R_IZR, <-(int_to_real RTP _ h2).
  apply le_to_real; auto.
- destruct h as (h,_).
  unfold is_minus_infinity in h.
  destruct x; easy.
- unfold is_plus_infinity in h.
  destruct h, h2, y; try easy.
Qed.

(* Why3 goal *)
Lemma ceil_to_real : forall (x:t), (is_finite x) ->
  ((to_real (roundToIntegral ieee_float.RoundingMode.RTP
  x)) = (BuiltIn.IZR (real.Truncate.ceil (to_real x)))).
Proof.
  intros x h.
  rewrite to_real_roundToIntegral; auto.
  unfold to_int.
  rewrite Z2R_IZR; reflexivity.
Qed.

(* Why3 goal *)
Lemma ceil_to_int : forall (m:ieee_float.RoundingMode.mode) (x:t), (is_finite
  x) -> ((to_int m (roundToIntegral ieee_float.RoundingMode.RTP
  x)) = (real.Truncate.ceil (to_real x))).
Proof.
  intros m x h.
  unfold to_int.
  rewrite to_real_roundToIntegral; auto.
  destruct (valid_rnd_round_mode m) as (_,h').
  destruct m; apply (h' (to_int RTP x)).
Qed.

(* Why3 goal *)
Lemma floor_le : forall (x:t), (is_finite x) -> (le
  (roundToIntegral ieee_float.RoundingMode.RTN x) x).
Proof.
  intros x h1.
  pose proof (roundToIntegral_finite RTN h1).
  rewrite le_to_real; auto.
  rewrite to_real_roundToIntegral; auto.
  simpl.
  apply Zfloor_lb.
Qed.

(* Why3 goal *)
Lemma floor_lest : forall (x:t) (y:t), ((le y x) /\ (is_int y)) -> (le y
  (roundToIntegral ieee_float.RoundingMode.RTN x)).
Proof.
  intros x y (h1,h2).
  destruct (le_special _ _ h1) as [h|[h|h]]; try easy.
  - destruct h.
    pose proof (roundToIntegral_finite RTN H0).
    rewrite le_to_real; auto.
    rewrite to_real_roundToIntegral; auto.
    simpl.
    rewrite (int_to_real RTN _ h2), <-Z2R_IZR.
    apply Z2R_le, Zfloor_lub.
    rewrite Z2R_IZR, <-(int_to_real RTN _ h2).
    apply le_to_real; auto.
  - destruct h2 as (h2,_).
    unfold is_minus_infinity in h.
    destruct y; easy.
  - unfold is_plus_infinity in h.
    destruct h; destruct H; destruct H0, h2, x; try easy.
Qed.

(* Why3 goal *)
Lemma floor_to_real : forall (x:t), (is_finite x) ->
  ((to_real (roundToIntegral ieee_float.RoundingMode.RTN
  x)) = (BuiltIn.IZR (real.Truncate.floor (to_real x)))).
Proof.
  intros x h.
  rewrite to_real_roundToIntegral; auto.
  unfold to_int.
  rewrite Z2R_IZR; reflexivity.
Qed.

(* Why3 goal *)
Lemma floor_to_int : forall (m:ieee_float.RoundingMode.mode) (x:t),
  (is_finite x) -> ((to_int m (roundToIntegral ieee_float.RoundingMode.RTN
  x)) = (real.Truncate.floor (to_real x))).
Proof.
  intros m x h.
  unfold to_int.
  rewrite to_real_roundToIntegral; auto.
  destruct (valid_rnd_round_mode m) as (_,h').
  destruct m; apply (h' (to_int RTN x)).
Qed.

Lemma same_sign_roundToIntegral:
  forall {x} (m:mode), ~ is_nan x -> same_sign (roundToIntegral m x) x.
Proof.
  intros x m h.
  unfold same_sign.
  destruct x; try (now elim h); try (destruct b; simpl; now auto); clear h.
  set (x:=B754_finite b m0 e e0).
  assert (no_overflow RTZ (IZR (to_int m x))) by
      now apply is_finite_to_int.
  destruct (of_int_correct H) as (_,(_,h1)); clear H.
  assert (is_finite x) as h2 by easy.
  pose proof (roundToIntegral_finite m h2).
  destruct b; simpl;[right|left];split;auto.
  + apply is_negative_Bsign; auto.
    change (~ is_nan (roundToIntegral m x)); destruct (roundToIntegral m x); easy.
    assert (to_int m x <= 0)%Z.
    apply is_negative_to_int; easy.
    destruct (Z_le_lt_eq_dec _ _ H0).
    - rewrite Zcompare_Lt in h1 by auto.
      destruct Z.eq_dec; auto.
    - destruct Z.eq_dec; auto.
  + apply is_positive_Bsign; auto.
    change (~ is_nan (roundToIntegral m x)); destruct (roundToIntegral m x); easy.
    assert (0 <= to_int m x)%Z.
    apply is_positive_to_int; easy.
    destruct (Z_le_lt_eq_dec _ _ H0).
    - rewrite Zcompare_Gt in h1 by auto.
      destruct Z.eq_dec; auto.
    - symmetry in e1.
      destruct Z.eq_dec; easy.
Qed.

Lemma same_sign_roundToIntegral2:
  forall {x} {m m':mode}, ~ is_nan x -> same_sign (roundToIntegral m x) (roundToIntegral m' x).
Proof.
  intros x m m' h.
  pose proof (same_sign_roundToIntegral m h).
  pose proof (same_sign_roundToIntegral m' h).
  unfold same_sign in *.
  pose proof (negative_xor_positive x).
  destruct H as [(H,H')|(H,H')], H0 as [(H0,H0')|(H0,H0')]; auto.
  contradict H1; auto.
  contradict H1; auto.
Qed.

Lemma no_overflow_to_real_min_RTN:
  forall x, is_finite x -> no_overflow RNE (to_real x - to_real (roundToIntegral RTN x)).
Proof.
  intros x h.
  rewrite to_real_roundToIntegral; auto.
  unfold to_int.
  apply Bounded_real_no_overflow.
  pose proof (Zfloor_ub (to_real x)).
  pose proof (Zfloor_lb (to_real x)).
  unfold in_range.
  pose proof max_real_ge_6.
  fold (to_real x).
  split; fourier.
Qed.

Lemma ceil_lb: forall x, ((Z2R (ceil x) - 1) < x).
Proof.
  intro.
  case (Req_dec (Z2R (Zfloor x)) x); intro.
  rewrite <-H, Zceil_Z2R, H; simpl; fourier.
  rewrite (Zceil_floor_neq _ H).
  rewrite Z2R_plus; simpl.
  pose proof (Zfloor_lb x).
  destruct (Rle_lt_or_eq_dec _ _ H0); try easy.
  fourier.
Qed.

Lemma no_overflow_to_real_RTP_min:
  forall x, is_finite x -> no_overflow RNE (to_real (roundToIntegral RTP x) - to_real x).
Proof.
  intros x h.
  rewrite to_real_roundToIntegral; auto.
  unfold to_int.
  apply Bounded_real_no_overflow.
  pose proof (Zceil_ub (to_real x)).
  pose proof (ceil_lb (to_real x)).
  unfold in_range.
  pose proof max_real_ge_6.
  fold (to_real x).
  split; fourier.
Qed.

(* Why3 goal *)
Lemma RNA_down : forall (x:t), (lt (sub ieee_float.RoundingMode.RNE x
  (roundToIntegral ieee_float.RoundingMode.RTN x))
  (sub ieee_float.RoundingMode.RNE
  (roundToIntegral ieee_float.RoundingMode.RTP x) x)) ->
  ((roundToIntegral ieee_float.RoundingMode.RNA
  x) = (roundToIntegral ieee_float.RoundingMode.RTN x)).
Proof.
  intros x h.
  destruct x; try easy.
  set (x:=(B754_finite b m e e0)); fold x in h.
  assert (forall m', is_finite (roundToIntegral m' x)) as h1.
  intro m'.
  apply roundToIntegral_finite; try easy.
  apply to_real_refl; try easy.
  2: apply same_sign_roundToIntegral2; easy.

  rewrite to_real_roundToIntegral, to_real_roundToIntegral; try easy.
  f_equal.
  unfold to_int.
  apply Znearest_imp.
  rewrite Rabs_pos_eq.
  2: pose proof (Zfloor_lb (to_real x)); fold (to_real x); fourier.

  assert (is_finite x) as x_fin by easy.
  pose proof (no_overflow_to_real_min_RTN _ x_fin) as h2.
  pose proof (no_overflow_to_real_RTP_min _ x_fin) as h3.

  assert (is_finite (roundToIntegral RTN x)) as rtn_fin by auto.
  assert (is_finite (roundToIntegral RTP x)) as rtp_fin by auto.
  destruct (sub_finite _ _ _ x_fin rtn_fin h2) as (h4,h4').
  destruct (sub_finite _ _ _ rtp_fin x_fin h3) as (h5,h5').
  clear x_fin h2 h3 rtn_fin rtp_fin.

  rewrite lt_finite in h; auto.
  rewrite h4' in h; auto.
  rewrite h5' in h; auto.

  fold (to_real x).
  case (Req_dec (Z2R (Zfloor (to_real x))) (to_real x)); intro.
  fourier.

  apply round_lt in h.
  rewrite to_real_roundToIntegral, to_real_roundToIntegral in h; try easy.
  unfold to_int in h.
  rewrite Zceil_floor_neq, Z2R_plus in h; auto.
  simpl (Z2R 1) in h.
  fold (to_real x) in h.
  fourier.
Qed.

(* Why3 goal *)
Lemma RNA_up : forall (x:t), (lt (sub ieee_float.RoundingMode.RNE
  (roundToIntegral ieee_float.RoundingMode.RTP x) x)
  (sub ieee_float.RoundingMode.RNE x
  (roundToIntegral ieee_float.RoundingMode.RTN x))) ->
  ((roundToIntegral ieee_float.RoundingMode.RNA
  x) = (roundToIntegral ieee_float.RoundingMode.RTP x)).
Proof.
  intros x h.
  destruct x; try easy.
  set (x:=(B754_finite b m e e0)); fold x in h.
  assert (forall m', is_finite (roundToIntegral m' x)) as h1.
  intro m'.
  apply roundToIntegral_finite; try easy.
  apply to_real_refl; try easy.
  2: apply same_sign_roundToIntegral2; easy.

  rewrite to_real_roundToIntegral, to_real_roundToIntegral; try easy.
  f_equal.
  unfold to_int.
  apply Znearest_imp.
  rewrite Rabs_left1.
  2: pose proof (Zceil_ub (to_real x)); fold (to_real x); fourier.
  rewrite Ropp_minus_distr.

  assert (is_finite x) as x_fin by easy.
  pose proof (no_overflow_to_real_min_RTN _ x_fin) as h2.
  pose proof (no_overflow_to_real_RTP_min _ x_fin) as h3.

  assert (is_finite (roundToIntegral RTN x)) as rtn_fin by auto.
  assert (is_finite (roundToIntegral RTP x)) as rtp_fin by auto.
  destruct (sub_finite _ _ _ x_fin rtn_fin h2) as (h4,h4').
  destruct (sub_finite _ _ _ rtp_fin x_fin h3) as (h5,h5').
  clear x_fin h2 h3 rtn_fin rtp_fin.

  rewrite lt_finite in h; auto.
  rewrite h4' in h; auto.
  rewrite h5' in h; auto.

  fold (to_real x).
  case (Req_dec (Z2R (Zceil (to_real x))) (to_real x)); intro.
  fourier.

  apply round_lt in h.
  rewrite to_real_roundToIntegral, to_real_roundToIntegral in h; try easy.
  unfold to_int in h.
  assert (Z2R (floor (to_real x)) <> to_real x).
  intro.
  rewrite <-H0 in H.
  rewrite Zceil_Z2R in H.
  auto.

  rewrite Zceil_floor_neq, Z2R_plus in h; auto.
  rewrite Zceil_floor_neq, Z2R_plus; auto.
  simpl (Z2R 1) in *; fold (to_real x) in h; fourier.
Qed.

Lemma sterbenz_round: forall x y m,
    to_real y / 2 <= to_real x <= 2 * to_real y ->
    round m (to_real x - to_real y) = to_real x - to_real y.
Proof.
  intros x y m h.
  assert (generic_format radix2 fexp (to_real x - to_real y)).
  { Require Flocq.Prop.Fprop_Sterbenz.
    apply Fprop_Sterbenz.sterbenz; auto.
    apply fexp_monotone.
    apply generic_format_B2R.
    apply generic_format_B2R. }
  apply round_generic; auto.
  apply valid_rnd_round_mode.
Qed.

Lemma sterbenz_round_opp: forall x y m,
    to_real y / 2 <= to_real x <= 2 * to_real y ->
    round m (to_real y - to_real x) = to_real y - to_real x.
Proof.
  intros x y m h.
  assert (generic_format radix2 fexp (to_real y - to_real x)).
  { Require Flocq.Prop.Fprop_Sterbenz.
    replace (to_real y - to_real x) with (- (to_real x - to_real y)) by ring.
    apply generic_format_opp.
    apply Fprop_Sterbenz.sterbenz; auto.
    apply fexp_monotone.
    apply generic_format_B2R.
    apply generic_format_B2R. }
  apply round_generic; auto.
  apply valid_rnd_round_mode.
Qed.

Lemma sterbenz_round_2: forall x y m, is_finite y -> is_finite x ->
    2 * to_real y <= to_real x <= to_real y / 2 ->
    round m (to_real x - to_real y) = to_real x - to_real y.
Proof.
  intros x y m h h' h2.
  replace (to_real x - to_real y) with (-to_real y - (- to_real x)) by field.
  destruct (neg_finite _ h) as (_,h1); rewrite <-h1;
  destruct (neg_finite _ h') as (_,h1'); rewrite <-h1'.
  apply sterbenz_round_opp.
  rewrite h1, h1'.
  destruct h2.
  assert (forall x, -x = -1*x) as h2 by (intro; ring).
  split.
  - rewrite h2, (h2 (to_real x)).
    rewrite Real.assoc_mul_div by (apply not_eq_sym, Rlt_not_eq; fourier).
    apply Rmult_le_compat_neg_l; fourier.
  - rewrite h2, (h2 (to_real y)).
    replace (2 * (-1 * to_real y)) with (-1 * (2 * to_real y)) by field.
    apply Rmult_le_compat_neg_l; fourier.
Qed.

Lemma RTN_not_far: forall x,
    is_finite x -> 1 <= to_real x ->
    to_real (roundToIntegral RTN x) / 2 <= to_real x <= 2 * to_real (roundToIntegral RTN x).
Proof.
  intros x h h1.
  rewrite floor_to_real; auto.
  pose proof (Zfloor_lb (to_real x));
  pose proof (Zfloor_ub (to_real x)).
  rewrite <-Z2R_IZR.
  split; try fourier.
  apply Rlt_le.
  apply Rlt_le_trans with (r2:=(Z2R (floor (to_real x)) + 1)); auto.
  assert (1 <= Z2R (floor (to_real x))).
  { pose proof (Rle_lt_trans _ _ _ h1 H0).
    change 1 with (Z2R 1) in *.
    rewrite <-Z2R_plus in H1.
    apply lt_Z2R in H1.
    apply Z2R_le.
    auto with zarith. }
  fourier.
Qed.

Lemma RTN_not_far_opp: forall x,
    is_finite x -> to_real x <= -/2 ->
    2 * to_real (roundToIntegral RTN x) <= to_real x <= to_real (roundToIntegral RTN x) / 2.
Proof.
  intros x h h1.
  rewrite floor_to_real; auto.
  pose proof (Zfloor_lb (to_real x));
  pose proof (Zfloor_ub (to_real x)).
  rewrite <- (Z2R_IZR (floor _)).
  split; try fourier.
  destruct (Rle_lt_dec (to_real x) (-1)); try fourier.
  assert (Z2R(floor (to_real x)) = -1).
  { assert (Z2R(floor(to_real x)) < Z2R 0) by (simpl Z2R; fourier).
    assert (Z2R(-2) < Z2R(floor(to_real x))) by (simpl Z2R; fourier).
    apply lt_Z2R in H1; apply lt_Z2R in H2.
    now replace (floor (to_real x)) with (-1)%Z by omega. }
  fourier.
Qed.

Lemma RTP_not_far: forall x,
    is_finite x -> /2 <= to_real x ->
    to_real x / 2 <= to_real (roundToIntegral RTP x) <= 2 * to_real x.
Proof.
  intros x h h1.
  rewrite ceil_to_real; auto.
  pose proof (ceil_lb (to_real x));
  pose proof (Zceil_ub (to_real x)).
  rewrite <- (Z2R_IZR (ceil _)).
  split; try fourier.
  destruct (Rle_lt_dec 1 (to_real x) ); try fourier.
  assert (Z2R (ceil (to_real x)) = 1).
  { assert (Z2R 0 < Z2R(ceil(to_real x))) by (simpl Z2R; fourier).
    assert (Z2R(ceil(to_real x)) < Z2R(2)) by (simpl Z2R; fourier).
    apply lt_Z2R in H1; apply lt_Z2R in H2.
    now replace (ceil (to_real x)) with 1%Z by omega. }
  fourier.
Qed.

Lemma RTP_not_far_opp: forall x,
    is_finite x -> to_real x <= -1 ->
    2 * to_real x <= to_real (roundToIntegral RTP x) <= to_real x / 2.
Proof.
  intros x h h1.
  rewrite ceil_to_real; auto.
  pose proof (ceil_lb (to_real x));
  pose proof (Zceil_ub (to_real x)).
  rewrite <- (Z2R_IZR (ceil _)).
  split; try fourier.
  apply Rmult_le_reg_r with (r:=2); try fourier.
  replace (to_real x / 2 * 2) with (to_real x) by field.
  apply Rlt_le.
  apply Rle_lt_trans with (r2:=(Z2R (ceil (to_real x)) - 1)); auto.
  assert (Z2R (ceil (to_real x)) <= -1).
  { pose proof (Rlt_le_trans _ _ _ H h1).
    rewrite <- (Z2R_minus _ 1) in H1.
    apply (lt_Z2R _ (-1)) in H1.
    apply (Z2R_le _ (-1)).
    auto with zarith. }
  fourier.
Qed.

Lemma round_plus_weak: forall x y m,
    Rabs (to_real x + to_real y) <= Rmin (Rabs (to_real x)) (Rabs (to_real y)) ->
    round m (to_real x + to_real y) = to_real x + to_real y.
Proof.
  intros x y m h.
  assert (generic_format radix2 fexp (to_real x + to_real y)).
  { Require Flocq.Prop.Fprop_Sterbenz.
    apply Fprop_Sterbenz.generic_format_plus_weak; auto.
    apply fexp_monotone.
    apply generic_format_B2R.
    apply generic_format_B2R. }
  apply round_generic; auto.
  apply valid_rnd_round_mode.
Qed.

Lemma half_bounded: bounded sb emax (shift_pos (sb_pos - 1) 1) (- sb) = true.
Proof.
  unfold bounded.
  apply Bool.andb_true_iff; split.
  unfold canonic_mantissa.
  apply Zeq_bool_true.
  rewrite Fcore_digits.Zpos_digits2_pos, shift_pos_correct.
  rewrite Zmult_1_r, Z.pow_pos_fold.
  rewrite Fcore_digits.Zdigits_Zpower by easy.
  rewrite Pos2Z.inj_sub by exact sb_gt_1.
  fold sb.
  unfold FLT_exp.
  replace (sb - 1 + 1 + - sb)%Z with 0%Z by ring.
  apply Zmax_l.
  pose sb_gt_1; pose Hemax'; omega.
  apply Zle_bool_true.
  pose Hemax'; pose sb_gt_1; omega.
Qed.

Definition half: t.
Proof.
  exact (B754_finite false _ _ half_bounded).
Defined.

Lemma half_to_real : ((to_real half) = (05 / 10)%R).
Proof.
  unfold B2R, half; simpl.
  rewrite shift_pos_correct.
  rewrite Z.pow_pos_fold.
  unfold F2R.
  unfold Fnum, Fexp.
  rewrite Zmult_1_r.
  change 2%Z with (radix_val radix2).
  rewrite Z2R_Zpower by easy.
  rewrite <-bpow_plus.
  rewrite Pos2Z.inj_sub by exact sb_gt_1.
  rewrite <-Pos2Z.opp_pos.
  replace (Z.pos sb_pos - 1 + - Z.pos sb_pos)%Z with (- 1)%Z by ring.
  unfold bpow.
  replace (Z.pow_pos radix2 1)%Z with 2%Z by auto with zarith.
  unfold Z2R, P2R.
  field.
Qed.

Lemma eq_diff_floor_ceil: forall {x}, eq (sub RNE x (roundToIntegral RTN x)) (sub RNE (roundToIntegral RTP x) x) -> to_real x - Z2R (floor (to_real x)) = Z2R (ceil (to_real x)) - to_real x.
Proof.
  intros x h.
  destruct x; try easy.
  - rewrite B754_zero_to_real.
    replace 0 with (Z2R 0) by auto.
    rewrite Zfloor_Z2R, Zceil_Z2R; auto.
  - simpl.
    replace 0 with (Z2R 0) by auto.
    rewrite Zfloor_Z2R, Zceil_Z2R; auto.
  - set (x:=(B754_finite b m e e0)); fold x in h.
    assert (forall m', is_finite (roundToIntegral m' x)) as h1 by
          (intro m'; apply roundToIntegral_finite; easy).

    assert (is_finite x) as x_fin by easy.
    pose proof (no_overflow_to_real_min_RTN _ x_fin) as h2.
    pose proof (no_overflow_to_real_RTP_min _ x_fin) as h3.

    assert (is_finite (roundToIntegral RTN x)) as rtn_fin by auto.
    assert (is_finite (roundToIntegral RTP x)) as rtp_fin by auto.
    destruct (sub_finite _ _ _ x_fin rtn_fin h2) as (h4,h4').
    destruct (sub_finite _ _ _ rtp_fin x_fin h3) as (h5,h5').
    clear h2 h3 rtn_fin rtp_fin.

    assert (h':=h).
    rewrite to_real_eq in h; auto.
    rewrite h4' in h; auto.
    rewrite h5' in h; auto.
    clear h4' h5'.

    destruct (Rle_lt_dec 1 (to_real x));[|destruct (Rle_lt_dec (to_real x) (-1))].

    + destruct (RTN_not_far x); auto.
      destruct (RTP_not_far x); auto; try fourier.
      rewrite sterbenz_round, sterbenz_round in h by auto.
      rewrite to_real_roundToIntegral, to_real_roundToIntegral in h; auto.

    + destruct (RTN_not_far_opp x); auto; try fourier.
      destruct (RTP_not_far_opp x); auto.
      rewrite sterbenz_round_2, sterbenz_round_2 in h by auto.
      rewrite to_real_roundToIntegral, to_real_roundToIntegral in h; auto.

    + destruct (Rle_lt_dec (to_real x) 0).
      destruct r1.
      * assert (floor (to_real x) = (-1)%Z) by
            (apply Zfloor_imp; simpl Z2R; split; fourier).
        assert (ceil (to_real x) = 0%Z) by
            (apply Zceil_imp; simpl Z2R; split; fourier).

        revert h.
        rewrite (to_real_roundToIntegral RTP) by easy.
        unfold to_int.
        fold (to_real x).
        rewrite H1.
        replace (Z2R 0 - to_real x) with (- to_real x) by (simpl Z2R; ring).
        rewrite <-(proj2 (neg_finite _ x_fin)).
        rewrite (Round_to_real RNE) by easy.
        rewrite (proj2 (neg_finite _ x_fin)).

        { destruct (Rle_lt_dec (to_real x) (-/2)).
        - assert (
               round RNE (to_real x - to_real (roundToIntegral RTN x)) =
               to_real x - to_real (roundToIntegral RTN x)) as aux1.
           { rewrite Real.infix_mn_def, <-(proj2 (neg_finite _ (h1 RTN))).
             apply round_plus_weak.
             rewrite (proj2 (neg_finite _ (h1 RTN))).
             rewrite to_real_roundToIntegral; auto.
             unfold to_int.
             fold (to_real x).
             rewrite H0.
             simpl Z2R.
             rewrite Ropp_involutive.
             rewrite Rabs_right, Rabs_left, Rabs_right, Rmin_left; fourier. }
           rewrite aux1.
           now rewrite to_real_roundToIntegral.

        - clear r r0.
           rewrite to_real_roundToIntegral by easy.
           unfold to_int.
           fold (to_real x).
           rewrite H0.
           simpl Z2R.
           replace (to_real x - -(1)) with (to_real x+1) by ring.
           pose proof (Rplus_lt_compat_r 1 _ _ r1).
           apply Rlt_le in H2.
           replace (-/2+1) with (5/10) in H2 by field.
           rewrite <-half_to_real in H2.
           apply (Round_monotonic RNE) in H2.
           rewrite Round_to_real in H2 by easy.
           rewrite half_to_real in H2.
           intros.
           fourier. }
      * assert (floor (to_real x) = 0%Z) by
            (apply Zfloor_imp; simpl Z2R; split; fourier).
        assert (ceil (to_real x) = 0%Z) by
            (apply Zceil_imp; simpl Z2R; split; fourier).
        rewrite H0, H1, H; auto.

      * assert (floor (to_real x) = 0%Z) by
            (apply Zfloor_imp; simpl Z2R; split; fourier).
        assert (ceil (to_real x) = 1%Z) by
            (apply Zceil_imp; simpl Z2R; split; fourier).

        revert h.
        rewrite (to_real_roundToIntegral RTN) by easy.
        unfold to_int.
        fold (to_real x).
        rewrite H.
        replace (to_real x - Z2R 0) with (to_real x) by (simpl Z2R ; ring).
        rewrite (Round_to_real RNE) by easy.

        { destruct (Rle_lt_dec (/2) (to_real x)).
        - assert (
               round RNE (to_real (roundToIntegral RTP x) - to_real x) =
               to_real (roundToIntegral RTP x) - to_real x) as aux1.
           { rewrite Real.infix_mn_def, <-(proj2 (neg_finite _ x_fin)).
             apply round_plus_weak.
             rewrite (proj2 (neg_finite _ x_fin)).
             rewrite to_real_roundToIntegral by easy.
             unfold to_int.
             fold (to_real x).
             rewrite H0.
             simpl Z2R.
             rewrite Rabs_right, Rabs_right, Rabs_left, Rmin_right; fourier. }
           rewrite aux1.
           now rewrite to_real_roundToIntegral.

        - clear r r0.
           rewrite to_real_roundToIntegral by easy.
           unfold to_int.
           fold (to_real x).
           rewrite H0.
           pose proof (Ropp_lt_contravar _ _ r2).
           pose proof (Rplus_lt_compat_l 1 _ _ H1).
           apply Rlt_le in H2.
           replace (1+-/2) with (5/10) in H2 by field.
           rewrite <-half_to_real in H2.
           rewrite <-Real.infix_mn_def in H2.
           apply (Round_monotonic RNE) in H2.
           rewrite Round_to_real in H2 by easy.
           rewrite half_to_real in H2.
           simpl Z2R.
           intros.
           fourier. }
Qed.

(* Why3 goal *)
Lemma RNA_down_tie : forall (x:t), (eq (sub ieee_float.RoundingMode.RNE x
  (roundToIntegral ieee_float.RoundingMode.RTN x))
  (sub ieee_float.RoundingMode.RNE
  (roundToIntegral ieee_float.RoundingMode.RTP x) x)) -> ((is_negative x) ->
  ((roundToIntegral ieee_float.RoundingMode.RNA
  x) = (roundToIntegral ieee_float.RoundingMode.RTN x))).
Proof.
  intros x h h'.
  destruct x; try easy.
  set (x:=(B754_finite b m e e0)); fold x in h, h'.
  assert (forall m', is_finite (roundToIntegral m' x)) as h1 by
        (intro m'; apply roundToIntegral_finite; easy).
  apply to_real_refl; auto.
  2: apply same_sign_roundToIntegral2; easy.
  apply eq_diff_floor_ceil in h.

  rewrite to_real_roundToIntegral, to_real_roundToIntegral; try easy.
  f_equal.
  unfold to_int.
  fold (to_real x).

  destruct (is_int_or_not x).
  rewrite (int_to_real RTZ _ H).
  destruct (valid_rnd_round_mode RNA) as (_,H').
  simpl in H'.
  rewrite <-Z2R_IZR, H', Zfloor_Z2R; reflexivity.

  unfold Znearest.
  rewrite Rcompare_Eq.
  rewrite Zle_bool_false; auto.
  apply lt_Z2R.
  apply Rle_lt_trans with (r2:=to_real x).
  apply Zfloor_lb.
  simpl Z2R; rewrite <-zeroF_to_real.
  apply lt_finite; try easy.
  rewrite is_negative_correct in h'.
  destruct h'; easy.

  rewrite Zceil_floor_neq in h.
  rewrite Z2R_plus in h.
  simpl Z2R at 3 in h.
  apply (Rplus_eq_compat_l (to_real x)) in h.
  apply (Rplus_eq_compat_l (- Z2R (floor (to_real x)))) in h.
  ring_simplify in h.
  replace (-2 * Z2R (floor (to_real x))) with
  (2 * - Z2R (floor (to_real x))) in h by ring.
  rewrite <-Rmult_plus_distr_l in h.
  apply (Rmult_eq_compat_l (/2)) in h.
  field_simplify in h.
  replace (- Z2R (floor (to_real x)) + to_real x) with
  (to_real x - Z2R (floor (to_real x))) in h by ring.
  field_simplify; auto.

  unfold is_int in H.
  apply Decidable.not_and in H.
  destruct H.
  now elim H.
  now apply not_eq_sym.
  now left.
Qed.

(* Why3 goal *)
Lemma RNA_up_tie : forall (x:t), (eq (sub ieee_float.RoundingMode.RNE
  (roundToIntegral ieee_float.RoundingMode.RTP x) x)
  (sub ieee_float.RoundingMode.RNE x
  (roundToIntegral ieee_float.RoundingMode.RTN x))) -> ((is_positive x) ->
  ((roundToIntegral ieee_float.RoundingMode.RNA
  x) = (roundToIntegral ieee_float.RoundingMode.RTP x))).
Proof.
  intros x h h'.
  destruct x; try easy.
  set (x:=(B754_finite b m e e0)); fold x in h, h'.
  assert (forall m', is_finite (roundToIntegral m' x)) as h1 by
        (intro m'; apply roundToIntegral_finite; easy).
  apply to_real_refl; auto.
  2: apply same_sign_roundToIntegral2; easy.
  apply eq_sym, eq_diff_floor_ceil in h.

  rewrite to_real_roundToIntegral, to_real_roundToIntegral; try easy.
  f_equal.
  unfold to_int.
  fold (to_real x).

  destruct (is_int_or_not x).
  rewrite (int_to_real RTZ _ H).
  destruct (valid_rnd_round_mode RNA) as (_,H').
  simpl in H'.
  rewrite <-Z2R_IZR, H', Zceil_Z2R; reflexivity.

  unfold Znearest.
  rewrite Rcompare_Eq.
  rewrite Zle_bool_true; auto.
  apply non_zero_positive_to_real in h'; try easy.
  apply Zfloor_lub.
  simpl Z2R; fourier.
  unfold is_zero.
  unfold eq. simpl.
  destruct b; easy.

  rewrite Zceil_floor_neq in h.
  rewrite Z2R_plus in h.
  simpl Z2R at 3 in h.
  apply (Rplus_eq_compat_l (to_real x)) in h.
  apply (Rplus_eq_compat_l (- Z2R (floor (to_real x)))) in h.
  ring_simplify in h.
  replace (-2 * Z2R (floor (to_real x))) with
  (2 * - Z2R (floor (to_real x))) in h by ring.
  rewrite <-Rmult_plus_distr_l in h.
  apply (Rmult_eq_compat_l (/2)) in h.
  field_simplify in h.
  replace (- Z2R (floor (to_real x)) + to_real x) with
  (to_real x - Z2R (floor (to_real x))) in h by ring.
  field_simplify; auto.

  unfold is_int in H.
  apply Decidable.not_and in H.
  destruct H.
  now elim H.
  now apply not_eq_sym.
  now left.
Qed.

(* Why3 goal *)
Lemma to_int_roundToIntegral : forall (m:ieee_float.RoundingMode.mode) (x:t),
  ((to_int m x) = (to_int m (roundToIntegral m x))).
Proof.
  intros m x.
  destruct x; try easy.
  unfold to_int at 2.
  rewrite to_real_roundToIntegral by easy.
  destruct (valid_rnd_round_mode m) as (_,h).
  pose proof (h (to_int m (B754_finite b m0 e e0))).
  now destruct m.
Qed.

(* Why3 goal *)
Lemma to_int_monotonic : forall (m:ieee_float.RoundingMode.mode) (x:t) (y:t),
  (is_finite x) -> ((is_finite y) -> ((le x y) -> ((to_int m x) <= (to_int m
  y))%Z)).
Proof.
intros m x y h1 h2 h3.
now apply to_int_le.
Qed.

(* Why3 goal *)
Lemma to_int_of_int : forall (m:ieee_float.RoundingMode.mode) (i:Z),
  (in_safe_int_range i) -> ((to_int m (of_int m i)) = i).
Proof.
intros m i (h1,h2).
apply eq_IZR.
rewrite <-int_to_real.
rewrite of_int_to_real.
apply Exact_rounding_for_integers; auto.
unfold in_safe_int_range; auto.
apply Bounded_real_no_overflow, rep_int_in_range; auto.
apply of_int_is_int.
pose proof pow2sb_lt_max_int.
unfold in_int_range; auto with zarith.
Qed.

(* Why3 goal *)
Lemma eq_to_int : forall (m:ieee_float.RoundingMode.mode) (x:t) (y:t),
  (is_finite x) -> ((eq x y) -> ((to_int m x) = (to_int m y))).
Proof.
intros m x y h1 h2.
apply to_int_eq, h2.
Qed.

(* Why3 goal *)
Lemma neg_to_int : forall (m:ieee_float.RoundingMode.mode) (x:t), (is_int
  x) -> ((to_int m (neg x)) = (-(to_int m x))%Z).
Proof.
intros m x h1.
apply neg_int_to_int; auto.
Qed.

(* Why3 goal *)
Lemma roundToIntegral_is_finite : forall (m:ieee_float.RoundingMode.mode)
  (x:t), (is_finite x) -> (is_finite (roundToIntegral m x)).
Proof.
intros m x h1.
apply roundToIntegral_finite, h1.
Qed.

End GenericFloat.
