Module AbLinearize


Require Import Utf8.
Require Import IdealExpr.
Require Import ZArith.
Require Import Coqlib.
Require Import ZIntervals FloatIntervals IdealIntervals.
Require Import Integers.
Require Import AdomLib.
Require Import AbIdealEnv.
Require Import Maps ShareTree Util TreeAl.
Require Import FloatLib.
Require Import LinearQuery.
Require Import FactMsgHelpers.

Transparent Float.add Float.sub Float.mul Float.div Float.neg Float.zero.

Local Existing Instance fitv_gamma_R.
Local Existing Instance fitv_gamma_oR.

Module Tp := Tree_Properties(T).
Module Tsp := ShareTree_Properties(T).

Definition t := unit.
Instance gamma_t : gamma_op t (var -> ideal_num) := fun _ _ => True.

Lemma list_norepet_rev:
  ∀ l, list_norepet (A:=var) l -> list_norepet (rev l).
Proof.
  induction l. auto. intros. inv H.
  simpl. apply list_norepet_append_commut. constructor. 2:auto.
  rewrite <- in_rev. auto.
Qed.

Definition deletek {A} k (l:list (var * A)) :=
  List.filter (fun kv => negb (eq_dec (fst kv) k)) l.

Lemma deletek_eq:
  ∀ A k l, ~List.In k (List.map fst l) ->
         deletek (A:=A) k l = l.
Proof.
  induction l as [|[]]. auto.
  simpl. destruct T.elt_eq. now intuition.
  intros. simpl. rewrite IHl. now auto. now intuition.
Qed.

Lemma deletek_in:
  ∀ A k l k'v',
    List.In k'v' (deletek (A:=A) k l) <->
    List.In k'v' l /\ (fst k'v') <> k.
Proof.
  induction l as [|[]]. simpl. now intuition. destruct k'v'.
  simpl. destruct T.elt_eq; simpl; setoid_rewrite IHl; intuition congruence.
Qed.

Lemma deletek_norepet:
  ∀ A k l,
    list_norepet (List.map fst l) ->
    list_norepet (List.map fst (deletek (A:=A) k l)).
Proof.
  induction l as [|[]]. now constructor.
  intros. inv H. simpl. destruct T.elt_eq. now auto.
  simpl. constructor. 2:now auto.
  contradict H2. rewrite in_map_iff in *. setoid_rewrite deletek_in in H2.
  firstorder.
Qed.

Lemma eval_linear_expr_exten:
  ∀ (P1 P2:R->Prop) ρ l1 l2,
    list_norepet (List.map fst l1) ->
    list_norepet (List.map fst l2) ->
    (∀ (k:var) v, List.In (k, v) l1 <-> List.In (k, v) l2) ->
  (P1P2) ->
  ∀ x,
    fold_right
      (fun (kitvs:var * (fitv*fitv)) X =>
         let '(k, (itvα, itvk)) := kitvs in
         fun y =>
           match rρ ρ k with
           | None => False
           | Some kx => (∃ α, α ∈ γ itvα /\ X (y - α * kx)) /\
                        rρ ρ k ∈ γ itvk
           end)
      P1 l1 x ->
    fold_right
      (fun (kitvs:var * (fitv*fitv)) X =>
         let '(k, (itvα, itvk)) := kitvs in
         fun y =>
           match rρ ρ k with
           | None => False
           | Some kx => (∃ α, α ∈ γ itvα /\ X (y - α * kx)) /\
                        rρ ρ k ∈ γ itvk
           end)
      P2 l2 x.
