Theory State_Restriction

theory State_Restriction
  imports "Voblint_VIMP.VIMP_Globals" "Voblint_Domain.Nonrelational_State"
    "Voblint_Domain.Reachability_Lift"
begin

section ‹Local/global restriction of abstract states›

text ‹
  A variable classifier selects one half of an abstract state and replaces the
  other with const‹bot›. Both restrictions specialize the generic
  const‹combine_env› selector. Their join recovers the original state;
  they preserve joins, are idempotent, and annihilate each other.
›

definition restrict_local_for ::
  "(vname => bool) => 'a::bounded_semilattice_sup_bot abs_state => 'a abs_state" where
  "restrict_local_for 𝒢 sigma = combine_env 𝒢 sigma bot"

definition restrict_global_for ::
  "(vname => bool) => 'a::bounded_semilattice_sup_bot abs_state => 'a abs_state" where
  "restrict_global_for 𝒢 sigma = combine_env 𝒢 bot sigma"

lemma restrict_local_for_join [simp]:
  "restrict_local_for 𝒢 (A ⊔ B) = restrict_local_for 𝒢 A ⊔ restrict_local_for 𝒢 B"
  unfolding restrict_local_for_def sup_fun_def by (rule ext) simp

lemma restrict_global_for_join [simp]:
  "restrict_global_for 𝒢 (A ⊔ B) = restrict_global_for 𝒢 A ⊔ restrict_global_for 𝒢 B"
  unfolding restrict_global_for_def sup_fun_def by (rule ext) simp

lemma restrict_local_for_idem [simp]:
  "restrict_local_for 𝒢 (restrict_local_for 𝒢 A) = restrict_local_for 𝒢 A"
  unfolding restrict_local_for_def by (rule ext) simp

lemma restrict_global_for_idem [simp]:
  "restrict_global_for 𝒢 (restrict_global_for 𝒢 A) = restrict_global_for 𝒢 A"
  unfolding restrict_global_for_def by (rule ext) simp

lemma map_lift_restrict_global_for_idem [simp]:
  fixes x :: "'a::bounded_semilattice_sup_bot abs_state lifted"
  shows "map_lift (restrict_global_for 𝒢) (map_lift (restrict_global_for 𝒢) x)
           = map_lift (restrict_global_for 𝒢) x"
  unfolding map_lift_comp o_def by simp

lemma restrict_local_for_restrict_global_for_bot [simp]:
  "restrict_local_for 𝒢 (restrict_global_for 𝒢 A) = bot"
  unfolding restrict_local_for_def restrict_global_for_def by (rule ext) simp

lemma restrict_global_for_restrict_local_for_bot [simp]:
  "restrict_global_for 𝒢 (restrict_local_for 𝒢 A) = bot"
  unfolding restrict_local_for_def restrict_global_for_def by (rule ext) simp

lemma restrict_local_for_global_join [simp]:
  "restrict_local_for 𝒢 d ⊔ restrict_global_for 𝒢 d = d"
  unfolding restrict_local_for_def restrict_global_for_def sup_fun_def
  by (rule ext) simp

lemma restrict_global_for_local_join [simp]:
  "restrict_global_for 𝒢 d ⊔ restrict_local_for 𝒢 d = d"
  unfolding restrict_local_for_def restrict_global_for_def sup_fun_def
  by (rule ext) simp

text ‹
  The selector equals the join of its disjoint local and global projections.
  This bridge lets state proofs use the restriction algebra without unfolding
  const‹combine_env›.
›

lemma combine_env_for_eq_restrictions:
  "combine_env 𝒢 sc se =
     restrict_local_for 𝒢 sc ⊔ restrict_global_for 𝒢 se"
  unfolding combine_env_def restrict_local_for_def restrict_global_for_def
    sup_fun_def
  by (rule ext) simp

subsection ‹Lifted restrictions›

text ‹The lifted rules transport the same join homomorphisms to reachable states.›

lemma map_lift_restrict_local_for_join [simp]:
  "map_lift (restrict_local_for 𝒢) (a ⊔ b)
     = map_lift (restrict_local_for 𝒢) a ⊔ map_lift (restrict_local_for 𝒢) b"
  by (rule map_lift_sup) simp

lemma map_lift_restrict_global_for_join [simp]:
  "map_lift (restrict_global_for 𝒢) (a ⊔ b)
     = map_lift (restrict_global_for 𝒢) a ⊔ map_lift (restrict_global_for 𝒢) b"
  by (rule map_lift_sup) simp

subsection ‹Splitting and rejoining›

text ‹Routing a state's two halves back through const‹combine_env› recovers it
  exactly: each half is already bottom outside the names it owns.›

lemma combine_env_restrict_id [simp]:
  "combine_env 𝒢 (restrict_local_for 𝒢 sigma) (restrict_global_for 𝒢 sigma) = sigma"
  by (simp add: combine_env_for_eq_restrictions)

subsection ‹Reading one selected name›

text ‹The two pointwise equations, so a proof about a single variable never has
  to unfold const‹combine_env›. The nesting law is what lets a combine step
  read a returned value straight out of the callee exit: every call site owns
  ‹ret_var› as its own compiler-internal name and never a user-declared global
  (‹reserved_ret_var›), so routing it through const‹combine_env› a second
  time would change nothing.›

lemma combine_env_local_eq [simp]:
  "¬ 𝒢 x ⟹ combine_env 𝒢 sc se x = sc x"
  by (simp add: combine_env_def)

lemma combine_env_global_eq [simp]:
  "𝒢 x ⟹ combine_env 𝒢 sc se x = se x"
  by (simp add: combine_env_def)

lemma combine_env_combine_env_left [simp]:
  "combine_env 𝒢 (combine_env 𝒢 dc g) (combine_env 𝒢 de g) = combine_env 𝒢 dc g"
  by (auto simp: combine_env_def)

end