Theory Check_Answer

theory Check_Answer
  imports Abstract_Checks Contextual_Check_Report
begin

section ‹A domain's answer to the value of an expression›

text ‹
  A domain with a check decision procedure answers ‹EvalInt e› for every
  comparison or logical operator ‹e›: such an expression evaluates to ‹0› or
  ‹1›, so a decided const‹sound_check_query.check_query› is the
  exact integer, and an undecided one is the interval ‹[0, 1]›. Other
  expressions it declines with ‹⊤›. A check is then classified from this
  answer exactly as the domain's own ‹classify_check› classified it.
›

definition bool_answer :: "bool option ⇒ answer" where
  "bool_answer r =
     (case r of
        Some b ⇒ answer_of_int (if b then 1 else 0)
      | None ⇒ answer_of_ivl (Ivl (Fin 0) (Fin 1)))"

lemma classify_answer_of_int:
  "classify_answer (answer_of_int n) =
     Decided (if n = 1 then Check_Proved else if n = 0 then Check_Refuted else Check_Unknown)"
  using answer_const_of_int[of n] unfolding answer_of_int_def by simp

lemma answer_const_bool_range: "answer_const (answer_of_ivl (Ivl (Fin 0) (Fin 1))) = None"
proof -
  have "congruence_constant (⊤ :: congruence) = None"
    by (cases "congruence_constant (⊤ :: congruence)")
       (auto dest!: gamma_congruence_constant simp: set_eq_iff, presburger)
  then show ?thesis
    by (simp add: answer_of_ivl_def int_dom_constant_def top_int_dom_ext_def top_sign_def)
qed

lemma classify_bool_answer:
  "classify_answer (bool_answer r) =
     Decided (case r of Some True ⇒ Check_Proved | Some False ⇒ Check_Refuted
                      | None ⇒ Check_Unknown)"
proof (cases r)
  case None
  then show ?thesis
    using answer_const_bool_range by (simp add: bool_answer_def answer_of_ivl_def)
qed (auto simp: bool_answer_def classify_answer_of_int)

context sound_check_query
begin

fun eval_answer :: "'d ⇒ query ⇒ answer" where
  "eval_answer d (EvalInt e) = (if bool_valued e then bool_answer (check_query e d) else ⊤)"

lemma eval_answer_sound:
  assumes mem: "s ∈ γS d"
  shows "eval_holds q (eval_answer d q) s"
proof (cases q)
  case (EvalInt e)
  show ?thesis
  proof (cases "bool_valued e")
    case True
    have v: "⟦e⟧e s = (if truthy (⟦e⟧e s) then 1 else 0)"
      by (rule aval_bool_valued[OF True])
    show ?thesis
    proof (cases "check_query e d")
      case None
      then show ?thesis
        using EvalInt True v by (simp add: bool_answer_def split: if_splits)
    next
      case (Some b)
      have "truthy (⟦e⟧e s) = b"
        by (rule check_query_sound[OF mem Some])
      then show ?thesis
        using EvalInt True v Some by (simp add: bool_answer_def split: if_splits)
    qed
  qed (simp add: EvalInt)
qed

lemma check_query_check_truth_exp: "check_query (check_truth_exp c) d = check_query c d"
  by (cases c) (simp_all add: check_truth_exp_def truthy_query_def)

theorem classify_eval_answer:
  "classify_answer (eval_answer d (check_query_of c)) = Decided (classify_check c d)"
  by (simp add: check_query_check_truth_exp classify_bool_answer classify_check_def
      split: option.splits bool.splits)

end

section ‹Deciding a check from an answer›

text ‹
  The one check consumer: ask the state's handler the check's question and read
  the verdict off the answer, as Goblint's ‹assert› analysis reads
  ‹Queries.eval_bool›. const‹classify_answer› reads an answer that
  admits no value as const‹Dead›, but the consumer reports it as
  const‹Check_Unknown›: a check is reported dead only where the state
  itself is const‹Bot›, never from the answer to its query.
›

definition answer_check :: "('v ⇒ query ⇒ answer) ⇒ exp ⇒ 'v ⇒ check_result" where
  "answer_check q c v =
     (case classify_answer (q v (check_query_of c)) of Decided r ⇒ r | _ ⇒ Check_Unknown)"

lemma answer_check_proved:
  assumes "answer_check q c v = Check_Proved"
    and "eval_holds (check_query_of c) (q v (check_query_of c)) s"
  shows "truthy (⟦c⟧e s)"
  using assms classify_answer_proved
  by (auto simp: answer_check_def split: lifted.splits)

lemma answer_check_refuted:
  assumes "answer_check q c v = Check_Refuted"
    and "eval_holds (check_query_of c) (q v (check_query_of c)) s"
  shows "¬ truthy (⟦c⟧e s)"
  using assms classify_answer_refuted
  by (auto simp: answer_check_def split: lifted.splits)

lemma (in sound_check_query) answer_check_eval_answer:
  "answer_check eval_answer = classify_check"
  by (simp add: fun_eq_iff answer_check_def classify_eval_answer del: eval_answer.simps)

end