Proof.
  induction l1 as [|[k [itvα itvk]]].
  - intros. destruct l2 as [|[k v]]. apply H2, H3.
    destruct (H1 k v) as [_ []]. left. auto.
  - intros. inv H. simpl in H3. destruct (rρ ρ k) as [kx|] eqn:EQkx. 2:contradiction.
    destruct H3 as [(αk & Hα & HαHx) Hty].
    apply IHl1 with (l2:=deletek k l2) in HαHx.
    + assert (List.In (k, (itvα, itvk)) l2). { apply H1. left. auto. } clear l1 IHl1 H1 H6 H7.
      revert x HαHx. induction l2 as [|[k' [itvα' itvk']] ?]; inv H0; intros. destruct H.
      simpl in HαHx. destruct @eq_dec. subst.
      * assert ((itvα, itvk) = (itvα', itvk')).
        { destruct H. congruence. clear - H H4. destruct H4.
          apply in_map with (f:=fst) in H. auto. }
        inv H0. clear H. simpl. rewrite EQkx. split. eexists. split. eauto.
        rewrite deletek_eq in HαHx by auto. auto. auto.
      * simpl in *. destruct (rρ ρ k'). 2:now auto.
        destruct HαHx as [(α' & Hα' & Hα'kx) Hty']. split. eexists. split. eauto.
        apply IHl2. auto. destruct H. congruence. auto.
        replace (x - α' * r - αk * kx) with (x - αk * kx - α' * r) by ring. auto.
        auto.
    + now auto.
    + apply deletek_norepet. auto.
    + intros. rewrite deletek_in, <- H1. simpl.
      rewrite in_map_iff in H6. clear -H6. firstorder. congruence.
    + now auto.
Qed.

Lemma eval_T_linear_expr_exten:
  ∀ ρ i g1 g2,
    (∀ k, T.get k g1 = T.get k g2) ->
    ∀ x, eval_T_linear_expr ρ (i, g1) x <-> eval_T_linear_expr ρ (i, g2) x.
Proof.
  intros ρ i g1 g2 Hg1g2. unfold eval_T_linear_expr.
  split; apply eval_linear_expr_exten;
  try (intros; now auto);
  try (apply T.elements_keys_norepet); intros;
  split; intro;
  apply T.elements_correct; apply T.elements_complete in H; rewrite <- H; auto.
Qed.

Lemma eval_T_linear_expr_set_None:
  ∀ ρ i g (k:var) kx,
  ∀ itvs α, α ∈ γ (fst itvs) ->
    rρ ρ k = Some kx ->
    rρ ρ k ∈ γ (snd itvs) ->
    T.get k g = None ->
  ∀ x, eval_T_linear_expr ρ (i, g) x ->
       eval_T_linear_expr ρ (i, T.set k itvs g) (x+α*kx).
Proof.
  intros ρ i g k kx itv α Hα Hkx Hty Hkg x.
  unfold eval_T_linear_expr. intros Hx.
  eapply eval_linear_expr_exten with (l1:=(k, itv)::T.elements g).
  - simpl. constructor. rewrite in_map_iff. intro.
    destruct H as ([] & ? & ?). simpl in H. subst.
    apply T.elements_complete in H0. congruence. apply T.elements_keys_norepet.
  - apply T.elements_keys_norepet.
  - intros. simpl. split; intro. destruct H.
    + inv H. apply T.elements_correct. apply T.gss.
    + apply T.elements_correct. rewrite T.gso.
      apply T.elements_complete. auto.
      intro. subst. apply T.elements_complete in H. congruence.
    + apply T.elements_complete in H. rewrite T.gsspec in H.
      destruct T.elt_eq. inv H. now auto.
      right. apply T.elements_correct. auto.
  - eauto.
  - destruct itv. simpl. rewrite Hkx. split. eexists. split. eauto.
    replace (x + α * kx - α * kx) with x by ring. auto. rewrite <- Hkx. auto.
Qed.

Lemma eval_T_linear_expr_rem:
  ∀ ρ i g (k:var) kx, rρ ρ k = Some kx ->
  ∀ itvs, T.get k g = Some itvs ->
  ∀ x, eval_T_linear_expr ρ (i, g) x ->
  (∃ α, α ∈ γ (fst itvs) /\
        eval_T_linear_expr ρ (i, T.remove k g) (x-α*kx)) /\
  rρ ρ k ∈ γ (snd itvs).
Proof.
  intros ρ i g k kx Hkx itv Hitv x.
  unfold eval_T_linear_expr. intros Hx. unfold eval_linear_expr in Hx.
  eapply eval_linear_expr_exten with (l2:=(k, itv)::deletek k (T.elements g)) in Hx.
  - simpl in Hx. rewrite Hkx in Hx. destruct itv.
    destruct Hx as [(α & Hα & Hxα) Hty]. split. exists α. split. now eauto.
    eapply eval_linear_expr_exten, Hxα.
    apply deletek_norepet. apply T.elements_keys_norepet. apply T.elements_keys_norepet.
    simpl. intros. rewrite deletek_in. simpl.
    split; intros.
    apply T.elements_correct. rewrite T.gro by intuition. apply T.elements_complete. apply H.
    apply T.elements_complete in H. rewrite T.grspec in H.
    destruct T.elt_eq. discriminate. split; [|auto]. apply T.elements_correct, H. eauto.
    rewrite Hkx. auto.
  - apply T.elements_keys_norepet.
  - simpl. constructor.
    rewrite in_map_iff. setoid_rewrite deletek_in. clear. now firstorder.
    apply deletek_norepet. apply T.elements_keys_norepet.
  - intros. simpl. rewrite deletek_in. simpl. split; intro.
    destruct (T.elt_eq k k0). left. apply T.elements_complete in H.
    subst k. rewrite H in Hitv. inv Hitv. auto. auto.
    destruct H as [|[]]. inv H. apply T.elements_correct. auto. auto.
  - auto.
Qed.

Lemma eval_T_linear_expr_set_Some:
  ∀ ρ i g (k:var) kx, rρ ρ k = Some kx ->
  ∀ itvs, T.get k g = Some itvs ->
  ∀ x, eval_T_linear_expr ρ (i, g) x ->
  ∃ α0, α0 ∈ γ (fst itvs) /\
        ∀ itv1 itv1' α1, α1 ∈ γ itv1 -> rρ ρ k ∈ γ itv1' ->
        eval_T_linear_expr ρ (i, T.set k (itv1, itv1') g) (x+(α1-α0)*kx).
Proof.
  intros ρ i g k kx Hkx itv0 Hitv0 x Hx.
  eapply eval_T_linear_expr_rem in Hx; eauto.
  destruct Hx as [(α & Hα & Hxα) Hty]. exists α. split. auto. intros itv1 ty α1 Hty' Hα1.
  apply eval_T_linear_expr_exten with (g1 := T.set k (itv1, ty) (T.remove k g)).
  intros. rewrite !T.gsspec, T.grspec. destruct T.elt_eq; auto.
  replace (x + (α1 - α) * kx) with (x - α * kx + α1 * kx) by ring.
  apply eval_T_linear_expr_set_None; auto using T.grs.
Qed.

Lemma eval_linear_expr_change_cst:
  ∀ ρ i0 g,
  ∀ x, eval_linear_expr ρ (i0, g) x ->
  ∃ α0, α0 ∈ γ i0 /\
        ∀ i1 α1, α1 ∈ γ i1 ->
        eval_linear_expr ρ (i1, g) (x+α1-α0).
Proof.
  intros ρ i0 g. induction g as [|(? & ? & ?)]; simpl.
  intros. eexists. split. apply H. intros. replace (x + α1 - x) with α1 by ring. auto.
  unfold eval_linear_expr. simpl. destruct (rρ ρ v). 2:contradiction.
  intros x [(α & Hα & Hxα) Hty]. apply IHg in Hxα. clear IHg.
  destruct Hxα as (α' & Hα' & Hxα').
  eexists. split. eauto. split. eexists. split. eauto.
  replace (x + α1 - α' - α * r) with (x - α * r + α1 - α') by ring.
  apply Hxα'. auto. auto.
Qed.

Definition add_T_linear_expr (e1 e2:T_linear_expr) : T_linear_expr :=
  (add_itv (fst e1) (fst e2),
   T.combine
     (fun ity1 ity2 =>
        match ity1, ity2 with
        | None, None => None
        | Some i, None => Some i
        | None, Some i => Some i
        | Some (i1, ty), Some (i2, _) => Some (add_itv i1 i2, ty)
        end)
     (snd e1) (snd e2)).

Definition add_T_linear_expr_alt (e1 e2:T_linear_expr) : T_linear_expr :=
  (add_itv (fst e1) (fst e2),
   T.fold
     (fun (g:T.t _) (k:var) ity1 =>
        match T.get k g with
        | None => T.set k ity1 g
        | Some (i2, _) => T.set k (add_itv (fst ity1) i2, snd ity1) g
        end) (snd e1) (snd e2)).

Lemma add_T_linear_expr_alt_eq:
  ∀ e1 e2 k,
    T.get k (snd (add_T_linear_expr_alt e1 e2)) =
    T.get k (snd (add_T_linear_expr e1 e2)).
Proof.
  intros. unfold add_T_linear_expr, add_T_linear_expr_alt. simpl.
  rewrite T.gcombine, T.fold_spec, <- fold_left_rev_right by reflexivity.
  pose proof @T.elements_correct _ (snd e1) k.
  setoid_rewrite in_rev in H.
  pose proof @T.elements_keys_norepet _ (snd e1). apply list_norepet_rev in H0.
  rewrite <- map_rev in H0.
  pose proof @T.elements_complete _ (snd e1) k.
  setoid_rewrite in_rev in H1.
  revert H H1. generalize (T.get k (snd e1)).
  induction (rev (T.elements (snd e1))).
  simpl; intros. destruct o. edestruct H. auto. destruct T.get; now auto.
  inv H0. specialize (IHl H3). simpl. intros.
  destruct a as [k' [i ty]]. simpl. change T.elt with var in *.
  destruct (T.get k'
             (fold_right
                (λ (y:var * (fitv*fitv)) (x:T.t _), match T.get (fst y) x with
                        | Some (i2, _) =>
                          T.set (fst y) (add_itv (fst (snd y)) i2, snd (snd y)) x
                        | None => T.set (fst y) (snd y) x
                        end) (snd e2) l))
  as [[]|] eqn:EQ; rewrite T.gsspec; destruct (T.elt_eq k k').
  - subst. rewrite (H1 (i, ty)) by auto.
    rewrite (IHl None) in EQ. destruct T.get as [[]|]; congruence. discriminate.
    intros. destruct H2. apply in_map with (f:=fst) in H0. now auto.
  - apply IHl.
    intros. edestruct H. eauto. congruence. auto. auto.
  - subst. rewrite (H1 (i, ty)) by auto.
    rewrite (IHl None) in EQ. destruct T.get; congruence. discriminate.
    intros. destruct H2. apply in_map with (f:=fst) in H0. now auto.
  - apply IHl.
    intros. edestruct H. eauto. congruence. auto. auto.
Qed.

Lemma add_T_linear_expr_alt_correct:
  ∀ ρ e1 e2 x1 x2,
    eval_T_linear_expr ρ e1 x1 ->
    eval_T_linear_expr ρ e2 x2 ->
    eval_T_linear_expr ρ (add_T_linear_expr_alt e1 e2) (x1+x2).
Proof.
  unfold eval_T_linear_expr at 1, add_T_linear_expr_alt. intros ρ e1 e2 x1 x2.
  rewrite !T.fold_spec. revert e2 x2 x1.
  induction (T.elements (snd e1)) as [|(k & itvα & itvk)]; simpl; intros.
  - destruct e2. unfold eval_T_linear_expr in H0. apply eval_linear_expr_change_cst in H0.
    destruct H0 as (α0 & Hα0 & Hα0x2).
    replace (x1+x2) with (x2 + (x1+α0) - α0) by ring.
    apply Hα0x2. apply add_itv_correct; auto.
  - unfold eval_linear_expr in H. simpl in H.
    destruct (rρ ρ k) eqn:EQrρ. 2:contradiction.
    destruct H as [(α & Hα & Hαx2) Hty].
    replace (x1+x2) with (x1-α*r+(x2+α*r)) by ring.
    eapply (IHl (_, _)). eauto. destruct e2. simpl.
    destruct (T.get k t0) as [[]|] eqn:EQ.
    + eapply eval_T_linear_expr_set_Some in EQ. 2:eauto. 2:eauto.
      destruct EQ as (α0 & Hα0 & Hα0x2).
      replace (x2 + α*r) with (x2 + (α + α0 - α0) * r) by ring.
      apply Hα0x2. apply add_itv_correct; auto.
      rewrite EQrρ. auto.
    + apply eval_T_linear_expr_set_None; auto. rewrite EQrρ. auto.
Qed.

Lemma add_T_linear_expr_correct:
  ∀ ρ e1 e2 x1 x2,
    eval_T_linear_expr ρ e1 x1 ->
    eval_T_linear_expr ρ e2 x2 ->
    eval_T_linear_expr ρ (add_T_linear_expr e1 e2) (x1+x2).
Proof.
  intros. eapply eval_T_linear_expr_exten.
  intro. symmetry. apply add_T_linear_expr_alt_eq.
  apply add_T_linear_expr_alt_correct; auto.
Qed.

Definition opp_T_linear_expr (e:T_linear_expr) : T_linear_expr :=
  (opp_itv (fst e), T.map1 (fun ity => (opp_itv (fst ity), snd ity)) (snd e)).

Lemma opp_T_linear_expr_correct:
  ∀ ρ e x,
    eval_T_linear_expr ρ e x ->
    eval_T_linear_expr ρ (opp_T_linear_expr e) (-x).
Proof.
  unfold eval_T_linear_expr. intros ρ e x Hx.
  eapply eval_linear_expr_exten
  with (l1 := List.map (fun ki => (fst ki, (opp_itv (fst (snd ki)), snd (snd ki)))) (T.elements (snd e))).
  - rewrite map_map. apply T.elements_keys_norepet.
  - apply T.elements_keys_norepet.
  - intros. unfold opp_T_linear_expr. simpl.
    pose proof (fun A g k v => conj (@T.elements_complete A g k v)
                                    (@T.elements_correct _ g k v):(_ <-> _)).
    rewrite in_map_iff. split; intros.
    + apply T.elements_correct. rewrite T.gmap1.
      destruct H0 as ([] & ? & ?). inv H0.
      apply T.elements_complete in H1. rewrite H1. auto.
    + apply T.elements_complete in H0. rewrite T.gmap1 in H0.
      destruct (T.get k (snd e)) as [[]|] eqn:EQ. 2:discriminate. inv H0.
      eexists (_, (_, _)). split. reflexivity. apply T.elements_correct. auto.
  - eauto.
  - revert x Hx. induction (T.elements (snd e)) as [|[k [itvα itvk]]]; simpl; intros.
    apply opp_itv_correct, Hx. unfold eval_linear_expr in *. simpl in Hx.
    destruct (rρ ρ k). 2:now auto.
    destruct Hx as [(α & Hα & Hxα) Hty].
    split. eexists. split. apply opp_itv_correct. apply Hα.
    replace (-x - - α*r) with (-(x-α*r)) by ring. now auto. auto.
Qed.

Definition mult_T_linear_expr (i:fitv) (e:T_linear_expr) : T_linear_expr :=
  (mult_itv i (fst e), T.map1 (fun itvs => (mult_itv i (fst itvs), snd itvs)) (snd e)).

Lemma mult_T_linear_expr_correct:
  ∀ ρ e x i y,
    eval_T_linear_expr ρ e x ->
    y ∈ γ i ->
    eval_T_linear_expr ρ (mult_T_linear_expr i e) (y * x).
Proof.
  unfold eval_T_linear_expr. intros ρ e x i y Hx Hy.
  eapply eval_linear_expr_exten
  with (l1 := List.map (fun ki => (fst ki, (mult_itv i (fst (snd ki)), snd (snd ki))))
                       (T.elements (snd e))).
  - rewrite map_map. apply T.elements_keys_norepet.
  - apply T.elements_keys_norepet.
  - intros. unfold mult_T_linear_expr. simpl.
    pose proof (fun A g k v => conj (@T.elements_complete A g k v)
                                    (@T.elements_correct _ g k v):(_ <-> _)).
    rewrite in_map_iff. setoid_rewrite H. rewrite T.gmap1. split.
    intros ([] & ? & ?). inv H0. erewrite T.elements_complete by apply H1. eauto.
    intros. destruct (T.get k (snd e)) as [[]|] eqn:EQ; inv H0.
    eexists (_, (_, _)). split. 2:apply H, EQ. auto.
  - eauto.
  - revert x Hx. induction (T.elements (snd e)) as [|[k [itvα itvk]]]; simpl; intros.
    apply mult_itv_correct, Hx. auto.
    unfold eval_linear_expr in Hx. simpl in Hx. destruct (rρ ρ k). 2:now auto.
    destruct Hx as [(α & Hα & Hxα) HH].
    split. eexists. split. apply mult_itv_correct. eauto. apply Hα.
    replace (y * x - y*α*r) with (y*(x-α*r)) by ring. now auto. auto.
Qed.

Definition inv_itv (i:fitv) : fitv+⊤ :=
  let 'FITV a b := i in
  if Fleb (@B754_finite 53 1024 false 1 (-1074) eq_refl) a ||
     Fleb b (@B754_finite 53 1024 true 1 (-1074) eq_refl) then
    Just (FITV (Bdiv 53 1024 eq_refl eq_refl Float.binop_pl mode_DN one b)
               (Bdiv 53 1024 eq_refl eq_refl Float.binop_pl mode_UP one a))
  else All.

Lemma inv_itv_correct:
  ∀ i x, x ∈ γ i -> (/x) ∈ γ (inv_itv i).
Proof.
  intros. destruct i. unfold inv_itv.
  destruct H.
  destruct (Fleb (@B754_finite 53 1024 false 1 (-1074) eq_refl) f) eqn:EQ1.
  2:destruct (Fleb f0 (@B754_finite 53 1024 true 1 (-1074) eq_refl)) eqn:EQ2.
  - pose proof Bdiv_correct 53 1024 eq_refl eq_refl Float.binop_pl mode_UP one f.
    pose proof Bdiv_correct 53 1024 eq_refl eq_refl Float.binop_pl mode_DN one f0.
    destruct f as [|[]| |[]]; try discriminate.
    assert ((B754_finite false m e e0:R) ≠ 0).
    { intro. apply F2R_eq_0_reg in H3. discriminate. }
    specialize (H1 H3). clear H3.
    unfold RF_le, Float.neg in H0. rewrite B2R_Bopp, is_finite_Bopp in H0.
    unfold is_finite in H0. apply Ropp_le_cancel in H0.
    assert (0 < B754_finite false m e e0).
    { apply F2R_gt_0_compat, eq_refl. }
    split; unfold RF_le, Float.neg.
    + clear H2. Rlt_bool_case H1.
      * destruct H1 as (-> & -> & _).
        eapply Rle_trans. apply Rinv_le, H0. now auto.
        eapply Rle_trans. apply round_UP_pt. apply (fexp_correct 53 1024), eq_refl.
        right. f_equal. compute. Psatz.lra.
      * destruct (Bdiv 53 1024 eq_refl eq_refl Float.binop_pl mode_UP one) as [| |? []|];
        inv H1. reflexivity.
    + unfold RF_le in H.
      destruct (is_finite f0) eqn:FINf0.
      2:subst f0; simpl; rewrite <- Ropp_0;
      eapply Ropp_le_contravar, Rlt_le, Rinv_0_lt_compat, Rlt_le_trans, H0; now auto.
      assert ((f0:R) <> 0) by Psatz.lra.
      rewrite is_finite_Bopp, B2R_Bopp.
      specialize (H2 H4). Rlt_bool_case H2.
      * destruct H2 as (-> & -> & _).
        eapply Ropp_le_contravar, Rle_trans, Rinv_le, H. 2:Psatz.lra.
        eapply Rle_trans. 2:apply round_DN_pt. 2:apply (fexp_correct 53 1024), eq_refl.
        right. f_equal. compute. Psatz.lra.
      * destruct f0; try (exfalso; apply H4, eq_refl).
        assert (b = false).
        { clear - H H0 H3. destruct b. 2:reflexivity.
          assert (0 < B754_finite true m0 e1 e2) by Psatz.lra.
          apply F2R_gt_0_reg in H1. discriminate. }
        subst b.
        destruct (Bdiv 53 1024 eq_refl eq_refl Float.binop_pl mode_DN one) as [| |? []|]; inv H2.
        apply Ropp_le_contravar. rewrite Rabs_right in H5.
        eapply Rle_trans. eapply Rle_trans, H5. compute; Psatz.lra.
        eapply Rle_trans. 2:apply Rinv_le, H. 2:Psatz.lra.
        simpl. unfold F2R at 1, Rdiv. simpl.
        rewrite Rinv_r, Rmult_1_l by Psatz.lra.
        apply round_DN_pt, (fexp_correct 53 1024), eq_refl.
        apply Rle_ge, round_ge_generic.
        apply fexp_correct, eq_refl. apply valid_rnd_round_mode. apply generic_format_0.
        apply Rmult_le_pos, Rlt_le, Rinv_0_lt_compat. apply F2R_ge_0_compat. discriminate.
        apply F2R_gt_0_compat. reflexivity.
  - clear EQ1.
    pose proof Bdiv_correct 53 1024 eq_refl eq_refl Float.binop_pl mode_UP one f.
    pose proof Bdiv_correct 53 1024 eq_refl eq_refl Float.binop_pl mode_DN one f0.
    destruct f0 as [|[]| |[]]; try discriminate.
    assert ((B754_finite true m e e0:R) ≠ 0).
    { intro. apply F2R_eq_0_reg in H3. discriminate. }
    specialize (H2 H3). clear H3.
    unfold RF_le, Float.neg in H, H0. rewrite B2R_Bopp, is_finite_Bopp in H0.
    unfold is_finite in H.
    assert (B754_finite true m e e0 < 0).
    { apply F2R_lt_0_compat, eq_refl. }
    split; unfold RF_le, Float.neg.
    + destruct (is_finite f) eqn:FINf.
      2:replace f with (B754_infinity true:float) by (destruct f as [|[]| |]; inv H0; reflexivity);
        simpl; eapply Ropp_le_cancel; rewrite Ropp_inv_permute, Ropp_0 by Psatz.lra;
        apply Rlt_le, Rinv_0_lt_compat; Psatz.lra.
      assert ((f:R) <> 0) by Psatz.lra.
      specialize (H1 H4). Rlt_bool_case H1.
      * destruct H1 as (-> & -> & _).
        eapply Rle_trans.
        apply Ropp_le_cancel. rewrite (Ropp_inv_permute x), Ropp_inv_permute.
        apply Rinv_le, H0. Psatz.lra. Psatz.lra. Psatz.lra.
        simpl. unfold Rdiv, F2R. rewrite Rinv_r, Rmult_1_l.
        apply round_UP_pt. apply (fexp_correct 53 1024), eq_refl. simpl. Psatz.lra.
      * destruct f; try (exfalso; apply H4, eq_refl).
        assert (b = true).
        { clear - H H0 H3. destruct b. reflexivity.
          assert (B754_finite false m0 e1 e2 < 0) by Psatz.lra.
          apply F2R_lt_0_reg in H1. discriminate. }
        subst b.
        destruct (Bdiv 53 1024 eq_refl eq_refl Float.binop_pl mode_UP one) as [| |? []|]; inv H1.
        rewrite Rabs_left1 in H5.
        eapply Rle_trans. 2:eapply Rle_trans. 2:apply Ropp_le_contravar, H5.
        2:compute; Psatz.lra.
        eapply Rle_trans. apply Ropp_le_cancel.
        rewrite Ropp_inv_permute, (Ropp_inv_permute x). apply Rinv_le, H0.
        Psatz.lra. Psatz.lra. Psatz.lra.
        rewrite Ropp_involutive.
        simpl. unfold F2R at 2, Rdiv. simpl.
        rewrite Rinv_r, Rmult_1_l by Psatz.lra.
        apply round_UP_pt, (fexp_correct 53 1024), eq_refl.
        apply round_le_generic.
        apply fexp_correct, eq_refl. apply valid_rnd_round_mode. apply generic_format_0.
        simpl. unfold Rdiv, F2R at 1. rewrite Rinv_r, Rmult_1_l.
        apply Ropp_le_cancel. rewrite Ropp_0, Ropp_inv_permute.
        apply Rlt_le, Rinv_0_lt_compat, Ropp_0_gt_lt_contravar, F2R_lt_0_compat, eq_refl.
        intro. apply F2R_eq_0_reg in H1. discriminate. simpl. Psatz.lra.
    + clear H1. Rlt_bool_case H2.
      * rewrite B2R_Bopp, is_finite_Bopp. destruct H2 as (-> & -> & _).
        rewrite Ropp_inv_permute, <- round_UP_opp by Psatz.lra.
        eapply Rle_trans. apply Rinv_le, Ropp_le_contravar, H.
        rewrite <- Ropp_0. apply Ropp_lt_contravar, F2R_lt_0_compat. reflexivity.
        eapply Rle_trans. apply round_UP_pt. apply (fexp_correct 53 1024), eq_refl.
        right. f_equal. rewrite <- Ropp_inv_permute. compute. Psatz.lra.
        intro. apply F2R_eq_0_reg in H2. discriminate.
      * destruct (Bdiv 53 1024 eq_refl eq_refl Float.binop_pl mode_DN one) as [| |? []|];
        inv H2. reflexivity.
  - exact I.
Qed.

Definition join_itv (i1 i2:fitv) : fitv :=
  match i1, i2 with
  | FITV m1 M1, FITV m2 M2 =>
    FITV (if Fleb m1 m2 || is_nan m2 then m1 else m2) (if Fleb M1 M2 || is_nan M1 then M2 else M1)
  end.

Lemma join_itv_correct:
  ∀ i1 i2 (x:R), x ∈ (γ i1 ∪ γ i2) -> x ∈ γ (join_itv i1 i2).
Proof.
  intros. destruct i1, i2, H as [[]|[]]; split.
  - destruct (Fleb f0 f2) eqn:FLEB.
    2:destruct f0; try discriminate; now auto.
    simpl. unfold RF_le in H |- *.
    destruct (is_finite f0) eqn:FINf0, (is_finite f2) eqn:FINf2.
    + rewrite Fleb_Rle in FLEB by auto. Rle_bool_case FLEB. Psatz.lra. discriminate.
    + destruct f2 as [|[]| |], f0 as [| | |[]]; try discriminate; auto.
    + subst f0. destruct f2; discriminate.
    + subst f0. destruct f2 as [|[]| |]; try discriminate; auto.
  - unfold RF_le, Float.neg in *. rewrite is_finite_Bopp, B2R_Bopp in *.
    destruct (Fleb f f1) eqn:FLEB.
    destruct f; try discriminate; now auto.
    destruct (is_finite f) eqn:FINf, (is_finite f1) eqn:FINf1; simpl.
    + rewrite is_finite_not_is_nan, FINf1 by auto.
      rewrite Fleb_Rle in FLEB by auto. Rle_bool_case FLEB. discriminate. Psatz.lra.
    + destruct f1 as [|[]| |], f as [| | |[]]; try discriminate; simpl; auto.
    + destruct f as [|[]| |]; inv H0. rewrite is_finite_not_is_nan, FINf1 by auto.
      destruct f1; discriminate.
    + destruct f as [|[]| |], f1 as [|[]| |]; try discriminate. reflexivity.
  - destruct (Fleb f0 f2) eqn:FLEB. now auto.
    simpl. unfold RF_le in H |- *.
    destruct (is_finite f0) eqn:FINf0, (is_finite f2) eqn:FINf2.
    + rewrite is_finite_not_is_nan, FINf0 by auto.
      rewrite Fleb_Rle in FLEB by auto. Rle_bool_case FLEB. discriminate. Psatz.lra.
    + subst f2. destruct f0 as [| | |[]]; discriminate.
    + destruct f0 as [|[]| |], f2; try discriminate; simpl; auto.
    + subst f2. destruct f0 as [|[]| |]; try discriminate; simpl; auto.
  - unfold RF_le, Float.neg in H0 |- *. rewrite is_finite_Bopp, B2R_Bopp in H0 |- *.
    destruct (Fleb f f1) eqn:FLEB. simpl. 2:destruct f1; try discriminate; now auto.
    destruct (is_finite f) eqn:FINf, (is_finite f1) eqn:FINf1; simpl.
    + rewrite Fleb_Rle in FLEB by auto. Rle_bool_case FLEB. Psatz.lra. discriminate.
    + destruct f1 as [|[]| |]; try discriminate. destruct f as [| | |[]]; try discriminate.
    + destruct f1, f as [|[]| |]; try discriminate; simpl; auto.
    + destruct f1 as [|[]| |]; try discriminate.
      destruct f as [|[]| |]; try discriminate; now auto.
Qed.

Definition double_mult_eps_itv : fitv :=
  FITV (@B754_finite 53 1024 false 9007199254740991 (-53) eq_refl)
       (@B754_finite 53 1024 false 4503599627370497 (-52) eq_refl).

Definition double_add_eps : float :=
  @B754_finite 53 1024 false 1 (-1074) eq_refl.

Definition double_add_eps_itv : fitv :=
  FITV (Bopp _ _ Float.neg_pl double_add_eps) double_add_eps.

Lemma double_round_NE_bounds:
  ∀ x,
    let r := round radix2 (Fcore_FLT.FLT_exp (3 - 1024 - 53) 53) (round_mode mode_NE) x in
    (∃ α, α ∈ γ double_mult_eps_itv /\ r = x*α) \/ (r-x) ∈ γ double_add_eps_itv.
Proof.
  intros.
  destruct (Req_dec x 0).
  - subst r x. rewrite round_0 by apply valid_rnd_round_mode.
    left. exists 1. compute; Psatz.lra.
  - pose proof @Fcore_ulp.error_le_half_ulp radix2 (Fcore_FLT.FLT_exp (3 - 1024 - 53) 53)
         (fexp_correct 53 1024 eq_refl) (negbZeven) x.
    change (Znearest (negbZeven)) with (round_mode mode_NE) in H0. fold r in H0.
    revert H0. generalize r. clear r. intro r.
    unfold Fcore_ulp.ulp, canonic_exp, Fcore_FLT.FLT_exp.
    rewrite Req_bool_false by auto.
    apply Z.max_case_strong.
    + left. exists (r/x). split. 2:field; auto.
      replace (ln_beta radix2 x - 53)%Z with (ln_beta radix2 x - 1 + - 52)%Z in H1 by ring.
      rewrite bpow_plus in H1.
      assert (bpow radix2 (ln_beta radix2 x-1) <= Rabs x).
      { apply Rnot_lt_le. intro. apply ln_beta_le_bpow in H2. 2:now auto. Psatz.lia. }
      change (bpow radix2 (-52)) with (/4503599627370496) in H1.
      assert (Rabs (r-x) <= /9007199254740992 * Rabs x) by Psatz.lra.
      clear -H3 H.
      assert (Rabs ((r-x)/x) <= / 9007199254740992).
      { unfold Rdiv. rewrite Rabs_mult, Rabs_Rinv by auto.
        eapply Rmult_le_reg_r. apply (Rabs_pos_lt _ H).
        rewrite Rmult_assoc, Rinv_l. Psatz.lra. apply Rabs_no_R0, H. }
      apply Rabs_le_inv in H0. replace ((r-x)/x) with (r/x-1) in H0 by (field; auto).
      split; unfold RF_le; simpl; unfold F2R, Z2R, bpow; simpl; Psatz.lra.
    + right. apply Rabs_le_inv in H1. destruct H1.
      split; eapply Rle_trans. apply H2. 2:apply Ropp_le_contravar, H1.
      compute. left. Psatz.lra. compute. left. Psatz.lra.
Qed.

Definition double_round_T_linear_expr (e:T_linear_expr) : T_linear_expr :=
  (join_itv (add_itv double_add_eps_itv (fst e))
            (mult_itv double_mult_eps_itv (fst e)),
   T.map1 (fun itvs => (mult_itv double_mult_eps_itv (fst itvs), snd itvs)) (snd e)).

Lemma double_round_T_linear_expr_correct:
  ∀ e ρ x, eval_T_linear_expr ρ e x ->
   let r := round radix2 (Fcore_FLT.FLT_exp (3 - 1024 - 53) 53) (round_mode mode_NE) x in
   eval_T_linear_expr ρ (double_round_T_linear_expr e) r.
Proof.
  intros.
  pose proof (double_round_NE_bounds x). fold r in H0. destruct H0 as [(α & Hα & Hxα)|].
  - pose proof (mult_T_linear_expr_correct ρ e x double_mult_eps_itv α H Hα).
    replace (α * x) with r in H0 by (rewrite Hxα; ring).
    unfold eval_T_linear_expr in *.
    eapply eval_linear_expr_exten, H0.
    apply T.elements_keys_norepet. apply T.elements_keys_norepet.
    split; auto. intros; apply join_itv_correct; auto.
  - revert H. destruct e.
    unfold double_round_T_linear_expr, eval_T_linear_expr, mult_T_linear_expr, fst, snd.
    intros.
    eapply eval_linear_expr_exten
    with (l1:=List.map (fun ki => (fst ki, (mult_itv double_mult_eps_itv (fst (snd ki)), snd (snd ki))))
                       (T.elements t0)).
    + rewrite map_map. apply T.elements_keys_norepet.
    + apply T.elements_keys_norepet.
    + intros. rewrite in_map_iff. split; intros.
      * apply T.elements_correct. rewrite T.gmap1.
        destruct H1 as ([] & A & B). apply pair_eq_inv in A. destruct A.
        simpl in H1. subst. unfold snd. apply T.elements_complete in B. rewrite B.
        auto.
      * apply T.elements_complete in H1. rewrite T.gmap1 in H1.
        destruct (T.get k t0) as [[]|] eqn:EQ. 2:discriminate.
        exists (k, (f0, f1)). split. f_equal. unfold option_map in H1.
        unfold snd, fst. congruence.
        apply T.elements_correct. auto.
    + eauto.
    + unfold eval_linear_expr in H. unfold fst, snd in *.
      revert H0 H. generalize r. clear r. revert x.
      induction (T.elements t0) as [|(? & ? & ?)].
      * unfold map, fold_right.
        intros. apply join_itv_correct. left.
        eapply add_itv_correct in H. 2:apply H0. replace (r-x+x) with r in H by ring. auto.
      * unfold map, fold_right. intros. destruct (rρ ρ e) eqn:EQ. 2:now auto.
        destruct H as [(? & ? & ?) ?]. split. 2:now auto.
        eexists. split. apply mult_itv_correct with (x:=1), H.
        compute; Psatz.lra. eapply IHl, H1.
        replace (r - 1 * x0 * r0 - (x - x0 * r0)) with (r-x) by ring. auto.
Qed.

Definition intervalize_linear_expr (e:linear_expr) : fitv :=
  List.fold_left
    (fun itv itvs =>
       let '(_, (itvα, itvk)) := itvs in
       add_itv (mult_itv itvα itvk) itv) (snd e) (fst e).

Lemma intervalize_linear_expr_correct :
  ∀ e ρ x,
    eval_linear_expr ρ e x ->
    x ∈ γ (intervalize_linear_expr e).
Proof.
  intros.
  unfold eval_linear_expr in H. unfold intervalize_linear_expr.
  revert x H. generalize (fst e). induction (snd e) as [|(k & itvα & itvk)]. now auto.
  intros. apply IHl.
  simpl in H. destruct (rρ ρ k) eqn:EQ. 2:contradiction.
  destruct H as [(α & Hα & Hαx) HH]. clear -Hα Hαx HH.
  revert x Hαx. induction l as [|(k' & itvα' & itvk')].
  - simpl. intros. replace x with (α*r+(x-α*r)) by ring.
    apply add_itv_correct. apply mult_itv_correct; auto. auto.
  - simpl. destruct (rρ ρ k'). 2:now auto.
    intros x [(α' & Hα' & Hαx') HH']. split. 2:auto.
    eexists. split. eauto. apply IHl.
    replace (x - α' * r0 - α * r) with (x - α * r - α' * r0) by ring.
    auto.
Qed.

Definition intervalize_T_linear_expr (e:T_linear_expr) : fitv :=
  T.fold1 (fun itv itvs => add_itv (mult_itv (fst itvs) (snd itvs)) itv) (snd e) (fst e).

Lemma intervalize_T_linear_expr_correct :
  ∀ e ρ x,
    eval_T_linear_expr ρ e x ->
    x ∈ γ (intervalize_T_linear_expr e).
Proof.
  intros.
  unfold eval_T_linear_expr in H. unfold intervalize_T_linear_expr.
  rewrite T.fold1_spec. change T.elt with var.
  apply intervalize_linear_expr_correct in H.
  unfold intervalize_linear_expr in H.
  match goal with
  | H : γ ?A x |- γ ?B x => assert (A = B)
  end. 2:congruence.
  simpl. clear H. rewrite <- !fold_left_rev_right.
  induction (rev (A:=(var*_)) (T.elements (snd e))) as [|(? & ? & ?)]. reflexivity.
  simpl. congruence.
Qed.

Definition double_round_over_T_linear_expr (e:T_linear_expr) : option T_linear_expr :=
  let le := double_round_T_linear_expr e in
  let 'FITV a b := intervalize_T_linear_expr le in
  if is_finite a && is_finite b then Some le else None.

Lemma double_round_over_T_linear_expr_correct:
  ∀ e ρ x, eval_T_linear_expr ρ e x ->
  let r := round radix2 (Fcore_FLT.FLT_exp (3 - 1024 - 53) 53) (round_mode mode_NE) x in
  ∀ e', double_round_over_T_linear_expr e = Some e' ->
  ∀ (f:float) sgn,
    (if Rlt_bool (Rabs r) (bpow radix2 1024) then (f:R) = r /\ is_finite f = true
     else (f:full_float) = binary_overflow 53 1024 mode_NE sgn) ->
  ∃ y, r_of_inum (INf f) = Some y /\ eval_T_linear_expr ρ e' y.
Proof.
  unfold double_round_over_T_linear_expr. intros.
  apply double_round_T_linear_expr_correct in H.
  rewrite Rlt_bool_true in H1.
  - unfold r_of_inum. destruct H1 as (? & ->). eexists. split. now eauto.
    destruct intervalize_T_linear_expr, @is_finite, @is_finite; inv H0. congruence.
  - apply intervalize_T_linear_expr_correct in H.
    destruct intervalize_T_linear_expr, H. unfold RF_le in H, H2.
    unfold Float.neg in H2. rewrite is_finite_Bopp, B2R_Bopp in H2.
    destruct (is_finite f0), (is_finite f1); inv H0.
    apply Rabs_lt. split.
    + eapply Rlt_le_trans, Ropp_le_cancel, H2.
      apply (fun H => proj1 (Rabs_lt_inv _ _ H)), abs_B2R_lt_emax.
    + eapply Rle_lt_trans, Rabs_lt_inv, abs_B2R_lt_emax. apply H.
Qed.

Definition single_mult_eps_itv : fitv :=
  FITV (@B754_finite 53 1024 false 9007198717870111 (-53) eq_refl)
       (@B754_finite 53 1024 false 4503599895805937 (-52) eq_refl).

Definition single_add_eps : float :=
  @B754_finite 53 1024 false 4503599627370496 (-202) eq_refl.

Definition single_add_eps_itv : fitv :=
  FITV (Bopp _ _ Float.neg_pl single_add_eps) single_add_eps.

Lemma single_round_NE_bounds:
  ∀ x,
    let r := round radix2 (Fcore_FLT.FLT_exp (3 - 128 - 24) 24) (round_mode mode_NE) x in
    (∃ α, α ∈ γ single_mult_eps_itv /\ r = x*α) \/
    (r-x) ∈ γ single_add_eps_itv.
Proof.
  intros.
  destruct (Req_dec x 0).
  - subst r x. rewrite round_0 by apply valid_rnd_round_mode.
    left. exists 1. compute; Psatz.lra.
  - unfold round in r. unfold scaled_mantissa, canonic_exp, Fcore_FLT.FLT_exp, F2R in r.
    simpl in r. revert r. apply Z.max_case_strong.
    + intros. left. exists (r/x). split. 2:field; auto. subst r.
      rewrite bpow_opp.
      set (xx := Rabs x / bpow radix2 (ln_beta radix2 x - 24)).
      assert ((Znearest (negbZeven) (x * / bpow radix2 (ln_beta radix2 x - 24)) *
               bpow radix2 (ln_beta radix2 x - 24) / x) =
              (Znearest (negbZeven) xx) / xx).
      { unfold xx, Rabs. destruct Rcase_abs.
        - rewrite Ropp_div, Znearest_opp, Z2R_opp.
          replace (-Znearestt0 : Z, negb (negb (Zeven (- (t0 + 1)))))
                             (x / bpow radix2 (ln_beta radix2 x - 24)) /
                   - (x / bpow radix2 (ln_beta radix2 x - 24)))
          with (Znearestt0 : Z, negb (negb (Zeven (- (t0 + 1)))))
                            (x / bpow radix2 (ln_beta radix2 x - 24)) *
                bpow radix2 (ln_beta radix2 x - 24) / x).
          2:field. f_equal. f_equal. f_equal. unfold Znearest, Rdiv.
          rewrite Zeven_opp, Zeven_plus. simpl. destruct Zeven; auto.
          split. apply Rgt_not_eq, bpow_gt_0. auto.
        - unfold Rdiv. field. split. apply Rgt_not_eq, bpow_gt_0. auto. }
      rewrite H1.
      assert (8388608 <= xx < 16777216).
      { subst xx. clear H1 H0. destruct ln_beta. simpl. specialize (a H). destruct a. split.
        - replace (ln_beta_val - 24)%Z with ((ln_beta_val - 1)+-23)%Z by ring.
          rewrite bpow_plus. simpl.
          apply Rmult_le_reg_r with (r:=bpow radix2 (ln_beta_val - 1)). apply bpow_gt_0.
          replace (Rabs x / (bpow radix2 (ln_beta_val - 1) * / 8388608) *
                   bpow radix2 (ln_beta_val - 1))
          with (Rabs x * 8388608). Psatz.lra.
          field. apply Rgt_not_eq, bpow_gt_0.
        - unfold Zminus. rewrite bpow_plus. simpl.
          apply Rmult_lt_reg_r with (r:=bpow radix2 ln_beta_val). apply bpow_gt_0.
          replace (Rabs x / (bpow radix2 ln_beta_val * / 16777216) *
                   bpow radix2 ln_beta_val)
          with (Rabs x * 16777216). Psatz.lra.
          field. apply Rgt_not_eq, bpow_gt_0. }
      revert H2. generalize xx. clear. intros.
      destruct (Rabs_le_inv _ _ (Znearest_N (negbZeven) xx)).
      split; unfold RF_le; simpl; unfold F2R; simpl.
      destruct (Z.eq_dec (Znearest (negbZeven) xx) 8388608).
      * rewrite e in *. simpl in *.
        eapply Rle_trans. eapply Rmult_le_compat_l, Rinv_le, H2. Psatz.lra. Psatz.lra. Psatz.lra.
      * assert (8388607 < Znearest (negbZeven) xx)%Z by (apply lt_Z2R; simpl; Psatz.lra).
        assert (8388609 <= Znearest (negbZeven) xx)%Z by Psatz.lia. clear H1 n.
        apply Z2R_le in H3. simpl in H3.
        eapply Rle_trans with
        (r2:=(Znearest (negbZeven) xx / (Znearest (negbZeven) xx - / 2))).
        apply Rmult_le_compat_l, Rinv_le; try Psatz.lra.
        replace (Znearest (negbZeven) xx / (Znearest (negbZeven) xx - / 2))
        with (1+/2 / (Znearest (negbZeven) xx - / 2)) by (field; Psatz.lra).
        replace (4503599895805937 * / 4503599627370496)
        with (1+/2/(4503599627370496 / 536870882)) by field.
        apply Rplus_le_compat_l, Rmult_le_compat_l, Rinv_le; Psatz.lra.
      * rewrite Ropp_mult_distr_l_reverse. apply Ropp_le_contravar.
        eapply Rle_trans with
          (r2:=(Znearest (negbZeven) xx / (Znearest (negbZeven) xx + / 2))),
          Rmult_le_compat_l, Rinv_le; try Psatz.lra.
        replace (Znearest (negbZeven) xx / (Znearest (negbZeven) xx + / 2))
        with (1-/2 / (Znearest (negbZeven) xx + / 2)) by (field; Psatz.lra).
        replace (9007198717870111 * / 9007199254740992)
        with (1-/2/(4503599627370496/536870881)) by field.
        apply Rplus_le_compat_l, Ropp_le_contravar, Rmult_le_compat_l, Rinv_le; try Psatz.lra.
        assert (8388607 < Znearest (negbZeven) xx)%Z by (apply lt_Z2R; simpl; Psatz.lra).
        assert (8388608 <= Znearest (negbZeven) xx)%Z by Psatz.lia.
        apply Z2R_le in H3. simpl in H3. Psatz.lra.
    + right. destruct (Rabs_le_inv _ _ (Znearest_N (negbZeven) (x * bpow radix2 (- -149)))).
      split; unfold RF_le; simpl in *; unfold F2R; simpl; Psatz.lra.
Qed.

Definition single_round_T_linear_expr (e:T_linear_expr) : T_linear_expr :=
  (join_itv (add_itv single_add_eps_itv (fst e))
            (mult_itv single_mult_eps_itv (fst e)),
   T.map1 (fun itvs => (mult_itv single_mult_eps_itv (fst itvs), snd itvs)) (snd e)).

Lemma single_round_T_linear_expr_correct:
  ∀ e ρ x, eval_T_linear_expr ρ e x ->
   let r := round radix2 (Fcore_FLT.FLT_exp (3 - 128 - 24) 24) (round_mode mode_NE) x in
   eval_T_linear_expr ρ (single_round_T_linear_expr e) r.
Proof.
  intros.
  pose proof (single_round_NE_bounds x). fold r in H0. destruct H0 as [(α & Hα & Hxα)|].
  - pose proof (mult_T_linear_expr_correct ρ e x single_mult_eps_itv α H Hα).
    replace (α * x) with r in H0 by (rewrite Hxα; ring).
    unfold eval_T_linear_expr in *.
    eapply eval_linear_expr_exten, H0.
    apply T.elements_keys_norepet.
    apply T.elements_keys_norepet.
    split; auto. intros; apply join_itv_correct; auto.
  - revert H. destruct e.
    unfold single_round_T_linear_expr, eval_T_linear_expr, mult_T_linear_expr, fst, snd.
    intros.
    eapply eval_linear_expr_exten
    with (l1:=List.map (fun ki => (fst ki, (mult_itv single_mult_eps_itv (fst (snd ki)), snd (snd ki))))
                       (T.elements t0)).
    + rewrite map_map. apply T.elements_keys_norepet.
    + apply T.elements_keys_norepet.
    + intros. rewrite in_map_iff. split; intros.
      * apply T.elements_correct. rewrite T.gmap1.
        destruct H1 as ([] & A & B). apply pair_eq_inv in A. destruct A.
        simpl in H1. subst. unfold snd. apply T.elements_complete in B. rewrite B.
        auto.
      * apply T.elements_complete in H1. rewrite T.gmap1 in H1.
        destruct (T.get k t0) as [[]|] eqn:EQ. 2:discriminate.
        exists (k, (f0, f1)). split. f_equal. unfold option_map in H1.
        unfold snd, fst. congruence.
        apply T.elements_correct. auto.
    + eauto.
    + unfold eval_linear_expr in H. unfold fst, snd in *.
      revert H0 H. generalize r. clear r. revert x.
      induction (T.elements t0) as [|(? & ? & ?)].
      * unfold map, fold_right.
        intros. apply join_itv_correct. left.
        eapply add_itv_correct in H. 2:apply H0. replace (r-x+x) with r in H by ring. auto.
      * unfold map, fold_right. intros. destruct (rρ ρ e) eqn:EQ. 2:now auto.
        destruct H as [(? & ? & ?) ?]. split. 2:now auto.
        eexists. split. apply mult_itv_correct with (x:=1), H.
        compute; Psatz.lra. eapply IHl, H1.
        replace (r - 1 * x0 * r0 - (x - x0 * r0)) with (r-x) by ring. auto.
Qed.

Definition single_ub : float :=
  @B754_finite 53 1024 false 9007198986305536 75 eq_refl.

Definition single_lb : float :=
  @B754_finite 53 1024 true 9007198986305536 75 eq_refl.

Definition single_round_over_T_linear_expr (e:T_linear_expr) : option T_linear_expr :=
  let 'FITV a b := intervalize_T_linear_expr e in
  if Fleb a single_lb || Fleb single_ub b then None
  else Some (single_round_T_linear_expr e).

Lemma single_round_over_T_linear_expr_correct:
  ∀ e ρ x, eval_T_linear_expr ρ e x ->
  let r := round radix2 (Fcore_FLT.FLT_exp (3 - 128 - 24) 24) (round_mode mode_NE) x in
  ∀ e', single_round_over_T_linear_expr e = Some e' ->
  ∀ (f:float32) sgn,
    (if Rlt_bool (Rabs r) (bpow radix2 128) then (f:R) = r /\ is_finite f = true
     else (f:full_float) = binary_overflow 24 128 mode_NE sgn) ->
  ∃ y, r_of_inum (INf (Float.of_single f)) = Some y /\ eval_T_linear_expr ρ e' y.
Proof.
  unfold single_round_over_T_linear_expr. intros.
  rewrite Rlt_bool_true in H1.
  - apply single_round_T_linear_expr_correct in H.
    unfold r_of_inum. edestruct floatofsingle_exact.
    destruct H1. rewrite H2, H3, H1 by auto. eexists. split. now eauto.
    destruct intervalize_T_linear_expr, @Fleb, @Fleb; inv H0. congruence.
  - apply intervalize_T_linear_expr_correct in H.
    destruct intervalize_T_linear_expr, H. unfold RF_le in H, H2.
    unfold Float.neg in H2. rewrite is_finite_Bopp, B2R_Bopp in H2.
    destruct (Fleb f0 single_lb) eqn:Ff0, (Fleb single_ub f1) eqn:Ff1; inv H0.
    assert (is_finite f0 = true).
    { destruct f0 as [|[]| |]; try reflexivity; discriminate. }
    assert (is_finite f1 = true).
    { destruct f1 as [|[]| |]; try reflexivity; discriminate. }
    destruct (Req_dec x 0). subst. rewrite round_0, Rabs_R0 by apply valid_rnd_round_mode. apply bpow_gt_0.
    rewrite Fleb_Rle in Ff0 by auto. rewrite Fleb_Rle in Ff1 by auto.
    Rle_bool_case Ff0; Rle_bool_case Ff1; try discriminate.
    rewrite H0 in H2. rewrite H3 in H.
    apply Rabs_lt.
    destruct (Rabs_le_inv _ _ (@Fcore_ulp.error_le_half_ulp radix2 _ (fexp_correct 24 128 eq_refl)
                                                            (negbZeven) x)).
    assert (Fcore_ulp.ulp radix2 (Fcore_FLT.FLT_exp (3 - 128 - 24) 24) x
            <= bpow radix2 104).
    { eapply Rle_trans. apply Fcore_ulp.ulp_le.
      apply fexp_correct. reflexivity.
      apply Fcore_FLT.FLT_exp_monotone.
      apply Rabs_le; split; eapply Rle_trans.
      2:apply Ropp_le_cancel, H2. 2:apply H.
      2:eapply Rle_trans, Rle_abs. 2:apply Rlt_le, H6.
      rewrite <- Ropp_involutive with (r:=f0). apply Ropp_le_contravar.
      eapply Rle_trans, Rle_abs. replace (B2R single_ub) with (-B2R single_lb). Psatz.lra.
      compute. Psatz.lra.
      unfold B2R, F2R, Fcore_ulp.ulp, canonic_exp, Fcore_FLT.FLT_exp; simpl.
      rewrite Req_bool_false by Psatz.lra.
      rewrite ln_beta_unique_pos with (e:=128%Z). apply Rle_refl.
      simpl. Psatz.lra. }
    replace (single_ub:R) with (bpow radix2 128 - /2 * bpow radix2 104) in H6 by (compute; field).
    replace (single_lb:R) with (/2 * bpow radix2 104 - bpow radix2 128) in H5 by (compute; field).
    unfold round_mode. Psatz.lra.
Qed.

Definition ZofB_T_linear_expr (e:T_linear_expr) : T_linear_expr :=
  (add_itv (fst e) (FITV (Float.neg one) one), snd e).

Lemma Telements_empty:
  T.elements (T.empty (fitv*fitv)) = List.nil.
Proof.
  pose proof @T.elements_complete _ (T.empty (fitv * fitv)).
  destruct T.elements as [|[]]. now auto.
  specialize (H _ _ (or_introl eq_refl)). rewrite T.gempty in H. discriminate.
Qed.

Lemma Telements_singleton:
  ∀ x y,
    T.elements (T.set x y (T.empty (fitv*fitv))) = (x,y)::List.nil.
Proof.
  intros.
  pose proof T.elements_complete (T.set x y (T.empty (fitv*fitv))).
  pose proof @T.elements_correct _ (T.set x y (T.empty (fitv*fitv))).
  pose proof T.elements_keys_norepet (T.set x y (T.empty (fitv*fitv))).
  destruct T.elements as [|[x1 y1][|[x2 y2]]].
  - destruct (H0 _ _ (T.gss _ _ _)).
  - destruct (H0 _ _ (T.gss _ _ _)) as [|[]]. f_equal. auto.
  - pose proof H _ _ (or_introl eq_refl).
    pose proof H _ _ (or_intror (or_introl eq_refl)).
    rewrite T.gsspec, T.gempty in H2, H3.
    destruct T.elt_eq, T.elt_eq; inv H2; inv H3.
    inv H1. destruct H4. left. auto.
Qed.

Lemma ZofB_T_linear_expr_correct:
  ∀ e ρ x, eval_T_linear_expr ρ e x ->
   eval_T_linear_expr ρ (ZofB_T_linear_expr e) (Ztrunc x).
Proof.
  intros. destruct e.
  replace (Ztrunc x:R) with (x+(Ztrunc x-x)) by ring.
  eapply eval_T_linear_expr_exten, add_T_linear_expr_correct with (e1:=(_, _)) (e2:=(_, _)).
  - intros. rewrite T.gcombine by auto. simpl. rewrite T.gempty.
    destruct T.get as [[]|]; auto.
  - auto.
  - unfold eval_T_linear_expr. simpl. rewrite Telements_empty.
    split; unfold Ztrunc, RF_le; destruct (Rlt_bool_spec x 0); simpl;
    unfold F2R, Zceil, Zfloor; simpl;
    rewrite ?Z2R_opp, Z2R_minus, Rinv_r, Z2R_IZR by Psatz.lra; simpl;
    destruct (archimed (-x)), (archimed x); Psatz.lra.
Qed.

Fixpoint linearize_expr (c:query_chan) (e:iexpr) : option T_linear_expr :=
  match e with
  | IEvar _ x =>
    match fitv_of_iitv (c.(get_itv) e) with
    | All => None
    | Just (FITV a b as i) =>
      if is_finite a && is_finite b then
        Some (FITV (B754_zero false) (B754_zero false), T.set x (FITV one one, i) (T.empty _))
      else None
    end
  | IEconst (INz z) => Some (fitv_of_z z, T.empty _)
  | IEconst (INf f) =>
    if is_finite f then Some (FITV f f, T.empty _) else None
  | IEZitv itv => Some (fitv_of_zitv itv, T.empty _)
  | IEunop (IOneg | IOnegf) e' =>
    do le' <- linearize_expr c e';
    ret (opp_T_linear_expr le')
  | IEunop IOsingleoffloat e' =>
    do le' <- linearize_expr c e';
    single_round_over_T_linear_expr le'
  | IEunop IOfloatofz e' =>
    do le' <- linearize_expr c e';
    double_round_over_T_linear_expr le'
  | IEunop IOsingleofz e' =>
    do le' <- linearize_expr c e';
    single_round_over_T_linear_expr le'
  | IEunop IOzoffloat e' =>
    do le' <- linearize_expr c e';
    ret (ZofB_T_linear_expr le')
  | IEbinop IOadd e1 e2 =>
    do le1 <- linearize_expr c e1;
    do le2 <- linearize_expr c e2;
    ret (add_T_linear_expr le1 le2)
  | IEbinop IOsub e1 e2 =>
    do le1 <- linearize_expr c e1;
    do le2 <- linearize_expr c e2;
    ret (add_T_linear_expr le1 (opp_T_linear_expr le2))
  | IEbinop IOmul e1 e2 =>
    match e2 with
    | IEconst (INz z) =>
      do le' <- linearize_expr c e1;
      ret (mult_T_linear_expr (fitv_of_z z) le')
    | IEZitv i =>
      do le' <- linearize_expr c e1;
      ret (mult_T_linear_expr (fitv_of_zitv i) le')
    | _ =>
      match c.(get_itv) e1 with
      | NotBot (Just (Az i)) =>
        do le' <- linearize_expr c e2;
        ret (mult_T_linear_expr (fitv_of_zitv i) le')
      | _ => None
      end
    end
  | IEbinop IOdiv e1 e2 =>
    do le' <- linearize_expr c e1;
    match c.(get_itv) e2 with
    | NotBot (Just (Az i)) =>
      match inv_itv (fitv_of_zitv i) with
      | Just i' => Some (ZofB_T_linear_expr (mult_T_linear_expr i' le'))
      | All => None
      end
    | _ => None
    end
  | IEbinop IOaddf e1 e2 =>
    do le1 <- linearize_expr c e1;
    do le2 <- linearize_expr c e2;
    double_round_over_T_linear_expr (add_T_linear_expr le1 le2)
  | IEbinop IOsubf e1 e2 =>
    do le1 <- linearize_expr c e1;
    do le2 <- linearize_expr c e2;
    double_round_over_T_linear_expr (add_T_linear_expr le1 (opp_T_linear_expr le2))
  | IEbinop IOmulf e1 e2 =>
    match e2 with
    | IEconst (INf f) =>
      if is_finite f then
        do le' <- linearize_expr c e1;
        double_round_over_T_linear_expr (mult_T_linear_expr (FITV f f) le')
      else
        None
    | _ =>
      match fitv_of_iitv (c.(get_itv) e1) with
      | Just (FITV a b as i) =>
        do le' <- linearize_expr c e2;
        double_round_over_T_linear_expr (mult_T_linear_expr i le')
      | _ => None
      end
    end
  | IEbinop IOdivf e1 e2 =>
    do le' <- linearize_expr c e1;
    match fitv_of_iitv (c.(get_itv) e2) with
    | Just (FITV a b as i) =>
      match inv_itv i with
      | Just i' => double_round_over_T_linear_expr (mult_T_linear_expr i' le')
      | All => None
      end
    | _ => None
    end
  | _ =>
    match fitv_of_iitv (c.(get_itv) e) with
    | Just i => Some (i, T.empty _)
    | _ => None
    end
  end.

Lemma linearize_expr_correct:
  ∀ c e le, linearize_expr c e = Some le ->
  ∀ ρ x, ρ ∈ γ c -> eval_iexpr ρ e x ->
  ∃ y, r_of_inum x = Some y /\ eval_T_linear_expr ρ le y.
Proof.
  induction e as [| | |[]|[]]; simpl; intros le Hle ρ x Hρ Hx;
  assert (Hx':=Hx); inv Hx';
  (eapply get_itv_correct, fitv_of_iitv_correct in Hx; [|apply Hρ]).
  - (* IEvar *)
    apply get_itv_correct with (e:=IEvar i v) (a:=ρ v) in Hρ. 2:constructor; now auto.
    apply fitv_of_iitv_correct in Hρ. destruct fitv_of_iitv; inv Hle.
    unfold eval_T_linear_expr. destruct f.
    destruct (is_finite f) eqn:FINf, (is_finite f0) eqn:FINf0; inv H0. simpl.
    rewrite Telements_singleton.
    unfold eval_linear_expr. simpl. unfold rρ. destruct r_of_inum. 2:now destruct Hρ.
    eexists. split. now eauto. split. 2:now auto.
    exists 1. unfold RF_le. compute; Psatz.lra.
  - (* IEconst *)
    unfold r_of_inum. destruct x; [destruct (is_finite f) eqn:FINf|]; inv Hle; eexists;
    (split; [now eauto|]); unfold eval_T_linear_expr, eval_linear_expr;
    simpl; rewrite Telements_empty; simpl.
    unfold RF_le, Float.neg. rewrite is_finite_Bopp, B2R_Bopp, FINf. Psatz.lra.
    apply fitv_of_z_correct.
  - (* IEZitv *)
    inv Hle. eexists. split. now eauto.
    unfold eval_T_linear_expr. simpl. rewrite Telements_empty.
    apply fitv_of_zitv_correct, H0.
  - (* IOneg *)
    inv H3. destruct (linearize_expr c e); inv Hle.
    edestruct IHe as (y & Hxy & Hley); eauto. inv Hxy. clear IHe.
    eexists. split. now eauto. simpl. rewrite Z2R_opp. apply opp_T_linear_expr_correct, Hley.
  - (* IOnot *)
    destruct fitv_of_iitv; inv Hle. destruct r_of_inum. 2:contradiction.
    unfold eval_T_linear_expr. simpl. rewrite Telements_empty. eauto.
  - (* IOnegf *)
    inv H3. destruct (linearize_expr c e); inv Hle.
    edestruct IHe as (y & Hxy & Hley); eauto. clear IHe.
    unfold r_of_inum, Float.neg in *. rewrite is_finite_Bopp, B2R_Bopp in *.
    destruct (is_finite f). 2:discriminate.
    eexists. split. eauto. apply opp_T_linear_expr_correct. congruence.
  - (* IOabsf *)
    destruct fitv_of_iitv; inv Hle. destruct r_of_inum. 2:contradiction.
    unfold eval_T_linear_expr. simpl. rewrite Telements_empty. eauto.
  - (* IOsingleoffloat *)
    inv H3. destruct (linearize_expr c e); inv Hle.
    edestruct IHe as (y & Hxy & Hley); eauto. clear IHe.
    eapply single_round_over_T_linear_expr_correct in H0. now eauto. now eauto.
    simpl in Hxy. destruct (is_finite f) eqn:FINf; inv Hxy.
    pose proof (Bconv_correct 53 1024 24 128 eq_refl eq_refl Float.to_single_pl mode_NE f FINf).
    Rlt_bool_case H.
    destruct H as (? & ? & _); eauto. eauto.
  - (* IOfloatofz *)
    inv H3. destruct (linearize_expr c e); inv Hle.
    edestruct IHe as (y & Hxy & Hley); eauto. clear IHe.
    eapply double_round_over_T_linear_expr_correct in H0. now eauto. now eauto.
    inv Hxy. pose proof BofZ_correct 53 1024 eq_refl eq_refl i.
    Rlt_bool_case H. destruct H as (? & ? & _); eauto. eauto.
  - (* IOsingleofz *)
    inv H3. destruct (linearize_expr c e); inv Hle.
    edestruct IHe as (y & Hxy & Hley); eauto. clear IHe.
    eapply single_round_over_T_linear_expr_correct in H0. now eauto. now eauto. inv Hxy.
    pose proof (BofZ_correct 24 128 eq_refl eq_refl i).
    Rlt_bool_case H.
    destruct H as (? & ? & _); eauto. eauto.
  - (* IOzoffloat *)
    inv H3. destruct (linearize_expr c e); inv Hle.
    edestruct IHe as (y & Hxy & Hley); eauto. clear IHe.
    rewrite ZofB_correct in H. simpl in Hxy. destruct @is_finite; inv Hxy. inv H.
    eapply ZofB_T_linear_expr_correct in Hley. simpl. eauto.
  - (* IOadd *)
    inv H5. destruct (linearize_expr c e1), (linearize_expr c e2); inv Hle.
    edestruct IHe1 as (y1 & Hxy1 & Hley1); eauto. clear IHe1.
    edestruct IHe2 as (y2 & Hxy2 & Hley2); eauto. clear IHe2.
    inv Hxy1. inv Hxy2. simpl. eexists. split. eauto.
    rewrite Z2R_plus. eapply add_T_linear_expr_correct; auto.
  - (* IOsub *)
    inv H5. destruct (linearize_expr c e1), (linearize_expr c e2); inv Hle.
    edestruct IHe1 as (y1 & Hxy1 & Hley1); eauto. clear IHe1.
    edestruct IHe2 as (y2 & Hxy2 & Hley2); eauto. clear IHe2.
    inv Hxy1. inv Hxy2. simpl. eexists. split. eauto.
    unfold Zminus. rewrite Z2R_plus, Z2R_opp. eapply add_T_linear_expr_correct. auto.
    apply opp_T_linear_expr_correct, Hley2.
  - (* IOmul *)
    inv H5. simpl. eexists. split. eauto. rewrite Z2R_mult.
    assert (match get_itv c e1 with
            | Bot => None
            | NotBot All => None
            | NotBot (Just (Az i0)) =>
                match linearize_expr c e2 with
                | Some le' => Some (mult_T_linear_expr (fitv_of_zitv i0) le')
                | None => None
                end
            | NotBot (Just (Af _)) => None
            end = Some le ->
            eval_T_linear_expr ρ le (i1*i2)).
    { clear Hle. intro. pose proof (get_itv_correct Hρ H2).
      destruct (get_itv c e1) as [|[|[]]]; try discriminate.
      destruct (linearize_expr c e2); inv H.
      edestruct IHe2 as (y2 & Hxy2 & Hley2); eauto. clear IHe2. inv Hxy2.
      apply mult_T_linear_expr_correct. auto.
      apply fitv_of_zitv_correct. auto. }
    destruct e2; auto. destruct i; auto.
    rewrite Rmult_comm. destruct (linearize_expr c e1); inv Hle.
    edestruct IHe1 as (y1 & Hxy1 & Hley1); eauto. clear IHe1. inv Hxy1.
    apply mult_T_linear_expr_correct. auto. inv H4. apply fitv_of_z_correct.
    rewrite Rmult_comm. destruct (linearize_expr c e1); inv Hle.
    edestruct IHe1 as (y1 & Hxy1 & Hley1); eauto. clear IHe1. inv Hxy1.
    apply mult_T_linear_expr_correct. auto. inv H4. apply fitv_of_zitv_correct, H3.
  - (* IOdiv *)
    inv H5. destruct (linearize_expr c e1). 2:discriminate.
    pose proof (get_itv_correct Hρ H4).
    destruct (get_itv c e2) as [|[|[]]]; try discriminate.
    edestruct IHe1 as (y & Hxy & Hley); eauto. clear IHe2 IHe1.
    inv Hxy. simpl. eexists. split. eauto.
    apply fitv_of_zitv_correct, inv_itv_correct in H0. destruct inv_itv; inv Hle.
    eapply mult_T_linear_expr_correct in H0. 2:apply Hley.
    apply ZofB_T_linear_expr_correct in H0.
    replace (i1 ÷ i2) with (Ztrunc (/i2*i1)). auto.
    rewrite Rmult_comm, Z.quot_div by auto. rewrite <- Zfloor_div by Psatz.lia.
    unfold Rdiv, Ztrunc, Zceil. destruct i1, i2; try congruence; simpl.
    + rewrite Rmult_0_l. apply (Ztrunc_Z2R 0).
    + rewrite Rmult_0_l. apply (Ztrunc_Z2R 0).
    + rewrite Rlt_bool_false. destruct Zfloor; auto.
      apply Rmult_le_pos, Rlt_le, Rinv_0_lt_compat.
      apply (Z2R_le 0 (Z.pos _)). discriminate.
      apply (Z2R_lt 0 (Z.pos _)). reflexivity.
    + rewrite <- Ropp_inv_permute, Ropp_mult_distr_r_reverse, Rlt_bool_true,
                 Ropp_involutive.
      destruct Zfloor; auto.
      apply Ropp_lt_gt_0_contravar, Rmult_gt_0_compat, Rinv_0_lt_compat.
      apply (Z2R_lt 0 (Z.pos _)). reflexivity.
      apply (Z2R_lt 0 (Z.pos _)). reflexivity.
      intro. apply (eq_Z2R (Z.pos _) 0) in H1. discriminate.
    + rewrite Ropp_mult_distr_l_reverse, Rlt_bool_true, Ropp_involutive.
      destruct Zfloor; auto.
      apply Ropp_lt_gt_0_contravar, Rmult_gt_0_compat, Rinv_0_lt_compat.
      apply (Z2R_lt 0 (Z.pos _)). reflexivity.
      apply (Z2R_lt 0 (Z.pos _)). reflexivity.
    + rewrite Ropp_mult_distr_l_reverse, <- Ropp_inv_permute, Ropp_mult_distr_r_reverse,
              Ropp_involutive, Rlt_bool_false.
      destruct Zfloor; auto.
      apply Rmult_le_pos, Rlt_le, Rinv_0_lt_compat.
      apply (Z2R_le 0 (Z.pos _)). discriminate.
      apply (Z2R_lt 0 (Z.pos _)). reflexivity.
      intro. apply (eq_Z2R (Z.pos _) 0) in H1. discriminate.
  - (* IOmod *)
    destruct fitv_of_iitv; inv Hle. destruct r_of_inum. 2:contradiction.
    unfold eval_T_linear_expr. simpl. rewrite Telements_empty. eauto.
  - (* IOand *)
    destruct fitv_of_iitv; inv Hle. destruct r_of_inum. 2:contradiction.
    unfold eval_T_linear_expr. simpl. rewrite Telements_empty. eauto.
  - (* IOor *)
    destruct fitv_of_iitv; inv Hle. destruct r_of_inum. 2:contradiction.
    unfold eval_T_linear_expr. simpl. rewrite Telements_empty. eauto.
  - (* IOxor *)
    destruct fitv_of_iitv; inv Hle. destruct r_of_inum. 2:contradiction.
    unfold eval_T_linear_expr. simpl. rewrite Telements_empty. eauto.
  - (* IOshl *)
    destruct fitv_of_iitv; inv Hle. destruct r_of_inum. 2:contradiction.
    unfold eval_T_linear_expr. simpl. rewrite Telements_empty. eauto.
  - (* IOshr *)
    destruct fitv_of_iitv; inv Hle. destruct r_of_inum. 2:contradiction.
    unfold eval_T_linear_expr. simpl. rewrite Telements_empty. eauto.
  - (* IOcmp *)
    destruct fitv_of_iitv; inv Hle. destruct r_of_inum. 2:contradiction.
    unfold eval_T_linear_expr. simpl. rewrite Telements_empty. eauto.
  - (* IOaddf *)
    inv H5. destruct (linearize_expr c e1), (linearize_expr c e2); inv Hle.
    edestruct IHe1 as (y1 & Hxy1 & Hley1); eauto. clear IHe1.
    edestruct IHe2 as (y2 & Hxy2 & Hley2); eauto. clear IHe2.
    eapply double_round_over_T_linear_expr_correct in H0.
    now eauto. apply add_T_linear_expr_correct; now eauto.
    unfold r_of_inum in Hxy1, Hxy2. unfold Float.add.
    destruct (is_finite f1) eqn:FINf1, (is_finite f2) eqn:FINf2;
    inv Hxy1; inv Hxy2.
    pose proof (Bplus_correct 53 1024 eq_refl eq_refl Float.binop_pl mode_NE
                              _ _ FINf1 FINf2).
    Rlt_bool_case H. destruct H as (? & ? & _). auto.
    destruct H. eauto.
  - (* IOsubf *)
    inv H5. destruct (linearize_expr c e1), (linearize_expr c e2); inv Hle.
    edestruct IHe1 as (y1 & Hxy1 & Hley1); eauto. clear IHe1.
    edestruct IHe2 as (y2 & Hxy2 & Hley2); eauto. clear IHe2.
    eapply double_round_over_T_linear_expr_correct in H0.
    now eauto. apply add_T_linear_expr_correct, opp_T_linear_expr_correct; now eauto.
    unfold r_of_inum in Hxy1, Hxy2. unfold Float.sub.
    destruct (is_finite f1) eqn:FINf1, (is_finite f2) eqn:FINf2;
    inv Hxy1; inv Hxy2. fold (f1 - f2).
    pose proof (Bminus_correct 53 1024 eq_refl eq_refl Float.binop_pl mode_NE
                              _ _ FINf1 FINf2).
    Rlt_bool_case H. destruct H as (? & ? & _). auto.
    destruct H. eauto.
  - (* IOmulf *)
    inv H5. unfold Float.mul.
    assert (match fitv_of_iitv (get_itv c e1) with
            | All => None
            | Just (FITV a b as i1) =>
              match linearize_expr c e2 with
              | Some le' =>
                double_round_over_T_linear_expr (mult_T_linear_expr i1 le')
              | None => None
              end
            end = Some le ->
              ∃ y : R,
                r_of_inum
                  (INf (Bmult 53 1024 eq_refl eq_refl Float.binop_pl mode_NE f1 f2)) =
                Some yeval_T_linear_expr ρ le y).
    { intro. clear Hle IHe1. pose proof Hρ. eapply get_itv_correct in Hρ. 2:now eauto.
      apply fitv_of_iitv_correct in Hρ.
      destruct (fitv_of_iitv (get_itv c e1)) as [|[]]. discriminate.
      destruct (linearize_expr c e2). 2:discriminate.
      edestruct IHe2 as (y & Hxy & Hley); eauto. clear IHe2.
      simpl in Hρ. destruct (is_finite f1) eqn:FINf1. 2:contradiction.
      eapply double_round_over_T_linear_expr_correct in H. eauto.
      apply mult_T_linear_expr_correct; eauto.
      simpl in Hxy. destruct (is_finite f2) eqn:FINf2; inv Hxy.
      pose proof Bmult_correct 53 1024 eq_refl eq_refl Float.binop_pl mode_NE f1 f2.
      rewrite FINf2, FINf1 in H1.
      Rlt_bool_case H1. destruct H1 as (? & ? & _); now eauto. eauto. }
    destruct e2; eauto. clear H IHe2. inv H4.
    destruct (is_finite f2) eqn:FINf2. 2:discriminate.
    destruct (linearize_expr c e1). 2:discriminate. simpl in Hle.
    edestruct IHe1 as (y & Hxy & Hley); eauto. clear IHe1.
    eapply double_round_over_T_linear_expr_correct in Hle. eauto.
    eapply mult_T_linear_expr_correct. eauto.
    split; unfold RF_le, Float.neg; rewrite ?is_finite_Bopp, ?B2R_Bopp, FINf2; apply Rle_refl.
    rewrite Rmult_comm.
    unfold r_of_inum in Hxy. destruct (is_finite f1) eqn:FINf1; inv Hxy.
    pose proof Bmult_correct 53 1024 eq_refl eq_refl Float.binop_pl mode_NE f1 f2.
    rewrite FINf2, FINf1 in H.
    Rlt_bool_case H. destruct H as (? & ? & _); now eauto. eauto.
  - (* IOdivf *)
    inv H5. destruct (linearize_expr c e1); inv Hle.
    edestruct IHe1 as (y & Hxy & Hley); eauto. clear IHe1 IHe2.
    eapply get_itv_correct, fitv_of_iitv_correct in H4. 2:now eauto.
    destruct (fitv_of_iitv (get_itv c e2)) as [|[]]. discriminate.
    unfold r_of_inum in H4. destruct (is_finite f2) eqn:FINf2. 2:contradiction.
    assert ((f2:R) ≠ 0).
    { destruct f2; try discriminate.
      2:intro HH; apply F2R_eq_0_reg in HH; destruct b; discriminate.
      clear - H0 H4.
      unfold inv_itv in H0.
      destruct H4. unfold RF_le, Float.neg in H, H1. rewrite B2R_Bopp, is_finite_Bopp in H1.
      replace (Fleb (@B754_finite 53 1024 false 1 (-1074) eq_refl) f) with false in H0.
      replace (Fleb f0 (@B754_finite 53 1024 true 1 (-1074) eq_refl)) with false in H0.
      discriminate.
      - clear H0 H1. destruct (is_finite f0) eqn:FINf0.
        2:subst f0; reflexivity.
        rewrite Fleb_Rle by auto. rewrite Rle_bool_false. auto.
        eapply Rlt_le_trans, H. compute. Psatz.lra.
      - clear H0 H. destruct (is_finite f) eqn:FINf.
        2:destruct f as [|[]| |]; inv H1; reflexivity.
        rewrite Fleb_Rle by auto. rewrite Rle_bool_false. auto.
        eapply Rle_lt_trans. apply Ropp_le_cancel, H1. compute. Psatz.lra. }
    apply (inv_itv_correct (FITV f f0)) in H4. destruct inv_itv. discriminate.
    unfold r_of_inum in Hxy. destruct (is_finite f1) eqn:FINf1; inv Hxy.
    eapply double_round_over_T_linear_expr_correct in H0. eauto.
    eapply mult_T_linear_expr_correct; now eauto.
    pose proof Bdiv_correct 53 1024 eq_refl eq_refl Float.binop_pl mode_NE f1 f2 H.
    unfold Float.div. rewrite FINf1 in H1.
    rewrite Rmult_comm. unfold Rdiv in H1. Rlt_bool_case H1. destruct H1 as (? & ? & _).
    auto. eauto.
  - (* IOcmpf *)
    destruct fitv_of_iitv; inv Hle. destruct r_of_inum. 2:contradiction.
    unfold eval_T_linear_expr. simpl. rewrite Telements_empty. eauto.
Qed.

Definition double_min_delta : float :=
  @B754_finite 53 1024 false 1 (-1074) eq_refl.

Lemma double_min_delta_correct :
  ∀ (f1 f2:float),
    is_finite f1 = true -> is_finite f2 = true ->
    (f1 < f2) -> (f1 - f2 <= (Float.neg double_min_delta)).
Proof.
  intros. destruct f1, f2; try discriminate.
  - simpl in H1. Psatz.lra.
  - simpl in *. apply F2R_gt_0_reg in H1. destruct b0. discriminate. clear H1.
    rewrite Rminus_0_l, F2R_Zopp with (m:=1%Z). apply Ropp_le_contravar. simpl.
    apply Rmult_le_compat. simpl. Psatz.lra. simpl. Psatz.lra.
    apply Z2R_le. simpl. Psatz.lia.
    apply bpow_le. simpl. unfold Fappli_IEEE.bounded in e0.
    rewrite Bool.andb_true_iff in e0. destruct e0.
    unfold canonic_mantissa, Fcore_FLT.FLT_exp in H1.
    apply Zeq_bool_eq in H1. Psatz.lia.
  - simpl in *. apply F2R_lt_0_reg in H1. destruct b. 2:discriminate. clear H1.
    simpl. rewrite Rminus_0_r, F2R_Zopp with (m:=1%Z), F2R_Zopp with (m:=Z.pos m).
    apply Ropp_le_contravar.
    apply Rmult_le_compat. simpl. Psatz.lra. simpl. Psatz.lra.
    apply Z2R_le. simpl. Psatz.lia.
    apply bpow_le. simpl. unfold Fappli_IEEE.bounded in e0.
    rewrite Bool.andb_true_iff in e0. destruct e0.
    unfold canonic_mantissa, Fcore_FLT.FLT_exp in H1.
    apply Zeq_bool_eq in H1. Psatz.lia.
  - simpl in *. unfold F2R, Fnum, Fexp in *.
    replace (bpow radix2 e) with (bpow radix2 (e-Z.min e e1+Z.min e e1))
      in H1 |- * by (f_equal; ring).
    replace (bpow radix2 e1) with (bpow radix2 (e1-Z.min e e1+Z.min e e1))
      in H1 |- * by (f_equal; ring).
    replace (bpow radix2 (-1074)) with (bpow radix2 (-1074-Z.min e e1+Z.min e e1))
      by (f_equal; ring).
    rewrite !bpow_plus, <- !Rmult_assoc, <- Rmult_minus_distr_r in *.
    apply Rmult_le_compat_r. apply bpow_ge_0.
    apply Rmult_lt_reg_r in H1. 2:apply bpow_gt_0.
    rewrite <- !Z2R_Zpower, <- !Z2R_mult, <- Z2R_minus in * by Psatz.lia.
    apply lt_Z2R in H1.
    apply Rle_trans with ((-1)%Z). apply Z2R_le. Psatz.lia.
    unfold Z2R, P2R. rewrite Ropp_mult_distr_l_reverse, Rmult_1_l.
    apply Ropp_le_contravar. change 1 with (bpow radix2 0). apply bpow_le.
    unfold Fappli_IEEE.bounded in e0, e2.
    rewrite Bool.andb_true_iff in e0, e2. destruct e0, e2.
    unfold canonic_mantissa, Fcore_FLT.FLT_exp in H2, H4.
    apply Zeq_bool_eq in H2. apply Zeq_bool_eq in H4. Psatz.lia.
Qed.

Definition T_linear_expr_is_top (le:T_linear_expr) : bool :=
  T.fold (fun acc x itvs => acc || fitv_is_top (fst itvs)) (snd le) false.

Definition T_linear_expr_is_nonzero (le:T_linear_expr) : bool :=
  let 'FITV a b := intervalize_T_linear_expr le in
  negb (Fleb a Float.zero && Fleb Float.zero b).

Lemma T_linear_expr_is_nonzero_correct:
  ∀ ρ le, eval_T_linear_expr ρ le 0 -> T_linear_expr_is_nonzero le = false.
Proof.
  intros. unfold T_linear_expr_is_nonzero.
  pose proof intervalize_T_linear_expr_correct _ _ _ H.
  destruct intervalize_T_linear_expr, H0. unfold RF_le, Float.neg in *.
  rewrite is_finite_Bopp, B2R_Bopp in H1.
  destruct (is_finite f0) eqn:FINf0, (is_finite f) eqn:FINf.
  - rewrite !Fleb_Rle, !Rle_bool_true; auto. simpl. Psatz.lra.
  - destruct f as [|[]| |]; try discriminate. simpl. rewrite Fleb_Rle, Rle_bool_true; auto.
  - subst f0. rewrite Fleb_Rle, Rle_bool_true; auto. simpl. Psatz.lra.
  - destruct f as [|[]| |]; try discriminate. subst. auto.
Qed.

Definition process_msg (m:message) (ab:unit * query_chan) :=
  let c := snd ab in
  let new_msg :=
    match m with
    | Fact_msg e b =>
      match simplify_bool_expr e c with
      | (IEbinop ((IOcmp _ | IOcmpf _) as op) e1 e2, b') =>
        do le1 <- linearize_expr c e1;
        do le2 <- linearize_expr c e2;
        let le := add_T_linear_expr le1 (opp_T_linear_expr le2) in
        do i <-
          match op, xorb b b' with
          | IOcmp Ceq, true | IOcmp Cne, false
          | IOcmpf Ceq, true | IOcmpf Cne, false =>
            ret (FITV Float.zero Float.zero)

          | (IOcmp Clt|IOcmpf Clt), false | (IOcmp Cge|IOcmpf Cge), true =>
            ret (FITV (B754_infinity true) Float.zero)
          | IOcmp Cle, false | IOcmp Cgt, true =>
            ret (FITV (B754_infinity true) (Float.neg one))
          | IOcmpf Cle, false | IOcmpf Cgt, true =>
            ret (FITV (B754_infinity true) (Float.neg double_min_delta))

          | (IOcmp Cgt|IOcmpf Cgt), false | (IOcmp Cle|IOcmpf Cle), true =>
            ret (FITV Float.zero (B754_infinity false))
          | IOcmp Cge, false | IOcmp Clt, true =>
            ret (FITV one (B754_infinity false))
          | IOcmpf Cge, false | IOcmpf Clt, true =>
            ret (FITV double_min_delta (B754_infinity false))

          | _, _ => None
          end;
          ret (add_T_linear_expr le (i, T.empty _))
      | _ => None
      end
    | Known_value_msg x v =>
      do itv <-
        match v with
        | INz z => ret (fitv_of_zitv (ZITV z z))
        | INf f => if is_finite f then ret (FITV f f) else None
        end;
      ret (opp_itv itv, T.set x (FITV one one, itv) (T.empty _))
    | Itv_msg x i =>
      match fitv_of_iitv (NotBot (Just i)) with
      | All => None
      | Just itv =>
        ret (opp_itv itv, T.set x (FITV one one, itv) (T.empty _))
      end
    | _ => None
    end
  in
  match new_msg with
  | None => NotBot (tt, m::List.nil)
  | Some m' =>
    if T_linear_expr_is_top m' then NotBot (tt, m::List.nil)
    else
      if T_linear_expr_is_nonzero m' then Bot
      else NotBot (tt, m::(Linear_zero_msg m')::List.nil)
  end.

Transparent Float.cmp.

Lemma process_msg_correct:
  forall m ρ ab,
    ρ ∈ γ ab -> ρ ∈ γ m ->
    ρ ∈ γ (process_msg m ab).
Proof.
  unfold process_msg; intros.
  match goal with
  | |- γ match ?O with _ => _ end ρ =>
    assert (forall le, O = Some le -> eval_T_linear_expr ρ le 0); [|destruct O]
  end.
  { destruct m; try discriminate.
    - simpl in H0. destruct (simplify_bool_expr i (snd ab)) eqn:EQ.
      eapply simplify_bool_expr_correct in EQ.
      2:now apply H0. 2:now apply H.
      destruct i0; try discriminate. destruct i0; try discriminate.
      + pose proof linearize_expr_correct (snd ab) i0_1.
        pose proof linearize_expr_correct (snd ab) i0_2.
        destruct linearize_expr. 2:discriminate.
        destruct linearize_expr. 2:discriminate.
        inv EQ. inv H9.
        edestruct H1 as (y & Hxy & Hy). now eauto. apply H. apply H6.
        edestruct H2 as (y' & Hxy' & Hy'). now eauto. apply H. apply H8.
        inv Hxy. inv Hxy'. clear H1 H2.
        assert (Zcmp c i1 i2 = xorb b b0)
          by (destruct b, b0, Zcmp; try discriminate; reflexivity).
        rewrite <- H1. clear H1 b b0 H0 H10 H6 H8. intros. simpl in H0.
        match type of H0 with
        | (do x <- ?O; _) = _ =>
          assert (forall i, O = Some i -> (i2-i1) ∈ γ i); [|destruct O; inv H0]
        end.
        { clear H0. intros.
          destruct c; simpl in H0; destruct (Z.compare_spec i1 i2); inv H0; split;
          try reflexivity;
          try (apply Z.le_succ_l, Z2R_le in H1;
               rewrite <- Z.add_1_r, Z2R_plus in H1; simpl in H1);
          unfold RF_le, B2R, F2R; simpl; try Psatz.lra. }
        specialize (H1 _ eq_refl).
        replace 0 with (i1-i2+(i2-i1)) by ring.
        apply add_T_linear_expr_correct.
        apply add_T_linear_expr_correct, opp_T_linear_expr_correct; now auto.
        unfold eval_T_linear_expr. simpl. rewrite Telements_empty. apply H1.
      + pose proof linearize_expr_correct (snd ab) i0_1.
        pose proof linearize_expr_correct (snd ab) i0_2.
        destruct linearize_expr. 2:discriminate.
        destruct linearize_expr. 2:discriminate.
        inv EQ. inv H9.
        edestruct H1 as (y & Hxy & Hy). now eauto. apply H. apply H6.
        edestruct H2 as (y' & Hxy' & Hy'). now eauto. apply H. apply H8.
        simpl in Hxy, Hxy'.
        destruct (is_finite f1) eqn:FINf1. 2:discriminate.
        destruct (is_finite f2) eqn:FINf2. 2:discriminate.
        inv Hxy. inv Hxy'.
        assert (Float.cmp c f1 f2 = xorb b b0)
          by (destruct b, b0, Float.cmp; try discriminate; reflexivity).
        rewrite <- H3. clear H1 b b0 H0 H10 H3 H2. intros. simpl in H0.
        match type of H0 with
        | (do x <- ?O; _) = _ =>
          assert (forall i, O = Some i -> (f2 - f1) ∈ γ i); [|destruct O; inv H0]
        end.
        { clear H0. intros. unfold Float.cmp in H0.
          rewrite Bcompare_correct in H0 by auto.
          destruct c; simpl cmp_of_comparison in H0;
          destruct (Rcompare_spec f1 f2);
          inv H0; split;
          try reflexivity;
          unfold RF_le, Float.zero; simpl; try Psatz.lra;
          rewrite ?Ropp_minus_distr; apply double_min_delta_correct; auto. }
        specialize (H1 _ eq_refl).
        replace 0 with (f1 - f2 + (f2 - f1)) by ring.
        apply add_T_linear_expr_correct.
        apply add_T_linear_expr_correct, opp_T_linear_expr_correct; now auto.
        unfold eval_T_linear_expr. simpl. rewrite Telements_empty. apply H1.
    - inv H0. intros.
      match type of H0 with
      | (do x <- ?O; _) = _ =>
        assert (∀ x, O = Some x ->
                ∃ y, r_of_inumv) = Some y /\ y ∈ γ x); [|destruct O; inv H0]
      end.
      { intros. destructv).
        - destruct (is_finite f) eqn:FINf; inv H1. inv H0.
          simpl. rewrite FINf.
          eexists. split. eauto. unfold RF_le, Float.neg.
          rewrite B2R_Bopp, is_finite_Bopp, FINf. Psatz.lra.
        - inv H1. inv H0. eexists. split. reflexivity.
          eapply (fitv_of_zitv_correct (ZITV z z)).
          simpl; Psatz.lia. }
      unfold eval_T_linear_expr, eval_linear_expr. simpl. rewrite Telements_singleton.
      edestruct H1 as (y & Hy & Hxy). now eauto. clear H1. unfold rρ. simpl. rewrite Hy.
      split. 2:now auto. exists 1. split. compute; Psatz.lra.
      replace (0-1*y) with (-y) by ring. apply opp_itv_correct, Hxy.
    - apply (fitv_of_iitv_correct (NotBot (Just i))) in H0.
      destruct fitv_of_iitv; intros; inv H1.
      unfold eval_T_linear_expr. simpl. rewrite Telements_singleton. red. red. red.
      unfold rρ. destruct r_of_inum. 2:now auto. split. 2:now auto.
      exists 1. split. compute; Psatz.lra.
      replace (0-1*r) with (-r) by ring. apply opp_itv_correct, H0. }
  - destruct T_linear_expr_is_top. repeat constructor; auto.
    erewrite T_linear_expr_is_nonzero_correct by eauto. repeat constructor; simpl; auto.
  - repeat constructor. now auto.
Qed.

Program Instance abVarExpEq_env : ab_ideal_env t t :=
  {| id_leb := fun _ _ => true;
     id_top := fun _ => NotBot (tt, List.nil);
     id_join := fun _ _ _ => NotBot (tt, List.nil);
     id_init_iter := fun _ => tt;
     id_widen := fun _ _ _ => (tt, NotBot (tt, List.nil));
     assign := fun _ _ _ _ => NotBot (tt, List.nil);
     forget := fun _ _ _ => NotBot (tt, List.nil);
     enrich_query_chan :=
       fun _ c =>
         {| get_var_ty := c.(get_var_ty);
            get_itv := c.(get_itv);
            get_congr := c.(get_congr);
            get_eq_expr := c.(get_eq_expr);
            AbIdealEnv.linearize_expr e :=
              match linearize_expr c e with
              | None => None
              | Some le =>
                if T_linear_expr_is_top le then None else Some le
              end
         |};
     process_msg := process_msg |}.
Next Obligation.
split; constructor. Qed.
Next Obligation.
split; constructor. Qed.
Next Obligation.
split; constructor. Qed.
Next Obligation.
split; constructor. Qed.
Next Obligation.
split. constructor. split; constructor. Qed.
Next Obligation.
split; constructor. Qed.
Next Obligation.
split; constructor. Qed.
Next Obligation.
apply process_msg_correct; auto. Qed.
Next Obligation.
  constructor; try apply H0.
  simpl. intros.
  pose proof linearize_expr_correct chan e. destruct linearize_expr. 2:discriminate.
  destruct T_linear_expr_is_top; inv H1. auto.
Qed.

Opaque t.