From 8b287a0ca715ff35d57b2289e91f5f8288e68ec1 Mon Sep 17 00:00:00 2001
From: Heiko Becker <hbecker@mpi-sws.org>
Date: Fri, 6 May 2022 13:16:15 +0200
Subject: [PATCH] Add a flipped version of the IEEE connection

---
 hol4/IEEE_reverseScript.sml | 1381 +++++++++++++++++++++++++++++++++++
 1 file changed, 1381 insertions(+)
 create mode 100644 hol4/IEEE_reverseScript.sml

diff --git a/hol4/IEEE_reverseScript.sml b/hol4/IEEE_reverseScript.sml
new file mode 100644
index 0000000..48b2edb
--- /dev/null
+++ b/hol4/IEEE_reverseScript.sml
@@ -0,0 +1,1381 @@
+(**
+  Connect FloVer's idealized machine semantics to 64-bit
+  IEEE-754 floating-point semantics
+**)
+open machine_ieeeTheory binary_ieeeTheory lift_ieeeTheory realTheory RealArith
+                        realLib;
+open MachineTypeTheory ExpressionsTheory RealSimpsTheory FloverTactics
+     CertificateCheckerTheory FPRangeValidatorTheory IntervalValidationTheory
+     ExpressionAbbrevsTheory
+     ExpressionSemanticsTheory FloverMapTheory RealRangeArithTheory
+     TypeValidatorTheory ErrorValidationTheory IntervalArithTheory AbbrevsTheory
+     CommandsTheory ssaPrgsTheory EnvironmentsTheory FloverMapTheory IEEE_connectionTheory;
+open preambleFloVer;
+
+val _ = new_theory "IEEE_reverse";
+
+Overload abs[local] = “real$abs”
+
+Definition toFlExp_def:
+  toFlExp ((Var v):real expr) = Var v ∧
+  toFlExp (Const m c) = Const m (real_to_fp64 dmode c) ∧
+  toFlExp (Unop u e1) = Unop u (toFlExp e1) ∧
+  toFlExp (Binop b e1 e2) = Binop b (toFlExp e1) (toFlExp e2) ∧
+  toFlExp (Fma e1 e2 e3) = Fma (toFlExp e1) (toFlExp e2) (toFlExp e3) ∧
+  toFlExp (Downcast m e1) = Downcast m (toFlExp e1)
+End
+
+Definition toFlCmd_def:
+  toFlCmd (Let m x e g) = Let m x (toFlExp e) (toFlCmd g) ∧
+  toFlCmd (Ret e) = Ret (toFlExp e)
+End
+
+Definition toFlEnv_def:
+  toFlEnv (E:num -> real option) :num -> word64 option =
+    λ x. case E x of
+         | NONE => NONE
+         | SOME v => SOME (real_to_fp64 dmode v)
+End
+
+Theorem float_to_real_round_robin:
+  !(f:('a,'b) float).
+     float_is_finite f ⇒
+     float_to_real ((real_to_float dmode (float_to_real f)):('a,'b) float) =
+     (float_to_real f)
+Proof
+  rpt strip_tac
+  \\ fs[dmode_def, real_to_float_def, float_round_def]
+  \\ reverse $ Cases_on ‘float_is_zero f’
+  >- gs[round_finite_normal_float_id]
+  \\ ‘∃ s e m. f = <|Sign := s; Exponent := e; Significand := m |>’
+    by (Cases_on ‘f’ \\ gs[float_component_equality])
+  \\ ‘float_to_real f = 0’
+    by (rveq \\ gs[float_tests, float_to_real_def])
+  \\ simp[]
+  (* Just for simplicity, could also prove that condition is true... *)
+  \\ TOP_CASE_TAC \\ gs[]
+QED
+
+(** Some eval test
+EVAL “float_to_real ((real_to_float roundTiesToEven (float_to_real <|Sign := 1w; Exponent := 0w:word11; Significand := 0w:52 word |>)):(52,11) float”
+|> CONV_RULE (RHS_CONV $ RAND_CONV $ binary_ieeeLib.float_round_CONV)
+**)
+
+Theorem eval_expr_gives_IEEE_reverse:
+  !(e:real expr) E1 E2 Gamma vR A fVars dVars.
+    validTypes e Gamma /\
+    approxEnv E1 (toRExpMap Gamma) A fVars dVars E2 /\
+    validRanges e A E1 (toRTMap (toRExpMap Gamma)) /\
+    validErrorbound e Gamma A dVars /\
+    FPRangeValidator e A Gamma dVars /\
+    eval_expr E2 (toRExpMap Gamma) e vR M64 /\
+    domain (usedVars e) DIFF domain dVars ⊆ domain fVars ∧
+    is64BitEval e /\
+    is64BitEnv Gamma /\
+    noDowncast e /\
+    (∀ x v. E2 x = SOME v ⇒ float_to_real ((real_to_float dmode v):(52,11) float) = v) ∧
+    (∀v.
+      v ∈ domain dVars ⇒
+      ∃vF m.
+      (E2 v = SOME vF ∧ FloverMapTree_find (Var v) Gamma = SOME m ∧
+      validFloatValue vF m)) ==>
+    ?v.
+      eval_expr_float (toFlExp e) (toFlEnv E2) = SOME v /\
+      eval_expr E2 (toRExpMap Gamma) e (fp64_to_real v) M64
+Proof
+  Induct_on `e` \\ rw[toFlExp_def]
+  \\ inversion `eval_expr _ _ _ _ _` eval_expr_cases
+  \\ once_rewrite_tac [eval_expr_float_def]
+  \\ fs[noDowncast_def]
+  >- (
+    simp [toFlEnv_def, eval_expr_cases, fp64_to_real_def,
+        real_to_fp64_def, fp64_to_float_float_to_fp64]
+    \\ gs[usedVars_def]
+    \\ first_x_assum $ qspecl_then [‘n’, ‘vR’] assume_tac
+    \\ gs[])
+  >- (
+    simp[eval_expr_cases, fp64_to_real_def, real_to_fp64_def,
+         fp64_to_float_float_to_fp64, real_to_float_def, dmode_def,
+         float_round_def, float_is_zero_to_real]
+    \\ gs[perturb_def]
+    \\ ‘eval_expr E2 (toRExpMap Gamma) (Const M64 v) v M64’
+      by (gs[eval_expr_cases]
+          \\ qexists_tac ‘0’ \\ gs[perturb_def, mTypeToR_pos])
+    \\ ‘validFloatValue v M64’
+       by (drule FPRangeValidator_sound
+           \\ disch_then $ qspecl_then [‘Const M64 v’, ‘v’, ‘M64’] mp_tac
+           \\ gs[eval_expr_cases] \\ impl_tac \\ gs[]
+           \\ qexists_tac ‘0’ \\ gs[perturb_def, mTypeToR_pos])
+    \\ first_x_assum $ assume_tac o SIMP_RULE std_ss [validFloatValue_def]
+    \\ gs[]
+    >- (
+      ‘normalizes (:52 # 11) v’ by gs[normalValue_implies_normalization]
+      \\ imp_res_tac relative_error
+      \\ qexists_tac ‘e’ \\ simp[float_is_zero_to_real]
+      \\ ‘~ (v = 0 ∨ 1 + e = 0)’
+        by (CCONTR_TAC \\ gs[] \\ rveq
+            >- (gs[normalizes_def]
+                \\ ‘0 < inv (2 pow 1022)’ by gs[]
+                \\ ‘0 < 0’ suffices_by gs[]
+                \\ irule REAL_LTE_TRANS \\ first_assum $ irule_at Any
+                \\ gs[])
+            \\ ‘e = -1’ by (pop_assum $ mp_tac \\ REAL_ARITH_TAC)
+            \\ ‘1 /2 pow 53 < 1’ by (
+              irule REAL_LTE_TRANS \\ qexists_tac ‘1/1’
+              \\ reverse conj_tac >- gs[]
+              \\ rewrite_tac [real_div]
+              \\ irule REAL_LT_LMUL_IMP \\ reverse conj_tac >- gs[]
+              \\ irule REAL_LT_INV \\ gs[]
+              \\ irule REAL_LT_TRANS \\ qexists_tac ‘&53’
+              \\ gs[POW_2_LT])
+            \\ ‘1 < 1:real’ suffices_by gs[]
+            \\ irule REAL_LET_TRANS \\ first_x_assum $ irule_at Any
+            \\ gs[])
+      \\ gs[perturb_def]
+      \\ imp_res_tac normal_not_denormal \\ gs[]
+      \\ fs[REAL_INV_1OVER, mTypeToR_def, isCompat_def])
+    >- (
+      gs[perturb_def, mTypeToR_def, float_is_zero_to_real]
+      \\ qspec_then ‘v’ mp_tac (Q.INST_TYPE [‘:'t’|->‘:52’,‘:'w’|->‘:11’] absolute_error_denormal)
+      \\ impl_tac
+      >- (‘abs v < 2 / 2 pow (INT_MAX (:11) - 1)’ by (
+           fs[denormalTranslatedValue_implies_finiteness,
+              float_is_finite, denormal_def, minValue_pos_def]
+           \\ rewrite_tac [INT_MAX_def, INT_MIN_def, dimindex_11, EVAL “2 ** (11 - 1) - 1 - 1”]
+           \\ irule REAL_LT_TRANS
+           \\ first_assum $ irule_at Any
+           \\ fs[minExponentPos_def])
+          \\ rpt conj_tac >- gs[]
+          >~ [‘1 < INT_MAX (:11)’] >- gs[]
+          \\ irule REAL_LET_TRANS \\ qexists_tac ‘2 / 2 pow (INT_MAX (:11) - 1)’
+          \\ reverse conj_tac
+          >- (pop_assum $ mp_tac \\ REAL_ARITH_TAC)
+          \\ gs[threshold_def]
+          \\ rewrite_tac [REAL_INV_1OVER] \\ EVAL_TAC)
+      \\ rpt strip_tac \\ qexists_tac ‘e’ \\ gs[]
+      \\ TOP_CASE_TAC \\ gs[minExponentPos_def])
+    >- (gvs[denormal_def] \\ qexists_tac ‘0’ \\ gs[mTypeToR_pos])
+  )
+  >- (
+    gs[eval_expr_float_def, eval_expr_cases]
+    \\ qpat_x_assum ‘validTypes _ _’ $ assume_tac o ONCE_REWRITE_RULE [validTypes_def]
+    \\ gs[]
+    \\ first_x_assum $ drule_then drule
+    \\ qpat_x_assum ‘validRanges _ _ _ _’ $ assume_tac o ONCE_REWRITE_RULE [validRanges_def]
+    \\ gs[]
+    \\ qpat_x_assum ‘validErrorbound _ _ _ _’ $ assume_tac o ONCE_REWRITE_RULE [validErrorbound_def]
+    \\ gs[]
+    \\ ‘FPRangeValidator e A Gamma dVars’ by (
+      qpat_x_assum ‘FPRangeValidator _ _ _ _’ $ assume_tac o ONCE_REWRITE_RULE [FPRangeValidator_def]
+      \\ gs[])
+    \\ gs[]
+    \\ ‘m' = M64’ by (Cases_on ‘m'’ \\ gs[isCompat_def])
+    \\ gvs[]
+    \\ disch_then drule \\ impl_tac
+    >- (gs[Once usedVars_def, Once is64BitEval_def] \\ metis_tac[])
+    \\ strip_tac \\ gs[eval_expr_float_def]
+    \\ simp[Once eval_expr_cases]
+    \\ first_x_assum $ irule_at Any \\ gs[isCompat_def]
+    \\ gs[fp64_negate_def, fp64_to_real_def, fp64_to_float_float_to_fp64,
+          evalUnop_def, float_to_real_negate])
+  >- (
+    qpat_x_assum ‘validErrorbound _ _ _ _’ $
+      mp_tac o ONCE_REWRITE_RULE [validErrorbound_def]
+    \\ fs[option_case_eq] \\ rpt (TOP_CASE_TAC \\ fs[]))
+  >- (
+    gs[eval_expr_float_def, eval_expr_cases]
+    \\ qpat_x_assum ‘validTypes _ _’ $ assume_tac o ONCE_REWRITE_RULE [validTypes_def]
+    \\ gs[]
+    \\ first_x_assum $ drule_then drule
+    \\ qpat_x_assum ‘validRanges _ _ _ _’ $ assume_tac o ONCE_REWRITE_RULE [validRanges_def]
+    \\ gs[]
+    \\ qpat_x_assum ‘validErrorbound _ _ _ _’ $ assume_tac o ONCE_REWRITE_RULE [validErrorbound_def]
+    \\ gs[]
+    \\ Cases_on ‘FloverMapTree_find e A’ \\ gs[] \\ PairCases_on ‘x’ \\ gs[]
+    \\ ‘FPRangeValidator e A Gamma dVars’ by (
+      qpat_x_assum ‘FPRangeValidator _ _ _ _’ $ assume_tac o ONCE_REWRITE_RULE [FPRangeValidator_def]
+      \\ gs[])
+    \\ gs[]
+    \\ ‘m1 = M64’ by (Cases_on ‘m1’ \\ gs[isCompat_def])
+    \\ gvs[] \\ disch_then drule \\ impl_tac
+    >- (gs[Once usedVars_def, Once is64BitEval_def] \\ metis_tac[])
+    \\ strip_tac \\ gs[eval_expr_float_def]
+    \\ first_assum $ irule_at Any \\ gs[isCompat_def]
+    \\ gs[fp64_to_real_def, fp64_sqrt_def, fp64_to_float_float_to_fp64,
+          dmode_def, evalUnop_def]
+    \\ ‘validFloatValue (float_to_real (fp64_to_float v)) M64’
+       by (drule FPRangeValidator_sound
+           \\ rpt $ disch_then drule \\ impl_tac
+           >- gs[Once usedVars_def]
+           \\ gs[])
+    \\ qpat_assum ‘validRanges e _ _ _’ $ mp_then Any strip_assume_tac validRanges_single
+    \\ rename [‘eval_expr E1 _ (toREval e) vR_e REAL’, ‘FloverMapTree_find e A = SOME (iv_e, err_e)’]
+    \\ ‘contained (float_to_real (fp64_to_float v))
+        (widenInterval (FST iv_e, SND iv_e) err_e)’
+      by (
+        irule distance_gives_iv
+        \\ qexists_tac `vR_e` \\ gvs[contained_def]
+        \\ drule validErrorbound_sound
+        \\ disch_then drule \\ gs[eval_Real_def, eval_Fin_def]
+        \\ disch_then $ qspec_then ‘vR_e’ mp_tac \\ impl_tac
+        >- gs[Once usedVars_def]
+        \\ strip_tac
+        \\ first_x_assum irule
+        \\ qexists_tac `M64`  \\ gs[])
+    \\ ‘0 < FST (widenInterval (FST iv_e, SND iv_e) err_e)’
+      by (
+      qpat_x_assum ‘validErrorbound _ _ _ _’
+      (fn thm => mp_tac (ONCE_REWRITE_RULE [validErrorbound_def] thm))
+      \\ fs[option_case_eq] \\ rpt (TOP_CASE_TAC \\ fs[]))
+    \\ ‘0 < float_to_real (fp64_to_float v)’
+      by (gs[contained_def, widenInterval_def] \\ irule REAL_LTE_TRANS
+            \\ asm_exists_tac \\ gs[])
+    \\ ‘(fp64_to_float v).Sign = 0w’
+      by imp_res_tac zero_lt_sign_zero
+    \\ ‘validFloatValue (evalUnop Sqrt (float_to_real (fp64_to_float v))) M64’
+      by (
+        drule FPRangeValidator_sound
+        \\ disch_then
+           (qspecl_then
+            [`Unop Sqrt e`,
+             `evalUnop Sqrt (float_to_real (fp64_to_float v))`, `M64`]
+            irule)
+        \\ gvs[]
+        \\ qexists_tac ‘e’ \\ fs[]
+        \\ rpt conj_tac
+        >- (simp[Once validTypes_def, isCompat_def] \\ first_x_assum MATCH_ACCEPT_TAC)
+        >- simp[Once validErrorbound_def]
+        >- (simp[Once validRanges_def] \\ asm_exists_tac \\ fs[]
+            \\ fs[] \\ rveq \\ fs[])
+        \\ irule Unop_sqrt'
+        \\ qexistsl_tac [‘0’, `M64`, ‘M64’, `float_to_real (fp64_to_float v)`]
+        \\ fs[perturb_def, evalUnop_def, mTypeToR_pos, isCompat_def])
+    \\ qpat_x_assum `validFloatValue (evalUnop _ _) M64` $
+                    assume_tac o SIMP_RULE std_ss [validFloatValue_def]
+    \\ gs[]
+    (* normal sqrt *)
+    >- (
+      Q.ISPEC_THEN `(fp64_to_float v):(52,11) float`
+                                      impl_subgoal_tac
+                                      float_sqrt_relative
+    >- (rpt conj_tac
+        \\ fs[validFloatValue_def,
+              normalTranslatedValue_implies_finiteness,
+              denormalTranslatedValue_implies_finiteness,
+              normalValue_implies_normalization,
+              GSYM float_is_zero_to_real, float_is_finite, evalUnop_def,
+              sqrtable_def,
+              normalizes_def])
+        \\ gvs[dmode_def] \\ qexists_tac ‘e'’
+        \\ fs[perturb_def, evalUnop_def]
+        \\ imp_res_tac normal_not_denormal \\ simp[]
+        \\ simp[REAL_INV_1OVER, mTypeToR_def, isCompat_def])
+    (* denormal sqrt *)
+    >- (
+      Q.ISPEC_THEN `(fp64_to_float v):(52,11) float`
+                                      impl_subgoal_tac
+                                      float_sqrt_relative_denorm
+      >- (
+        rpt conj_tac
+        >- fs[validFloatValue_def,
+              normalTranslatedValue_implies_finiteness,
+              denormalTranslatedValue_implies_finiteness,
+              normalValue_implies_normalization, float_is_finite,
+              GSYM float_is_zero_to_real, evalUnop_def]
+        >- fs[sqrtable_def]
+        >- (
+          fs[normalTranslatedValue_implies_finiteness,
+             denormalTranslatedValue_implies_finiteness,
+             normalValue_implies_normalization, float_is_finite,
+             GSYM float_is_zero_to_real, evalUnop_def, denormal_def, minValue_pos_def]
+          \\ irule REAL_LT_TRANS
+          \\ qexists_tac ‘abs (sqrt (float_to_real (fp64_to_float v)))* &(2 ** minExponentPos M64)’
+          \\ gs[] \\ EVAL_TAC)
+        >- (
+          irule REAL_LT_TRANS \\ qexists_tac ‘maxValue M64’
+          \\ fs[threshold_64_bit_lt_maxValue, denormal_def]
+          \\ irule REAL_LTE_TRANS \\ qexists_tac ‘minValue_pos M64’
+          \\ fs[minValue_pos_def, maxValue_def, GSYM REAL_OF_NUM_POW, evalUnop_def]
+          \\ EVAL_TAC)
+        \\ fs[INT_MAX_def, INT_MIN_def, dimindex_11])
+      \\ gvs[dmode_def] \\ qexists_tac ‘e'’
+      \\ fs[perturb_def, evalUnop_def]
+      \\ simp[REAL_INV_1OVER, mTypeToR_def, minExponentPos_def])
+    (* sqrt 0 *)
+    \\ ‘0 < sqrt (float_to_real (fp64_to_float v))’ by (irule SQRT_POS_LT \\ gs[])
+    \\ gs[evalUnop_def])
+  >~ [‘Binop b e1 e2’]
+  >- (
+    imp_res_tac validRanges_single
+    \\ rw_thm_asm `validTypes _ _` validTypes_def
+    \\ rw_thm_asm `validRanges _ _ _ _` validRanges_def
+    \\ fs[eval_expr_float_def, optionLift_def]
+    \\ imp_res_tac validTypes_exec
+    \\ rveq
+    \\ `m1 = M64 /\ m2 = M64`
+      by (fs[is64BitEnv_def]
+          \\ conj_tac \\ first_x_assum irule \\ find_exists_tac \\ fs[])
+    \\ rveq
+    \\ rw_thm_asm `is64BitEval _` is64BitEval_def \\ fs[]
+    \\ ntac 2
+      (first_x_assum
+       (qspecl_then [`E1`, `E2`, `Gamma`] assume_tac))
+    \\ first_x_assum (qspecl_then [`v1`, `A`, `fVars`, `dVars`] destruct)
+    >- (
+      rpt conj_tac \\ fs[]
+      >- (
+        qpat_x_assum ‘validErrorbound _ _ _ _’
+        (fn thm => mp_tac (ONCE_REWRITE_RULE [validErrorbound_def] thm))
+        \\ fs[option_case_eq] \\ rpt (TOP_CASE_TAC \\ fs[]))
+      >- (
+        rw_thm_asm `FPRangeValidator _ _ _ _` FPRangeValidator_def
+        \\ fs[] \\ rveq
+        \\ rw_asm_star `FloverMapTree_find (Binop _ _ _) A = _`
+        \\ rw_asm_star `FloverMapTree_find (Binop _ _ _) Gamma = _`)
+      \\ rw_thm_asm `domain (usedVars _) DIFF _ SUBSET _` usedVars_def
+      \\ fs[domain_union, DIFF_DEF, SUBSET_DEF, Once usedVars_def]
+      \\ rpt strip_tac \\ first_x_assum irule \\ simp[Once usedVars_def])
+    \\ first_x_assum (qspecl_then [`v2`, `A`, `fVars`, `dVars`] destruct)
+    >- (
+      rpt conj_tac \\ fs[]
+      >- (
+        qpat_x_assum ‘validErrorbound _ _ _ _’
+        (fn thm => mp_tac (ONCE_REWRITE_RULE [validErrorbound_def] thm))
+        \\ fs[option_case_eq] \\ rpt (TOP_CASE_TAC \\ fs[]))
+      >- (
+        rw_thm_asm `FPRangeValidator _ _ _ _` FPRangeValidator_def
+        \\ fs[] \\ rveq
+        \\ rw_asm_star `FloverMapTree_find (Binop _ _ _) A = _`
+        \\ rw_asm_star `FloverMapTree_find (Binop _ _ _) Gamma = _`)
+      \\ rw_thm_asm `domain (usedVars _) DIFF _ SUBSET _` usedVars_def
+      \\ fs[domain_union, DIFF_DEF, SUBSET_DEF])
+    \\ fs[]
+    \\ rename1 `eval_expr_float (toFlExp e1) _ = SOME vF1`
+    \\ rename1 `eval_expr_float (toFlExp e2) _ = SOME vF2`
+    \\ imp_res_tac validRanges_single
+    \\ rename1 `FloverMapTree_find e2 A = SOME (iv2, err2)`
+    \\ rename1 `FloverMapTree_find e1 A = SOME (iv1, err1)`
+    \\ rename1 `eval_expr E1 _ (toREval e2) nR2 REAL`
+    \\ rename1 `eval_expr E1 _ (toREval e1) nR1 REAL`
+    (* Obtain evaluation for E2 *)
+    \\ ‘!vF2 m2. eval_expr E2 (toRExpMap Gamma) e2 vF2 m2 ==>
+       abs (nR2 - vF2) <= err2’
+      by (qspecl_then [`e2`, `E1`, `E2`, `A`,`nR2`,
+                       `err2`, `FST iv2`, `SND iv2`, `fVars`,
+                       `dVars`,`Gamma`] destruct validErrorbound_sound
+          \\ rpt conj_tac \\ fs[]
+          >- (fs [DIFF_DEF, SUBSET_DEF]
+              \\ rpt strip_tac \\ first_x_assum irule
+              \\ once_rewrite_tac [usedVars_def] \\ fs[domain_union])
+        \\ qpat_x_assum ‘validErrorbound _ _ _ _’
+        (fn thm => mp_tac (ONCE_REWRITE_RULE [validErrorbound_def] thm))
+        \\ fs[option_case_eq] \\ rpt (TOP_CASE_TAC \\ fs[]))
+      \\ `contained (float_to_real (fp64_to_float vF2))
+            (widenInterval (FST iv2, SND iv2) err2)`
+        by (irule distance_gives_iv
+            \\ qexists_tac `nR2` \\ fs [contained_def]
+            \\ first_x_assum irule
+            \\ qexists_tac `M64` \\ gs[fp64_to_real_def])
+    \\ `b = Div ==> float_to_real (fp64_to_float vF2) <> 0`
+      by (strip_tac \\ rveq
+          \\ qpat_x_assum ‘validErrorbound _ _ _ _’
+            (fn thm => mp_tac (ONCE_REWRITE_RULE [validErrorbound_def] thm))
+          \\ fs[widenInterval_def, contained_def, noDivzero_def]
+          \\ rpt strip_tac
+          >- (CCONTR_TAC \\ fs[] \\ rveq
+              \\ `0 < 0:real`
+                by (irule REAL_LET_TRANS
+                    \\ qexists_tac `SND iv2 + err2` \\ fs[])
+              \\ fs[])
+          >- (CCONTR_TAC \\ fs[] \\ rveq
+              \\ `0 < 0:real`
+                by (irule REAL_LTE_TRANS
+                    \\ qexists_tac `FST iv2 - err2` \\ fs[])
+              \\ fs[])
+          >- (CCONTR_TAC \\ fs[] \\ rveq
+              \\ `0 < 0:real`
+                by (irule REAL_LET_TRANS
+                    \\ qexists_tac `SND iv2 + err2` \\ fs[])
+              \\ fs[])
+          \\ CCONTR_TAC \\ fs[] \\ rveq
+          \\ `0 < 0:real`
+            by (irule REAL_LTE_TRANS
+                \\ qexists_tac `FST iv2 - err2` \\ fs[])
+          \\ fs[])
+      \\ `validFloatValue
+            (evalBinop b (float_to_real (fp64_to_float vF1))
+             (float_to_real (fp64_to_float vF2))) M64`
+          by (drule FPRangeValidator_sound
+              \\ disch_then
+                   (qspecl_then
+                     [`Binop b e1 e2`,
+                      `evalBinop b (float_to_real (fp64_to_float vF1))
+                        (float_to_real (fp64_to_float vF2))`,
+                      `M64`] irule)
+               \\ fs[]
+               \\ qexistsl_tac [`e1`, `e2`] \\ fs[]
+               \\ rpt conj_tac
+               >- (simp[Once validTypes_def] \\ first_x_assum MATCH_ACCEPT_TAC)
+               >- (simp[Once validRanges_def] \\ asm_exists_tac \\ fs[]
+                   \\ fs[] \\ rveq \\ fs[])
+              \\ irule Binop_dist'
+              \\ qexistsl_tac [‘0’, `M64`, `M64`, ‘M64’, `float_to_real (fp64_to_float vF1)`,
+                                `float_to_real (fp64_to_float vF2)`]
+              \\ Cases_on `b`
+              \\ fs[perturb_def, evalBinop_def, mTypeToR_pos, fp64_to_real_def])
+      \\ `validFloatValue (float_to_real (fp64_to_float vF1)) M64`
+           by (drule FPRangeValidator_sound
+               \\ disch_then
+                    (qspecl_then
+                       [`e1`, `float_to_real (fp64_to_float vF1)`,
+                         `M64`] irule)
+               \\ fs[]
+               \\ qexistsl_tac [`e1`] \\ fs[]
+               \\ rpt conj_tac
+               >- (rw_thm_asm `domain (usedVars _) DIFF _ SUBSET _` usedVars_def
+                   \\ fs[domain_union, DIFF_DEF, SUBSET_DEF, Once usedVars_def])
+               >- (rw_thm_asm `FPRangeValidator _ _ _ _` FPRangeValidator_def
+                   \\ fs[] \\ rveq
+                   \\ rw_asm_star `FloverMapTree_find (Binop _ _ _) A = _`
+                   \\ rw_asm_star `FloverMapTree_find (Binop _ _ _) Gamma = _`)
+               >- (
+                qpat_x_assum ‘validErrorbound _ _ _ _’
+                (fn thm => mp_tac (ONCE_REWRITE_RULE [validErrorbound_def] thm))
+                \\ fs[option_case_eq] \\ rpt (TOP_CASE_TAC \\ fs[]))
+               \\ gs[fp64_to_real_def])
+      \\ `validFloatValue (float_to_real (fp64_to_float vF2)) M64`
+           by (drule FPRangeValidator_sound
+               \\ disch_then
+                    (qspecl_then
+                       [`e2`, `float_to_real (fp64_to_float vF2)`,
+                         `M64`] irule)
+               \\ fs[]
+               \\ qexists_tac `e2` \\ fs[]
+               \\ rpt conj_tac
+               >- (rw_thm_asm `domain (usedVars _) DIFF _ SUBSET _` usedVars_def
+                   \\ fs[domain_union, DIFF_DEF, SUBSET_DEF])
+               >- (rw_thm_asm `FPRangeValidator _ _ _ _` FPRangeValidator_def
+                   \\ fs[] \\ rveq
+                   \\ rw_asm_star `FloverMapTree_find (Binop _ _ _) A = _`
+                   \\ rw_asm_star `FloverMapTree_find (Binop _ _ _) Gamma = _`)
+               >- (
+                qpat_x_assum ‘validErrorbound _ _ _ _’
+                (fn thm => mp_tac (ONCE_REWRITE_RULE [validErrorbound_def] thm))
+                \\ fs[option_case_eq] \\ rpt (TOP_CASE_TAC \\ fs[]))
+               \\ gs[fp64_to_real_def])
+    \\ qpat_x_assum `validFloatValue (evalBinop _ _ _) M64`
+                    $ assume_tac o SIMP_RULE std_ss [validFloatValue_def]
+    (** Case distinction for operator **)
+    \\ Cases_on `b` \\ fs[GSYM noDivzero_def, optionLift_def, PULL_EXISTS]
+    \\ simp[Once eval_expr_cases]
+    (* Addition, result normal *)
+    >- (
+      fs[fp64_add_def, fp64_to_float_float_to_fp64, evalBinop_def]
+      \\ Q.ISPECL_THEN [`(fp64_to_float vF1):(52,11) float`,
+                          `(fp64_to_float vF2):(52,11) float`]
+          impl_subgoal_tac
+          float_add_relative
+      >- (
+        rpt conj_tac
+        \\ fs[validFloatValue_def,
+              normalTranslatedValue_implies_finiteness,
+              denormalTranslatedValue_implies_finiteness,
+              normalValue_implies_normalization,
+              GSYM float_is_zero_to_real, float_is_finite, evalBinop_def])
+      \\ fs[dmode_def]
+      \\ qexistsl_tac [`M64`, `M64`, `float_to_real (fp64_to_float vF1)`,
+                       `float_to_real (fp64_to_float vF2)`, `e`]
+      \\ fs[perturb_def, evalBinop_def]
+      \\ imp_res_tac normal_not_denormal
+      \\ simp[REAL_INV_1OVER, mTypeToR_def, fp64_to_real_def, fp64_to_float_float_to_fp64]
+      \\ gs[fp64_to_real_def])
+      (* addition, result denormal *)
+    >- (
+      fs[fp64_add_def, fp64_to_float_float_to_fp64, evalBinop_def]
+      \\ Q.ISPECL_THEN [`(fp64_to_float vF1):(52,11) float`,
+                        `(fp64_to_float vF2):(52,11) float`]
+          impl_subgoal_tac
+          float_add_relative_denorm
+      >- (
+        rpt conj_tac
+        >- fs[validFloatValue_def,
+              normalTranslatedValue_implies_finiteness,
+              denormalTranslatedValue_implies_finiteness,
+              normalValue_implies_normalization, float_is_finite,
+              GSYM float_is_zero_to_real, evalBinop_def]
+        >- fs[validFloatValue_def,
+              normalTranslatedValue_implies_finiteness,
+              denormalTranslatedValue_implies_finiteness,
+              normalValue_implies_normalization, float_is_finite,
+              GSYM float_is_zero_to_real, evalBinop_def]
+        >- (
+          fs[normalTranslatedValue_implies_finiteness,
+             denormalTranslatedValue_implies_finiteness,
+             normalValue_implies_normalization, float_is_finite,
+             GSYM float_is_zero_to_real, evalBinop_def, denormal_def, minValue_pos_def]
+          \\ irule REAL_LT_TRANS
+          \\ qpat_x_assum ‘abs _  * _ < 1’ $ irule_at Any
+          \\ fs[GSYM REAL_OF_NUM_POW, minExponentPos_def])
+        >- (
+          irule REAL_LT_TRANS \\ qexists_tac ‘maxValue M64’
+          \\ fs[threshold_64_bit_lt_maxValue, denormal_def]
+          \\ irule REAL_LTE_TRANS \\ qexists_tac ‘minValue_pos M64’
+          \\ fs[minValue_pos_def, maxValue_def, GSYM REAL_OF_NUM_POW]
+          \\ EVAL_TAC)
+        \\ fs[INT_MAX_def, INT_MIN_def, dimindex_11])
+      \\ fs[dmode_def]
+      \\ qexistsl_tac [`M64`, `M64`, `float_to_real (fp64_to_float vF1)`,
+                       `float_to_real (fp64_to_float vF2)`, `e`]
+      \\ fs[perturb_def, evalBinop_def]
+      \\ fs[mTypeToR_def, minExponentPos_def, REAL_INV_1OVER, fp64_to_real_def,
+            fp64_to_float_float_to_fp64])
+      (* result = 0 *)
+      >- (IMP_RES_TAC validValue_gives_float_value
+          \\ fs[REAL_LNEG_UNIQ, evalBinop_def]
+          \\ fs[fp64_add_def, dmode_def, fp64_to_float_float_to_fp64]
+          \\ fs[float_add_def, float_round_with_flags_def]
+          \\ qexistsl_tac [`M64`, `M64`, `float_to_real (fp64_to_float vF1)`,
+                           `float_to_real (fp64_to_float vF2)`, `0:real`]
+          \\ fs[perturb_def, mTypeToR_pos, evalBinop_def]
+          \\ `2 * abs (0:real) <= ulp (:52 #11)`
+            by (fs[REAL_ABS_0, ulp_def, ULP_def, REAL_OF_NUM_POW]
+                \\ once_rewrite_tac [real_div]
+                \\ irule REAL_LE_MUL \\ fs[]
+                \\ irule REAL_LE_INV \\ fs[])
+          \\ fs[float_to_real_round_zero_is_zero, fp64_to_real_def,
+                fp64_to_float_float_to_fp64])
+      (* Subtraction, normal value *)
+    >- (
+      fs[fp64_sub_def, fp64_to_float_float_to_fp64, evalBinop_def]
+      \\ Q.ISPECL_THEN [`(fp64_to_float vF1):(52,11) float`,
+                                             `(fp64_to_float vF2):(52,11) float`]
+          impl_subgoal_tac
+          float_sub_relative
+      >- (
+        rpt conj_tac
+        \\ fs[validFloatValue_def,
+              normalTranslatedValue_implies_finiteness,
+              denormalTranslatedValue_implies_finiteness,
+              normalValue_implies_normalization,
+              GSYM float_is_zero_to_real, float_is_finite, evalBinop_def])
+      \\ fs[dmode_def]
+      \\ qexistsl_tac [`M64`, `M64`, `float_to_real (fp64_to_float vF1)`,
+                       `float_to_real (fp64_to_float vF2)`, `e`]
+      \\ fs[perturb_def, evalBinop_def]
+      \\ imp_res_tac normal_not_denormal
+      \\ simp[REAL_INV_1OVER, mTypeToR_def, fp64_to_real_def, fp64_to_float_float_to_fp64]
+      \\ gs[fp64_to_real_def])
+      (* Subtraction, denormal value *)
+    >- (
+      fs[fp64_sub_def, fp64_to_float_float_to_fp64, evalBinop_def]
+      \\ Q.ISPECL_THEN [`(fp64_to_float vF1):(52,11) float`,
+                                             `(fp64_to_float vF2):(52,11) float`]
+          impl_subgoal_tac
+          float_sub_relative_denorm
+      >- (
+        rpt conj_tac
+        >- fs[validFloatValue_def,
+              normalTranslatedValue_implies_finiteness,
+              denormalTranslatedValue_implies_finiteness,
+              normalValue_implies_normalization, float_is_finite,
+              GSYM float_is_zero_to_real, evalBinop_def]
+        >- fs[validFloatValue_def,
+              normalTranslatedValue_implies_finiteness,
+              denormalTranslatedValue_implies_finiteness,
+              normalValue_implies_normalization, float_is_finite,
+              GSYM float_is_zero_to_real, evalBinop_def]
+        >- (
+          fs[normalTranslatedValue_implies_finiteness,
+             denormalTranslatedValue_implies_finiteness,
+             normalValue_implies_normalization, float_is_finite,
+             GSYM float_is_zero_to_real, evalBinop_def, denormal_def, minValue_pos_def]
+          \\ irule REAL_LT_TRANS
+          \\ qpat_x_assum ‘abs _ * _ < 1’ $ irule_at Any
+          \\ gs[] \\ EVAL_TAC)
+        >- (
+          irule REAL_LT_TRANS \\ qexists_tac ‘maxValue M64’
+          \\ fs[threshold_64_bit_lt_maxValue, denormal_def]
+          \\ irule REAL_LTE_TRANS \\ qexists_tac ‘minValue_pos M64’
+          \\ fs[minValue_pos_def, maxValue_def, GSYM REAL_OF_NUM_POW]
+          \\ EVAL_TAC)
+        \\ fs[INT_MAX_def, INT_MIN_def, dimindex_11])
+      \\ fs[dmode_def]
+      \\ qexistsl_tac [`M64`, `M64`, `float_to_real (fp64_to_float vF1)`,
+                       `float_to_real (fp64_to_float vF2)`, `e`]
+      \\ fs[perturb_def, evalBinop_def, fp64_to_real_def, fp64_to_float_float_to_fp64]
+      \\ fs[mTypeToR_def, minExponentPos_def, REAL_INV_1OVER])
+      (* subtraction, result = 0 *)
+    >- (
+      fs[evalBinop_def]
+      \\ qpat_x_assum `float_to_real (fp64_to_float _) = _` MP_TAC
+      \\ simp[real_sub, REAL_LNEG_UNIQ, evalBinop_def]
+      \\ fs[fp64_sub_def, dmode_def, fp64_to_float_float_to_fp64]
+      \\ fs[float_sub_def]
+      \\ fs[perturb_def, mTypeToR_pos, evalBinop_def]
+      \\ fs[validValue_gives_float_value, float_round_with_flags_def]
+      \\ strip_tac
+      \\ qexistsl_tac [`M64`, `M64`, `float_to_real (fp64_to_float vF1)`,
+                       `float_to_real (fp64_to_float vF2)`, `0:real`]
+      \\ fs[perturb_def, mTypeToR_pos, evalBinop_def]
+      \\ fs[validValue_gives_float_value, float_round_with_flags_def]
+      \\ `2 * abs (0:real) <= ulp (:52 #11)`
+        by (fs[REAL_ABS_0, ulp_def, ULP_def, REAL_OF_NUM_POW]
+            \\ once_rewrite_tac [real_div]
+            \\ irule REAL_LE_MUL \\ fs[]
+            \\ irule REAL_LE_INV \\ fs[])
+      \\ fs[ float_to_real_round_zero_is_zero, fp64_to_real_def, fp64_to_float_float_to_fp64])
+    (* Multiplication, normal result *)
+    >- (
+      fs[fp64_mul_def, fp64_to_float_float_to_fp64, evalBinop_def]
+      \\ Q.ISPECL_THEN [`(fp64_to_float vF1):(52,11) float`,
+                        `(fp64_to_float vF2):(52,11) float`]
+          impl_subgoal_tac
+          float_mul_relative
+      >- (
+        rpt conj_tac
+        \\ fs[validFloatValue_def,
+              normalTranslatedValue_implies_finiteness,
+              denormalTranslatedValue_implies_finiteness,
+              normalValue_implies_normalization,
+              GSYM float_is_zero_to_real, float_is_finite, evalBinop_def])
+      \\ fs[dmode_def]
+      \\ qexistsl_tac [`M64`, `M64`, `float_to_real (fp64_to_float vF1)`,
+                       `float_to_real (fp64_to_float vF2)`, `e`]
+      \\ fs[perturb_def, evalBinop_def]
+      \\ imp_res_tac normal_not_denormal
+      \\ fs[mTypeToR_def, REAL_INV_1OVER, fp64_to_real_def, fp64_to_float_float_to_fp64])
+    (* Multiplication, denormal result *)
+    >- (
+      fs[fp64_mul_def, fp64_to_float_float_to_fp64, evalBinop_def]
+      \\ Q.ISPECL_THEN [`(fp64_to_float vF1):(52,11) float`,
+                        `(fp64_to_float vF2):(52,11) float`]
+          impl_subgoal_tac
+          float_mul_relative_denorm
+      >- (
+        rpt conj_tac
+        >- fs[validFloatValue_def,
+              normalTranslatedValue_implies_finiteness,
+              denormalTranslatedValue_implies_finiteness,
+              normalValue_implies_normalization, float_is_finite,
+              GSYM float_is_zero_to_real, evalBinop_def]
+        >- fs[validFloatValue_def,
+              normalTranslatedValue_implies_finiteness,
+              denormalTranslatedValue_implies_finiteness,
+              normalValue_implies_normalization, float_is_finite,
+              GSYM float_is_zero_to_real, evalBinop_def]
+        >- (
+          fs[normalTranslatedValue_implies_finiteness,
+             denormalTranslatedValue_implies_finiteness,
+             normalValue_implies_normalization, float_is_finite,
+             GSYM float_is_zero_to_real, evalBinop_def, denormal_def, minValue_pos_def]
+          \\ irule REAL_LT_TRANS
+          \\ qpat_x_assum ‘abs _ * _ < 1’ $ irule_at Any
+          \\ gs[minExponentPos_def]
+          \\ irule REAL_LET_TRANS
+          \\ qexists_tac ‘1 * abs (float_to_real (fp64_to_float vF1) * float_to_real (fp64_to_float vF2)) ’
+          \\ reverse conj_tac >- gs[]
+          \\ irule REAL_LT_RMUL_IMP \\ fs[float_is_zero_def]
+          \\ imp_res_tac validValue_gives_float_value \\ gvs[])
+        >- (
+          irule REAL_LT_TRANS \\ qexists_tac ‘maxValue M64’
+          \\ fs[threshold_64_bit_lt_maxValue, denormal_def]
+          \\ irule REAL_LTE_TRANS \\ qexists_tac ‘minValue_pos M64’
+          \\ fs[minValue_pos_def, maxValue_def, GSYM REAL_OF_NUM_POW]
+          \\ EVAL_TAC)
+        \\ fs[INT_MAX_def, INT_MIN_def, dimindex_11])
+      \\ fs[dmode_def]
+      \\ qexistsl_tac [`M64`, `M64`, `float_to_real (fp64_to_float vF1)`,
+                       `float_to_real (fp64_to_float vF2)`, `e`]
+      \\ fs[perturb_def, evalBinop_def, fp64_to_real_def, fp64_to_float_float_to_fp64]
+      \\ fs[mTypeToR_def, minExponentPos_def, REAL_INV_1OVER])
+    (* multiplication, result = 0 *)
+    >- (
+      fs[evalBinop_def, REAL_ENTIRE, fp64_mul_def, float_mul_def,
+         GSYM float_is_zero_to_real, float_is_zero_def]
+      THENL [ Cases_on `float_value (fp64_to_float vF1)`,
+              Cases_on `float_value (fp64_to_float vF2)`]
+      \\ fs[validValue_gives_float_value]
+      \\ fs[float_round_with_flags_def, dmode_def,
+            fp64_to_float_float_to_fp64, perturb_def]
+      \\ Cases_on `(fp64_to_float vF1).Sign ≠ (fp64_to_float vF2).Sign`
+      \\ `2 * abs (0:real) <= ulp (:52 #11)`
+        by (fs[REAL_ABS_0, ulp_def, ULP_def, REAL_OF_NUM_POW]
+            \\ once_rewrite_tac [real_div]
+            \\ irule REAL_LE_MUL \\ fs[]
+            \\ irule REAL_LE_INV \\ fs[])
+      \\ fs [round_roundTiesToEven_is_plus_zero,
+             round_roundTiesToEven_is_minus_zero, zero_to_real,
+             fp64_to_real_def, fp64_to_float_float_to_fp64
+             ]
+      \\ qexistsl_tac [`M64`, `M64`, `float_to_real (fp64_to_float vF1)`,
+                       `float_to_real (fp64_to_float vF2)`, `0:real`]
+      \\ rveq
+      \\ fs[GSYM float_is_zero_to_real, float_is_zero_def,
+            mTypeToR_pos, perturb_def])
+      (* Division, normal result *)
+    >- (
+      fs[fp64_div_def, fp64_to_float_float_to_fp64, evalBinop_def]
+      \\ Q.ISPECL_THEN [`(fp64_to_float vF1):(52,11) float`,
+                        `(fp64_to_float vF2):(52,11) float`]
+          impl_subgoal_tac
+          float_div_relative
+      >- (rpt conj_tac
+          \\ fs[validFloatValue_def,
+                normalTranslatedValue_implies_finiteness,
+                denormalTranslatedValue_implies_finiteness,
+                normalValue_implies_normalization,
+                GSYM float_is_zero_to_real, float_is_finite, evalBinop_def])
+      \\ fs[dmode_def]
+      \\ qexistsl_tac [`M64`, `M64`, `float_to_real (fp64_to_float vF1)`,
+                       `float_to_real (fp64_to_float vF2)`, `e`]
+      \\ fs[perturb_def, evalBinop_def, fp64_to_real_def, fp64_to_float_float_to_fp64]
+      \\ imp_res_tac normal_not_denormal
+      \\ gs[mTypeToR_def] \\ rewrite_tac[REAL_INV_1OVER]
+      \\ qpat_x_assum ‘_ ≤ 1’ mp_tac \\ simp[])
+      (* Division, denormal result *)
+    >- (
+      fs[fp64_div_def, fp64_to_float_float_to_fp64, evalBinop_def]
+      \\ Q.ISPECL_THEN [`(fp64_to_float vF1):(52,11) float`,
+                        `(fp64_to_float vF2):(52,11) float`]
+          impl_subgoal_tac
+          float_div_relative_denorm
+      >- (
+        rpt conj_tac
+        >- fs[validFloatValue_def,
+              normalTranslatedValue_implies_finiteness,
+              denormalTranslatedValue_implies_finiteness,
+              normalValue_implies_normalization, float_is_finite,
+              GSYM float_is_zero_to_real, evalBinop_def]
+        >- fs[validFloatValue_def,
+              normalTranslatedValue_implies_finiteness,
+              denormalTranslatedValue_implies_finiteness,
+              normalValue_implies_normalization, float_is_finite,
+              GSYM float_is_zero_to_real, evalBinop_def]
+        >- fs[validFloatValue_def,
+              normalTranslatedValue_implies_finiteness,
+              denormalTranslatedValue_implies_finiteness,
+              normalValue_implies_normalization, float_is_finite,
+              GSYM float_is_zero_to_real, evalBinop_def]
+        >- (
+          fs[normalTranslatedValue_implies_finiteness,
+             denormalTranslatedValue_implies_finiteness,
+             normalValue_implies_normalization, float_is_finite,
+             GSYM float_is_zero_to_real, evalBinop_def, denormal_def, minValue_pos_def]
+          \\ irule REAL_LT_TRANS
+          \\ qpat_x_assum ‘_ < 1’ $ irule_at Any \\ gs[]
+          \\ EVAL_TAC)
+        >- (
+          irule REAL_LT_TRANS \\ qexists_tac ‘maxValue M64’
+          \\ fs[threshold_64_bit_lt_maxValue, denormal_def]
+          \\ irule REAL_LTE_TRANS \\ qexists_tac ‘minValue_pos M64’
+          \\ fs[minValue_pos_def, maxValue_def, GSYM REAL_OF_NUM_POW]
+          \\ irule REAL_LE_TRANS \\ qexists_tac ‘1’ \\ conj_tac
+          >- (once_rewrite_tac[GSYM REAL_INV1] \\ irule REAL_INV_LE_ANTIMONO_IMPR
+              \\ fs[POW_2_LE1])
+          \\ fs[POW_2_LE1] \\ EVAL_TAC)
+        \\ fs[INT_MAX_def, INT_MIN_def, dimindex_11])
+      \\ fs[dmode_def]
+      \\ qexistsl_tac [`M64`, `M64`, `float_to_real (fp64_to_float vF1)`,
+                       `float_to_real (fp64_to_float vF2)`, `e`]
+      \\ fs[perturb_def, evalBinop_def, fp64_to_real_def, fp64_to_float_float_to_fp64]
+      \\ gs[mTypeToR_def, minExponentPos_def] \\ rewrite_tac[REAL_INV_1OVER]
+      \\ qpat_x_assum ‘_ ≤ 1’ mp_tac \\ simp[])
+    (* division, result = 0 *)
+    >- (
+      fs[fp64_div_def, dmode_def, fp64_to_float_float_to_fp64,
+         float_div_def, evalBinop_def]
+      \\ `float_to_real (fp64_to_float vF1) = 0`
+        by (imp_res_tac div_eq0_general)
+      \\ rw_thm_asm `float_to_real (fp64_to_float vF1) = 0` (GSYM float_is_zero_to_real)
+      \\ fs[float_is_zero_def]
+      \\ Cases_on `float_value (fp64_to_float vF1)`
+      \\ fs[validValue_gives_float_value]
+      \\ simp [float_round_with_flags_def]
+      \\ Cases_on `(fp64_to_float vF1).Sign ≠ (fp64_to_float vF2).Sign`
+      \\ `2 * abs (0:real) <= ulp (:52 #11)`
+        by (fs[REAL_ABS_0, ulp_def, ULP_def, REAL_OF_NUM_POW]
+            \\ once_rewrite_tac [real_div]
+            \\ irule REAL_LE_MUL \\ fs[]
+            \\ irule REAL_LE_INV \\ fs[])
+      \\ fs [round_roundTiesToEven_is_plus_zero,
+             round_roundTiesToEven_is_minus_zero, zero_to_real]
+      \\ rveq
+      \\ `float_to_real (fp64_to_float vF1) = 0:real`
+          by (fs[GSYM float_is_zero_to_real, float_is_zero_def])
+      \\ qexistsl_tac [`M64`, `M64`, `float_to_real (fp64_to_float vF1)`,
+                       `float_to_real (fp64_to_float vF2)`, `0:real`]
+      \\ fs[perturb_def, mTypeToR_pos, float_to_real_round_zero_is_zero]
+      \\ gs[fp64_to_real_def, fp64_to_float_float_to_fp64]))
+  >~ [‘Fma e1 e2 e3’]
+  >- (
+    imp_res_tac validRanges_single
+    \\ rw_thm_asm `validTypes _ _` validTypes_def
+    \\ rw_thm_asm `validRanges _ _ _ _` validRanges_def
+    \\ fs[eval_expr_float_def, optionLift_def]
+    \\ IMP_RES_TAC validTypes_exec
+    \\ rveq \\ first_x_assum kall_tac
+    \\ `m1 = M64 /\ m2 = M64 /\ m3 = M64`
+      by (fs[is64BitEnv_def]
+          \\ rpt conj_tac \\ first_x_assum irule \\ find_exists_tac \\ fs[])
+    \\ rveq
+    \\ rw_thm_asm `is64BitEval _` is64BitEval_def \\ fs[]
+    \\ ntac 3
+            (first_x_assum
+             (qspecl_then [`E1`, `E2`, `Gamma`] assume_tac))
+    \\ first_x_assum (qspecl_then [`v1`, `A`, `fVars`, `dVars`] destruct)
+    >- (
+      rpt conj_tac \\ fs[]
+      >- (
+        qpat_x_assum ‘validErrorbound _ _ _ _’
+        (fn thm => assume_tac (ONCE_REWRITE_RULE [validErrorbound_def] thm))
+        \\ fs[option_case_eq]
+        \\ pop_assum mp_tac \\ rpt (TOP_CASE_TAC \\ fs[]))
+      >- (rw_thm_asm `FPRangeValidator _ _ _ _` FPRangeValidator_def
+          \\ fs[] \\ rveq
+          \\ rw_asm_star `FloverMapTree_find (Fma _ _ _) A = _`
+          \\ rw_asm_star `FloverMapTree_find (Fma _ _ _) Gamma = _`)
+      \\ rw_thm_asm `domain (usedVars _) DIFF _ SUBSET _` usedVars_def
+      \\ fs[domain_union, DIFF_DEF, SUBSET_DEF, Once usedVars_def]
+      \\ rpt strip_tac \\ first_x_assum irule \\ simp[Once usedVars_def])
+    \\ first_x_assum (qspecl_then [`v2`, `A`, `fVars`, `dVars`] destruct)
+    >- (
+      rpt conj_tac \\ fs[]
+      >- (
+        qpat_x_assum ‘validErrorbound _ _ _ _’
+        (fn thm => assume_tac (ONCE_REWRITE_RULE [validErrorbound_def] thm))
+        \\ fs[option_case_eq]
+        \\ pop_assum mp_tac \\ rpt (TOP_CASE_TAC \\ fs[]))
+      >- (rw_thm_asm `FPRangeValidator _ _ _ _` FPRangeValidator_def
+          \\ fs[] \\ rveq
+          \\ rw_asm_star `FloverMapTree_find (Fma _ _ _) A = _`
+          \\ rw_asm_star `FloverMapTree_find (Fma _ _ _) Gamma = _`)
+      \\ rw_thm_asm `domain (usedVars _) DIFF _ SUBSET _` usedVars_def
+      \\ fs[domain_union, DIFF_DEF, SUBSET_DEF])
+    \\ first_x_assum (qspecl_then [`v3`, `A`, `fVars`, `dVars`] destruct)
+    >- (
+      rpt conj_tac \\ fs[]
+      >- (
+        qpat_x_assum ‘validErrorbound _ _ _ _’
+        (fn thm => assume_tac (ONCE_REWRITE_RULE [validErrorbound_def] thm))
+        \\ fs[option_case_eq]
+        \\ pop_assum mp_tac \\ rpt (TOP_CASE_TAC \\ fs[]))
+      >- (rw_thm_asm `FPRangeValidator _ _ _ _` FPRangeValidator_def
+          \\ fs[] \\ rveq
+          \\ rw_asm_star `FloverMapTree_find (Fma _ _ _) A = _`
+          \\ rw_asm_star `FloverMapTree_find (Fma _ _ _) Gamma = _`)
+      \\ rw_thm_asm `domain (usedVars _) DIFF _ SUBSET _` usedVars_def
+      \\ fs[domain_union, DIFF_DEF, SUBSET_DEF])
+      \\ rename1 ‘eval_expr_float (toFlExp e1) (toFlEnv E2) = SOME vF1’
+      \\ rename1 ‘eval_expr_float (toFlExp e2) (toFlEnv E2) = SOME vF2’
+      \\ rename1 ‘eval_expr_float (toFlExp e3) (toFlEnv E2) = SOME vF3’
+      \\ `validFloatValue
+            (evalFma (float_to_real (fp64_to_float vF1))
+             (float_to_real (fp64_to_float vF2))
+             (float_to_real (fp64_to_float vF3))) M64`
+          by (drule FPRangeValidator_sound
+              \\ disch_then
+                   (qspecl_then
+                     [`Fma e1 e2 e3`,
+                      `evalFma (float_to_real (fp64_to_float vF1))
+                      (float_to_real (fp64_to_float vF2))
+                      (float_to_real (fp64_to_float vF3))`,
+                      `M64`] irule)
+               \\ fs[]
+               \\ qexistsl_tac [`e1`, `e2`, ‘e3’] \\ fs[]
+               \\ rpt conj_tac
+               >- (simp[Once validTypes_def] \\ first_x_assum MATCH_ACCEPT_TAC)
+               >- (simp[Once validRanges_def] \\ find_exists_tac \\ fs[]
+                   \\ fs[] \\ rveq \\ fs[])
+               \\ simp[Once eval_expr_cases]
+               \\ qexistsl_tac [`M64`, `M64`, ‘M64’, `float_to_real (fp64_to_float vF1)`,
+                                `float_to_real (fp64_to_float vF2)`,
+                                `float_to_real (fp64_to_float vF3)`, `0:real`]
+               \\ fs[perturb_def, evalFma_def, mTypeToR_pos, fp64_to_real_def])
+      \\ `validFloatValue (float_to_real (fp64_to_float vF1)) M64`
+           by (drule FPRangeValidator_sound
+               \\ disch_then
+                    (qspecl_then
+                       [`e1`, `float_to_real (fp64_to_float vF1)`,
+                         `M64`] irule)
+               \\ fs[]
+               \\ qexistsl_tac [`e1`] \\ fs[]
+               \\ rpt conj_tac
+               >- (rw_thm_asm `domain (usedVars _) DIFF _ SUBSET _` usedVars_def
+                   \\ fs[domain_union, DIFF_DEF, SUBSET_DEF, Once usedVars_def])
+               >- (rw_thm_asm `FPRangeValidator _ _ _ _` FPRangeValidator_def
+                   \\ fs[] \\ rveq
+                   \\ rw_asm_star `FloverMapTree_find (Fma _ _ _) A = _`
+                   \\ rw_asm_star `FloverMapTree_find (Fma _ _ _) Gamma = _`)
+               >- (
+                qpat_x_assum ‘validErrorbound _ _ _ _’
+                (fn thm => assume_tac (ONCE_REWRITE_RULE [validErrorbound_def] thm))
+                \\ fs[option_case_eq]
+                \\ pop_assum mp_tac \\ rpt (TOP_CASE_TAC \\ fs[]))
+               \\ fs[fp64_to_real_def])
+      \\ `validFloatValue (float_to_real (fp64_to_float vF2)) M64`
+           by (drule FPRangeValidator_sound
+               \\ disch_then
+                    (qspecl_then
+                       [`e2`, `float_to_real (fp64_to_float vF2)`,
+                         `M64`] irule)
+               \\ fs[]
+               \\ qexists_tac `e2` \\ fs[]
+               \\ rpt conj_tac
+               >- (rw_thm_asm `domain (usedVars _) DIFF _ SUBSET _` usedVars_def
+                   \\ fs[domain_union, DIFF_DEF, SUBSET_DEF])
+               >- (rw_thm_asm `FPRangeValidator _ _ _ _` FPRangeValidator_def
+                   \\ fs[] \\ rveq
+                   \\ rw_asm_star `FloverMapTree_find (Fma _ _ _) A = _`
+                   \\ rw_asm_star `FloverMapTree_find (Fma _ _ _) Gamma = _`)
+               >- (
+                qpat_x_assum ‘validErrorbound _ _ _ _’
+                (fn thm => assume_tac (ONCE_REWRITE_RULE [validErrorbound_def] thm))
+                \\ fs[option_case_eq]
+                \\ pop_assum mp_tac \\ rpt (TOP_CASE_TAC \\ fs[]))
+               \\ gs[fp64_to_real_def])
+      \\ `validFloatValue (float_to_real (fp64_to_float vF3)) M64`
+           by (drule FPRangeValidator_sound
+               \\ disch_then
+                    (qspecl_then
+                       [`e3`, `float_to_real (fp64_to_float vF3)`,
+                         `M64`] irule)
+               \\ fs[]
+               \\ qexists_tac `e3` \\ fs[]
+               \\ rpt conj_tac
+               >- (rw_thm_asm `domain (usedVars _) DIFF _ SUBSET _` usedVars_def
+                   \\ fs[domain_union, DIFF_DEF, SUBSET_DEF])
+               >- (rw_thm_asm `FPRangeValidator _ _ _ _` FPRangeValidator_def
+                   \\ fs[] \\ rveq
+                   \\ rw_asm_star `FloverMapTree_find (Fma _ _ _) A = _`
+                   \\ rw_asm_star `FloverMapTree_find (Fma _ _ _) Gamma = _`)
+               >- (
+                qpat_x_assum ‘validErrorbound _ _ _ _’
+                (fn thm => assume_tac (ONCE_REWRITE_RULE [validErrorbound_def] thm))
+                \\ fs[option_case_eq]
+                \\ pop_assum mp_tac \\ rpt (TOP_CASE_TAC \\ fs[]))
+               \\ gs[fp64_to_real_def])
+    \\ qpat_x_assum ‘validFloatValue (evalFma _ _ _) _’
+                    (assume_tac o SIMP_RULE std_ss [validFloatValue_def])
+    \\ fs[optionLift_def]
+    \\ simp[Once eval_expr_cases, PULL_EXISTS]
+    \\ rewrite_tac [CONJ_ASSOC]
+    \\ ntac 3 (once_rewrite_tac [CONJ_COMM] \\ asm_exists_tac \\ fs[])
+    \\ fs[evalFma_def, evalBinop_def, fp64_mul_add_def, fp64_to_real_def,
+          fp64_to_float_float_to_fp64]
+    >- (
+      Q.ISPECL_THEN [`(fp64_to_float vF1):(52,11) float`,
+                     `(fp64_to_float vF2):(52,11) float`,
+                     `(fp64_to_float vF3):(52,11) float`]
+      impl_subgoal_tac
+      float_mul_add_relative
+      >- (
+        rpt conj_tac
+        \\ fs[validFloatValue_def,
+              normalTranslatedValue_implies_finiteness,
+              denormalTranslatedValue_implies_finiteness,
+              normalValue_implies_normalization,
+              GSYM float_is_zero_to_real, float_is_finite, evalFma_def, evalBinop_def])
+      \\ fs[dmode_def]
+      \\ fs[perturb_def]
+      \\ qexistsl_tac [`e`]
+      \\ pop_assum (rewrite_tac o single)
+      \\ fs[perturb_def, evalBinop_def]
+      \\ imp_res_tac normal_not_denormal
+      \\ fs[mTypeToR_def, REAL_INV_1OVER])
+      (* result denormal *)
+    >- (
+      fs[fp64_mul_add_def, fp64_to_float_float_to_fp64, evalFma_def]
+      \\ Q.ISPECL_THEN [`(fp64_to_float vF1):(52,11) float`,
+                        `(fp64_to_float vF2):(52,11) float`,
+                        `(fp64_to_float vF3):(52,11) float`]
+          impl_subgoal_tac
+          float_mul_add_relative_denorm
+      >- (
+        rpt conj_tac
+        >- fs[validFloatValue_def,
+              normalTranslatedValue_implies_finiteness,
+              denormalTranslatedValue_implies_finiteness,
+              normalValue_implies_normalization, float_is_finite,
+              GSYM float_is_zero_to_real, evalBinop_def]
+        >- fs[validFloatValue_def,
+              normalTranslatedValue_implies_finiteness,
+              denormalTranslatedValue_implies_finiteness,
+              normalValue_implies_normalization, float_is_finite,
+              GSYM float_is_zero_to_real, evalBinop_def]
+        >- fs[validFloatValue_def,
+              normalTranslatedValue_implies_finiteness,
+              denormalTranslatedValue_implies_finiteness,
+              normalValue_implies_normalization, float_is_finite,
+              GSYM float_is_zero_to_real, evalBinop_def]
+        >- (
+          fs[normalTranslatedValue_implies_finiteness,
+             denormalTranslatedValue_implies_finiteness,
+             normalValue_implies_normalization, float_is_finite,
+             GSYM float_is_zero_to_real, evalBinop_def, denormal_def, minValue_pos_def]
+          \\ irule REAL_LT_TRANS
+          \\ qpat_x_assum ‘_ < 1’ $ irule_at Any \\ gs[]
+          \\ EVAL_TAC)
+        >- (
+          irule REAL_LT_TRANS \\ qexists_tac ‘maxValue M64’
+          \\ fs[threshold_64_bit_lt_maxValue, denormal_def]
+          \\ irule REAL_LTE_TRANS \\ qexists_tac ‘minValue_pos M64’
+          \\ fs[minValue_pos_def, maxValue_def, GSYM REAL_OF_NUM_POW]
+          \\ EVAL_TAC)
+        \\ fs[INT_MAX_def, INT_MIN_def, dimindex_11])
+      \\ fs[dmode_def] \\ qexists_tac ‘e’
+      \\ fs[perturb_def, evalFma_def]
+      \\ fs[mTypeToR_def, minExponentPos_def, REAL_INV_1OVER])
+      (* result = 0 *)
+    >- (
+      imp_res_tac validValue_gives_float_value
+      \\ fs[REAL_LNEG_UNIQ, evalFma_def, evalBinop_def]
+      \\ fs[fp64_add_def, dmode_def, fp64_mul_def, fp64_to_float_float_to_fp64]
+      \\ fs[float_mul_add_def, float_round_with_flags_def]
+      \\ qexists_tac `0:real`
+      \\ fs[perturb_def, mTypeToR_pos, evalBinop_def]
+      \\ fs[float_is_nan_def, float_is_infinite_def]
+      \\ `2 * abs (0:real) <= ulp (:52 #11)`
+        by (fs[REAL_ABS_0, ulp_def, ULP_def, REAL_OF_NUM_POW]
+            \\ once_rewrite_tac [real_div]
+            \\ irule REAL_LE_MUL \\ fs[]
+            \\ irule REAL_LE_INV \\ fs[])
+      \\ fs[float_to_real_round_zero_is_zero]))
+QED
+
+(** The below does not work because of double-rounding **)
+(*
+Theorem bstep_gives_IEEE:
+  !(f:real cmd) E1 E2 Gamma vR vF A fVars dVars outVars.
+    approxEnv E1 (toRExpMap Gamma) A fVars dVars E2 /\
+    ssa f (union fVars dVars) outVars /\
+    validTypesCmd f Gamma /\
+    validRangesCmd f A E1 (toRTMap (toRExpMap Gamma)) /\
+    validErrorboundCmd f Gamma A dVars /\
+    FPRangeValidatorCmd f A Gamma dVars /\
+    bstep (toREvalCmd f) E1 (toRTMap (toRExpMap Gamma)) vR REAL /\
+    bstep f E2 (toRExpMap Gamma) vF M64 /\
+    domain (freeVars f) DIFF domain dVars ⊆ domain fVars ∧
+    is64BitBstep f /\
+    is64BitEnv Gamma /\
+    noDowncastFun f /\
+    (∀ x v. E2 x = SOME v ⇒ float_to_real ((real_to_float dmode v):(52,11) float) = v) ∧
+    (∀v.
+      v ∈ domain dVars ⇒
+      ∃vF m.
+      (E2 v = SOME vF ∧ FloverMapTree_find (Var v) Gamma = SOME m ∧
+      validFloatValue vF m)) ==>
+    ?v.
+      bstep_float (toFlCmd f) (toFlEnv E2) = SOME v /\
+      bstep f E2 (toRExpMap Gamma)
+        (fp64_to_real v) M64
+Proof
+  reverse $ Induct_on `f`
+  \\ simp [toFlCmd_def, Once toREvalCmd_def, is64BitBstep_def,
+                 noDowncastFun_def]
+  \\ rpt strip_tac
+  \\ rpt (inversion `bstep (Let _ _ _ _) _ _ _ _` bstep_cases)
+  \\ inversion `ssa _ _ _` ssa_cases
+  \\ once_rewrite_tac [bstep_float_def]
+  \\ fs[noDowncast_def]
+  \\ qpat_x_assum ‘validErrorboundCmd _ _ _ _’
+     (fn thm => mp_tac (ONCE_REWRITE_RULE[validErrorboundCmd_def] thm))
+  \\ fs[option_case_eq]
+  >- (
+    strip_tac \\ fs[bstep_cases, bstep_float_def]
+    \\ irule eval_expr_gives_IEEE_reverse
+    \\ rpt conj_tac
+    \\ fs[Once validTypesCmd_def]
+    >- first_x_assum $ irule_at Any
+    \\ gs[getRetExp_def, freeVars_def]
+    \\ rpt $ first_x_assum $ irule_at Any
+    \\ conj_tac
+    \\ fs[freeVars_def, Once FPRangeValidatorCmd_def, validRangesCmd_def])
+  \\ rpt (TOP_CASE_TAC \\ fs[])
+  \\ rpt strip_tac \\ gvs[]
+  \\ `?v_e. eval_expr_float (toFlExp e) (toFlEnv E2) = SOME v_e /\
+     eval_expr E2 (toRExpMap Gamma) e (fp64_to_real v_e) M64`
+    by (irule eval_expr_gives_IEEE_reverse \\ rpt conj_tac \\ fs[]
+        >- fs[Once validTypesCmd_def]
+        >- first_x_assum $ irule_at Any
+        \\ qexistsl_tac [`A`, `E1`, `dVars`, `fVars`]
+        \\ rpt conj_tac \\ fs[]
+        >- (fs[Once freeVars_def, domain_union, DIFF_DEF, SUBSET_DEF]
+            \\ rpt strip_tac
+            \\ `x IN domain fVars \/  x IN domain dVars`
+              by (first_x_assum irule \\ fs[]))
+        >- (fs [Once FPRangeValidatorCmd_def])
+        \\ fs[Once validRangesCmd_def])
+  \\ fs[Once validRangesCmd_def]
+  \\ imp_res_tac validRanges_single
+  \\ imp_res_tac validRangesCmd_single
+  \\ fs[Once validTypesCmd_def]
+  (* prove validity of errorbound for floating-point value *)
+  \\ qspecl_then
+     [`e`, `E1`, `E2`, `A`, `v'`, `r`,
+      `FST iv`, `SND iv`, `fVars`, `dVars`, `Gamma`]
+     impl_subgoal_tac
+     validErrorbound_sound
+  >- (
+    fs[DIFF_DEF, SUBSET_DEF]
+    \\ rpt strip_tac \\ first_x_assum irule
+    \\ fs[Once freeVars_def, domain_union]
+    \\ CCONTR_TAC \\ fs[] \\ rveq \\ fs[]
+    \\ `n IN domain fVars \/ n IN domain dVars`
+      by (first_x_assum irule \\ fs[]))
+  \\ fs[]
+  \\ imp_res_tac meps_0_deterministic \\ rveq
+  \\ rename1 `FST iv <= vR_e`
+  \\ rename1 ‘FloverMapTree_find (getRetExp (Let M64 n e f)) A =
+              SOME (ivR,errR)’
+  \\ rename1 `FST iv_e <= vR_e`
+  \\ rename1 `abs (vR_e - nF) <= err_e`
+  \\ `abs (vR_e - (fp64_to_real v_e)) <= err_e`
+    by (first_x_assum irule \\ fs[]
+        \\ first_x_assum $ irule_at Any)
+  \\ fs[getRetExp_def]
+  \\ rename1 `FloverMapTree_find (getRetExp f) A = SOME (iv_f, err_f)`
+  (* Now construct a new evaluation according to our big-step semantics
+           using lemma validErrorboundCmd_gives_eval *)
+  \\ qspecl_then
+     [ `f`, `A`, `updEnv n vR_e E1`,
+       `updEnv n (fp64_to_real v_e) E2`,
+       `outVars`, `fVars`, `insert n () dVars`, `vR`, `FST iv_f`, `SND iv_f`,
+       `err_f`, `M64`, `Gamma`]
+     impl_subgoal_tac
+     validErrorboundCmd_gives_eval
+  >- (
+    fs[] \\ rpt conj_tac
+    >- (
+      irule approxEnvUpdBound
+      \\ fs[lookup_NONE_domain, toRExpMap_def])
+    >- (
+      irule ssa_equal_set
+      \\ qexists_tac `insert n () (union fVars dVars)`
+      \\ conj_tac \\ TRY (fs[] \\ FAIL_TAC "")
+      \\ rewrite_tac [domain_union, domain_insert]
+      \\ rewrite_tac [UNION_DEF, INSERT_DEF]
+      \\ fs[EXTENSION]
+      \\ rpt strip_tac
+      \\ metis_tac[])
+    >- (
+      fs[DIFF_DEF, domain_insert, SUBSET_DEF]
+      \\ rpt strip_tac \\ first_x_assum irule
+      \\ fs[Once freeVars_def]
+      \\ simp[Once freeVars_def, domain_union]))
+  \\ fs[optionLift_def]
+  (* Instantiate IH with newly obtained evaluation, to get the conclusion *)
+  \\ first_x_assum
+     (qspecl_then [`updEnv n vR_e E1`,
+                   `updEnv n (fp64_to_real v_e) E2`,
+                   `Gamma`, `vR`, `vF'`, `A`, `fVars`,
+                   `insert n () dVars`, `outVars`]
+      impl_subgoal_tac)
+  >- (
+    simp[Once validErrorboundCmd_def]
+    \\ fs [Once FPRangeValidatorCmd_def, Once validTypesCmd_def,
+           Once validRangesCmd_def]
+    \\ rpt conj_tac
+    >- (
+      drule approxEnvUpdBound
+      \\ rpt $ disch_then drule
+      \\ fs[domain_lookup, lookup_NONE_domain, toRExpMap_def])
+    >- (
+      irule ssa_equal_set
+      \\ qexists_tac `insert n () (union fVars dVars)`
+      \\ conj_tac \\ TRY (fs[] \\ FAIL_TAC "")
+      \\ rewrite_tac [domain_union, domain_insert]
+      \\ rewrite_tac [UNION_DEF, INSERT_DEF]
+      \\ fs[EXTENSION]
+      \\ rpt strip_tac
+      \\ metis_tac[])
+    >- first_x_assum MATCH_ACCEPT_TAC
+    >- (
+      reverse (sg `m' = M64`)
+      >- (rveq \\ fs[])
+      \\ irule bstep_Gamma_det \\ rpt (find_exists_tac \\ fs[]))
+    >- (
+      fs[DIFF_DEF, domain_insert, SUBSET_DEF]
+      \\ rpt strip_tac \\ first_x_assum irule
+      \\ fs[Once freeVars_def]
+      \\ simp[Once freeVars_def, domain_union])
+    >- (rpt strip_tac \\ Cases_on ‘x = n’ \\ gs[]
+        >- (rveq \\ simp[fp64_to_real_def]
+            \\ ‘float_is_finite (fp64_to_float v_e)’
+              by (gs[float_is_finite_def]
+                  \\‘validFloatValue (float_to_real (fp64_to_float v_e)) M64’
+                  suffices_by (gs[validValue_gives_float_value])
+                  \\ irule FPRangeValidator_sound
+                  \\ rpt $ first_x_assum $ irule_at Any
+                  \\ gs[fp64_to_real_def]
+                  \\ fs[Once freeVars_def, domain_union, DIFF_DEF, SUBSET_DEF]
+                  \\ rpt strip_tac \\ first_x_assum irule \\ fs[]
+                  \\ CCONTR_TAC \\ fs[]
+                  \\ rveq \\ fs[]
+                  \\ metis_tac [])
+            \\ simp[float_to_real_round_robin])
+        \\ first_x_assum irule \\ first_x_assum $ irule_at Any)
+    >- (
+      rpt strip_tac \\ simp[updEnv_def]
+      \\ rveq \\ fs[]
+      >- (
+        irule FPRangeValidator_sound
+        \\ rpt $ first_x_assum $ irule_at Any \\ conj_tac
+        \\ fs[Once freeVars_def, domain_union, DIFF_DEF, SUBSET_DEF]
+        \\ rpt strip_tac \\ first_x_assum irule \\ fs[]
+        \\ CCONTR_TAC \\ fs[]
+        \\ rveq \\ fs[]
+        \\ metis_tac [])
+      \\ IF_CASES_TAC \\ fs[]
+      \\ irule FPRangeValidator_sound
+      \\ rpt $ first_x_assum $ irule_at Any \\ conj_tac
+      \\ fs[Once freeVars_def, domain_union, DIFF_DEF, SUBSET_DEF])
+    \\ fs[]
+    (** DOUBLE ROUNDING HERE.... **)
+    \\ ‘updFlEnv n v_e (toFlEnv E2) = toFlEnv (updEnv n (fp64_to_real v_e) E2)’
+      by (gs[updFlEnv_def, toFlEnv_def, FUN_EQ_THM]
+          \\ strip_tac \\ Cases_on ‘x = n’
+          \\
+          \\ gs[real_to_fp64_def, fp64_to_real_def]
+    \\ irule let_b \\ fs[toRExpMap_def] \\ find_exists_tac
+        \\ fs[] \\ irule bstep_eq_env
+        \\ find_exists_tac \\ fs[]
+        \\ strip_tac \\ fs[toREnv_def, updEnv_def, updFlEnv_def]
+        \\ IF_CASES_TAC \\ fs[])
+QED
+*)
+
+val found_tac = TRY (last_x_assum irule \\ find_exists_tac \\ fs[] \\ FAIL_TAC "")
+  \\ first_x_assum irule \\ find_exists_tac \\ fs[];
+
+Theorem IEEE_connection_expr:
+  !(e:real expr) (A:analysisResult) (P:precond) E1 E2 defVars fVars Gamma.
+      is64BitEval e /\
+      is64BitEnv defVars /\
+      noDowncast e /\
+      fVars_P_sound fVars E1 P /\
+      (domain (usedVars e)) SUBSET (domain fVars) /\
+      CertificateChecker e A P defVars = SOME Gamma /\
+      (∀ x v. E2 x = SOME v ⇒
+              float_to_real ((real_to_float dmode v):(52,11) float) = v) ∧
+      approxEnv E1 (toRExpMap Gamma) A fVars LN E2 ⇒
+        ?iv err vR vF. (* m, currently = M64 *)
+          FloverMapTree_find e A = SOME (iv, err) /\
+          eval_expr E1 (toRTMap (toRExpMap Gamma)) (toREval e) vR REAL /\
+          eval_expr_float (toFlExp e) (toFlEnv E2) = SOME vF /\
+          eval_expr E2 (toRExpMap Gamma) e (fp64_to_real vF) M64 /\
+          abs (vR - (fp64_to_real vF)) <= err
+Proof
+  simp [CertificateChecker_def]
+  \\ rpt strip_tac
+  \\ Cases_on `getValidMap defVars e FloverMapTree_empty` \\ fs[]
+  \\ rveq \\ imp_res_tac getValidMap_top_correct
+  \\ `validTypes e Gamma`
+    by (first_x_assum irule
+        \\ fs[FloverMapTree_empty_def, FloverMapTree_mem_def, FloverMapTree_find_def])
+  \\ `! e m. FloverMapTree_find e Gamma = SOME m ==> m = M64`
+      by (rpt strip_tac \\ irule getValidMap_preserving
+          \\ qexistsl_tac [`FloverMapTree_empty`, `defVars`, `e`, `e'`, `Gamma`]
+          \\ rpt conj_tac \\ rpt strip_tac
+          \\ fs[FloverMapTree_empty_def, FloverMapTree_find_def , is64BitEnv_def]
+          \\ first_x_assum irule \\ find_exists_tac \\ fs[])
+  \\ drule validIntervalbounds_sound
+  \\ rpt $ disch_then drule
+  \\ disch_then (qspecl_then [`fVars`,`E1`, `Gamma`] destruct)
+  \\ fs[dVars_range_valid_def, fVars_P_sound_def]
+  \\ drule validErrorbound_sound
+  \\ rpt $ disch_then drule
+  \\ imp_res_tac validRanges_single
+  \\ disch_then (qspecl_then [`vR`, `err`, `FST iv`, `SND iv`] destruct)
+  \\ fs[]
+  \\ qspecl_then [`e`, `E1`, `E2`, `Gamma`, `nF`, `A`, `fVars`, `LN`]
+       destruct
+       eval_expr_gives_IEEE_reverse
+  >- (
+    rpt conj_tac \\ fs[]
+    >- (
+      `FloverMapTree_find e Gamma = SOME M64`
+         by (irule typing_expr_64bit \\ fs[is64BitEnv_def]
+             \\ first_x_assum MATCH_ACCEPT_TAC)
+      \\ `m = M64`
+        by (irule validTypes_exec \\ rpt (find_exists_tac \\ fs[]))
+      \\ rveq \\ fs[])
+    \\ fs[is64BitEnv_def] \\ first_x_assum MATCH_ACCEPT_TAC)
+  \\ rpt $ first_assum $ irule_at Any
+QED
+
+(** Does not work because of let-bindings **)
+(*
+Theorem IEEE_connection_cmds:
+  ! (f:word64 cmd) (A:analysisResult) (P:precond) E1 E2 defVars (fVars:num_set)
+      Gamma.
+      is64BitBstep (toRCmd f) /\
+      is64BitEnv defVars /\
+      noDowncastFun (toRCmd f) /\
+      fVars_P_sound fVars E1 P /\
+      (domain (freeVars (toRCmd f))) SUBSET (domain fVars) /\
+      CertificateCheckerCmd (toRCmd f) A P defVars = SOME Gamma /\
+      approxEnv E1 (toRExpMap Gamma) A (freeVars (toRCmd f)) LN (toREnv E2) ==>
+        ?iv err vR vF. (* m, currently = M64 *)
+          FloverMapTree_find (getRetExp (toRCmd f)) A = SOME (iv, err) /\
+          bstep (toREvalCmd (toRCmd f)) E1 (toRTMap (toRExpMap Gamma)) vR REAL /\
+          bstep_float f E2 = SOME vF /\
+          bstep (toRCmd f) (toREnv E2) (toRExpMap Gamma)
+            (float_to_real (fp64_to_float vF)) M64 /\
+          abs (vR - (float_to_real (fp64_to_float vF))) <= err
+Proof
+  simp [CertificateCheckerCmd_def]
+  \\ rpt strip_tac
+  \\ Cases_on `getValidMapCmd defVars (toRCmd f) FloverMapTree_empty` \\ fs[]
+  \\ rveq \\ IMP_RES_TAC getValidMapCmd_correct
+  \\ `validTypesCmd (toRCmd f) Gamma`
+    by (first_x_assum irule
+        \\ fs[FloverMapTree_empty_def, FloverMapTree_mem_def, FloverMapTree_find_def])
+  \\ `! e m. FloverMapTree_find e Gamma = SOME m ==> m = M64`
+    by (rpt strip_tac \\ irule getValidMapCmd_preserving
+        \\ qexistsl_tac [`FloverMapTree_empty`, `defVars`, `e`, `toRCmd f`, `Gamma`]
+        \\ rpt conj_tac \\ rpt strip_tac
+        \\ fs[FloverMapTree_empty_def, FloverMapTree_find_def , is64BitEnv_def]
+        \\ first_x_assum irule \\ find_exists_tac \\ fs[])
+  \\ `?outVars. ssa (toRCmd f) (freeVars (toRCmd f)) outVars`
+      by (match_mp_tac validSSA_sound \\ fs[])
+  \\ qspecl_then
+       [`toRCmd f`, `A`, `E1`, `freeVars (toRCmd f)`, `LN`, `outVars`, `P`, `Gamma`]
+       destruct validIntervalboundsCmd_sound
+  \\ fs[dVars_range_valid_def, fVars_P_sound_def]
+  >- (rpt strip_tac \\ first_x_assum irule \\ fs[SUBSET_DEF])
+  \\ IMP_RES_TAC validRangesCmd_single
+  \\ qspecl_then
+       [`toRCmd f`, `A`, `E1`, `toREnv E2`, `outVars`, `freeVars (toRCmd f)`, `LN`, `vR`, `FST iv_e`,
+        `SND iv_e`, `err_e`, `M64`, `Gamma`]
+        destruct validErrorboundCmd_gives_eval
+  \\ fs[]
+  \\ rpt (find_exists_tac \\ fs[])
+  \\ qspecl_then
+      [`f`, `E1`, `E2`, `toREnv E2`, `Gamma`, `vR`, `vF`, `A`, `freeVars (toRCmd f)`, `LN`, `outVars`]
+      destruct
+      bstep_gives_IEEE
+  >- (fs[is64BitEnv_def]
+      \\ conj_tac
+      >- (`FloverMapTree_find (getRetExp (toRCmd f)) Gamma = SOME M64`
+          by (irule typing_cmd_64bit
+              \\ fs[is64BitEnv_def] \\ first_x_assum MATCH_ACCEPT_TAC)
+          \\ `m = M64`
+          by (drule validTypesCmd_single
+              \\ disch_then assume_tac \\ fs[]
+              \\ fs[] \\ rveq \\ fs[]
+              \\ first_x_assum irule
+              \\ qexistsl_tac [`toREnv E2`, `Gamma`, `vF`] \\ fs[])
+          \\ rveq \\ fs[])
+      \\ first_x_assum MATCH_ACCEPT_TAC)
+  \\ find_exists_tac \\ fs[]
+  \\ drule validErrorboundCmd_sound
+  \\ rpt (disch_then drule)
+  \\ rename1 `bstep (toRCmd f) (toREnv E2) _ vF2 m2`
+  \\ disch_then
+      (qspecl_then
+        [`outVars`, `vR`, `vF2`, `FST iv_e`, `SND iv_e`, `err_e`, `m2`] destruct)
+  \\ fs[]
+QED
+*)
+
+val _ = export_theory ();
-- 
GitLab