theory PF_Ternary_Translation
  imports "../PF_Ruleset_Transformations"
          "../PF_Primitive_Matchers"
          PF_Semantics_Ternary
          PF_Unknown_Match_Tacs
          PF_Predicates
begin

lemma filter_approx'_to_pf_approx:
  assumes "\<forall> d. (filter_approx' l1 \<gamma> p d = filter_approx' l2 \<gamma> p d)"
  shows "pf_approx l1 \<gamma> p = pf_approx l2 \<gamma> p" unfolding pf_approx_def using assms by simp

lemma and_each_false[simp]:
  assumes "(ternary_ternary_eval (map_match_tac (fst \<gamma>) p m)) = TernaryFalse"
  shows "filter_approx' (and_each m l) \<gamma> p d = d"
  using assms
  by (induction l)
     (auto split:line.splits decision_wrap.splits
           simp:filter_approx'_cases and_line_cases matches_def eval_ternary_simps_simple(3))

lemma and_each_true[simp]:
  assumes "(ternary_ternary_eval (map_match_tac (fst \<gamma>) p m)) = TernaryTrue"
  shows "filter_approx' (and_each m l) \<gamma> p d = filter_approx' l \<gamma> p d"
  using assms
proof(induction l arbitrary: d)
  case Nil
  then show ?case by simp
next
  case (Cons a l)
  then show ?case
    by (cases d;cases a)
       (auto simp:matches_def eval_ternary_simps_simple(1))
qed

(* remove anchors *)

(* remove_anchors implementation and remove_anchors_ok in Pf_To_SimpleFirewall *)

lemma remove_anchors_preserves_semantics :
  assumes "no_match_quick rs"
      and "no_unknown_anchors \<gamma> rs"
    shows "pf_approx (remove_anchors rs) \<gamma> p =
           pf_approx rs \<gamma> p"
proof(-)
  have "filter_approx' rs \<gamma> p d =
        filter_approx' (remove_anchors rs) \<gamma> p d" for d
    using assms
  proof(induction rs arbitrary:d rule:remove_anchors.induct)
    case 1
    then show ?case by simp
  next
    case (2 r l rs)
    then show ?case
    proof(cases "(ternary_ternary_eval (map_match_tac (fst \<gamma>) p (anchor_rule.get_match r)))")
      case TernaryTrue
      then show ?thesis using 2 apply (auto simp add: filter_approx'_chain)
        by (cases d;auto simp add:matches_def no_match_quick_def no_unknown_anchors_def)
    next
      case TernaryFalse
      then show ?thesis using 2 apply (auto simp add: filter_approx'_chain)
        by (cases d;auto simp add:matches_def no_match_quick_def no_unknown_anchors_def)
    next
      case TernaryUnknown
      then show ?thesis using 2(4) by (auto simp:no_unknown_anchors_def)
    qed
  next
    case (3 v rs)
    then show ?case apply (simp add:no_match_quick_def no_unknown_anchors_def)
      by (metis (no_types, hide_lams) append_Cons append_Nil filter_approx'_chain)
  qed
  then show ?thesis by (simp add: filter_approx'_to_pf_approx)
qed

(* lemma and_each_preserves_action_and_quick[simp,intro]:
  assumes "all_PfRules_P (\<lambda>r. P (pf_rule.get_action r) (pf_rule.get_quick r)) ls"
  shows "all_PfRules_P (\<lambda>r. P (pf_rule.get_action r) (pf_rule.get_quick r)) (and_each m ls)"
  using assms proof(induction ls)
  case Nil
  then show ?case by simp
next
  case (Cons l ls)
  then show ?case by(cases l;auto)
qed *)

lemma and_each_preserves_no_match_quick[simp,intro]:
  assumes "no_match_quick l"
  shows "no_match_quick (and_each m l)"
  using assms
proof(induction l)
  case Nil
  then show ?case by simp
next
  case (Cons a l)
  then show ?case by (cases a) (auto simp:no_match_quick_def)
qed

lemma no_match_quick_append[simp]:
  "no_match_quick (l1@l2) \<longleftrightarrow> no_match_quick l1 \<and> no_match_quick l2"
  by (auto simp:no_match_quick_def)

lemma remove_anchors_preserves_no_match_quick:
  assumes "no_match_quick l"
  shows "no_match_quick (remove_anchors l)"
  using assms
proof(induction l rule:remove_anchors.induct)
  case 1
  then show ?case by simp
next
  case (2 r l rs)
  have "no_match_quick (remove_anchors l)" using 2(1) 2(3) by (simp add:no_match_quick_def)
  then have *:"no_match_quick (and_each (anchor_rule.get_match r) (remove_anchors l))" by simp
  have "no_match_quick (remove_anchors rs)" using 2(2) 2(3) by (simp add:no_match_quick_def)
  then show ?case using * by simp
next
  case (3 v rs)
  then show ?case by (simp add:no_match_quick_def)
qed

lemma and_each_preserves_good_match_expr:
  assumes "good_match_expr ctx m"
      and "wf_ruleset ctx rs"
    shows "wf_ruleset ctx (and_each m rs)"
  using assms
proof(induction m rs rule:and_each.induct)
  case (1 uu)
  then show ?case by simp
next
  case (2 m l ls)
  have "wf_ruleset ctx [l]" using 2(3) unfolding wf_ruleset_def by simp
  then have "wf_ruleset ctx [and_line m l]" apply(cases l) using 2(2) unfolding wf_ruleset_def by (simp add:good_match_expr_def)+
  moreover have "wf_ruleset ctx ls" using 2(3) unfolding wf_ruleset_def by simp
  ultimately show ?case using 2 unfolding wf_ruleset_def by simp
qed

lemma remove_anchors_preserves_wf_ruleset:
  assumes "wf_ruleset ctx rs"
  shows "wf_ruleset ctx (remove_anchors rs)"
  using assms
proof(induction rs rule:remove_anchors.induct)
case 1
then show ?case by simp
next
  case (2 r l rs)
  have "good_match_expr ctx (anchor_rule.get_match r)" using 2(3) unfolding wf_ruleset_def by simp
  moreover have "wf_ruleset ctx l" using 2(3) unfolding wf_ruleset_def by simp
  moreover have "wf_ruleset ctx rs" using 2(3) unfolding wf_ruleset_def by simp
  ultimately show ?case using 2 by (simp add:and_each_preserves_good_match_expr)
next
  case (3 v rs)
  have "good_match_expr ctx (pf_rule.get_match v)" using 3(2) unfolding wf_ruleset_def by simp
  moreover have "wf_ruleset ctx rs" using 3(2) unfolding wf_ruleset_def by simp
  ultimately show ?case unfolding wf_ruleset_def
    using "3.IH" wf_ruleset_def by auto
qed

(* helpers for remove quick *)

lemma remove_suffix[simp]:
  assumes "\<not>matches \<gamma> (pf_rule.get_match r) (pf_rule.get_action r) (unwrap_decision (filter_approx' l \<gamma> p d)) p"
  shows "filter_approx' (l@[(PfRule r)]) \<gamma> p d = filter_approx' l \<gamma> p d"
  using assms by (cases "filter_approx' l \<gamma> p d") (simp add: filter_approx'_chain)+

lemma no_quick_preliminary:
  assumes "no_quick rules"
      and "no_anchors rules" (* not necessary but makes things easier *)
    shows "is_Preliminary (filter_approx' rules \<gamma> p (Preliminary d))"
  using assms by (induction rules arbitrary: d) (auto split:line.splits simp:filter_approx'_cases)

(* remove quick *)

fun remove_quick_approx :: "'a ruleset \<Rightarrow> 'a ruleset" where
"remove_quick_approx [] = []" |
"remove_quick_approx ((PfRule r)#ls) =
(if (get_quick r)
  then (remove_quick_approx ls) @ [PfRule (r\<lparr>get_quick := False\<rparr>)]
  else (PfRule r)#(remove_quick_approx ls))"

lemma remove_quick_approx_ok:
  assumes "no_anchors rs"
  shows "no_quick (remove_quick_approx rs)"
    using assms by (induction rs rule:remove_quick_approx.induct) auto

lemma remove_quick_approx_preserves_no_anchors:
  assumes "no_anchors rs"
  shows "no_anchors (remove_quick_approx rs)"
  using assms by (induction rs rule:remove_quick_approx.induct) simp+

lemma remove_quick_approx_preserves_no_match:
  assumes "no_anchors rs"
      and "no_match rs"
    shows "no_match (remove_quick_approx rs)"
  using assms
  by (induction rs rule:remove_quick_approx.induct) simp+

lemma remove_quick_approx_preserves_semantics:
  assumes "no_anchors rs"
      and "no_match rs"
      and "good_matcher \<gamma>"
    shows "pf_approx rs \<gamma> p = pf_approx (remove_quick_approx rs) \<gamma> p"
proof(-)
  from assms have "(unwrap_decision (filter_approx' rs \<gamma> p d) =
                    unwrap_decision (filter_approx' (remove_quick_approx rs) \<gamma> p d))" for d
  proof(induction rs arbitrary:d rule:remove_quick_approx.induct)
  case 1
  then show ?case by simp
  next
    case (2 r ls)
    then show ?case
    proof(cases d)
      case (Final x1)
      then show ?thesis by auto
    next
      case (Preliminary x2)
      have "no_anchors (remove_quick_approx ls)"
        using 2 by (simp add:remove_quick_approx_preserves_no_anchors)
      moreover have "no_quick (remove_quick_approx ls)"
        using 2 by (simp add:remove_quick_approx_ok)
      ultimately have prem:"is_Preliminary(filter_approx' (remove_quick_approx ls) \<gamma> p (Preliminary x2))"
        using no_quick_preliminary by metis
      show ?thesis
      proof(cases "get_quick r")
        case quick:True
        then show ?thesis
        proof(cases "(ternary_ternary_eval (map_match_tac (fst \<gamma>) p (pf_rule.get_match r)))")
          case TernaryTrue
          then have "(filter_approx' (PfRule r # ls) \<gamma> p (Preliminary x2)) =
                      Final (action_to_decision (pf_rule.get_action r) x2)"
            (is "?dw = Final ?d")
            using quick TernaryTrue by (simp add:matches_def)
          then have res1: "unwrap_decision ?dw = ?d" by simp
          have "filter_approx' (remove_quick_approx (PfRule r # ls)) \<gamma> p (Preliminary x2) =
                filter_approx' [PfRule (r\<lparr>get_quick := False\<rparr>)] \<gamma> p (filter_approx' (remove_quick_approx ls) \<gamma> p (Preliminary x2))"
            using quick by (simp add:filter_approx'_chain)
          then have "\<exists> d. filter_approx' (remove_quick_approx (PfRule r # ls)) \<gamma> p (Preliminary x2) =
                          filter_approx' [PfRule (r\<lparr>get_quick := False\<rparr>)] \<gamma> p (Preliminary d)" using prem is_Preliminary_def by auto
          then obtain d' where "filter_approx' (remove_quick_approx (PfRule r # ls)) \<gamma> p (Preliminary x2) =
                               filter_approx' [PfRule (r\<lparr>get_quick := False\<rparr>)] \<gamma> p (Preliminary d')" by blast
          then have "filter_approx' (remove_quick_approx (PfRule r # ls)) \<gamma> p (Preliminary x2) =
                     Preliminary (action_to_decision (pf_rule.get_action r) d')"
            (is "?dw = Preliminary ?d")
            using TernaryTrue by (simp add:matches_def)
          then have res2:"unwrap_decision ?dw = ?d" by simp
          show ?thesis using Preliminary quick TernaryTrue res1 res2 2(4)
            by (auto split:action.splits simp:matches_def action_to_decision_cases)
        next
          case TernaryFalse
          then have res1:"filter_approx' (PfRule r # ls) \<gamma> p (Preliminary x2) =
                     filter_approx' ls \<gamma> p (Preliminary x2)" by (simp add:matches_def)
          have res2:"filter_approx' ((remove_quick_approx ls)@[PfRule (r\<lparr>get_quick := False\<rparr>)]) \<gamma> p (Preliminary x2) =
                     filter_approx' (remove_quick_approx ls) \<gamma> p (Preliminary x2)"
            using TernaryFalse by (simp add:matches_def)
          show ?thesis using Preliminary TernaryFalse res1 res2 2
            by (auto split:action.splits simp:matches_def action_to_decision_cases)
        next
          case TernaryUnknown
          then show ?thesis
          proof(cases "(snd \<gamma>) (pf_rule.get_action r) x2 p")
            case True
            then have "(filter_approx' (PfRule r # ls) \<gamma> p (Preliminary x2)) = Final (action_to_decision (pf_rule.get_action r) x2)" (is "?dw = Final ?d")
              using quick TernaryUnknown by (simp add:matches_def)
            then have res1: "unwrap_decision ?dw = ?d" by simp
            have "filter_approx' (remove_quick_approx (PfRule r # ls)) \<gamma> p (Preliminary x2) =
                  filter_approx' [PfRule (r\<lparr>get_quick := False\<rparr>)] \<gamma> p (filter_approx' (remove_quick_approx ls) \<gamma> p (Preliminary x2))"
              using quick by (simp add:filter_approx'_chain)
            then have "\<exists> d. filter_approx' (remove_quick_approx (PfRule r # ls)) \<gamma> p (Preliminary x2) =
                          filter_approx' [PfRule (r\<lparr>get_quick := False\<rparr>)] \<gamma> p (Preliminary d)" using prem is_Preliminary_def by auto
            then obtain d' where "filter_approx' (remove_quick_approx (PfRule r # ls)) \<gamma> p (Preliminary x2) =
                                  filter_approx' [PfRule (r\<lparr>get_quick := False\<rparr>)] \<gamma> p (Preliminary d')" by blast
            moreover have "(snd \<gamma>) (pf_rule.get_action r) d' p"
              using True 2(4) 2(5) unfolding good_matcher_def apply(auto split:action.splits) by metis
            ultimately have "filter_approx' (remove_quick_approx (PfRule r # ls)) \<gamma> p (Preliminary x2) =
                       Preliminary (action_to_decision (pf_rule.get_action r) d')" (is "?dw = Preliminary ?d")
            using TernaryUnknown True 2 by (simp add:matches_def)
          then have res2:"unwrap_decision ?dw = ?d" by simp
          show ?thesis using Preliminary True TernaryUnknown res1 res2 2(4) by (auto split:action.splits simp:matches_def action_to_decision_cases)
          next
            case False
            then have res1: "unwrap_decision (filter_approx' (PfRule r # ls) \<gamma> p (Preliminary x2)) =
                             unwrap_decision (filter_approx' ls \<gamma> p (Preliminary x2))"
              using TernaryUnknown by (simp add:matches_def)
            obtain d' where *:"filter_approx' (remove_quick_approx ls) \<gamma> p (Preliminary x2) =
                             (Preliminary d')" using prem is_Preliminary_def by blast
            then have "filter_approx' ((remove_quick_approx ls)@[PfRule (r\<lparr>get_quick := False\<rparr>)]) \<gamma> p (Preliminary x2) =
                       filter_approx' [PfRule (r\<lparr>get_quick := False\<rparr>)] \<gamma> p (Preliminary d')"
              by(simp add:filter_approx'_chain)
            moreover have "\<not>(snd \<gamma>) (pf_rule.get_action r) d' p"
              using False 2(4) 2(5) unfolding good_matcher_def apply(auto split:action.splits) by metis
            ultimately have "(filter_approx' (remove_quick_approx (PfRule r # ls)) \<gamma> p (Preliminary x2)) = (Preliminary d')" (is "?dw = (Preliminary ?d)")
              using TernaryUnknown quick by (simp add:matches_def)
            then have res2:"unwrap_decision ?dw = ?d" by auto
            have a:"unwrap_decision (filter_approx' (remove_quick_approx ls) \<gamma> p (Preliminary x2)) = d'" using * by simp
            then have "unwrap_decision (filter_approx' ls \<gamma> p (Preliminary x2)) = d'" using a 2 by auto
            then have "unwrap_decision (filter_approx' (PfRule r # ls) \<gamma> p (Preliminary x2)) = d'" using res1 TernaryUnknown False quick by simp
            then show ?thesis using Preliminary res2 by simp
          qed
        qed
      next
        case False
        then show ?thesis using 2 Preliminary by auto
      qed
    qed
  next
  case (3 vb vc va)
    then show ?case by auto
  qed
  then show ?thesis by (simp add:pf_approx_def)
qed

lemma remove_quick_approx_preserves_wf_ruleset:
  assumes "wf_ruleset ctx rs"
      and "no_anchors rs"
  shows "wf_ruleset ctx (remove_quick_approx rs)"
  using assms
proof(induction rs rule:remove_quick_approx.induct)
  case 1
  then show ?case by simp
next
  case (2 r ls)
  have r_gm:"good_match_expr ctx (pf_rule.get_match r)" using 2(3) unfolding wf_ruleset_def by simp
  have ls_wf:"wf_ruleset ctx ls" using 2(3) unfolding wf_ruleset_def by simp
  then show ?case
  proof(cases "get_quick r")
    case True
    have "wf_ruleset ctx [PfRule (r\<lparr>get_quick := False\<rparr>)]" using r_gm  wf_ruleset_def
      by (simp add:ls_wf r_gm good_match_expr_def)
    then show ?thesis using 2 True by (simp add:ls_wf r_gm good_match_expr_def)
  next
    case False
    then have "wf_ruleset ctx (remove_quick_approx ls)" using "2.IH" 2(4) ls_wf by simp
    then show ?thesis unfolding wf_ruleset_def using r_gm by simp
  qed
next
  case (3 vb vc va)
  then show ?case by simp
qed

(* remove matches *)

fun remove_matches :: "'a ruleset \<Rightarrow> 'a ruleset" where
"remove_matches [] = []"
|"remove_matches ((PfRule r)#ls) =
(if ((pf_rule.get_action r) = ActionMatch)
  then remove_matches ls
  else (PfRule r)#remove_matches ls)"
|"remove_matches (l#ls) = l#(remove_matches ls)"

lemma remove_matches_preserves_filter_semantics:
  assumes "no_match_quick rs"
      and "no_anchors rs"
    shows "filter_approx' rs \<gamma> p (Preliminary d) =
           filter_approx' (remove_matches rs) \<gamma> p (Preliminary d)"
  using assms
  by (induction rs arbitrary:d rule: remove_matches.induct; (simp add:no_match_quick_def))

lemma remove_matches_preserves_semantics:
  assumes "no_match_quick rs"
      and "no_anchors rs"
    shows "pf_approx rs \<gamma> p = pf_approx (remove_matches rs) \<gamma> p"
  using assms unfolding pf_approx_def by (simp add:remove_matches_preserves_filter_semantics)

lemma remove_matches_ok:
  assumes "no_match_quick rs"
      and "no_anchors rs"
    shows "no_match (remove_matches rs)"
  using assms by (induction rs rule: remove_matches.induct) (simp add:no_match_quick_def)+

lemma remove_matches_preserves_no_anchors:
  assumes "no_anchors rs"
    shows "no_anchors (remove_matches rs)"
  using assms by (induction rs rule: remove_matches.induct) simp+

lemma remove_matches_preserves_wf_ruleset:
  assumes "wf_ruleset ctx rs"
      and "no_anchors rs"
  shows "wf_ruleset ctx (remove_matches rs)"
  using assms
proof (induction rs rule:remove_matches.induct)
  case (2 r ls)
  have "good_match_expr ctx (pf_rule.get_match r)" using 2 unfolding wf_ruleset_def by simp
  moreover have "wf_ruleset ctx ls" using 2 unfolding wf_ruleset_def by simp
  ultimately show ?case using 2 unfolding wf_ruleset_def by (cases "pf_rule.get_action r = ActionMatch") simp+
qed simp+

(* to_simple_ruleset *)

definition to_simple_ruleset :: "'a line list \<Rightarrow> 'a line list" where
"to_simple_ruleset rs = remove_quick_approx (remove_matches (remove_anchors rs))"

lemma to_simple_ruleset:
  assumes "no_match_quick rs"
      and "good_matcher \<gamma>"
      and "no_unknown_anchors \<gamma> rs"
  shows
 "pf_approx rs \<gamma> p =
  pf_approx (to_simple_ruleset rs) \<gamma> p"
  and "simple_ruleset (to_simple_ruleset rs)"
proof(-)
  have *: "(pf_approx rs \<gamma> p) =
        (pf_approx (remove_anchors rs) \<gamma> p)"
    (is "?original = pf_approx (?anchors_removed) ?m ?p")
    using assms by (simp add:remove_anchors_preserves_semantics)
  have na:"no_anchors ?anchors_removed"
    by (simp add:remove_anchors_ok)
  have nmq:"no_match_quick ?anchors_removed"
    using assms remove_anchors_preserves_no_match_quick by blast
  have *: "?original = pf_approx (remove_matches ?anchors_removed) ?m ?p"
(is "?original = pf_approx (?matches_removed) ?m ?p")
    using * na nmq assms by(auto simp add:remove_matches_preserves_semantics)
  have nm:"no_match ?matches_removed"
    using na nmq remove_matches_ok by blast
  have na:"no_anchors ?matches_removed"
    using na using remove_matches_preserves_no_anchors by blast
  have *: "?original = pf_approx (remove_quick_approx ?matches_removed) ?m ?p"
    (is "?original = pf_approx (?quick_removed) ?m ?p")
    using * na nm assms by(simp add:remove_quick_approx_preserves_semantics)
  then show "pf_approx rs \<gamma> p =
  pf_approx (to_simple_ruleset rs) \<gamma> p"
    unfolding to_simple_ruleset_def by simp
  have nq:"no_quick ?quick_removed"
    using na remove_quick_approx_ok by blast
  have nm:"no_match ?quick_removed"
    using na nm remove_quick_approx_preserves_no_match by blast
  have na:"no_anchors ?quick_removed"
    using na remove_quick_approx_preserves_no_anchors by blast
  from nq nm na show "simple_ruleset (to_simple_ruleset rs)"
    unfolding to_simple_ruleset_def by (simp add:simple_ruleset_def)
qed

lemma to_simple_ruleset_preserves_wf_ruleset:
  assumes "wf_ruleset ctx rs"
  shows "wf_ruleset ctx (to_simple_ruleset rs)"
  unfolding to_simple_ruleset_def
  using assms
remove_anchors_ok remove_anchors_preserves_wf_ruleset
remove_matches_preserves_no_anchors remove_matches_preserves_wf_ruleset
remove_quick_approx_preserves_wf_ruleset
  by metis

(* simple ruleset reverse *)

(* Accept is arbitrary here, \<gamma> should be independent of d *)
fun match_pf_rule :: "'a line \<Rightarrow> ('a,'p) match_tac \<Rightarrow> 'p \<Rightarrow> bool" where
"match_pf_rule (PfRule r) \<gamma> p = matches \<gamma> (pf_rule.get_match r) (pf_rule.get_action r) decision.Accept p"

lemma rev_preserves_no_match[simp]:
  assumes "no_match rs"
  shows "no_match (rev rs)"
  using assms by (induction rs) auto

lemma rev_preserves_no_quick[simp]:
  assumes "no_quick rs"
  shows "no_quick (rev rs)"
  using assms by (induction rs) auto

lemma rev_preserves_no_anchors[simp]:
  assumes "no_anchors rs"
  shows "no_anchors (rev rs)"
  using assms by (induction rs) auto

lemma good_matcher_match:
  assumes "good_matcher \<gamma>"
      and "matches \<gamma> m a d p"
      and "a \<noteq> ActionMatch"
    shows "\<And>d. matches \<gamma> m a d p"
  using assms
  apply(cases "(ternary_ternary_eval (map_match_tac (fst \<gamma>) p m))")
    apply (auto simp:matches_def good_matcher_def) by metis

lemma good_matcher_match_not:
  assumes "good_matcher \<gamma>"
      and "\<not>matches \<gamma> m a d p"
      and "a \<noteq> ActionMatch"
    shows "\<And>d. \<not>matches \<gamma> m a d p"
  using assms
  apply(cases "(ternary_ternary_eval (map_match_tac (fst \<gamma>) p m))")
  apply (auto simp:matches_def good_matcher_def) by metis

lemma pf_reverse_semantics:
  assumes "simple_ruleset rs"
      and "good_matcher \<gamma>"
    shows "pf_approx (rev rs) \<gamma> p = (case (find (\<lambda>r. match_pf_rule r \<gamma> p) rs) of
(Some (PfRule r)) \<Rightarrow> (action_to_decision (pf_rule.get_action r) decision.Accept)
| None \<Rightarrow> decision.Accept)"
    using assms unfolding pf_approx_def simple_ruleset_def
proof(induction rs)
  case Nil
  then show ?case by simp
next
  case (Cons a ls)
  then show ?case
  proof(cases a)
    case (PfRule r)
    then show ?thesis
    proof(cases "matches \<gamma> (pf_rule.get_match r) (pf_rule.get_action r) decision.Accept p")
      have "is_Preliminary (filter_approx' (rev ls) \<gamma> p (Preliminary decision.Accept))"
        using Cons by (simp add:no_quick_preliminary[of "(rev ls)"])
      then obtain d where *:"(filter_approx' (rev ls) \<gamma> p (Preliminary decision.Accept)) = (Preliminary d)"
        using is_Preliminary_def by blast
      case True
      then have "matches \<gamma> (pf_rule.get_match r) (pf_rule.get_action r) d p"
        using Cons PfRule by(auto simp:good_matcher_match)
      then have "(filter_approx' (rev ls @ [PfRule r]) \<gamma> p (Preliminary decision.Accept)) =
                        Preliminary (action_to_decision (pf_rule.get_action r) d)"
        using Cons PfRule * by (simp add:filter_approx'_chain)
      then show ?thesis using Cons PfRule True by (cases "pf_rule.get_action r") auto
    next
      case False
      then have "\<not>matches \<gamma> (pf_rule.get_match r) (pf_rule.get_action r)
                    (unwrap_decision (filter_approx' (rev ls) \<gamma> p (Preliminary decision.Accept))) p"
        using Cons PfRule by(auto simp:good_matcher_match_not)
      then have "filter_approx' (rev ls @ [PfRule r]) \<gamma> p (Preliminary decision.Accept) =
                 filter_approx' (rev ls) \<gamma> p (Preliminary decision.Accept)" by simp
      then show ?thesis using Cons PfRule False by auto
    qed
  next
    case (Anchor x21 x22)
    then show ?thesis using Cons by auto
  qed
qed

lemma pf_reverse_semantics':
  assumes "simple_ruleset rs"
      and "good_matcher \<gamma>"
    shows "pf_approx rs \<gamma> p = (case (find (\<lambda>r. match_pf_rule r \<gamma> p) (rev rs)) of
(Some (PfRule r)) \<Rightarrow> (action_to_decision (pf_rule.get_action r) decision.Accept)
| None \<Rightarrow> decision.Accept)"
proof(-)
  have "simple_ruleset (rev rs)" using assms by (induction rs) (auto simp add:simple_ruleset_def)
  then show ?thesis using assms pf_reverse_semantics[where rs="(rev rs)"] by simp
qed

end


(*  Title:      ZF/Constructible/Separation.thy
    Author:     Lawrence C Paulson, Cambridge University Computer Laboratory
*)

section\<open>Early Instances of Separation and Strong Replacement\<close>

theory Separation imports L_axioms WF_absolute begin

text\<open>This theory proves all instances needed for locale \<open>M_basic\<close>\<close>

text\<open>Helps us solve for de Bruijn indices!\<close>
lemma nth_ConsI: "[|nth(n,l) = x; n \<in> nat|] ==> nth(succ(n), Cons(a,l)) = x"
by simp

lemmas nth_rules = nth_0 nth_ConsI nat_0I nat_succI
lemmas sep_rules = nth_0 nth_ConsI FOL_iff_sats function_iff_sats
                   fun_plus_iff_sats

lemma Collect_conj_in_DPow:
     "[| {x\<in>A. P(x)} \<in> DPow(A);  {x\<in>A. Q(x)} \<in> DPow(A) |]
      ==> {x\<in>A. P(x) & Q(x)} \<in> DPow(A)"
by (simp add: Int_in_DPow Collect_Int_Collect_eq [symmetric])

lemma Collect_conj_in_DPow_Lset:
     "[|z \<in> Lset(j); {x \<in> Lset(j). P(x)} \<in> DPow(Lset(j))|]
      ==> {x \<in> Lset(j). x \<in> z & P(x)} \<in> DPow(Lset(j))"
apply (frule mem_Lset_imp_subset_Lset)
apply (simp add: Collect_conj_in_DPow Collect_mem_eq
                 subset_Int_iff2 elem_subset_in_DPow)
done

lemma separation_CollectI:
     "(\<And>z. L(z) ==> L({x \<in> z . P(x)})) ==> separation(L, \<lambda>x. P(x))"
apply (unfold separation_def, clarify)
apply (rule_tac x="{x\<in>z. P(x)}" in rexI)
apply simp_all
done

text\<open>Reduces the original comprehension to the reflected one\<close>
lemma reflection_imp_L_separation:
      "[| \<forall>x\<in>Lset(j). P(x) \<longleftrightarrow> Q(x);
          {x \<in> Lset(j) . Q(x)} \<in> DPow(Lset(j));
          Ord(j);  z \<in> Lset(j)|] ==> L({x \<in> z . P(x)})"
apply (rule_tac i = "succ(j)" in L_I)
 prefer 2 apply simp
apply (subgoal_tac "{x \<in> z. P(x)} = {x \<in> Lset(j). x \<in> z & (Q(x))}")
 prefer 2
 apply (blast dest: mem_Lset_imp_subset_Lset)
apply (simp add: Lset_succ Collect_conj_in_DPow_Lset)
done

text\<open>Encapsulates the standard proof script for proving instances of
      Separation.\<close>
lemma gen_separation:
 assumes reflection: "REFLECTS [P,Q]"
     and Lu:         "L(u)"
     and collI: "!!j. u \<in> Lset(j)
                \<Longrightarrow> Collect(Lset(j), Q(j)) \<in> DPow(Lset(j))"
 shows "separation(L,P)"
apply (rule separation_CollectI)
apply (rule_tac A="{u,z}" in subset_LsetE, blast intro: Lu)
apply (rule ReflectsE [OF reflection], assumption)
apply (drule subset_Lset_ltD, assumption)
apply (erule reflection_imp_L_separation)
  apply (simp_all add: lt_Ord2, clarify)
apply (rule collI, assumption)
done

text\<open>As above, but typically @{term u} is a finite enumeration such as
  @{term "{a,b}"}; thus the new subgoal gets the assumption
  @{term "{a,b} \<subseteq> Lset(i)"}, which is logically equivalent to
  @{term "a \<in> Lset(i)"} and @{term "b \<in> Lset(i)"}.\<close>
lemma gen_separation_multi:
 assumes reflection: "REFLECTS [P,Q]"
     and Lu:         "L(u)"
     and collI: "!!j. u \<subseteq> Lset(j)
                \<Longrightarrow> Collect(Lset(j), Q(j)) \<in> DPow(Lset(j))"
 shows "separation(L,P)"
apply (rule gen_separation [OF reflection Lu])
apply (drule mem_Lset_imp_subset_Lset)
apply (erule collI)
done

subsection\<open>Separation for Intersection\<close>

lemma Inter_Reflects:
     "REFLECTS[\<lambda>x. \<forall>y[L]. y\<in>A \<longrightarrow> x \<in> y,
               \<lambda>i x. \<forall>y\<in>Lset(i). y\<in>A \<longrightarrow> x \<in> y]"
by (intro FOL_reflections)

lemma Inter_separation:
     "L(A) ==> separation(L, \<lambda>x. \<forall>y[L]. y\<in>A \<longrightarrow> x\<in>y)"
apply (rule gen_separation [OF Inter_Reflects], simp)
apply (rule DPow_LsetI)
 txt\<open>I leave this one example of a manual proof.  The tedium of manually
      instantiating @{term i}, @{term j} and @{term env} is obvious.\<close>
apply (rule ball_iff_sats)
apply (rule imp_iff_sats)
apply (rule_tac [2] i=1 and j=0 and env="[y,x,A]" in mem_iff_sats)
apply (rule_tac i=0 and j=2 in mem_iff_sats)
apply (simp_all add: succ_Un_distrib [symmetric])
done

subsection\<open>Separation for Set Difference\<close>

lemma Diff_Reflects:
     "REFLECTS[\<lambda>x. x \<notin> B, \<lambda>i x. x \<notin> B]"
by (intro FOL_reflections)

lemma Diff_separation:
     "L(B) ==> separation(L, \<lambda>x. x \<notin> B)"
apply (rule gen_separation [OF Diff_Reflects], simp)
apply (rule_tac env="[B]" in DPow_LsetI)
apply (rule sep_rules | simp)+
done

subsection\<open>Separation for Cartesian Product\<close>

lemma cartprod_Reflects:
     "REFLECTS[\<lambda>z. \<exists>x[L]. x\<in>A & (\<exists>y[L]. y\<in>B & pair(L,x,y,z)),
                \<lambda>i z. \<exists>x\<in>Lset(i). x\<in>A & (\<exists>y\<in>Lset(i). y\<in>B &
                                   pair(##Lset(i),x,y,z))]"
by (intro FOL_reflections function_reflections)

lemma cartprod_separation:
     "[| L(A); L(B) |]
      ==> separation(L, \<lambda>z. \<exists>x[L]. x\<in>A & (\<exists>y[L]. y\<in>B & pair(L,x,y,z)))"
apply (rule gen_separation_multi [OF cartprod_Reflects, of "{A,B}"], auto)
apply (rule_tac env="[A,B]" in DPow_LsetI)
apply (rule sep_rules | simp)+
done

subsection\<open>Separation for Image\<close>

lemma image_Reflects:
     "REFLECTS[\<lambda>y. \<exists>p[L]. p\<in>r & (\<exists>x[L]. x\<in>A & pair(L,x,y,p)),
           \<lambda>i y. \<exists>p\<in>Lset(i). p\<in>r & (\<exists>x\<in>Lset(i). x\<in>A & pair(##Lset(i),x,y,p))]"
by (intro FOL_reflections function_reflections)

lemma image_separation:
     "[| L(A); L(r) |]
      ==> separation(L, \<lambda>y. \<exists>p[L]. p\<in>r & (\<exists>x[L]. x\<in>A & pair(L,x,y,p)))"
apply (rule gen_separation_multi [OF image_Reflects, of "{A,r}"], auto)
apply (rule_tac env="[A,r]" in DPow_LsetI)
apply (rule sep_rules | simp)+
done

subsection\<open>Separation for Converse\<close>

lemma converse_Reflects:
  "REFLECTS[\<lambda>z. \<exists>p[L]. p\<in>r & (\<exists>x[L]. \<exists>y[L]. pair(L,x,y,p) & pair(L,y,x,z)),
     \<lambda>i z. \<exists>p\<in>Lset(i). p\<in>r & (\<exists>x\<in>Lset(i). \<exists>y\<in>Lset(i).
                     pair(##Lset(i),x,y,p) & pair(##Lset(i),y,x,z))]"
by (intro FOL_reflections function_reflections)

lemma converse_separation:
     "L(r) ==> separation(L,
         \<lambda>z. \<exists>p[L]. p\<in>r & (\<exists>x[L]. \<exists>y[L]. pair(L,x,y,p) & pair(L,y,x,z)))"
apply (rule gen_separation [OF converse_Reflects], simp)
apply (rule_tac env="[r]" in DPow_LsetI)
apply (rule sep_rules | simp)+
done

subsection\<open>Separation for Restriction\<close>

lemma restrict_Reflects:
     "REFLECTS[\<lambda>z. \<exists>x[L]. x\<in>A & (\<exists>y[L]. pair(L,x,y,z)),
        \<lambda>i z. \<exists>x\<in>Lset(i). x\<in>A & (\<exists>y\<in>Lset(i). pair(##Lset(i),x,y,z))]"
by (intro FOL_reflections function_reflections)

lemma restrict_separation:
   "L(A) ==> separation(L, \<lambda>z. \<exists>x[L]. x\<in>A & (\<exists>y[L]. pair(L,x,y,z)))"
apply (rule gen_separation [OF restrict_Reflects], simp)
apply (rule_tac env="[A]" in DPow_LsetI)
apply (rule sep_rules | simp)+
done

subsection\<open>Separation for Composition\<close>

lemma comp_Reflects:
     "REFLECTS[\<lambda>xz. \<exists>x[L]. \<exists>y[L]. \<exists>z[L]. \<exists>xy[L]. \<exists>yz[L].
                  pair(L,x,z,xz) & pair(L,x,y,xy) & pair(L,y,z,yz) &
                  xy\<in>s & yz\<in>r,
        \<lambda>i xz. \<exists>x\<in>Lset(i). \<exists>y\<in>Lset(i). \<exists>z\<in>Lset(i). \<exists>xy\<in>Lset(i). \<exists>yz\<in>Lset(i).
                  pair(##Lset(i),x,z,xz) & pair(##Lset(i),x,y,xy) &
                  pair(##Lset(i),y,z,yz) & xy\<in>s & yz\<in>r]"
by (intro FOL_reflections function_reflections)

lemma comp_separation:
     "[| L(r); L(s) |]
      ==> separation(L, \<lambda>xz. \<exists>x[L]. \<exists>y[L]. \<exists>z[L]. \<exists>xy[L]. \<exists>yz[L].
                  pair(L,x,z,xz) & pair(L,x,y,xy) & pair(L,y,z,yz) &
                  xy\<in>s & yz\<in>r)"
apply (rule gen_separation_multi [OF comp_Reflects, of "{r,s}"], auto)
txt\<open>Subgoals after applying general ``separation'' rule:
     @{subgoals[display,indent=0,margin=65]}\<close>
apply (rule_tac env="[r,s]" in DPow_LsetI)
txt\<open>Subgoals ready for automatic synthesis of a formula:
     @{subgoals[display,indent=0,margin=65]}\<close>
apply (rule sep_rules | simp)+
done

subsection\<open>Separation for Predecessors in an Order\<close>

lemma pred_Reflects:
     "REFLECTS[\<lambda>y. \<exists>p[L]. p\<in>r & pair(L,y,x,p),
                    \<lambda>i y. \<exists>p \<in> Lset(i). p\<in>r & pair(##Lset(i),y,x,p)]"
by (intro FOL_reflections function_reflections)

lemma pred_separation:
     "[| L(r); L(x) |] ==> separation(L, \<lambda>y. \<exists>p[L]. p\<in>r & pair(L,y,x,p))"
apply (rule gen_separation_multi [OF pred_Reflects, of "{r,x}"], auto)
apply (rule_tac env="[r,x]" in DPow_LsetI)
apply (rule sep_rules | simp)+
done

subsection\<open>Separation for the Membership Relation\<close>

lemma Memrel_Reflects:
     "REFLECTS[\<lambda>z. \<exists>x[L]. \<exists>y[L]. pair(L,x,y,z) & x \<in> y,
            \<lambda>i z. \<exists>x \<in> Lset(i). \<exists>y \<in> Lset(i). pair(##Lset(i),x,y,z) & x \<in> y]"
by (intro FOL_reflections function_reflections)

lemma Memrel_separation:
     "separation(L, \<lambda>z. \<exists>x[L]. \<exists>y[L]. pair(L,x,y,z) & x \<in> y)"
apply (rule gen_separation [OF Memrel_Reflects nonempty])
apply (rule_tac env="[]" in DPow_LsetI)
apply (rule sep_rules | simp)+
done

subsection\<open>Replacement for FunSpace\<close>

lemma funspace_succ_Reflects:
 "REFLECTS[\<lambda>z. \<exists>p[L]. p\<in>A & (\<exists>f[L]. \<exists>b[L]. \<exists>nb[L]. \<exists>cnbf[L].
            pair(L,f,b,p) & pair(L,n,b,nb) & is_cons(L,nb,f,cnbf) &
            upair(L,cnbf,cnbf,z)),
        \<lambda>i z. \<exists>p \<in> Lset(i). p\<in>A & (\<exists>f \<in> Lset(i). \<exists>b \<in> Lset(i).
              \<exists>nb \<in> Lset(i). \<exists>cnbf \<in> Lset(i).
                pair(##Lset(i),f,b,p) & pair(##Lset(i),n,b,nb) &
                is_cons(##Lset(i),nb,f,cnbf) & upair(##Lset(i),cnbf,cnbf,z))]"
by (intro FOL_reflections function_reflections)

lemma funspace_succ_replacement:
     "L(n) ==>
      strong_replacement(L, \<lambda>p z. \<exists>f[L]. \<exists>b[L]. \<exists>nb[L]. \<exists>cnbf[L].
                pair(L,f,b,p) & pair(L,n,b,nb) & is_cons(L,nb,f,cnbf) &
                upair(L,cnbf,cnbf,z))"
apply (rule strong_replacementI)
apply (rule_tac u="{n,B}" in gen_separation_multi [OF funspace_succ_Reflects],
       auto)
apply (rule_tac env="[n,B]" in DPow_LsetI)
apply (rule sep_rules | simp)+
done

subsection\<open>Separation for a Theorem about @{term "is_recfun"}\<close>

lemma is_recfun_reflects:
  "REFLECTS[\<lambda>x. \<exists>xa[L]. \<exists>xb[L].
                pair(L,x,a,xa) & xa \<in> r & pair(L,x,b,xb) & xb \<in> r &
                (\<exists>fx[L]. \<exists>gx[L]. fun_apply(L,f,x,fx) & fun_apply(L,g,x,gx) &
                                   fx \<noteq> gx),
   \<lambda>i x. \<exists>xa \<in> Lset(i). \<exists>xb \<in> Lset(i).
          pair(##Lset(i),x,a,xa) & xa \<in> r & pair(##Lset(i),x,b,xb) & xb \<in> r &
                (\<exists>fx \<in> Lset(i). \<exists>gx \<in> Lset(i). fun_apply(##Lset(i),f,x,fx) &
                  fun_apply(##Lset(i),g,x,gx) & fx \<noteq> gx)]"
by (intro FOL_reflections function_reflections fun_plus_reflections)

lemma is_recfun_separation:
     \<comment>\<open>for well-founded recursion\<close>
     "[| L(r); L(f); L(g); L(a); L(b) |]
     ==> separation(L,
            \<lambda>x. \<exists>xa[L]. \<exists>xb[L].
                pair(L,x,a,xa) & xa \<in> r & pair(L,x,b,xb) & xb \<in> r &
                (\<exists>fx[L]. \<exists>gx[L]. fun_apply(L,f,x,fx) & fun_apply(L,g,x,gx) &
                                   fx \<noteq> gx))"
apply (rule gen_separation_multi [OF is_recfun_reflects, of "{r,f,g,a,b}"],
            auto)
apply (rule_tac env="[r,f,g,a,b]" in DPow_LsetI)
apply (rule sep_rules | simp)+
done

subsection\<open>Instantiating the locale \<open>M_basic\<close>\<close>
text\<open>Separation (and Strong Replacement) for basic set-theoretic constructions
such as intersection, Cartesian Product and image.\<close>

lemma M_basic_axioms_L: "M_basic_axioms(L)"
  apply (rule M_basic_axioms.intro)
       apply (assumption | rule
         Inter_separation Diff_separation cartprod_separation image_separation
         converse_separation restrict_separation
         comp_separation pred_separation Memrel_separation
         funspace_succ_replacement is_recfun_separation)+
  done

theorem M_basic_L: "PROP M_basic(L)"
by (rule M_basic.intro [OF M_trivial_L M_basic_axioms_L])

interpretation L?: M_basic L by (rule M_basic_L)

end


theory Array_Map_Impl
imports
  "../Sep_Main" Imp_Map_Spec Array_Blit
  "~~/src/HOL/Library/Code_Target_Numeral"
begin
  subsection "Array Map"

  type_synonym 'v array_map = "'v option array"
  definition "iam_initial_size \<equiv> 8::nat"

  definition "iam_of_list l i \<equiv> if i<length l then l!i else None"

  definition is_iam :: "(nat\<rightharpoonup>'a) \<Rightarrow> ('a::heap) array_map \<Rightarrow> assn" where
    "is_iam m a \<equiv> \<exists>\<^sub>Al. a\<mapsto>\<^sub>al * \<up>(m=iam_of_list l)"

  definition iam_new_sz :: "nat \<Rightarrow> ('v::heap) array_map Heap"
    where "iam_new_sz sz \<equiv> Array.new sz None"

  definition iam_new :: "('v::heap) array_map Heap"
    where "iam_new \<equiv> iam_new_sz iam_initial_size"

  definition iam_lookup
    :: "nat \<Rightarrow> ('v::heap) array_map \<Rightarrow> 'v option Heap"
    where "iam_lookup k a = do {
      l\<leftarrow>Array.len a;
      if k < l then Array.nth a k else return None
    }"

  definition iam_delete
    :: "nat \<Rightarrow> ('v::heap) array_map \<Rightarrow> ('v::heap) array_map Heap"
  where "iam_delete k a = do {
      l\<leftarrow>Array.len a;
      if k < l then Array.upd k None a else return a
    }"

  lemma [code]: "iam_delete k a \<equiv> upd_oo (return a) k None a"
    unfolding upd_oo_def iam_delete_def .

  definition iam_update
    :: "nat \<Rightarrow> 'v::heap \<Rightarrow> 'v array_map \<Rightarrow> 'v array_map Heap"
    where "iam_update k v a = do {
      l\<leftarrow>Array.len a;
      a\<leftarrow>if k>=l then do {
          let newsz = max (k+1) (2 * l + 3);
          array_grow a newsz None
        } else return a;

      Array.upd k (Some v) a
    }"

  lemma [code]: "iam_update k v a = upd_oo
    (do {
      l\<leftarrow>Array.len a;
      let newsz = max (k+1) (2 * l + 3);
      a\<leftarrow>array_grow a newsz None;
      Array.upd k (Some v) a
    })
    k (Some v) a"
  proof -
    have [simp]:
      "\<And>x t e. do {
        l\<leftarrow>Array.len a;
        if x l then
          t l
        else do {
          l'\<leftarrow>Array.len a;
          e l l'
        }
      }
      =
      do {
        l\<leftarrow>Array.len a;
        if x l then t l else e l l
      }"
      apply (auto
        simp: bind_def execute_len
        split: option.split
        intro!: ext
      )
      done

    show ?thesis
      unfolding upd_oo_def iam_update_def
      apply simp
      apply (rule cong[OF arg_cong, where f1=bind])
      apply simp
      apply (rule ext)
      apply auto
      done
  qed

  lemma precise_iam: "precise is_iam"
    apply rule
    by (auto simp add: is_iam_def dest: preciseD[OF snga_prec])

  lemma iam_new_abs: "iam_of_list (replicate n None) = Map.empty"
    unfolding iam_of_list_def[abs_def]
    by auto

  lemma iam_new_sz_rule: "<emp> iam_new_sz n < is_iam Map.empty >"
    unfolding iam_new_sz_def is_iam_def[abs_def]
    by (sep_auto simp: iam_new_abs)

  lemma iam_new_rule: "<emp> iam_new < is_iam Map.empty >"
    unfolding iam_new_def by (sep_auto heap: iam_new_sz_rule)

  lemma iam_lookup_rule: "< is_iam m p >
    iam_lookup k p
    <\<lambda>r. is_iam m p * \<up>(r=m k) >"
    unfolding iam_lookup_def is_iam_def
    by (sep_auto simp: iam_lookup_abs1 iam_lookup_abs2)

  lemma iam_delete_abs1: "k<length l
    \<Longrightarrow> iam_of_list (l[k := None]) = iam_of_list l |` (- {k})"
    unfolding iam_of_list_def[abs_def]
    by (auto intro!: ext simp: restrict_map_def)

  lemma iam_delete_abs2: "\<not>k<length l
    \<Longrightarrow> iam_of_list l |` (- {k}) = iam_of_list l"
    unfolding iam_of_list_def[abs_def]
    by (auto intro!: ext simp: restrict_map_def)

  lemma iam_delete_rule: "< is_iam m p >
    iam_delete k p
    <\<lambda>r. is_iam (m|`(-{k})) r>"
    unfolding is_iam_def iam_delete_def
    by (sep_auto simp: iam_delete_abs1 iam_delete_abs2)

  lemma iam_update_abs1: "iam_of_list (l@replicate n None) = iam_of_list l"
    unfolding iam_of_list_def[abs_def]
    by (auto intro!: ext simp: nth_append)

  lemma iam_update_abs2: "\<not> length l \<le> k
    \<Longrightarrow> iam_of_list (l[k := Some v]) = iam_of_list l(k \<mapsto> v)"
    unfolding iam_of_list_def[abs_def]
    by auto

  lemma iam_update_rule:
    "< is_iam m p > iam_update k v p <\<lambda>r. is_iam (m(k\<mapsto>v)) r>\<^sub>t"
    unfolding is_iam_def iam_update_def
    by (sep_auto
      decon: decon_split_if
      simp: iam_update_abs1 iam_update_abs2)

  interpretation iam: imp_map is_iam
    apply unfold_locales
    by (rule precise_iam)
  interpretation iam: imp_map_empty is_iam iam_new
    apply unfold_locales
    by (sep_auto heap: iam_new_rule)
  interpretation iam_sz: imp_map_empty is_iam "iam_new_sz sz"
    apply unfold_locales
    by (sep_auto heap: iam_new_sz_rule)

  interpretation iam: imp_map_lookup is_iam iam_lookup
    apply unfold_locales
    by (sep_auto heap: iam_lookup_rule)
  interpretation iam: imp_map_delete is_iam iam_delete
    apply unfold_locales
    by (sep_auto heap: iam_delete_rule)
  interpretation iam: imp_map_update is_iam iam_update
    apply unfold_locales
    by (sep_auto heap: iam_update_rule)

end