Module FloatLib


Require Import Coqlib Utf8 Util Axioms ToString.
Require Import ZArith.
Require Import Integers.
Require Import Reals.
Require Export Fappli_IEEE.
Require Export Fappli_IEEE_bits.
Require Export Fappli_IEEE_extra.
Require Export Fappli_double_round.
Require Export Fcore_FLT.
Require Export Fcore_float_prop.
Require Export Fcore_defs.
Require Export Fcore_Raux.
Require Export Fcore_Zaux.
Require Export Fcore_generic_fmt.
Require Export Fcore_rnd_ne.
Require Export Fcore_digits.
Require Export Floats.

Open Scope R.

Arguments is_finite {prec emax} f.
Arguments is_finite_strict {prec emax} f.
Arguments is_nan {prec emax} f.
Arguments B2R {prec emax} f.
Arguments Bsign {prec emax} x.
Arguments ZofB {prec emax} f.
Arguments B2FF {prec emax} x.
Arguments B754_zero {prec emax} _.
Arguments B754_infinity {prec emax} _.
Arguments B754_nan {prec emax} _ _.
Arguments B754_finite {prec emax} _ m e _.

Identity Coercion f2b64 : float >-> binary64.
Identity Coercion f2b32 : float32 >-> binary32.
Identity Coercion b642bf : binary64 >-> binary_float.
Identity Coercion b322bf : binary32 >-> binary_float.
Coercion B2R : binary_float >-> R.
Coercion Z2R : Z >-> R.
Coercion B2FF : binary_float >-> full_float.

Ltac Rlt_bool_case H :=
  match type of H with
    | context [Rlt_bool ?x ?y] =>
      destruct (Rlt_bool_spec x y)
  end.

Ltac Rle_bool_case H :=
  match type of H with
    | context [Rle_bool ?x ?y] =>
      destruct (Rle_bool_spec x y)
  end.

