Theory PAC_Checker

(*
  File:         PAC_Checker.thy
  Author:       Mathias Fleury, Daniela Kaufmann, JKU
  Maintainer:   Mathias Fleury, JKU
*)
theory PAC_Checker
  imports PAC_Polynomials_Operations
    PAC_Checker_Specification
    PAC_Map_Rel
    Show.Show
    Show.Show_Instances
    PAC_Misc
begin

section ‹Executable Checker›

text ‹In this layer we finally refine the checker to executable code.›

subsection ‹Definitions›

text ‹Compared to the previous layer, we add an error message when an error is discovered. We do not
  attempt to prove anything on the error message (neither that there really is an error, nor that the
  error message is correct).
›

paragraph ‹Extended error message›

datatype 'a code_status =
  is_cfailed: CFAILED (the_error: 'a) |
  CSUCCESS |
  is_cfound: CFOUND

text ‹In the following function, we merge errors. We will never merge an error message with an
  another error message; hence we do not attempt to concatenate error messages.
›
fun merge_cstatus where
  merge_cstatus (CFAILED a) _ = CFAILED a |
  merge_cstatus _ (CFAILED a) = CFAILED a |
  merge_cstatus CFOUND _ = CFOUND |
  merge_cstatus _ CFOUND = CFOUND |
  merge_cstatus _ _ = CSUCCESS

definition code_status_status_rel :: ('a code_status × status) set where
code_status_status_rel =
  {(CFOUND, FOUND), (CSUCCESS, SUCCESS)} 
  {(CFAILED a, FAILED)| a. True}

lemma in_code_status_status_rel_iff[simp]:
  (CFOUND, b)  code_status_status_rel  b = FOUND
  (a, FOUND)  code_status_status_rel  a = CFOUND
  (CSUCCESS, b)  code_status_status_rel  b = SUCCESS
  (a, SUCCESS)  code_status_status_rel  a = CSUCCESS
  (a, FAILED)  code_status_status_rel  is_cfailed a
  (CFAILED C, b)  code_status_status_rel  b = FAILED
  by (cases a; cases b; auto simp: code_status_status_rel_def; fail)+


paragraph ‹Refinement relation›

fun pac_step_rel_raw :: ('olbl × 'lbl) set  ('a × 'b) set  ('c × 'd) set  ('a, 'c, 'olbl) pac_step  ('b, 'd, 'lbl) pac_step  bool where
pac_step_rel_raw R1 R2 R3 (Add p1 p2 i r) (Add p1' p2' i' r') 
   (p1, p1')  R1  (p2, p2')  R1  (i, i')  R1 
   (r, r')  R2 |
pac_step_rel_raw R1 R2 R3 (Mult p1 p2 i r) (Mult p1' p2' i' r') 
   (p1, p1')  R1  (p2, p2')  R2  (i, i')  R1 
   (r, r')  R2 |
pac_step_rel_raw R1 R2 R3 (Del p1) (Del p1') 
   (p1, p1')  R1 |
pac_step_rel_raw R1 R2 R3 (Extension i x p1) (Extension j x' p1') 
   (i, j)  R1  (x, x')  R3  (p1, p1')  R2 |
pac_step_rel_raw R1 R2 R3 _ _  False

fun pac_step_rel_assn :: ('olbl  'lbl  assn)  ('a  'b  assn)  ('c  'd  assn)  ('a, 'c, 'olbl) pac_step  ('b, 'd, 'lbl) pac_step  assn where
pac_step_rel_assn R1 R2 R3 (Add p1 p2 i r) (Add p1' p2' i' r') =
   R1 p1 p1' * R1 p2 p2' * R1 i i' *
   R2 r r' |
pac_step_rel_assn R1 R2 R3 (Mult p1 p2 i r) (Mult p1' p2' i' r') =
   R1 p1 p1' * R2 p2 p2' * R1 i i' *
   R2 r r' |
pac_step_rel_assn R1 R2 R3 (Del p1) (Del p1') =
   R1 p1 p1' |
pac_step_rel_assn R1 R2 R3 (Extension i x p1) (Extension i' x' p1') =
   R1 i i' * R3 x x' * R2 p1 p1' |
pac_step_rel_assn R1 R2 _ _ _ = false

lemma pac_step_rel_assn_alt_def:
  pac_step_rel_assn R1 R2 R3 x y = (
  case (x, y) of
      (Add p1 p2 i r, Add p1' p2' i' r') 
        R1 p1 p1' * R1 p2 p2' * R1 i i' * R2 r r'
    | (Mult p1 p2 i r, Mult p1' p2' i' r') 
        R1 p1 p1' * R2 p2 p2' * R1 i i' * R2 r r'
    | (Del p1, Del p1')  R1 p1 p1'
    | (Extension i x p1, Extension i' x' p1')  R1 i i' * R3 x x' * R2 p1 p1'
    | _  false)
    by (auto split: pac_step.splits)


paragraph ‹Addition checking›

definition error_msg where
  error_msg i msg = CFAILED (''s CHECKING failed at line '' @ show i @ '' with error '' @ msg)

definition error_msg_notin_dom_err where
  error_msg_notin_dom_err = '' notin domain''

definition error_msg_notin_dom :: nat  string where
  error_msg_notin_dom i = show i @ error_msg_notin_dom_err

definition error_msg_reused_dom where
  error_msg_reused_dom i = show i @ '' already in domain''


definition error_msg_not_equal_dom where
  error_msg_not_equal_dom p q pq r = show p @ '' + '' @ show q @ '' = '' @ show pq @ '' not equal'' @ show r


definition check_not_equal_dom_err :: llist_polynomial  llist_polynomial  llist_polynomial  llist_polynomial  string nres where
  check_not_equal_dom_err p q pq r = SPEC (λ_. True)


definition vars_llist :: llist_polynomial  string set where
vars_llist  xs = (set ` fst ` set xs)


definition check_addition_l :: _  _  string set  nat  nat  nat  llist_polynomial  string code_status nres where
check_addition_l spec A 𝒱 p q i r = do {
   let b = p ∈# dom_m A  q ∈# dom_m A  i ∉# dom_m A  vars_llist r  𝒱;
   if ¬b
   then RETURN (error_msg i ((if p ∉# dom_m A then error_msg_notin_dom p else []) @ (if q ∉# dom_m A then error_msg_notin_dom p else []) @
      (if i ∈# dom_m A then error_msg_reused_dom p else [])))
   else do {
     ASSERT (p ∈# dom_m A);
     let p = the (fmlookup A p);
     ASSERT (q ∈# dom_m A);
     let q = the (fmlookup A q);
     pq  add_poly_l (p, q);
     b  weak_equality_l pq r;
     b'  weak_equality_l r spec;
     if b then (if b' then RETURN CFOUND else RETURN CSUCCESS)
     else do {
       c  check_not_equal_dom_err p q pq r;
       RETURN (error_msg i c)}
   }
}


paragraph ‹Multiplication checking›

definition check_mult_l_dom_err :: bool  nat  bool  nat  string nres where
  check_mult_l_dom_err p_notin p i_already i = SPEC (λ_. True)


definition check_mult_l_mult_err :: llist_polynomial  llist_polynomial  llist_polynomial  llist_polynomial  string nres where
  check_mult_l_mult_err p q pq r = SPEC (λ_. True)


definition check_mult_l :: _  _  _  nat llist_polynomial   nat  llist_polynomial  string code_status nres where
check_mult_l spec A 𝒱 p q i r = do {
    let b = p ∈# dom_m A  i ∉# dom_m A  vars_llist q  𝒱 vars_llist r  𝒱;
    if ¬b
    then do {
      c  check_mult_l_dom_err (p ∉# dom_m A) p (i ∈# dom_m A) i;
      RETURN (error_msg i c)}
    else do {
       ASSERT (p ∈# dom_m A);
       let p = the (fmlookup A p);
       pq  mult_poly_full p q;
       b  weak_equality_l pq r;
       b'  weak_equality_l r spec;
       if b then (if b' then RETURN CFOUND else RETURN CSUCCESS) else do {
         c  check_mult_l_mult_err p q pq r;
         RETURN (error_msg i c)
       }
     }
  }


paragraph ‹Deletion checking›

definition check_del_l :: _  _  nat  string code_status nres where
check_del_l spec A p = RETURN CSUCCESS


paragraph ‹Extension checking›

definition check_extension_l_dom_err :: nat  string nres where
  check_extension_l_dom_err p = SPEC (λ_. True)


definition check_extension_l_no_new_var_err :: llist_polynomial  string nres where
  check_extension_l_no_new_var_err p = SPEC (λ_. True)

definition check_extension_l_new_var_multiple_err :: string  llist_polynomial  string nres where
  check_extension_l_new_var_multiple_err v p = SPEC (λ_. True)

definition check_extension_l_side_cond_err
  :: string  llist_polynomial  llist_polynomial  llist_polynomial  string nres
where
  check_extension_l_side_cond_err v p p' q = SPEC (λ_. True)

definition check_extension_l
  :: _  _  string set  nat  string  llist_polynomial  (string code_status) nres
where
check_extension_l spec A 𝒱 i v p = do {
  let b = i ∉# dom_m A  v  𝒱  ([v], -1)  set p;
  if ¬b
  then do {
    c  check_extension_l_dom_err i;
    RETURN (error_msg i c)
  } else do {
      let p' = remove1 ([v], -1) p;
      let b = vars_llist p'  𝒱;
      if ¬b
      then do {
        c  check_extension_l_new_var_multiple_err v p';
        RETURN (error_msg i c)
      }
      else do {
         p2  mult_poly_full p' p';
         let p' = map (λ(a,b). (a, -b)) p';
         q  add_poly_l (p2, p');
         eq  weak_equality_l q [];
         if eq then do {
           RETURN (CSUCCESS)
         } else do {
          c  check_extension_l_side_cond_err v p p' q;
          RETURN (error_msg i c)
        }
      }
    }
  }


lemma check_extension_alt_def:
  check_extension A 𝒱 i v p  do {
    b  SPEC(λb. b  i ∉# dom_m A  v  𝒱);
    if ¬b
    then RETURN (False)
    else do {
         p'  RETURN (p + Var v);
         b  SPEC(λb. b  vars p'  𝒱);
         if ¬b
         then RETURN (False)
         else do {
           pq  mult_poly_spec p' p';
           let p' = - p';
           p  add_poly_spec pq p';
           eq  weak_equality p 0;
           if eq then RETURN(True)
           else RETURN (False)
       }
     }
   }
proof -
  have [intro]: ab  𝒱 
       vars ba  𝒱 
       MPoly_Type.coeff (ba + Var ab) (monomial (Suc 0) ab) = 1 for ab ba
    by (subst coeff_add[symmetric], subst not_in_vars_coeff0)
      (auto simp flip: coeff_add monom.abs_eq
        simp: not_in_vars_coeff0 MPoly_Type.coeff_def
          Var.abs_eq Var0_def lookup_single_eq monom.rep_eq)
  have [simp]: MPoly_Type.coeff p (monomial (Suc 0) ab) = -1
     if vars (p + Var ab)  𝒱
       ab  𝒱
     for ab
   proof -
     define q where q  p + Var ab
     then have p: p = q - Var ab
       by auto
     show ?thesis
       unfolding p
       apply (subst coeff_minus[symmetric], subst not_in_vars_coeff0)
       using that unfolding q_def[symmetric]
       by (auto simp flip: coeff_minus simp: not_in_vars_coeff0
           Var.abs_eq Var0_def simp flip: monom.abs_eq
           simp: not_in_vars_coeff0 MPoly_Type.coeff_def
           Var.abs_eq Var0_def lookup_single_eq monom.rep_eq)
  qed
  have [simp]: vars (p - Var ab) = vars (Var ab - p) for ab
    using vars_uminus[of p - Var ab]
    by simp
  show ?thesis
    unfolding check_extension_def
    apply (auto 5 5 simp: check_extension_def weak_equality_def
      mult_poly_spec_def field_simps
      add_poly_spec_def power2_eq_square cong: if_cong
      intro!: intro_spec_refine[where R=Id, simplified]
      split: option.splits dest: ideal.span_add)
   done
qed

(* Copy of WB_More_Refinement *)
lemma RES_RES_RETURN_RES: RES A  (λT. RES (f T)) = RES ((f ` A))
  by (auto simp: pw_eq_iff refine_pw_simps)


lemma check_add_alt_def:
  check_add A 𝒱 p q i r 
    do {
     b  SPEC(λb. b  p ∈# dom_m A  q ∈# dom_m A  i ∉# dom_m A  vars r  𝒱);
     if ¬b
     then RETURN False
     else do {
       ASSERT (p ∈# dom_m A);
       let p = the (fmlookup A p);
       ASSERT (q ∈# dom_m A);
       let q = the (fmlookup A q);
       pq  add_poly_spec p q;
       eq  weak_equality pq r;
       RETURN eq
     }
  } (is _  ?A)
proof -
  have check_add_alt_def: check_add A 𝒱 p q i r = do {
     b  SPEC(λb. b  p ∈# dom_m A  q ∈# dom_m A  i ∉# dom_m A  vars r  𝒱);
     if ¬b then SPEC(λb. b  p ∈# dom_m A  q ∈# dom_m A  i ∉# dom_m A  vars r  𝒱 
            the (fmlookup A p) + the (fmlookup A q) - r   ideal polynomial_bool)
     else
       SPEC(λb. b  p ∈# dom_m A  q ∈# dom_m A  i ∉# dom_m A  vars r  𝒱 
            the (fmlookup A p) + the (fmlookup A q) - r   ideal polynomial_bool)}
   (is _ = ?B)
    by (auto simp: check_add_def RES_RES_RETURN_RES)
   have ?A   Id (check_add A 𝒱 p q i r)
     apply refine_vcg
     apply ((auto simp: check_add_alt_def weak_equality_def
        add_poly_spec_def RES_RES_RETURN_RES summarize_ASSERT_conv
      cong: if_cong
      intro!: ideal.span_zero;fail)+)
      done
   then show ?thesis
     unfolding check_add_alt_def[symmetric]
     by simp
qed

lemma check_mult_alt_def:
  check_mult A 𝒱 p q i r 
    do {
     b  SPEC(λb. b  p ∈# dom_m A  i ∉# dom_m A  vars q  𝒱   vars r  𝒱);
     if ¬b
     then RETURN False
     else do {
       ASSERT (p ∈# dom_m A);
       let p = the (fmlookup A p);
       pq  mult_poly_spec p q;
       p  weak_equality pq r;
       RETURN p
     }
  }
  unfolding check_mult_def
  apply (rule refine_IdD)
  by refine_vcg
   (auto simp: check_mult_def weak_equality_def
      mult_poly_spec_def RES_RES_RETURN_RES
    intro!:  ideal.span_zero
      exI[of _ the (fmlookup A p) * q])

primrec insort_key_rel :: "('b  'b  bool)  'b  'b list  'b list" where
"insort_key_rel f x [] = [x]" |
"insort_key_rel f x (y#ys) =
  (if f x y then (x#y#ys) else y#(insort_key_rel f x ys))"

lemma set_insort_key_rel[simp]: set (insort_key_rel R x xs) = insert x (set xs)
  by (induction xs)
   auto

lemma sorted_wrt_insort_key_rel:
   totalp_on (insert x (set xs)) R  transp R  reflp R 
    sorted_wrt R xs  sorted_wrt R (insort_key_rel R x xs)
  by (induction xs)
   (auto dest: transpD reflpD simp: totalp_on_def)

lemma sorted_wrt_insort_key_rel2:
   totalp_on (insert x (set xs)) R  transp R  x  set xs 
    sorted_wrt R xs  sorted_wrt R (insort_key_rel R x xs)
  by (induction xs)
   (auto dest: transpD simp: totalp_on_def in_mono)


paragraph ‹Step checking›

definition PAC_checker_l_step ::  _  string code_status × string set × _  (llist_polynomial, string, nat) pac_step  _ where
  PAC_checker_l_step = (λspec (st', 𝒱, A) st. case st of
     Add _ _ _ _ 
       do {
         r  full_normalize_poly (pac_res st);
        eq  check_addition_l spec A 𝒱 (pac_src1 st) (pac_src2 st) (new_id st) r;
        let _ = eq;
        if ¬is_cfailed eq
        then RETURN (merge_cstatus st' eq,
          𝒱, fmupd (new_id st) r A)
        else RETURN (eq, 𝒱, A)
   }
   | Del _ 
       do {
        eq  check_del_l spec A (pac_src1 st);
        let _ = eq;
        if ¬is_cfailed eq
        then RETURN (merge_cstatus st' eq, 𝒱, fmdrop (pac_src1 st) A)
        else RETURN (eq, 𝒱, A)
   }
   | Mult _ _ _ _ 
       do {
         r  full_normalize_poly (pac_res st);
         q  full_normalize_poly (pac_mult st);
        eq  check_mult_l spec A 𝒱 (pac_src1 st) q (new_id st) r;
        let _ = eq;
        if ¬is_cfailed eq
        then RETURN (merge_cstatus st' eq,
          𝒱, fmupd (new_id st) r A)
        else RETURN (eq, 𝒱, A)
   }
   | Extension _ _ _ 
       do {
         r  full_normalize_poly (([new_var st], -1) # (pac_res st));
        (eq)  check_extension_l spec A 𝒱 (new_id st) (new_var st) r;
        if ¬is_cfailed eq
        then do {
          RETURN (st',
            insert (new_var st) 𝒱, fmupd (new_id st) r A)}
        else RETURN (eq, 𝒱, A)
   }
 )

lemma pac_step_rel_raw_def:
  K, V, R pac_step_rel_raw = pac_step_rel_raw K V R
  by (auto intro!: ext simp: relAPP_def)

definition mononoms_equal_up_to_reorder where
  mononoms_equal_up_to_reorder xs ys 
     map (λ(a, b).  (mset a, b)) xs = map (λ(a, b). (mset a, b)) ys


 definition normalize_poly_l where
  normalize_poly_l p = SPEC (λp'.
     normalize_poly_p** ((λ(a, b). (mset a, b)) `# mset p) ((λ(a, b). (mset a, b)) `# mset p') 
     0 ∉# snd `# mset p' 
     sorted_wrt (rel2p (term_order_rel ×r int_rel)) p' 
     ( x  mononoms p'. sorted_wrt (rel2p var_order_rel) x))


definition remap_polys_l_dom_err :: string nres where
  remap_polys_l_dom_err = SPEC (λ_. True)


definition remap_polys_l :: llist_polynomial  string set  (nat, llist_polynomial) fmap 
   (_ code_status × string set × (nat, llist_polynomial) fmap) nres where
  remap_polys_l spec = (λ𝒱 A. do{
   dom  SPEC(λdom. set_mset (dom_m A)  dom  finite dom);
   failed  SPEC(λ_::bool. True);
   if failed
   then do {
      c  remap_polys_l_dom_err;
      RETURN (error_msg (0 :: nat) c, 𝒱, fmempty)
   }
   else do {
     (b, 𝒱, A)  FOREACH dom
       (λi (b, 𝒱,  A').
          if i ∈# dom_m A
          then  do {
            p  full_normalize_poly (the (fmlookup A i));
            eq  weak_equality_l p spec;
            𝒱  RETURN(𝒱  vars_llist (the (fmlookup A i)));
            RETURN(b  eq, 𝒱, fmupd i p A')
          } else RETURN (b, 𝒱, A'))
       (False, 𝒱, fmempty);
     RETURN (if b then CFOUND else CSUCCESS, 𝒱, A)
 }})

definition PAC_checker_l where
  PAC_checker_l spec A b st = do {
    (S, _)  WHILET
       (λ((b, A), n). ¬is_cfailed b  n  [])
       (λ((bA), n). do {
          ASSERT(n  []);
          S  PAC_checker_l_step spec bA (hd n);
          RETURN (S, tl n)
        })
      ((b, A), st);
    RETURN S
  }


subsection ‹Correctness›

text ‹We now enter the locale to reason about polynomials directly.›

context poly_embed
begin

abbreviation pac_step_rel where
  pac_step_rel  p2rel (Id, fully_unsorted_poly_rel O mset_poly_rel, var_rel pac_step_rel_raw)

abbreviation fmap_polys_rel where
  fmap_polys_rel  nat_rel, sorted_poly_rel O mset_poly_relfmap_rel

lemma
  normalize_poly_p s0 s 
        (s0, p)  mset_poly_rel 
        (s, p)  mset_poly_rel
  by (auto simp: mset_poly_rel_def normalize_poly_p_poly_of_mset)

lemma vars_poly_of_vars:
  vars (poly_of_vars a :: int mpoly)  (φ ` set_mset a)
  by (induction a)
   (auto simp: vars_mult_Var)

lemma vars_polynomial_of_mset:
  vars (polynomial_of_mset za)  (image φ ` (set_mset o fst) ` set_mset za)
  apply (induction za)
  using vars_poly_of_vars
  by (fastforce elim!: in_vars_addE simp: vars_mult_Const split: if_splits)+

lemma fully_unsorted_poly_rel_vars_subset_vars_llist:
  (A, B)  fully_unsorted_poly_rel O mset_poly_rel  vars B  φ ` vars_llist A
  by (auto simp: fully_unsorted_poly_list_rel_def mset_poly_rel_def
      set_rel_def var_rel_def br_def vars_llist_def list_rel_append2 list_rel_append1
      list_rel_split_right_iff list_mset_rel_def image_iff
      unsorted_term_poly_list_rel_def list_rel_split_left_iff
    dest!: set_rev_mp[OF _ vars_polynomial_of_mset] split_list
    dest: multi_member_split
    dest: arg_cong[of mset _ add_mset _ _ set_mset])

lemma fully_unsorted_poly_rel_extend_vars:
  (A, B)  fully_unsorted_poly_rel O mset_poly_rel 
  (x1c, x1a)  var_relset_rel 
   RETURN (x1c  vars_llist A)
      (var_relset_rel)
       (SPEC ((⊆) (x1a  vars (B))))
  using fully_unsorted_poly_rel_vars_subset_vars_llist[of A B]
  apply (subst RETURN_RES_refine_iff)
  apply clarsimp
  apply (rule exI[of _ x1a  φ ` vars_llist A])
  apply (auto simp: set_rel_def var_rel_def br_def
    dest: fully_unsorted_poly_rel_vars_subset_vars_llist)
  done

lemma remap_polys_l_remap_polys:
  assumes
    AB: (A, B)  nat_rel, fully_unsorted_poly_rel O mset_poly_relfmap_rel and
    spec: (spec, spec')  sorted_poly_rel O mset_poly_rel and
    V: (𝒱, 𝒱')  var_relset_rel
  shows remap_polys_l spec 𝒱 A 
     (code_status_status_rel ×r var_relset_rel ×r fmap_polys_rel) (remap_polys spec' 𝒱' B)
  (is _   ?R _)
proof -
  have 1: inj_on id (dom :: nat set) for dom
    by auto
  have H: x ∈# dom_m A 
     (p. (the (fmlookup A x), p)  fully_unsorted_poly_rel 
       (p, the (fmlookup B x))  mset_poly_rel  thesis) 
     thesis for x thesis
     using fmap_rel_nat_the_fmlookup[OF AB, of x x] fmap_rel_nat_rel_dom_m[OF AB] by auto
  have full_normalize_poly: full_normalize_poly (the (fmlookup A x))
         (sorted_poly_rel O mset_poly_rel)
          (SPEC
            (λp. the (fmlookup B x') - p  More_Modules.ideal polynomial_bool 
                 vars p  vars (the (fmlookup B x'))))
      if x_dom: x ∈# dom_m A and (x, x')  Id for x x'
      apply (rule H[OF x_dom])
      subgoal for p
      apply (rule full_normalize_poly_normalize_poly_p[THEN order_trans])
      apply assumption
      subgoal
        using that(2) apply -
        unfolding conc_fun_chain[symmetric]
        by (rule ref_two_step', rule RES_refine)
         (auto simp: rtranclp_normalize_poly_p_poly_of_mset
          mset_poly_rel_def ideal.span_zero)
      done
      done

  have H': (p, pa)  sorted_poly_rel O mset_poly_rel 
     weak_equality_l p spec  SPEC (λeqa. eqa  pa = spec') for p pa
    using spec by (auto simp: weak_equality_l_def weak_equality_spec_def
        list_mset_rel_def br_def mset_poly_rel_def
      dest: list_rel_term_poly_list_rel_same_rightD sorted_poly_list_relD)

  have emp: (𝒱, 𝒱')  var_relset_rel 
    ((False, 𝒱, fmempty), False, 𝒱', fmempty)  bool_rel ×r var_relset_rel ×r fmap_polys_rel for 𝒱 𝒱'
    by auto
  show ?thesis
    using assms
    unfolding remap_polys_l_def remap_polys_l_dom_err_def
      remap_polys_def prod.case
    apply (refine_rcg full_normalize_poly fmap_rel_fmupd_fmap_rel)
    subgoal
      by auto
    subgoal
      by auto
    subgoal
      by (auto simp: error_msg_def)
    apply (rule 1)
    subgoal by auto
    apply (rule emp)
    subgoal
      using V by auto
    subgoal by auto
    subgoal by auto
    subgoal by (rule H')
    apply (rule fully_unsorted_poly_rel_extend_vars)
    subgoal by (auto intro!: fmap_rel_nat_the_fmlookup)
    subgoal by (auto intro!: fmap_rel_fmupd_fmap_rel)
    subgoal by (auto intro!: fmap_rel_fmupd_fmap_rel)
    subgoal by auto
    subgoal by auto
    done
qed


lemma fref_to_Down_curry:
  (uncurry f, uncurry g)  [P]f A  Bnres_rel 
     (x x' y y'. P (x', y')  ((x, y), (x', y'))  A  f x y   B (g x' y'))
  unfolding fref_def uncurry_def nres_rel_def
  by auto

lemma weak_equality_spec_weak_equality:
  (p, p')  mset_poly_rel 
    (r, r')  mset_poly_rel 
    weak_equality_spec p r  weak_equality p' r'
  unfolding weak_equality_spec_def weak_equality_def
  by (auto simp: mset_poly_rel_def)


lemma weak_equality_l_weak_equality_l'[refine]:
  weak_equality_l p q   bool_rel (weak_equality p' q')
  if (p, p')  sorted_poly_rel O mset_poly_rel
    (q, q')  sorted_poly_rel O mset_poly_rel
  for p p' q q'
  using that
  by (auto intro!: weak_equality_l_weak_equality_spec[THEN fref_to_Down_curry, THEN order_trans]
         ref_two_step'
         weak_equality_spec_weak_equality
      simp flip: conc_fun_chain)

lemma error_msg_ne_SUCCES[iff]:
  error_msg i m  CSUCCESS
  error_msg i m  CFOUND
  is_cfailed (error_msg i m)
  ¬is_cfound (error_msg i m)
  by (auto simp: error_msg_def)

lemma sorted_poly_rel_vars_llist:
  (r, r')  sorted_poly_rel O mset_poly_rel 
   vars r'  φ ` vars_llist r
  apply (auto simp: mset_poly_rel_def
      set_rel_def var_rel_def br_def vars_llist_def list_rel_append2 list_rel_append1
      list_rel_split_right_iff list_mset_rel_def image_iff sorted_poly_list_rel_wrt_def
    dest!: set_rev_mp[OF _ vars_polynomial_of_mset]
    dest!: split_list)
    apply (auto dest!: multi_member_split simp: list_rel_append1
      term_poly_list_rel_def eq_commute[of _ mset _]
      list_rel_split_right_iff list_rel_append2 list_rel_split_left_iff
      dest: arg_cong[of mset _ add_mset _ _ set_mset])
    done


lemma check_addition_l_check_add:
  assumes (A, B)  fmap_polys_rel and (r, r')  sorted_poly_rel O mset_poly_rel
    (p, p')  Id (q, q')  Id (i, i')  nat_rel
    (𝒱', 𝒱)  var_relset_rel
  shows
    check_addition_l spec A 𝒱' p q i r   {(st, b). (¬is_cfailed st  b) 
       (is_cfound st  spec = r)} (check_add B 𝒱 p' q' i' r')
proof -
  have [refine]:
    add_poly_l (p, q)   (sorted_poly_rel O mset_poly_rel) (add_poly_spec p' q')
    if (p, p')  sorted_poly_rel O mset_poly_rel
      (q, q')  sorted_poly_rel O mset_poly_rel
    for p p' q q'
    using that
    by (auto intro!: add_poly_l_add_poly_p'[THEN order_trans] ref_two_step'
         add_poly_p'_add_poly_spec
      simp flip: conc_fun_chain)

  show ?thesis
    using assms
    unfolding check_addition_l_def
      check_not_equal_dom_err_def apply -
    apply (rule order_trans)
    defer
    apply (rule ref_two_step')
    apply (rule check_add_alt_def)
    apply refine_rcg
    subgoal
      by (drule sorted_poly_rel_vars_llist)
       (auto simp: set_rel_def var_rel_def br_def)
    subgoal
      by auto
    subgoal
      by auto
    subgoal
      by auto
    subgoal
      by auto
    subgoal
      by auto
    subgoal
      by auto
    subgoal
      by (auto simp: weak_equality_l_def bind_RES_RETURN_eq)
    done
qed

lemma check_del_l_check_del:
  (A, B)  fmap_polys_rel  (x3, x3a)  Id  check_del_l spec A (pac_src1 (Del x3))
      {(st, b). (¬is_cfailed st  b)  (b  st = CSUCCESS)} (check_del B (pac_src1 (Del x3a)))
  unfolding check_del_l_def check_del_def
  by (refine_vcg lhs_step_If RETURN_SPEC_refine)
    (auto simp: fmap_rel_nat_rel_dom_m bind_RES_RETURN_eq)

lemma check_mult_l_check_mult:
  assumes (A, B)  fmap_polys_rel and (r, r')  sorted_poly_rel O mset_poly_rel and
    (q, q')  sorted_poly_rel O mset_poly_rel
    (p, p')  Id (i, i')  nat_rel (𝒱, 𝒱')  var_relset_rel
  shows
    check_mult_l spec A 𝒱 p q i r    {(st, b). (¬is_cfailed st  b) 
       (is_cfound st  spec = r)} (check_mult B 𝒱' p' q' i' r')
proof -
  have [refine]:
    mult_poly_full p q   (sorted_poly_rel O mset_poly_rel) (mult_poly_spec p' q')
    if (p, p')  sorted_poly_rel O mset_poly_rel
      (q, q')  sorted_poly_rel O mset_poly_rel
    for p p' q q'
    using that
    by (auto intro!: mult_poly_full_mult_poly_p'[THEN order_trans] ref_two_step'
         mult_poly_p'_mult_poly_spec
      simp flip: conc_fun_chain)

  show ?thesis
    using assms
    unfolding check_mult_l_def
      check_mult_l_mult_err_def check_mult_l_dom_err_def apply -
    apply (rule order_trans)
    defer
    apply (rule ref_two_step')
    apply (rule check_mult_alt_def)
    apply refine_rcg
    subgoal
      by (drule sorted_poly_rel_vars_llist)+
        (fastforce simp: set_rel_def var_rel_def br_def)
    subgoal
      by auto
    subgoal
      by auto
    subgoal
      by auto
    subgoal
      by auto
    subgoal
      by (auto simp: weak_equality_l_def bind_RES_RETURN_eq)
    done
qed


lemma normalize_poly_normalize_poly_spec:
  assumes (r, t)  unsorted_poly_rel O mset_poly_rel
  shows
    normalize_poly r  (sorted_poly_rel O mset_poly_rel) (normalize_poly_spec t)
proof -
  obtain s where
    rs: (r, s)  unsorted_poly_rel and
    st: (s, t)  mset_poly_rel
    using assms by auto
  show ?thesis
    by (rule normalize_poly_normalize_poly_p[THEN order_trans, OF rs])
     (use st in auto dest!: rtranclp_normalize_poly_p_poly_of_mset
      intro!: ref_two_step' RES_refine exI[of _ t]
      simp: normalize_poly_spec_def ideal.span_zero mset_poly_rel_def
      simp flip: conc_fun_chain)
qed

lemma remove1_list_rel:
  (xs, ys)  R list_rel 
  (a, b)  R 
  IS_RIGHT_UNIQUE R 
  IS_LEFT_UNIQUE R 
  (remove1 a xs, remove1 b ys)  Rlist_rel
  by (induction xs ys rule: list_rel_induct)
   (auto simp: single_valued_def IS_LEFT_UNIQUE_def)

lemma remove1_list_rel2:
  (xs, ys)  R list_rel 
  (a, b)  R 
  (c. (a, c)  R  c = b) 
  (c. (c, b)  R  c = a) 
  (remove1 a xs, remove1 b ys)  Rlist_rel
  apply (induction xs ys rule: list_rel_induct)
   apply (solves simp (no_asm))
  by (smt (verit) list_rel_simp(4) remove1.simps(2))

lemma remove1_sorted_poly_rel_mset_poly_rel:
  assumes
    (r, r')  sorted_poly_rel O mset_poly_rel and
    ([a], 1)  set r
  shows
    (remove1 ([a], 1) r, r' - Var (φ a))
           sorted_poly_rel O mset_poly_rel
proof -
   have [simp]: ([a], {#a#})  term_poly_list_rel
     aa. ([a], aa)  term_poly_list_rel  aa = {#a#}
     by (auto simp: term_poly_list_rel_def)
  have H:
    aa. ([a], aa)  term_poly_list_rel  aa = {#a#}
     aa. (aa, {#a#})  term_poly_list_rel  aa = [a]
     by (auto simp: single_valued_def IS_LEFT_UNIQUE_def
       term_poly_list_rel_def)

  have [simp]: Const (1 :: int) = (1 :: int mpoly)
    by (simp add: Const.abs_eq Const0_one one_mpoly.abs_eq)
  have [simp]: sorted_wrt term_order (map fst r) 
         sorted_wrt term_order (map fst (remove1 ([a], 1) r))
    by (induction r) auto
  have [intro]: distinct (map fst r)  distinct (map fst (remove1 x r)) for x
    by (induction r) (auto dest: notin_set_remove1)
  have [simp]: (r, ya)  term_poly_list_rel ×r int_rellist_rel 
         polynomial_of_mset (mset ya) -  Var (φ a) =
         polynomial_of_mset (remove1_mset ({#a#}, 1) (mset ya)) for ya
    using assms
     by (auto simp: list_rel_append1 list_rel_split_right_iff
       dest!: split_list)

  show ?thesis
    using assms
    apply (elim relcompEpair)
    apply (rename_tac za, rule_tac b = remove1_mset ({#a#}, 1) za in relcompI)
    apply (auto simp: mset_poly_rel_def sorted_poly_list_rel_wrt_def Collect_eq_comp'
      intro!: relcompI[of _ remove1 ({#a#}, 1) ya
        for ya :: (string multiset × int) list]  remove1_list_rel2 intro: H
      simp: list_mset_rel_def br_def
      dest: in_diffD)
    done
qed

lemma remove1_sorted_poly_rel_mset_poly_rel_minus:
  assumes
    (r, r')  sorted_poly_rel O mset_poly_rel and
    ([a], -1)  set r
  shows
    (remove1 ([a], -1) r, r' + Var (φ a))
           sorted_poly_rel O mset_poly_rel
proof -
   have [simp]: ([a], {#a#})  term_poly_list_rel
     aa. ([a], aa)  term_poly_list_rel  aa = {#a#}
     by (auto simp: term_poly_list_rel_def)
  have H:
    aa. ([a], aa)  term_poly_list_rel  aa = {#a#}
     aa. (aa, {#a#})  term_poly_list_rel  aa = [a]
     by (auto simp: single_valued_def IS_LEFT_UNIQUE_def
       term_poly_list_rel_def)

  have [simp]: Const (1 :: int) = (1 :: int mpoly)
    by (simp add: Const.abs_eq Const0_one one_mpoly.abs_eq)
  have [simp]: sorted_wrt term_order (map fst r) 
         sorted_wrt term_order (map fst (remove1 ([a], -1) r))
    by (induction r) auto
  have [intro]: distinct (map fst r)  distinct (map fst (remove1 x r)) for x
    apply (induction r) apply auto
    by (meson img_fst in_set_remove1D)
  have [simp]: (r, ya)  term_poly_list_rel ×r int_rellist_rel 
         polynomial_of_mset (mset ya) +  Var (φ a) =
         polynomial_of_mset (remove1_mset ({#a#}, -1) (mset ya)) for ya
    using assms
     by (auto simp: list_rel_append1 list_rel_split_right_iff
       dest!: split_list)

  show ?thesis
    using assms
    apply (elim relcompEpair)
    apply (rename_tac za, rule_tac b = remove1_mset ({#a#}, -1) za in relcompI)
    by (auto simp: mset_poly_rel_def sorted_poly_list_rel_wrt_def Collect_eq_comp'
       dest: in_diffD
       intro!: relcompI[of _ remove1 ({#a#}, -1) ya
         for ya :: (string multiset × int) list]  remove1_list_rel2 intro: H
      simp: list_mset_rel_def br_def)
qed


lemma insert_var_rel_set_rel:
  (𝒱, 𝒱')  var_relset_rel 
  (yb, x2)  var_rel 
  (insert yb 𝒱, insert x2 𝒱')  var_relset_rel
  by (auto simp: var_rel_def set_rel_def)

lemma var_rel_set_rel_iff:
  (𝒱, 𝒱')  var_relset_rel 
  (yb, x2)  var_rel 
  yb  𝒱  x2   𝒱'
  using φ_inj inj_eq by (fastforce simp: var_rel_def set_rel_def br_def)


lemma check_extension_l_check_extension:
  assumes (A, B)  fmap_polys_rel and (r, r')  sorted_poly_rel O mset_poly_rel and
    (i, i')  nat_rel (𝒱, 𝒱')  var_relset_rel (x, x')  var_rel
  shows
    check_extension_l spec A 𝒱 i x r 
      {((st), (b)).
        (¬is_cfailed st  b) 
       (is_cfound st  spec = r)} (check_extension B 𝒱' i' x' r')
proof -
  have x' = φ x
    using assms(5) by (auto simp: var_rel_def br_def)
  have [refine]:
    mult_poly_full p q   (sorted_poly_rel O mset_poly_rel) (mult_poly_spec p' q')
    if (p, p')  sorted_poly_rel O mset_poly_rel
      (q, q')  sorted_poly_rel O mset_poly_rel
    for p p' q q'
    using that
    by (auto intro!: mult_poly_full_mult_poly_p'[THEN order_trans] ref_two_step'
         mult_poly_p'_mult_poly_spec
      simp flip: conc_fun_chain)
  have [refine]:
    add_poly_l (p, q)   (sorted_poly_rel O mset_poly_rel) (add_poly_spec p' q')
    if (p, p')  sorted_poly_rel O mset_poly_rel
      (q, q')  sorted_poly_rel O mset_poly_rel
    for p p' q q'
    using that
    by (auto intro!: add_poly_l_add_poly_p'[THEN order_trans] ref_two_step'
         add_poly_p'_add_poly_spec
      simp flip: conc_fun_chain)

  have [simp]: (l, l')  term_poly_list_rel ×r int_rellist_rel 
       (map (λ(a, b). (a, - b)) l, map (λ(a, b). (a, - b)) l')
        term_poly_list_rel ×r int_rellist_rel for l l'
     by (induction l l'  rule: list_rel_induct)
        (auto simp: list_mset_rel_def br_def)

  have [intro!]:
    (x2c, za)  term_poly_list_rel ×r int_rellist_rel O list_mset_rel 
     (map (λ(a, b). (a, - b)) x2c,
        {#case x of (a, b)  (a, - b). x ∈# za#})
        term_poly_list_rel ×r int_rellist_rel O list_mset_rel for x2c za
     apply (auto)
     subgoal for y
       apply (induction x2c y  rule: list_rel_induct)
       apply (auto simp: list_mset_rel_def br_def)
       apply (rename_tac a ba aa l l', rule_tac b = (aa, - ba) # map (λ(a, b). (a, - b)) l' in relcompI)
       by auto
     done
  have [simp]: (λx. fst (case x of (a, b)  (a, - b))) = fst
    by (auto intro: ext)

  have uminus: (x2c, x2a)  sorted_poly_rel O mset_poly_rel 
       (map (λ(a, b). (a, - b)) x2c,
        - x2a)
        sorted_poly_rel O mset_poly_rel for x2c x2a x1c x1a
     apply (clarsimp simp: sorted_poly_list_rel_wrt_def
      mset_poly_rel_def)
    apply (rule_tac b = (λ(a, b). (a, - b)) `# za in relcompI)
    by (auto simp: sorted_poly_list_rel_wrt_def
      mset_poly_rel_def comp_def polynomial_of_mset_uminus)
   have [simp]: ([], 0)  sorted_poly_rel O mset_poly_rel
     by (auto simp: sorted_poly_list_rel_wrt_def
      mset_poly_rel_def list_mset_rel_def br_def
      intro!: relcompI[of _ {#}])
   show ?thesis
     unfolding check_extension_l_def
       check_extension_l_dom_err_def
       check_extension_l_no_new_var_err_def
       check_extension_l_new_var_multiple_err_def
       check_extension_l_side_cond_err_def
      apply (rule order_trans)
      defer
      apply (rule ref_two_step')
      apply (rule check_extension_alt_def)
      apply (refine_vcg )
      subgoal using assms(1,3,4,5)
        by (auto simp: var_rel_set_rel_iff)
      subgoal using assms(1,3,4,5)
        by (auto simp: var_rel_set_rel_iff)
      subgoal by auto
      subgoal by auto
      apply (subst x' = φ x, rule remove1_sorted_poly_rel_mset_poly_rel_minus)
      subgoal using assms by auto
      subgoal using assms by auto
      subgoal using sorted_poly_rel_vars_llist[of r r'] assms
        by (force simp: set_rel_def var_rel_def br_def
          dest!: sorted_poly_rel_vars_llist)
      subgoal by auto
      subgoal by auto
      subgoal using assms by auto
      subgoal using assms by auto
      apply (rule uminus)
      subgoal using assms by auto
      subgoal using assms by auto
      subgoal using assms by auto
      subgoal using assms by auto
      subgoal using assms by auto
      done
qed


lemma full_normalize_poly_diff_ideal:
  fixes dom
  assumes (p, p')  fully_unsorted_poly_rel O mset_poly_rel
  shows
    full_normalize_poly p
      (sorted_poly_rel O mset_poly_rel)
       (normalize_poly_spec p')
proof -
  obtain q where
    pq: (p, q)  fully_unsorted_poly_rel  and qp':(q, p')  mset_poly_rel
    using assms by auto
   show ?thesis
     unfolding normalize_poly_spec_def
     apply (rule full_normalize_poly_normalize_poly_p[THEN order_trans])
     apply (rule pq)
     unfolding conc_fun_chain[symmetric]
     by (rule ref_two_step', rule RES_refine)
       (use qp' in auto dest!: rtranclp_normalize_poly_p_poly_of_mset
            simp: mset_poly_rel_def ideal.span_zero)
qed

lemma insort_key_rel_decomp:
   ys zs. xs = ys @ zs  insort_key_rel R x xs = ys @ x # zs
  apply (induction xs)
  subgoal by auto
  subgoal for a xs
    by (force intro: exI[of _ a # _])
  done

lemma list_rel_append_same_length:
   length xs = length xs'  (xs @ ys, xs' @ ys')  Rlist_rel  (xs, xs')  Rlist_rel  (ys, ys')  Rlist_rel
  by (auto simp: list_rel_def list_all2_append2 dest: list_all2_lengthD)

lemma term_poly_list_rel_list_relD: (ys, cs)  term_poly_list_rel ×r int_rellist_rel 
       cs = map (λ(a, y). (mset a, y)) ys
  by (induction ys arbitrary: cs)
   (auto simp: term_poly_list_rel_def list_rel_def list_all2_append list_all2_Cons1 list_all2_Cons2)

lemma term_poly_list_rel_single: ([x32], {#x32#})  term_poly_list_rel
  by (auto simp: term_poly_list_rel_def)

lemma unsorted_poly_rel_list_rel_list_rel_uminus:
   (map (λ(a, b). (a, - b)) r, yc)
        unsorted_term_poly_list_rel ×r int_rellist_rel 
       (r, map (λ(a, b). (a, - b)) yc)
        unsorted_term_poly_list_rel ×r int_rellist_rel
  by (induction r arbitrary: yc)
   (auto simp: elim!: list_relE3)

lemma mset_poly_rel_minus: ({#(a, b)#}, v')  mset_poly_rel 
       (mset yc, r')  mset_poly_rel 
       (r, yc)
        unsorted_term_poly_list_rel ×r int_rellist_rel 
       (add_mset (a, b) (mset yc),
        v' + r')
        mset_poly_rel
  by (induction r arbitrary: r')
    (auto simp: mset_poly_rel_def polynomial_of_mset_uminus)

lemma fully_unsorted_poly_rel_diff:
   ([v], v')  fully_unsorted_poly_rel O mset_poly_rel 
   (r, r')  fully_unsorted_poly_rel O mset_poly_rel 
    (v # r,
     v' + r')
     fully_unsorted_poly_rel O mset_poly_rel
  apply auto
  apply (rule_tac b = y + ya in relcompI)
  apply (auto simp: fully_unsorted_poly_list_rel_def list_mset_rel_def br_def)
  apply (rule_tac b = yb @ yc in relcompI)
  apply (auto elim!: list_relE3 simp: unsorted_poly_rel_list_rel_list_rel_uminus mset_poly_rel_minus)
  done

lemma PAC_checker_l_step_PAC_checker_step:
  assumes
    (Ast, Bst)  code_status_status_rel ×r var_relset_rel ×r fmap_polys_rel and
    (st, st')  pac_step_rel and
    spec: (spec, spec')  sorted_poly_rel O mset_poly_rel
  shows
    PAC_checker_l_step spec Ast st   (code_status_status_rel ×r var_relset_rel ×r fmap_polys_rel) (PAC_checker_step spec' Bst st')
proof -
  obtain A 𝒱 cst B 𝒱' cst' where
   Ast: Ast = (cst, 𝒱, A) and
   Bst: Bst = (cst', 𝒱', B) and
   𝒱[intro]: (𝒱, 𝒱')   var_relset_rel  and
   AB: (A, B)  fmap_polys_rel
     (cst, cst')  code_status_status_rel
    using assms(1)
    by (cases Ast; cases Bst; auto)
  have [refine]: (r, ra)  sorted_poly_rel O mset_poly_rel 
       (eqa, eqaa)
        {(st, b). (¬ is_cfailed st  b)  (is_cfound st  spec = r)} 
       RETURN eqa
         code_status_status_rel
          (SPEC
            (λst'. (¬ is_failed st' 
                   is_found st' 
                    ra - spec'  More_Modules.ideal polynomial_bool)))
     for r ra eqa eqaa
     using spec
     by (cases eqa)
       (auto intro!: RETURN_RES_refine dest!: sorted_poly_list_relD
         simp: mset_poly_rel_def ideal.span_zero)
  have [simp]: (eqa, st'a)  code_status_status_rel 
       (merge_cstatus cst eqa, merge_status cst' st'a)
        code_status_status_rel for eqa st'a
     using AB
     by (cases eqa; cases st'a)
       (auto simp: code_status_status_rel_def)
  have [simp]: (merge_cstatus cst CSUCCESS, cst')  code_status_status_rel
    using AB
    by (cases st)
      (auto simp: code_status_status_rel_def)
  have [simp]: (x32, x32a)  var_rel 
        ([([x32], -1::int)], -Var x32a)  fully_unsorted_poly_rel O mset_poly_rel for x32 x32a
   by (auto simp: mset_poly_rel_def fully_unsorted_poly_list_rel_def list_mset_rel_def br_def
         unsorted_term_poly_list_rel_def var_rel_def Const_1_eq_1
       intro!: relcompI[of _ {#({#x32#}, -1 :: int)#}]
         relcompI[of _ [({#x32#}, -1)]])
  have H3: p - Var a = (-Var a) + p for p :: int mpoly and a
    by auto
  show ?thesis
    using assms(2)
    unfolding PAC_checker_l_step_def PAC_checker_step_def Ast Bst prod.case
    apply (cases st; cases st'; simp only: p2rel_def pac_step.case
      pac_step_rel_raw_def mem_Collect_eq prod.case pac_step_rel_raw.simps)
    subgoal
      apply (refine_rcg normalize_poly_normalize_poly_spec
        check_mult_l_check_mult check_addition_l_check_add
        full_normalize_poly_diff_ideal)
      subgoal using AB by auto
      subgoal using AB by auto
      subgoal by auto
      subgoal by auto
      subgoal by auto
      subgoal by (auto intro: 𝒱)
      apply assumption+
      subgoal
        by (auto simp: code_status_status_rel_def)
      subgoal
        by (auto intro!: fmap_rel_fmupd_fmap_rel
          fmap_rel_fmdrop_fmap_rel AB)
      subgoal using AB by auto
      done
    subgoal
      apply (refine_rcg normalize_poly_normalize_poly_spec
        check_mult_l_check_mult check_addition_l_check_add
        full_normalize_poly_diff_ideal[unfolded normalize_poly_spec_def[symmetric]])
      subgoal using AB by auto
      subgoal using AB by auto
      subgoal using AB by auto
      subgoal by auto
      subgoal by auto
      subgoal by auto
      apply assumption+
      subgoal
        by (auto simp: code_status_status_rel_def)
      subgoal
        by (auto intro!: fmap_rel_fmupd_fmap_rel
          fmap_rel_fmdrop_fmap_rel AB)
      subgoal using AB by auto
      done
    subgoal
      apply (refine_rcg full_normalize_poly_diff_ideal
        check_extension_l_check_extension)
      subgoal using AB by (auto intro!: fully_unsorted_poly_rel_diff[of _ -Var _ :: int mpoly, unfolded H3[symmetric]] simp: comp_def case_prod_beta)
      subgoal using AB by auto
      subgoal using AB by auto
      subgoal by auto
      subgoal by auto
      subgoal
        by (auto simp: code_status_status_rel_def)
      subgoal
        by (auto simp: AB
          intro!: fmap_rel_fmupd_fmap_rel insert_var_rel_set_rel)
      subgoal
        by (auto simp: code_status_status_rel_def AB
          code_status.is_cfailed_def)
      done
    subgoal
      apply (refine_rcg normalize_poly_normalize_poly_spec
        check_del_l_check_del check_addition_l_check_add
        full_normalize_poly_diff_ideal[unfolded normalize_poly_spec_def[symmetric]])
      subgoal using AB by auto
      subgoal using AB by auto
      subgoal
        by (auto intro!: fmap_rel_fmupd_fmap_rel
          fmap_rel_fmdrop_fmap_rel code_status_status_rel_def AB)
      subgoal
        by (auto intro!: fmap_rel_fmupd_fmap_rel
          fmap_rel_fmdrop_fmap_rel AB)
      done
    done
qed

lemma code_status_status_rel_discrim_iff:
  (x1a, x1c)  code_status_status_rel  is_cfailed x1a  is_failed x1c
  (x1a, x1c)  code_status_status_rel  is_cfound x1a  is_found x1c
  by (cases x1a; cases x1c; auto; fail)+

lemma PAC_checker_l_PAC_checker:
  assumes
    (A, B)  var_relset_rel ×r fmap_polys_rel and
    (st, st')  pac_step_rellist_rel and
    (spec, spec')  sorted_poly_rel O mset_poly_rel and
    (b, b')  code_status_status_rel
  shows
    PAC_checker_l spec A b st   (code_status_status_rel ×r var_relset_rel ×r fmap_polys_rel) (PAC_checker spec' B b' st')
proof -
  have [refine0]: (((b, A), st), (b', B), st')  ((code_status_status_rel ×r var_relset_rel ×r fmap_polys_rel) ×r  pac_step_rellist_rel)
    using assms by (auto simp: code_status_status_rel_def)
  show ?thesis
    using assms
    unfolding PAC_checker_l_def PAC_checker_def
    apply (refine_rcg PAC_checker_l_step_PAC_checker_step
      WHILEIT_refine[where R = ((bool_rel ×r var_relset_rel ×r fmap_polys_rel) ×r pac_step_rellist_rel)])
    subgoal by (auto simp: code_status_status_rel_discrim_iff)
    subgoal by auto
    subgoal by (auto simp: neq_Nil_conv)
    subgoal by (auto simp: neq_Nil_conv intro!: param_nth)
    subgoal by (auto simp: neq_Nil_conv)
    subgoal by auto
    done
qed

end

lemma less_than_char_of_char[code_unfold]:
  (x, y)  less_than_char  (of_char x :: nat) < of_char y
  by (auto simp: less_than_char_def less_char_def)


lemmas [code] =
  add_poly_l'.simps[unfolded var_order_rel_def]

export_code add_poly_l' in SML module_name test

definition full_checker_l
  :: llist_polynomial  (nat, llist_polynomial) fmap  (_, string, nat) pac_step list 
    (string code_status × _) nres
where
  full_checker_l spec A st = do {
    spec'  full_normalize_poly spec;
    (b, 𝒱, A)  remap_polys_l spec' {} A;
    if is_cfailed b
    then RETURN (b, 𝒱, A)
    else do {
      let 𝒱 = 𝒱  vars_llist spec;
      PAC_checker_l spec' (𝒱, A) b st
    }
  }


context poly_embed
begin


term normalize_poly_spec
thm full_normalize_poly_diff_ideal[unfolded normalize_poly_spec_def[symmetric]]
abbreviation unsorted_fmap_polys_rel where
  unsorted_fmap_polys_rel  nat_rel, fully_unsorted_poly_rel O mset_poly_relfmap_rel

lemma full_checker_l_full_checker:
 assumes
    (A, B)  unsorted_fmap_polys_rel and
    (st, st')  pac_step_rellist_rel and
    (spec, spec')  fully_unsorted_poly_rel O mset_poly_rel
  shows
    full_checker_l spec A st   (code_status_status_rel ×r var_relset_rel ×r fmap_polys_rel) (full_checker spec' B st')
proof -
  have [refine]:
    (spec, spec')  sorted_poly_rel O mset_poly_rel 
    (𝒱, 𝒱')  var_relset_rel 
    remap_polys_l spec 𝒱 A  (code_status_status_rel ×r var_relset_rel ×r fmap_polys_rel)
        (remap_polys_change_all spec' 𝒱' B) for spec spec' 𝒱 𝒱'
    apply (rule remap_polys_l_remap_polys[THEN order_trans, OF assms(1)])
    apply assumption+
    apply (rule ref_two_step[OF order.refl])
    apply(rule remap_polys_spec[THEN order_trans])
    by (rule remap_polys_polynomial_bool_remap_polys_change_all)

  show ?thesis
    unfolding full_checker_l_def full_checker_def
    apply (refine_rcg remap_polys_l_remap_polys
       full_normalize_poly_diff_ideal[unfolded normalize_poly_spec_def[symmetric]]
       PAC_checker_l_PAC_checker)
    subgoal
      using assms(3) .
    subgoal by auto
    subgoal by (auto simp: is_cfailed_def is_failed_def)
    subgoal by auto
    apply (rule fully_unsorted_poly_rel_extend_vars)
    subgoal using assms(3) .
    subgoal by auto
    subgoal by auto
    subgoal
      using assms(2) by (auto simp: p2rel_def)
    subgoal by auto
    done
qed


lemma full_checker_l_full_checker':
  (uncurry2 full_checker_l, uncurry2 full_checker) 
  ((fully_unsorted_poly_rel O mset_poly_rel) ×r unsorted_fmap_polys_rel) ×r pac_step_rellist_rel f
    (code_status_status_rel ×r var_relset_rel ×r fmap_polys_rel)nres_rel
  apply (intro frefI nres_relI)
  using full_checker_l_full_checker by force

end

definition remap_polys_l2 :: llist_polynomial  string set  (nat, llist_polynomial) fmap  _ nres where
  remap_polys_l2 spec = (λ𝒱 A. do{
   n  upper_bound_on_dom A;
   b  RETURN (n  2^64);
   if b
   then do {
     c  remap_polys_l_dom_err;
     RETURN (error_msg (0 ::nat) c, 𝒱, fmempty)
   }
   else do {
       (b, 𝒱, A)  nfoldli ([0..<n]) (λ_. True)
       (λi (b, 𝒱, A').
          if i ∈# dom_m A
          then do {
            ASSERT(fmlookup A i  None);
            p  full_normalize_poly (the (fmlookup A i));
            eq  weak_equality_l p spec;
            𝒱  RETURN (𝒱  vars_llist (the (fmlookup A i)));
            RETURN(b  eq, 𝒱, fmupd i p A')
          } else RETURN (b, 𝒱, A')
        )
       (False, 𝒱, fmempty);
     RETURN (if b then CFOUND else CSUCCESS, 𝒱, A)
    }
 })

lemma remap_polys_l2_remap_polys_l:
  remap_polys_l2 spec 𝒱 A   Id (remap_polys_l spec 𝒱 A)
proof -
  have [refine]: (A, A')  Id  upper_bound_on_dom A
      {(n, dom). dom = set [0..<n]} (SPEC (λdom. set_mset (dom_m A')  dom  finite dom)) for A A'
    unfolding upper_bound_on_dom_def
    apply (rule RES_refine)
    apply (auto simp: upper_bound_on_dom_def)
    done
  have 1: inj_on id dom for dom
    by auto
  have 2: x ∈# dom_m A 
       x' ∈# dom_m A' 
       (x, x')  nat_rel 
       (A, A')  Id 
       full_normalize_poly (the (fmlookup A x))
         Id
          (full_normalize_poly (the (fmlookup A' x')))
       for A A' x x'
       by (auto)
  have 3: (n, dom)  {(n, dom). dom = set [0..<n]} 
       ([0..<n], dom)  nat_rellist_set_rel for n dom
  by (auto simp: list_set_rel_def br_def)
  have 4: (p,q)  Id 
    weak_equality_l p spec  Id (weak_equality_l q spec) for p q spec
    by auto

  have 6: a = b  (a, b)  Id for a b
    by auto
  show ?thesis
    unfolding remap_polys_l2_def remap_polys_l_def
    apply (refine_rcg LFO_refine[where R= Id ×r Idset_rel ×r Id])
    subgoal by auto
    subgoal by auto
    subgoal by auto
    apply (rule 3)
    subgoal by auto
    subgoal by (simp add: in_dom_m_lookup_iff)
    subgoal by (simp add: in_dom_m_lookup_iff)
    apply (rule 2)
    subgoal by auto
    subgoal by auto
    subgoal by auto
    subgoal by auto
    apply (rule 4; assumption)
    apply (rule 6)
    subgoal by auto
    subgoal by auto
    subgoal by auto
    subgoal by auto
    subgoal by auto
    done
qed

end