Ltac check_nodup H :=
  let typ := type of H in
  match goal with
    | [H':typ |- _] =>
      match H' with
        | H => fail 1
        | _ => fail 2
      end
    | _ => idtac
  end.

Ltac use_float_spec :=
  let H := fresh in
  match goal with
    | [_ : context [binary_normalize ?prec ?emax ?Hprec ?Hemax ?mode ?m ?e ?s] |- _] =>
      assert (H:=binary_normalize_correct prec emax Hprec Hemax mode m e s);
      check_nodup H
    | [|- context [binary_normalize ?prec ?emax ?Hprec ?Hemax ?mode ?m ?e ?s]] =>
      assert (H:=binary_normalize_correct prec emax Hprec Hemax mode m e s);
      check_nodup H
    | [_ : context [Bconv ?prec1 ?emax1 ?prec2 ?emax2 ?Hprec2 ?Hemax2 ?pl ?mode ?x] |- _] =>
      assert (H:=Bconv_correct prec1 emax1 prec2 emax2 Hprec2 Hemax2 pl mode x);
      check_nodup H
    | [|- context [Bconv ?prec1 ?emax1 ?prec2 ?emax2 ?Hprec2 ?Hemax2 ?pl ?mode ?x]] =>
      assert (H:=Bconv_correct prec1 emax1 prec2 emax2 Hprec2 Hemax2 pl mode x);
      check_nodup H
    | [_ : context [BofZ ?prec ?emax ?Hprec ?Hemax ?x] |- _] =>
      assert (H:=BofZ_correct prec emax Hprec Hemax x);
      check_nodup H
    | [|- context [BofZ ?prec ?emax ?Hprec ?Hemax ?x]] =>
      assert (H:=BofZ_correct prec emax Hprec Hemax x);
      check_nodup H
    | [_ : context [Bplus ?prec ?emax ?Hprec ?Hemax ?pl ?mode ?x ?y] |- _] =>
      assert (H:=Bplus_correct prec emax Hprec Hemax pl mode x y);
      check_nodup H
    | [|-context [Bplus ?prec ?emax ?Hprec ?Hemax ?pl ?mode ?x ?y]] =>
      assert (H:=Bplus_correct prec emax Hprec Hemax pl mode x y);
      check_nodup H
    | [_ : context [Bminus ?prec ?emax ?Hprec ?Hemax ?pl ?mode ?x ?y] |- _] =>
      assert (H:=Bminus_correct prec emax Hprec Hemax pl mode x y);
      check_nodup H
    | [|-context [Bminus ?prec ?emax ?Hprec ?Hemax ?pl ?mode ?x ?y]] =>
      assert (H:=Bminus_correct prec emax Hprec Hemax pl mode x y);
      check_nodup H
    | [_ : context [Bmult ?prec ?emax ?Hprec ?Hemax ?pl ?mode ?x ?y] |- _] =>
      assert (H:=Bmult_correct prec emax Hprec Hemax pl mode x y);
      check_nodup H
    | [|-context [Bmult ?prec ?emax ?Hprec ?Hemax ?pl ?mode ?x ?y]] =>
      assert (H:=Bmult_correct prec emax Hprec Hemax pl mode x y);
      check_nodup H
    | [_ : context [Bdiv ?prec ?emax ?Hprec ?Hemax ?pl ?mode ?x ?y] |- _] =>
      assert (H:=Bdiv_correct prec emax Hprec Hemax pl mode x y);
      check_nodup H
    | [|-context [Bdiv ?prec ?emax ?Hprec ?Hemax ?pl ?mode ?x ?y]] =>
      assert (H:=Bdiv_correct prec emax Hprec Hemax pl mode x y);
      check_nodup H
  end.

Ltac use_float_specs := repeat use_float_spec.

Transparent Float32.add Float.add Float32.sub Float.sub Float32.mul Float.mul
            Float32.div Float.div Float.to_single Float.of_single Float.to_int
            Float32.to_int Float.to_intu Float32.to_intu Float.to_long
            Float.to_longu Float32.to_long Float32.to_longu
            Float.neg Float.cmp.
Transparent Float.transform_quiet_pl Float.reduce_pl Float.to_single_pl.
Transparent Archi.choose_binop_pl_32 Archi.choose_binop_pl_64
            Archi.default_pl_32 Archi.default_pl_64
            Archi.float_of_single_preserves_sNaN.

Lemma prec53_gt_0: Fcore_FLX.Prec_gt_0 53.
Proof.
exact eq_refl. Qed.

Hint Resolve fexp_correct valid_rnd_round_mode prec53_gt_0.

Lemma Bsign_false_ge_0:
  ∀ prec emax (f:binary_float prec emax), Bsign f = false -> 0 <= f.
Proof.
  destruct f as [[]|[]|[]|[]]; try discriminate; intros _; try apply Rle_refl.
  apply F2R_ge_0_compat. discriminate.
Qed.

Lemma Bsign_true_le_0:
    ∀ prec emax (f:binary_float prec emax), Bsign f = true -> f <= 0.
Proof.
  destruct f as [[]|[]|[]|[]]; try discriminate; intros _; try apply Rle_refl.
  apply F2R_le_0_compat. discriminate.
Qed.

Definition Fleb {prec emax} f1 f2 :=
  match Bcompare prec emax f1 f2 with
  | Some (Eq|Lt) => true
  | Some Gt | None => false
  end.

Lemma Fleb_Rle:
  ∀ prec emax (f1 f2:binary_float prec emax),
    is_finite f1 = true -> is_finite f2 = true ->
    Fleb f1 f2 = Rle_bool f1 f2.
Proof.
  intros. unfold Fleb.
  rewrite Bcompare_correct by auto. auto.
Qed.

Lemma Bcompare_nan:
  ∀ prec emax f1 f2,
    is_nan f1 = false -> is_nan f2 = false ->
    Bcompare prec emax f1 f2 <> None.
Proof.
  intros. intro.
  destruct f1 as [|[]| |[]], f2 as [|[]| |[]]; try discriminate.
  - simpl in H1. destruct Zcompare; discriminate.
  - simpl in H1. destruct Zcompare; discriminate.
Qed.

Lemma Fleb_total:
  ∀ prec emax (f1 f2:binary_float prec emax),
    is_nan f1 = false -> is_nan f2 = false ->
    Fleb f1 f2 = true \/ Fleb f2 f1 = true.
Proof.
  unfold Fleb.
  intros. rewrite Bcompare_swap. pose proof Bcompare_nan _ _ _ _ H0 H.
  destruct Bcompare as [[]|]; auto.
Qed.

Lemma Bcompare_refl:
  ∀ prec emax f,
    is_nan f = false ->
    Bcompare prec emax f f = Some Eq.
Proof.
  intros.
  pose proof (Bcompare_nan _ _ _ _ H H).
  pose proof (Bcompare_swap _ _ f f).
  destruct Bcompare as [[]|]; simpl in H1; congruence.
Qed.

Lemma Fleb_refl:
  ∀ prec emax (f:binary_float prec emax), is_nan f = false -> Fleb f f = true.
Proof.
  intros. unfold Fleb. rewrite Bcompare_refl; auto.
Qed.

Hint Resolve Fleb_refl.

Lemma Bcompare_trans:
  ∀ prec emax f1 f2 f3 c,
    is_nan f2 = false ->
    Bcompare prec emax f1 f2 = c ->
    Bcompare prec emax f2 f3 = c ->
    Bcompare prec emax f1 f3 = c.
Proof.
  unfold Bcompare. intros.
  destruct f1 as [|[]| |[]], f2 as [|[]| |[]], f3 as [|[]| |[]];
    simpl in H; try congruence;
  destruct (Z.compare_spec e e1), (Z.compare_spec e1 e3), (Z.compare_spec e e3);
  subst; try omega; try congruence;
  rewrite !Pos.compare_cont_spec in *;
  destruct (Pos.compare_spec m0 m1), (Pos.compare_spec m m0), (Pos.compare_spec m m1);
  simpl in H1; try congruence; Psatz.lia.
Qed.

Lemma Bcompare_Eq_trans:
  ∀ prec emax f1 f2 f3 c,
    Bcompare prec emax f1 f2 = Some Eq ->
    Bcompare prec emax f2 f3 = c ->
    Bcompare prec emax f1 f3 = c.
Proof.
  unfold Bcompare. intros.
  destruct f1 as [|[]| |[]], f2 as [|[]| |[]], f3 as [|[]| |[]];
    try congruence;
  destruct (Z.compare_spec e e1), (Z.compare_spec e1 e3), (Z.compare_spec e e3);
  subst; try omega; try congruence;
  rewrite !Pos.compare_cont_spec in *;
  destruct (Pos.compare_spec m0 m1), (Pos.compare_spec m m0), (Pos.compare_spec m m1);
  simpl in H; try congruence; Psatz.lia.
Qed.

Lemma Bcompare_trans_Eq:
  ∀ prec emax f1 f2 f3 c,
    Bcompare prec emax f1 f2 = c ->
    Bcompare prec emax f2 f3 = Some Eq ->
    Bcompare prec emax f1 f3 = c.
Proof.
  unfold Bcompare. intros.
  destruct f1 as [|[]| |[]], f2 as [|[]| |[]], f3 as [|[]| |[]];
    try congruence;
  destruct (Z.compare_spec e e1), (Z.compare_spec e1 e3), (Z.compare_spec e e3);
  subst; try omega; try congruence;
  rewrite !Pos.compare_cont_spec in *;
  destruct (Pos.compare_spec m0 m1), (Pos.compare_spec m m0), (Pos.compare_spec m m1);
  simpl in H0; try congruence; Psatz.lia.
Qed.

Lemma Fleb_nan_l:
  ∀ prec emax (f1 f2:binary_float prec emax),
    Fleb f1 f2 = true -> is_nan f1 = false.
Proof.
  intros. unfold Fleb in H. destruct f1, f2; simpl in *; congruence.
Qed.

Hint Resolve Fleb_nan_l.

Lemma Fleb_nan_r:
  ∀ prec emax (f1 f2:binary_float prec emax),
    Fleb f1 f2 = true -> is_nan f2 = false.
Proof.
  intros. unfold Fleb in H. destruct f1 as [|[]| |[]], f2; simpl in *; congruence.
Qed.

Hint Resolve Fleb_nan_r.

Lemma Fleb_trans:
  ∀ prec emax (f1 f2 f3:binary_float prec emax),
    Fleb f1 f2 = true ->
    Fleb f2 f3 = true ->
    Fleb f1 f3 = true.
Proof.
  intros. assert (is_nan f2 = false) by eauto. unfold Fleb in *.
  pose proof (Bcompare_trans _ _ f1 f2 f3).
  pose proof (Bcompare_trans_Eq _ _ f1 f2 f3).
  pose proof (Bcompare_Eq_trans _ _ f1 f2 f3).
  destruct Bcompare as [[]|], Bcompare as [[]|], Bcompare as [[]|]; try reflexivity;
  (specialize (H2 _ H1 eq_refl eq_refl) || clear H2);
  (specialize (H3 _ eq_refl eq_refl) || clear H3);
  (specialize (H4 _ eq_refl eq_refl) || clear H4);
  discriminate.
Qed.

Lemma fneg_decr:
  forall a b, Fleb (Float.neg b) (Float.neg a) = Fleb a b.
Proof.
  intros.
  destruct a as [|[]| |[]], b as [|[]| |[]]; try reflexivity;
  unfold Fleb; simpl;
  rewrite Z.compare_antisym, Pos.compare_cont_antisym;
  destruct Z.compare; reflexivity.
Qed.

Lemma binary_normalize_is_nan:
  forall prec emax H1 H2 m e s mode,
    is_nan (binary_normalize prec emax H1 H2 mode m e s) = false.
Proof.
  intros. use_float_spec.
  destruct Rlt_bool.
  destruct H as [? []]. destruct binary_normalize; try discriminate; reflexivity.
  destruct mode; destruct binary_normalize; try reflexivity; destruct n; try discriminate;
    destruct Rlt_bool; discriminate.
Qed.

Lemma floatofsingle_exact:
  forall f, (Float.of_single f:R) = f
            /\ (is_finite f = true -> is_finite (Float.of_single f) = true).
Proof.
  destruct f; try (split; try discriminate; reflexivity). unfold Float.of_single.
  use_float_specs.
  rewrite round_generic in H. 2:auto.
  rewrite Rlt_bool_true in H. now intuition.
  eapply Rlt_le_trans, bpow_le. apply abs_B2R_lt_emax. omega.
  eapply generic_inclusion_ln_beta, generic_format_B2R with (x:=B754_finite _ _ _ e0).
  unfold FLT_exp. zify; omega.
Qed.

Lemma floatofsingle_nan:
  ∀ f, is_nan f = false ->
       is_nan (Float.of_single f) = false.
Proof.
  destruct f; try reflexivity; try discriminate.
  intros. apply binary_normalize_is_nan.
Qed.

Lemma zoffloat_incr:
  forall prec emax (f1 f2:binary_float prec emax) z1 z2,
    Fleb f1 f2 = true ->
    ZofB f1 = Some z1 ->
    ZofB f2 = Some z2 ->
    (z1 <= z2)%Z.
Proof.
  intros.
  rewrite ZofB_correct in *.
  destruct (is_finite f1) eqn:FIN1; inv H0.
  destruct (is_finite f2) eqn:FIN2; inv H1.
  rewrite Fleb_Rle in H by auto. Rle_bool_case H; [|discriminate].
  apply Ztrunc_le, H0.
Qed.

Definition Zoffloat_DN (f:float): option Z :=
  match f with
  | B754_finite s m (Zpos e) _ => Some (cond_Zopp s (Zpos m) * Zpower_pos radix2 e)%Z
  | B754_finite s m 0 _ => Some (cond_Zopp s (Zpos m))
  | B754_finite s m (Zneg e) _ => Some (cond_Zopp s (Zpos m) / Zpower_pos radix2 e)%Z
  | B754_zero _ => Some 0%Z
  | _ => None
  end.

Lemma Zoffloat_DN_correct:
  forall f : float,
    match Zoffloat_DN f with
    | Some n =>
      is_finite f = true /\ (n:R) = round radix2 (Fcore_FIX.FIX_exp 0) (round_mode mode_DN) f
    | None => is_finite f = false
    end.
Proof.
  destruct f; try now intuition.
  - rewrite round_0. easy. auto.
  - destruct e.
    + split. reflexivity.
      rewrite round_generic. symmetry. now apply Rmult_1_r.
      auto.
      apply Fcore_FIX.generic_format_FIX.
      exists (Float radix2 (cond_Zopp b (Zpos m)) 0). split; reflexivity.
    + split; [reflexivity|].
      rewrite round_generic, Z2R_mult, Z2R_Zpower_pos, <- bpow_powerRZ;
        try easy. apply generic_format_F2R; discriminate.
    + split; [reflexivity|].
      rewrite (Fcalc_round.inbetween_float_DN _ _ _ (cond_Zopp b (Zpos m) / Z.pow_pos radix2 p)
                (Fcalc_bracket.new_location (Zpower_pos radix2 p) (cond_Zopp b (Zpos m) mod Zpower_pos radix2 p) Fcalc_bracket.loc_Exact)).
      * symmetry. apply Rmult_1_r.
      * unfold canonic_exp, Fcore_FIX.FIX_exp; replace Z0 with (Zneg p + Zpos p)%Z by apply Zplus_opp_r.
        apply (Fcalc_bracket.inbetween_float_new_location radix2 _ _ _ _ (Zpos p)); [reflexivity|].
        apply Fcalc_bracket.inbetween_Exact. reflexivity.
Qed.

Lemma fadd_commut:
  forall a b pl,
    Bplus 53 1024 (@eq_refl _ Lt) (@eq_refl _ Lt) pl mode_NE a b =
    Bplus 53 1024 (@eq_refl _ Lt) (@eq_refl _ Lt) (fun a b => pl b a) mode_NE b a.
Proof.
  intros.
  destruct a as [[]|[]| |], b as [[]|[]| |]; try reflexivity.
  simpl.
  rewrite Zplus_comm, Zmin_comm. reflexivity.
Qed.

Lemma Bconv_Bopp:
  ∀ prec emax
    prec' emax' Hprec' Hemax'
    conv_pl opp_pl opp_pl'
    x,
    (∀ s pl, (let '(s, pl) := opp_pl s pl in conv_pl s pl) =
             (let '(s, pl) := conv_pl s pl in opp_pl' s pl)) ->
    Bopp prec' emax' opp_pl' (Bconv prec emax prec' emax' Hprec' Hemax' conv_pl mode_NE x) =
    Bconv prec emax prec' emax' Hprec' Hemax' conv_pl mode_NE (Bopp prec emax opp_pl x).
Proof.
  intros.
  use_float_specs.
  rewrite B2R_Bopp, round_NE_opp, Rabs_Ropp in H1.
  destruct x; try reflexivity; intros.
  unfold Bopp, Bconv.
  specialize (H b n). destruct (conv_pl b n), (opp_pl b n). rewrite H. auto.
  assert (∀ prec emax (x:binary_float prec emax),
            is_finite x = true -> (x:R) <> 0 -> is_finite_strict x = true).
  { destruct x; try discriminate; try reflexivity.
    destruct 2. auto. }
  specialize (H0 eq_refl). specialize (H1 eq_refl).
  change ZnearestE with (round_mode mode_NE) in H1.
  Rlt_bool_case H1.
  - destruct H0 as (? & ? & ?). destruct H1 as (? & ? & ?).
    apply B2R_Bsign_inj.
    rewrite is_finite_Bopp. auto.
    auto.
    rewrite B2R_Bopp, H0, H1. auto.
    rewrite H7. clear - H5 H4. destruct Bconv; simpl in H5; subst b; auto. discriminate.
  - apply B2FF_inj with (y:=B754_infinity _) in H0.
    apply B2FF_inj with (y:=B754_infinity _) in H1.
    rewrite H0, H1. auto.
Qed.

Lemma Bconv_Babs:
  ∀ prec emax
    prec' emax' Hprec' Hemax'
    conv_pl abs_pl abs_pl'
    x,
    (∀ s pl, (let '(s, pl) := abs_pl s pl in conv_pl s pl) =
             (let '(s, pl) := conv_pl s pl in abs_pl' s pl)) ->
    Babs prec' emax' abs_pl' (Bconv prec emax prec' emax' Hprec' Hemax' conv_pl mode_NE x) =
    Bconv prec emax prec' emax' Hprec' Hemax' conv_pl mode_NE (Babs prec emax abs_pl x).
Proof.
  intros.
  use_float_specs.
  rewrite B2R_Babs, round_NE_abs, Rabs_Rabsolu in H1 by auto.
  destruct x; try reflexivity; intros.
  unfold Babs, Bconv.
  specialize (H b n). destruct (conv_pl b n), (abs_pl b n). rewrite H. auto.
  assert (∀ prec emax (x:binary_float prec emax),
             is_finite x = true -> (x:R) <> 0 -> is_finite_strict x = true).
  { destruct x; try discriminate; try reflexivity.
    destruct 2. auto. }
  specialize (H0 eq_refl). specialize (H1 eq_refl).
  change ZnearestE with (round_mode mode_NE) in H1.
  Rlt_bool_case H1.
  - destruct H0 as (? & ? & ?). destruct H1 as (? & ? & ?).
    apply B2R_Bsign_inj.
    rewrite is_finite_Babs. auto.
    auto.
    rewrite B2R_Babs, H0, H1. auto.
    rewrite H7, !Bsign_Babs. auto. auto. destruct Bconv; try reflexivity; discriminate.
  - apply B2FF_inj with (y:=B754_infinity _) in H0.
    apply B2FF_inj with (y:=B754_infinity _) in H1.
    rewrite H0, H1. auto.
Qed.

Lemma Bconv_Bplus:
  ∀ prec emax Hprec Hemax
    prec' emax' Hprec' Hemax'
    conv_pl conv_pl' plus_pl plus_pl'
    x y,
    (2*prec + 1 <= prec')%Z -> (emax < emax')%Z ->

    is_nan x = false -> is_nan y = false ->

    (∀ x y, is_nan x = false -> is_nan y = false ->
     ∀ x' y', is_nan x' = false -> is_nan y' = false ->
     (let '(b, pl) := plus_pl' x' y' in conv_pl b pl) = plus_pl x y) ->

    Bplus prec emax Hprec Hemax plus_pl mode_NE x y =
    Bconv prec' emax' prec emax Hprec Hemax conv_pl mode_NE
      (Bplus prec' emax' Hprec' Hemax' plus_pl' mode_NE
        (Bconv prec emax prec' emax' Hprec' Hemax' conv_pl' mode_NE x)
        (Bconv prec emax prec' emax' Hprec' Hemax' conv_pl' mode_NE y)).
Proof.
  intros * prec_le emax_le NANx NANy NAN. unfold Fcore_FLX.Prec_gt_0 in *.
  destruct (is_finite x) eqn:FINx;
    [assert (Hx:=FINx);
     eapply (Bconv_widen_exact prec emax prec' emax' Hprec Hprec' Hemax')
     with (conv_nan:=conv_pl') (m:=mode_NE) in Hx; eauto; try omega|];
  (destruct (is_finite y) eqn:FINy;
    [assert (Hy:=FINy);
     eapply (Bconv_widen_exact prec emax prec' emax' Hprec Hprec' Hemax')
     with (conv_nan:=conv_pl') (m:=mode_NE) in Hy; eauto; try omega|]).
  - use_float_specs. clear H H0.
    destruct Hx as (? & ? & ?), Hy as (? & ? & ?).
    rewrite H, H4, H5, H7, Rlt_bool_true in H2.
    + destruct H2 as (? & ? & ?); auto.
      assert (round radix2 (FLT_exp (3-emax-prec) prec) (round_mode mode_NE)
                    (round radix2 (FLT_exp (3-emax'-prec') prec')
                           (round_mode mode_NE) (x + y)) =
              round radix2 (FLT_exp (3-emax-prec) prec)
                    (round_mode mode_NE) (x + y)).
      { apply double_round_plus_FLT; auto. omega.
        apply FLT_format_B2R. auto. apply FLT_format_B2R. auto. }
      rewrite H2, H10, H9 in H1.
      Rlt_bool_case H1.
      * destruct H1 as (? & ? & ?), H3 as (? & ? & ?); auto.
        apply B2R_Bsign_inj; auto; congruence.
      * apply B2FF_inj. destruct H3; auto. rewrite H1, H3; auto. f_equal.
        replace 0 with (0+0) by ring.
        destruct x, y; try discriminate; simpl in H12; subst b.
        simpl. rewrite Rcompare_Eq by auto. destruct b0; auto.
        destruct b0; [rewrite Rcompare_Lt|rewrite Rcompare_Gt]; auto; apply Rplus_lt_compat;
        (apply F2R_lt_0_compat || apply F2R_gt_0_compat); reflexivity.
    + assert (∀ (x:binary_float prec emax), Rabs x <=
                   Fcore_ulp.pred radix2 (FLT_exp (3 - emax - prec) prec) (bpow radix2 emax)).
      { intros.
        apply Fcore_ulp.le_pred_lt. now auto.
        apply generic_format_abs, generic_format_B2R.
        apply generic_format_bpow. unfold FLT_exp. zify; omega.
        apply abs_B2R_lt_emax. }
      pose proof (H8 x). specialize (H8 y).
      eapply Rplus_le_compat in H8. 2:apply H9. clear H9.
      eapply Rle_trans in H8. 2:apply Rabs_triang.
      rewrite <- round_NE_abs by auto.
      eapply Rle_lt_trans. apply round_le, H8. auto. apply (valid_rnd_round_mode mode_NE).
      rewrite <- double.
      unfold Fcore_ulp.pred, Fcore_ulp.succ, Fcore_ulp.pred_pos.
      rewrite Rle_bool_false, !Ropp_involutive by (apply Ropp_lt_gt_0_contravar, Rlt_gt, bpow_gt_0).
      rewrite ln_beta_bpow, Z.add_simpl_r, Req_bool_true, Rmult_minus_distr_l by auto.
      replace (FLT_exp (3 - emax - prec) prec emax) with (emax - prec)%Z
        by (unfold FLT_exp; zify; omega).
      rewrite round_generic. 2:apply (valid_rnd_round_mode mode_NE).
      * assert (1+emax <= emax')%Z by omega.
        apply bpow_le with (r:=radix2) in H9.
        rewrite bpow_plus in H9. simpl in H9.
        pose proof (bpow_gt_0 radix2 (emax - prec)). Psatz.lra.
      * apply generic_format_FLT.
        exists {| Fnum := radix2^prec - 1; Fexp := emax - prec + 1 |}.
        { split; [|split].
          - unfold F2R, Fnum, Fexp.
            rewrite Z2R_minus, Z2R_Zpower, bpow_plus,
                    Rmult_minus_distr_r, <- Rmult_assoc, <- bpow_plus,
                    Zplus_minus by omega.
            simpl. ring.
          - unfold Fnum.
            assert (0 < radix2 ^ prec)%Z by (apply Zpower_gt_0; omega).
            assert (prec <= prec')%Z by omega. apply Zpower_le with (r:=radix2) in H10.
            zify; omega.
          - unfold Fexp. omega. }
  - destruct Hx as (? & ? & ?).
    destruct x, y; try discriminate.
    + auto.
    + destruct Bconv; try discriminate; auto.
  - destruct Hy as (? & ? & ?).
    destruct x, y; try discriminate.
    + auto.
    + destruct Bconv; try discriminate; auto.
  - destruct x, y; try discriminate.
    simpl. destruct (eqb b b0). auto. simpl.
    specialize (NAN (B754_infinity b) (B754_infinity b0) eq_refl eq_refl
                    (B754_infinity b) (B754_infinity b0) eq_refl eq_refl).
    rewrite <- NAN.
    destruct plus_pl'. simpl. destruct conv_pl. auto.
Qed.

Lemma Bconv_Bmult:
  ∀ prec emax Hprec Hemax
    prec' emax' Hprec' Hemax'
    conv_pl conv_pl' mult_pl mult_pl'
    x y,
    (2*prec <= prec')%Z -> (2*emax <= emax')%Z ->

    is_nan x = false -> is_nan y = false ->

    (∀ x y, is_nan x = false -> is_nan y = false ->
     ∀ x' y', is_nan x' = false -> is_nan y' = false ->
     (let '(b, pl) := mult_pl' x' y' in conv_pl b pl) = mult_pl x y) ->

    Bmult prec emax Hprec Hemax mult_pl mode_NE x y =
    Bconv prec' emax' prec emax Hprec Hemax conv_pl mode_NE
      (Bmult prec' emax' Hprec' Hemax' mult_pl' mode_NE
        (Bconv prec emax prec' emax' Hprec' Hemax' conv_pl' mode_NE x)
        (Bconv prec emax prec' emax' Hprec' Hemax' conv_pl' mode_NE y)).
Proof.
  intros * prec_le emax_le NANx NANy NAN. unfold Fcore_FLX.Prec_gt_0 in *.
  destruct (is_finite x) eqn:FINx;
    [assert (Hx:=FINx);
     eapply (Bconv_widen_exact prec emax prec' emax' Hprec Hprec' Hemax')
     with (conv_nan:=conv_pl') (m:=mode_NE) in Hx; eauto; try omega|];
  (destruct (is_finite y) eqn:FINy;
    [assert (Hy:=FINy);
     eapply (Bconv_widen_exact prec emax prec' emax' Hprec Hprec' Hemax')
     with (conv_nan:=conv_pl') (m:=mode_NE) in Hy; eauto; try omega|]).
  - use_float_specs. clear H H0.
    destruct Hx as (? & ? & ?), Hy as (? & ? & ?).
    rewrite H, H4, H5, H7, Rlt_bool_true in H2.
    + destruct H2 as (? & ? & ?); auto.
      assert (round radix2 (FLT_exp (3-emax-prec) prec) (round_mode mode_NE)
                    (round radix2 (FLT_exp (3-emax'-prec') prec') (round_mode mode_NE) (x * y)) =
              round radix2 (FLT_exp (3-emax-prec) prec) (round_mode mode_NE) (x * y)).
      { apply double_round_mult_FLT; auto. omega.
        apply FLT_format_B2R. auto. apply FLT_format_B2R. auto. }
      rewrite H2, H10, H9 in H1. 2:apply is_finite_not_is_nan; rewrite H8, H0, H6; auto.
      Rlt_bool_case H1.
      * destruct H1 as (? & ? & ?), H3 as (? & ? & ?). rewrite H8, H0, H6. auto.
        apply B2R_Bsign_inj. rewrite H14, FINx, FINy. auto. apply H12.
        congruence.
        rewrite H15, H13. auto.
        apply is_finite_not_is_nan. rewrite H14, FINx, FINy. auto.
      * apply B2FF_inj. rewrite H1, H3. auto.
        rewrite H8, H0, H6. auto.
    + rewrite round_generic, Rabs_mult.
      eapply Rlt_le_trans.
      apply Rmult_le_0_lt_compat. apply Rabs_pos. apply Rabs_pos.
      apply abs_B2R_lt_emax. apply abs_B2R_lt_emax.
      rewrite <- bpow_plus. apply bpow_le. omega.
      apply (valid_rnd_round_mode mode_NE).
      eapply double_round_mult_aux; try apply generic_format_B2R.
      split; intros; unfold FLT_exp; zify; omega.
  - destruct Hx as (? & ? & ?).
    destruct x, y; try discriminate.
    + specialize (NAN (B754_zero b) (B754_infinity b0) eq_refl eq_refl
                      (B754_zero b) (B754_infinity b0) eq_refl eq_refl).
      simpl.
      destruct mult_pl', mult_pl. simpl. rewrite NAN. auto.
    + destruct Bconv; try discriminate; auto.
      symmetry in H. apply F2R_eq_0_reg in H. destruct b; discriminate.
      simpl in H1. subst b1. auto.
  - destruct Hy as (? & ? & ?).
    destruct x, y; try discriminate.
    + specialize (NAN (B754_infinity b) (B754_zero b0) eq_refl eq_refl
                      (B754_infinity b) (B754_zero b0) eq_refl eq_refl).
      simpl.
      destruct mult_pl', mult_pl. simpl. rewrite NAN. auto.
    + destruct Bconv; try discriminate; auto.
      symmetry in H. apply F2R_eq_0_reg in H. destruct b0; discriminate.
      simpl in H1. subst b1. auto.
  - destruct x, y; try discriminate. auto.
Qed.

Lemma Bconv_Bdiv:
  ∀ prec emax Hprec Hemax
    prec' emax' Hprec' Hemax'
    conv_pl conv_pl' div_pl div_pl'
    x y,
    (2 * prec <= prec')%Z -> (2 + emax + 2 * prec <= emax' + prec')%Z ->
    (-3 + 2 * emax + prec <= emax')%Z ->

    is_nan x = false -> is_nan y = false ->

    (∀ x y, is_nan x = false -> is_nan y = false ->
     ∀ x' y', is_nan x' = false -> is_nan y' = false ->
     (let '(b, pl) := div_pl' x' y' in conv_pl b pl) = div_pl x y) ->

    Bdiv prec emax Hprec Hemax div_pl mode_NE x y =
    Bconv prec' emax' prec emax Hprec Hemax conv_pl mode_NE
      (Bdiv prec' emax' Hprec' Hemax' div_pl' mode_NE
        (Bconv prec emax prec' emax' Hprec' Hemax' conv_pl' mode_NE x)
        (Bconv prec emax prec' emax' Hprec' Hemax' conv_pl' mode_NE y)).
Proof.
  intros * prec_le le1 le2 NANx NANy NAN. unfold Fcore_FLX.Prec_gt_0 in *.
  destruct (is_finite x) eqn:FINx;
    [assert (Hx:=FINx);
     eapply (Bconv_widen_exact prec emax prec' emax' Hprec Hprec' Hemax')
     with (conv_nan:=conv_pl') (m:=mode_NE) in Hx; eauto; try omega|];
  (destruct (is_finite y) eqn:FINy;
    [assert (Hy:=FINy);
     eapply (Bconv_widen_exact prec emax prec' emax' Hprec Hprec' Hemax')
     with (conv_nan:=conv_pl') (m:=mode_NE) in Hy; eauto; try omega|]).
  destruct (Req_EM_T y 0).
  - destruct y; try discriminate.
    + destruct x; try discriminate.
      specialize (NAN (B754_zero b0) (B754_zero b) eq_refl eq_refl
                      (B754_zero b0) (B754_zero b) eq_refl eq_refl).
      simpl. rewrite <- NAN. destruct div_pl'. simpl. destruct conv_pl. auto.
      destruct Hx as (? & ? & ?). destruct Bconv; try discriminate.
      symmetry in H. apply F2R_eq_0_reg in H. destruct b0; discriminate.
      simpl in H1. subst b1. auto.
    + apply F2R_eq_0_reg in e. destruct b; discriminate.
  - use_float_specs. clear H H0.
    destruct Hx as (? & ? & ?), Hy as (? & ? & ?).
    rewrite H, H4, H5, H7, Rlt_bool_true in H2.
    + destruct H2 as (? & ? & ?); auto.
      assert (round radix2 (FLT_exp (3-emax-prec) prec) (round_mode mode_NE)
                    (round radix2 (FLT_exp (3-emax'-prec') prec') (round_mode mode_NE) (x / y)) =
              round radix2 (FLT_exp (3-emax-prec) prec) (round_mode mode_NE) (x / y)).
      { apply double_round_div_FLT; auto. exists 1%Z. auto. omega.
        apply FLT_format_B2R. auto. apply FLT_format_B2R. auto. }
      rewrite H2, H10, H9 in H1. 2:apply is_finite_not_is_nan; rewrite H8; auto.
      Rlt_bool_case H1.
      * destruct H1 as (? & ? & ?), H3 as (? & ? & ?); auto. rewrite H8; auto.
        apply B2R_Bsign_inj. rewrite H14; auto. apply H12.
        congruence.
        rewrite H15, H13. auto.
        apply is_finite_not_is_nan. rewrite H14; auto.
      * apply B2FF_inj. rewrite H1, H3. auto. auto.
        rewrite H8; auto.
    + rewrite <- round_NE_abs by auto.
      unfold Rdiv. rewrite Rabs_mult, Rabs_Rinv by auto.
      assert (bpow radix2 (3-emax-prec) <= Rabs y).
      { revert n.
        edestruct FLT_format_B2R with (x:=y) as ([] & -> & ? & ?). auto.
        rewrite <- F2R_Zabs. unfold F2R, Fcore_defs.Fnum, Fcore_defs.Fexp in *.
        intro.
        assert (Fnum <> 0%Z).
        { contradict n. subst. simpl. ring. }
        rewrite <- Rmult_1_l at 1.
        apply Rmult_le_compat. Psatz.lra. apply bpow_ge_0.
        apply Z2R_le with (m:=1%Z). zify; omega.
        apply bpow_le. auto. }
      apply Rinv_le in H8. 2:apply bpow_gt_0. rewrite <- bpow_opp in H8.
      assert (Rabs x <= Fcore_ulp.pred radix2 (FLT_exp (3 - emax - prec) prec) (bpow radix2 emax)).
      { intros.
        apply Fcore_ulp.le_pred_lt. auto.
        apply generic_format_abs, generic_format_B2R.
        apply generic_format_bpow. unfold FLT_exp. zify; omega.
        apply abs_B2R_lt_emax. }
      eapply Rle_lt_trans. apply round_le. auto. apply (valid_rnd_round_mode mode_NE).
      apply Rmult_le_compat.
      apply Rabs_pos.
      apply Rlt_le, Rinv_0_lt_compat.
      destruct (Rabs_pos y). auto. apply Rabs_no_R0 in n. destruct n. auto.
      apply H9. apply H8.
      unfold Fcore_ulp.pred, Fcore_ulp.succ, Fcore_ulp.pred_pos.
      rewrite Rle_bool_false, !Ropp_involutive by (apply Ropp_lt_gt_0_contravar, Rlt_gt, bpow_gt_0).
      rewrite ln_beta_bpow, Z.add_simpl_r, Req_bool_true, Rmult_minus_distr_r,
              <- !bpow_plus by auto.
      replace (FLT_exp (3 - emax - prec) prec emax) with (emax-prec)%Z
        by (unfold FLT_exp; zify; omega).
      rewrite round_generic.
      assert (0 < bpow radix2 (emax - prec + - (3 - emax - prec)))
        by apply bpow_gt_0.
      assert (emax + - (3 - emax - prec) <= emax')%Z by omega.
      apply bpow_le with (r:=radix2) in H11. Psatz.lra.
      apply (valid_rnd_round_mode mode_NE).
      apply generic_format_FLT.
      exists {| Fnum := radix2^prec - 1; Fexp := emax - prec + - (3 - emax - prec) |}.
      { split; [|split].
        - unfold F2R, Fnum, Fexp.
          rewrite Z2R_minus, Z2R_Zpower, Rmult_minus_distr_r.
          f_equal. rewrite <- bpow_plus. f_equal. ring.
          unfold Z2R, P2R. ring. omega.
        - unfold Fnum.
          assert (0 < radix2 ^ prec)%Z by (apply Zpower_gt_0; omega).
          assert (prec <= prec')%Z by omega. apply Zpower_le with (r:=radix2) in H11.
          zify; omega.
        - unfold Fexp. omega. }
  - destruct Hx as (? & ? & ?).
    destruct x, y; try discriminate.
    + auto.
    + destruct Bconv; try discriminate; simpl in H1; subst b1; auto.
  - destruct Hy as (? & ? & ?).
    destruct x, y; try discriminate.
    + auto.
    + destruct Bconv; try discriminate; simpl in H1; subst b1; auto.
  - destruct x, y; try discriminate. simpl.
    specialize (NAN (B754_infinity b) (B754_infinity b0) eq_refl eq_refl
                    (B754_infinity b) (B754_infinity b0) eq_refl eq_refl).
    rewrite <- NAN. destruct div_pl'. simpl. destruct conv_pl. auto.
Qed.

Lemma transform_quiet_32_64:
  ∀ n,
    Float32.transform_quiet_pl n =
    Float.reduce_pl (Float.transform_quiet_pl (Float.expand_pl n)).
Proof.
  intros.
  unfold Float32.transform_quiet_pl, Float.transform_quiet_pl, Float.reduce_pl, Float.expand_pl.
  f_equal.
  assert (∀ (b:bool) (e:b=b), e = eq_refl).
  { destruct b; intros;
    exact (match e with eq_refl => eq_refl end). }
  assert (∀ (a b:bool) (e1 e2:a=b), e1 = e2).
  { intros. destruct a, b; try discriminate; rewrite H; apply H. }
  apply H0.
Qed.

Lemma plus_double_round:
  ∀ (x y:float32),
    Float32.add x y = Float.to_single (Float.add (Float.of_single x) (Float.of_single y)).
Proof.
  intros.
  destruct (is_nan x) eqn:NANx, (is_nan y) eqn:NANy.
  - destruct x, y; try discriminate. simpl.
    rewrite 2!Float.transform_quiet_pl_idempotent.
    f_equal. apply transform_quiet_32_64.
  - pose proof (floatofsingle_nan _ NANy).
    destruct x; try discriminate; simpl;
    destruct (Float.of_single y), y; try discriminate; simpl;
    rewrite !Float.transform_quiet_pl_idempotent; f_equal;
    apply transform_quiet_32_64.
  - pose proof (floatofsingle_nan _ NANx).
    destruct y; try discriminate; simpl;
    destruct (Float.of_single x), x; try discriminate; simpl;
    rewrite !Float.transform_quiet_pl_idempotent; f_equal;
    apply transform_quiet_32_64.
  - apply Bconv_Bplus. omega. omega. auto. auto.
    intros.
    assert ((let '(s, pl) := Archi.default_pl_64 in Float.to_single_pl s pl) =
            Archi.default_pl_32).
    { unfold Float.transform_quiet_pl, Float.to_single_pl, Archi.default_pl_32, Float.reduce_pl. simpl.
      f_equal. f_equal.
      assert (∀ (b:bool) (e:b=b), e = eq_refl).
      { destruct b; intros;
        exact (match e with eq_refl => eq_refl end). }
      apply H3. }
    destruct x0, y0, x', y'; try discriminate; apply H3.
Qed.

Lemma sub_double_round:
  ∀ (x y:float32),
    Float32.sub x y = Float.to_single (Float.sub (Float.of_single x) (Float.of_single y)).
Proof.
  intros.
  destruct (is_nan x) eqn:NANx, (is_nan y) eqn:NANy.
  - destruct x, y; try discriminate. simpl.
    rewrite 2!Float.transform_quiet_pl_idempotent.
    f_equal. apply transform_quiet_32_64.
  - pose proof (floatofsingle_nan _ NANy).
    destruct x; try discriminate; simpl;
    destruct (Float.of_single y), y; try discriminate; simpl;
    rewrite !Float.transform_quiet_pl_idempotent; f_equal;
    apply transform_quiet_32_64.
  - pose proof (floatofsingle_nan _ NANx).
    destruct y; try discriminate; simpl;
    destruct (Float.of_single x), x; try discriminate; simpl;
    rewrite !Float.transform_quiet_pl_idempotent; f_equal;
    apply transform_quiet_32_64.
  - unfold Float32.sub, Float.sub, Bminus, Float.of_single.
    erewrite Bconv_Bopp.
    apply Bconv_Bplus. omega. omega. auto.
    destruct y; try discriminate; reflexivity.
    intros.
    assert ((let '(s, pl) := Archi.default_pl_64 in Float.to_single_pl s pl) =
            Archi.default_pl_32).
    { unfold Float.transform_quiet_pl, Float.to_single_pl, Archi.default_pl_32, Float.reduce_pl. simpl.
      f_equal. f_equal.
      assert (∀ (b:bool) (e:b=b), e = eq_refl).
      { destruct b; intros;
        exact (match e with eq_refl => eq_refl end). }
      apply H3. }
    destruct x0, y0, x', y'; try discriminate; apply H3.
    auto.
Qed.

Lemma mult_double_round:
  ∀ (x y:float32),
    Float32.mul x y = Float.to_single (Float.mul (Float.of_single x) (Float.of_single y)).
Proof.
  intros.
  destruct (is_nan x) eqn:NANx, (is_nan y) eqn:NANy.
  - destruct x, y; try discriminate. simpl.
    rewrite 2!Float.transform_quiet_pl_idempotent.
    f_equal. apply transform_quiet_32_64.
  - pose proof (floatofsingle_nan _ NANy).
    destruct x; try discriminate; simpl;
    destruct (Float.of_single y), y; try discriminate; simpl;
    rewrite !Float.transform_quiet_pl_idempotent; f_equal;
    apply transform_quiet_32_64.
  - pose proof (floatofsingle_nan _ NANx).
    destruct y; try discriminate; simpl;
    destruct (Float.of_single x), x; try discriminate; simpl;
    rewrite !Float.transform_quiet_pl_idempotent; f_equal;
    apply transform_quiet_32_64.
  - apply Bconv_Bmult. omega. omega. auto. auto.
    intros.
    assert ((let '(s, pl) := Archi.default_pl_64 in Float.to_single_pl s pl) =
            Archi.default_pl_32).
    { unfold Float.transform_quiet_pl, Float.to_single_pl, Archi.default_pl_32, Float.reduce_pl. simpl.
      f_equal. f_equal.
      assert (∀ (b:bool) (e:b=b), e = eq_refl).
      { destruct b; intros;
        exact (match e with eq_refl => eq_refl end). }
      apply H3. }
    destruct x0, y0, x', y'; try discriminate; apply H3.
Qed.

Lemma div_double_round:
  ∀ (x y:float32),
    Float32.div x y = Float.to_single (Float.div (Float.of_single x) (Float.of_single y)).
Proof.
  intros.
  destruct (is_nan x) eqn:NANx, (is_nan y) eqn:NANy.
  - destruct x, y; try discriminate. simpl.
    rewrite 2!Float.transform_quiet_pl_idempotent.
    f_equal. apply transform_quiet_32_64.
  - pose proof (floatofsingle_nan _ NANy).
    destruct x; try discriminate; simpl;
    destruct (Float.of_single y), y; try discriminate; simpl;
    rewrite !Float.transform_quiet_pl_idempotent; f_equal;
    apply transform_quiet_32_64.
  - pose proof (floatofsingle_nan _ NANx).
    destruct y; try discriminate; simpl;
    destruct (Float.of_single x), x; try discriminate; simpl;
    rewrite !Float.transform_quiet_pl_idempotent; f_equal;
    apply transform_quiet_32_64.
  - apply Bconv_Bdiv. omega. omega. omega. auto. auto.
    intros.
    assert ((let '(s, pl) := Archi.default_pl_64 in Float.to_single_pl s pl) =
            Archi.default_pl_32).
    { unfold Float.transform_quiet_pl, Float.to_single_pl, Archi.default_pl_32, Float.reduce_pl. simpl.
      f_equal. f_equal.
      assert (∀ (b:bool) (e:b=b), e = eq_refl).
      { destruct b; intros;
        exact (match e with eq_refl => eq_refl end). }
      apply H3. }
    destruct x0, y0, x', y'; try discriminate; apply H3.
Qed.

Instance FloatToString : ToString float :=
  fun f =>
    match f with
    | Fappli_IEEE.B754_zero sg => if sg then "-0.0" else "+0.0"
    | Fappli_IEEE.B754_infinity sg => if sg then "-∞" else "+∞"
    | Fappli_IEEE.B754_nan sg pl =>
      (if sg then "NAN(-," else "NAN(+,") ++ (to_string (proj1_sig pl)) ++ ")"
    | Fappli_IEEE.B754_finite sg m e _ =>
      if Z.eqb e (-1074) then
        (if sg then "-" else "+") ++ (to_string m) ++ ".p" ++ (to_string (e))
      else
        (if sg then "-." else "+.") ++ (to_string m) ++ "p" ++ (to_string (e+56)%Z)
    end.

Instance Float32ToString : ToString float32 :=
  fun f =>
    match f with
    | Fappli_IEEE.B754_zero sg => if sg then "-0.0" else "+0.0"
    | Fappli_IEEE.B754_infinity sg => if sg then "-∞" else "+∞"
    | Fappli_IEEE.B754_nan sg pl =>
      (if sg then "NAN(-," else "NAN(+,") ++ (to_string (proj1_sig pl)) ++ ")"
    | Fappli_IEEE.B754_finite sg m e _ =>
      if Z.eqb e (-149) then
        (if sg then "-" else "+") ++ (to_string m) ++ ".p" ++ (to_string e)
      else
        (if sg then "-." else "+.") ++ (to_string m) ++ "p" ++ (to_string (e+24)%Z)
    end.

Definition one : float :=
  BofZ 53 1024 eq_refl eq_refl 1.