Theory Normalized_Zone_Semantics_Impl
theory Normalized_Zone_Semantics_Impl
  imports
    Timed_Automata.Normalized_Zone_Semantics
    Worklist_Algorithms.Worklist_Locales
    TA_DBM_Operations_Impl
    Floyd_Warshall.FW_Code
    Munta_Base.TA_More
begin
unbundle no_library_syntax
section ‹Implementation of Reachability Checking›
hide_const D
no_notation Extended_Nat.infinity_class.infinity ("∞")
definition default_ceiling where
  "default_ceiling A = (
    let M = (λ c. {m . (c, m) ∈ Timed_Automata.clkp_set A}) in
      (λ x. if M x = {} then 0 else nat (Max (M x))))"
subsection ‹Outdated Material›
definition default_ceiling_real where
  "default_ceiling_real A = (
    let M = (λ c. {m . (c, m) ∈ Timed_Automata.clkp_set A}) in
      (λ x. if M x = {} then 0 else nat (floor (Max (M x)) + 1)))"
lemma standard_abstraction_real:
  assumes
    "finite (Timed_Automata.clkp_set A)" "finite (Timed_Automata.collect_clkvt (trans_of A))"
    "∀(_,m::real) ∈ Timed_Automata.clkp_set A. m ∈ ℕ"
  shows "Timed_Automata.valid_abstraction A (clk_set A) (default_ceiling_real A)"
proof -
  from assms have 1: "finite (clk_set A)" by auto
  have 2: "Timed_Automata.collect_clkvt (trans_of A) ⊆ clk_set A" by auto
  from assms obtain L where L: "distinct L" "set L = Timed_Automata.clkp_set A"
    by (meson finite_distinct_list)
  let ?M = "λ c. {m . (c, m) ∈ Timed_Automata.clkp_set A}"
  let ?X = "clk_set A"
  let ?m = "map_of L"
  let ?k = "λ x. if ?M x = {} then 0 else nat (floor (Max (?M x)) + 1)"
  { fix c m assume A: "(c, m) ∈ Timed_Automata.clkp_set A"
    from assms(1) have "finite (snd ` Timed_Automata.clkp_set A)" by auto
    moreover have "?M c ⊆ (snd ` Timed_Automata.clkp_set A)" by force
    ultimately have fin: "finite (?M c)" by (blast intro: finite_subset)
    then have "Max (?M c) ∈ {m . (c, m) ∈ Timed_Automata.clkp_set A}" using Max_in A by auto
    with assms(3) have "Max (?M c) ∈ ℕ" by auto
    then have "floor (Max (?M c)) = Max (?M c)" by (metis Nats_cases floor_of_nat of_int_of_nat_eq)
    with A have *: "?k c = Max (?M c) + 1"
    proof auto
      fix n :: int and x :: real
      assume "Max {m. (c, m) ∈ Timed_Automata.clkp_set A} = real_of_int n"
      then have "real_of_int (n + 1) ∈ ℕ"
        using ‹Max {m. (c, m) ∈ Timed_Automata.clkp_set A} ∈ ℕ› by auto
      then show "real (nat (n + 1)) = real_of_int n + 1"
        by (metis Nats_cases ceiling_of_int nat_int of_int_1 of_int_add of_int_of_nat_eq)
    qed
    from fin A have "Max (?M c) ≥ m" by auto
    moreover from A assms(3) have "m ∈ ℕ" by auto
    ultimately have "m ≤ ?k c" "m ∈ ℕ" "c ∈ clk_set A" using A * by force+
  }
  then have "∀(x, m) ∈ Timed_Automata.clkp_set A. m ≤ ?k x ∧ x ∈ clk_set A ∧ m ∈ ℕ" by blast
  with 1 2 have "Timed_Automata.valid_abstraction A ?X ?k" by - (standard, assumption+)
  then show ?thesis unfolding default_ceiling_real_def by auto
qed
lemma standard_abstraction_int:
  assumes
    "finite (Timed_Automata.clkp_set A)" "finite (Timed_Automata.collect_clkvt (trans_of A))"
    "∀(_,m::int) ∈ Timed_Automata.clkp_set A. m ∈ ℕ"
  and "clk_set A ⊆ X" "finite X"
  shows "Timed_Automata.valid_abstraction A X (default_ceiling A)"
proof -
  from ‹_ ⊆ X› have 2: "Timed_Automata.collect_clkvt (trans_of A) ⊆ X" by auto
  from assms obtain L where L: "distinct L" "set L = Timed_Automata.clkp_set A"
    by (meson finite_distinct_list)
  let ?M = "λ c. {m . (c, m) ∈ Timed_Automata.clkp_set A}"
  let ?X = "clk_set A"
  let ?m = "map_of L"
  let ?k = "λ x. if ?M x = {} then 0 else nat (Max (?M x))"
  { fix c m assume A: "(c, m) ∈ Timed_Automata.clkp_set A"
    from assms(1) have "finite (snd ` Timed_Automata.clkp_set A)" by auto
    moreover have "?M c ⊆ (snd ` Timed_Automata.clkp_set A)" by force
    ultimately have fin: "finite (?M c)" by (blast intro: finite_subset)
    then have "Max (?M c) ∈ {m . (c, m) ∈ Timed_Automata.clkp_set A}" using Max_in A by auto
    with assms(3) have "Max (?M c) ∈ ℕ" by auto
    with A have "?k c = nat (Max (?M c))" by auto
    from fin A have "Max (?M c) ≥ m" by auto
    moreover from A assms(3) have "m ∈ ℕ" by auto
    ultimately have "m ≤ ?k c" "m ∈ ℕ" "c ∈ X" using A ‹clk_set A ⊆ X› by force+
  }
  then have "∀(x, m) ∈ Timed_Automata.clkp_set A. m ≤ ?k x ∧ x ∈ X ∧ m ∈ ℕ" by blast
  with ‹finite X› 2 have "Timed_Automata.valid_abstraction A X ?k" by - (standard; assumption)
  then show ?thesis unfolding default_ceiling_def by auto
qed
definition default_numbering where
  "default_numbering A = (SOME v. bij_betw v A {1..card A} ∧
  (∀ c ∈ A. v c > 0) ∧
  (∀ c. c ∉ A ⟶ v c > card A))"
lemma default_numbering:
  assumes "finite S"
  defines "v ≡ default_numbering S"
  defines "n ≡ card S"
  shows "bij_betw v S {1..n}" (is "?A")
  and "∀ c ∈ S. v c > 0" (is "?B")
  and "∀ c. c ∉ S ⟶ v c > n" (is "?C")
proof -
  from standard_numbering[OF ‹finite S›] obtain v' and n' :: nat where v':
    "bij_betw v' S {1..n'} ∧ (∀ c ∈ S. v' c > 0) ∧ (∀ c. c ∉ S ⟶ v' c > n')"
  by metis
  moreover from this(1) ‹finite S› have "n' = n" unfolding n_def by (auto simp: bij_betw_same_card)
  ultimately have "?A ∧ ?B ∧ ?C" unfolding v_def default_numbering_def
  by - (drule someI[where x = v']; simp add: n_def)
  then show ?A ?B ?C by auto
qed
lemma finite_ta_Regions'_int:
  fixes A :: "('a, 'c, int, 's) ta"
  defines "x ≡ SOME x. x ∉ clk_set A"
  assumes "finite_ta A"
  shows "Regions' (clk_set A) (default_numbering (clk_set A)) (card (clk_set A)) x"
proof -
  from assms obtain x' where x': "x' ∉ clk_set A" unfolding finite_ta_def by auto
  then have x: "x ∉ clk_set A" unfolding x_def by (rule someI)
  from ‹finite_ta A› have "finite (clk_set A)" unfolding finite_ta_def by auto
  with default_numbering[of "clk_set A"] assms have
            "bij_betw (default_numbering (clk_set A)) (clk_set A) {1..(card (clk_set A))}"
            "∀c∈clk_set A. 0 < (default_numbering (clk_set A)) c"
            "∀c. c ∉ clk_set A ⟶ (card (clk_set A)) < (default_numbering (clk_set A)) c"
  by auto
  then show ?thesis using x assms unfolding finite_ta_def by - (standard, auto)
qed
lemma finite_ta_RegionsD_int:
  fixes A :: "('a, 'c, int, 's) ta"
  assumes "finite_ta A"
  defines "S ≡ clk_set A"
  defines "v ≡ default_numbering S"
  defines "n ≡ card S"
  defines "x ≡ SOME x. x ∉ S"
  defines "k ≡ default_ceiling A"
  shows
    "Regions' (clk_set A) v n x" "Timed_Automata.valid_abstraction A (clk_set A) k"
    "global_clock_numbering A v n"
proof -
  from standard_abstraction_int assms have k:
    "Timed_Automata.valid_abstraction A (clk_set A) k"
  unfolding finite_ta_def by blast
  from finite_ta_Regions'_int[OF ‹finite_ta A›] have *: "Regions' (clk_set A) v n x" unfolding assms .
  then interpret interp: Regions' "clk_set A" k v n x .
  from interp.clock_numbering have "global_clock_numbering A v n" by blast
  with * k show
    "Regions' (clk_set A) v n x" "Timed_Automata.valid_abstraction A (clk_set A) k"
    "global_clock_numbering A v n"
  .
qed
subsection ‹Misc›
lemma finite_project_snd:
  assumes "finite S" "⋀ a. finite (f a ` T)"
  shows "finite ((λ (a, b) . f a b) ` (S × T))" (is "finite ?S")
proof -
  have "?S = {x . ∃ a b. a ∈ S ∧ (b ∈ T ∧ x = f a b)}" by auto
  then show ?thesis
    using [[simproc add: finite_Collect]]
    by (auto simp: assms)
qed
lemma list_all_upt:
  fixes a b i :: nat
  shows "list_all (λ x. x < b) [a..<b]"
unfolding list_all_iff by auto
lemma norm_k_cong:
  assumes "∀ i ≤ n. k i = k' i"
  shows "norm M k n = norm M k' n"
using assms unfolding norm_def by fastforce
lemma norm_upd_norm'':
  fixes k :: "nat list"
  assumes "length k ≥ Suc n"
  shows "curry (norm_upd M k n) = norm (curry M) (λ i. real (k ! i)) n"
 apply (simp add: curry_def norm_upd_norm)
 apply (rule norm_k_cong)
using assms by simp
lemma normalized_integral_dbms_finite':
  assumes "length k = Suc n"
  shows "finite {norm_upd M (k :: nat list) n | M. dbm_default (curry M) n}" (is "finite ?S")
proof -
  let ?k = "(λ i. k ! i)"
  have "norm_upd M k n = uncurry (norm (curry M) ?k n)" for M
    using assms by (subst norm_upd_norm, subst norm_k_cong) auto
  then have
    "?S ⊆ uncurry `
      {norm (curry M) (λi. k ! i) n | M. dbm_default (curry M) n}"
    by auto
  moreover have "finite …"
    by (rule finite_imageI, rule finite_subset[rotated])
       (rule normalized_integral_dbms_finite[where n = n and k = ?k], blast)
  ultimately show ?thesis
    by (auto intro: finite_subset)
qed
lemma And_commute:
  "And A B = And B A"
by (auto intro: min.commute)
lemma FW'_diag_preservation:
  assumes "∀ i ≤ n. M (i, i) ≤ 0"
  shows "∀ i ≤ n. (FW' M n) (i, i) ≤ 0"
using assms FW_diag_preservation[of n "curry M"] unfolding FW'_def by auto
lemma FW_neg_diag_preservation:
  "M i i < 0 ⟹ i ≤ n ⟹ (FW M n) i i < 0"
using fw_mono[of i n i M n] by auto
lemma FW'_neg_diag_preservation:
  assumes "M (i, i) < 0" "i ≤ n"
  shows "(FW' M n) (i, i) < 0"
using assms FW_neg_diag_preservation[of "curry M"] unfolding FW'_def by auto
lemma norm_empty_diag_preservation_int:
  fixes k :: "nat ⇒ nat"
  assumes "i ≤ n"
  assumes "M i i < Le 0"
  shows "norm M k n i i < Le 0"
  using assms unfolding norm_def norm_diag_def by (force simp: Let_def less dest: dbm_lt_trans)
lemma norm_diag_preservation_int:
  fixes k :: "nat ⇒ nat"
  assumes "i ≤ n"
  assumes "M i i ≤ Le 0"
  shows "norm M k n i i ≤ Le 0"
  using assms unfolding norm_def norm_diag_def
  by (force simp: Let_def less_eq dbm_le_def dest: dbm_lt_trans)
lemma And_diag1:
  assumes "A i i ≤ 0"
  shows "(And A B) i i ≤ 0"
using assms by (auto split: split_min)
lemma And_diag2:
  assumes "B i i ≤ 0"
  shows "(And A B) i i ≤ 0"
using assms by (auto split: split_min)
lemma abstra_upd_diag_preservation:
  assumes "i ≤ n" "constraint_clk ac ≠ 0"
  shows "(abstra_upd ac M) (i, i) = M (i, i)"
using assms by (cases ac) auto
lemma abstr_upd_diag_preservation:
  assumes "i ≤ n" "∀ c ∈ collect_clks cc. c ≠ 0"
  shows "(abstr_upd cc M) (i, i) = M (i, i)"
using assms unfolding abstr_upd_def
by (induction cc arbitrary: M) (auto simp: abstra_upd_diag_preservation)
lemma abstr_upd_diag_preservation':
  assumes "∀ i ≤ n. M (i, i) ≤ 0" "∀ c ∈ collect_clks cc. c ≠ 0"
  shows "∀ i ≤ n. (abstr_upd cc M) (i, i) ≤ 0"
using assms unfolding abstr_upd_def
by (induction cc arbitrary: M) (auto simp: abstra_upd_diag_preservation)
lemma up_diag_preservation:
  assumes "M i i ≤ 0"
  shows "(up M) i i ≤ 0"
  using assms unfolding up_def by (auto split: split_min)
lemma reset_diag_preservation:
  assumes "M i i ≤ 0" "d ≤ 0"
  shows "reset M n k d i i ≤ 0"
  using assms min_le_iff_disj unfolding neutral reset_def by auto
lemma reset'_diag_preservation:
  assumes "∀ i ≤ n. M i i ≤ 0" "d ≤ 0"
  shows "∀ i ≤ n. reset' M n cs v d i i ≤ 0"
  using assms
  by (induction cs) (auto intro: reset_diag_preservation)
subsection ‹Implementation Semantics›
lemma FW'_out_of_bounds1:
  assumes "i' > n"
  shows "(FW' M n) (i', j') = M (i', j')"
unfolding FW'_def using FW_out_of_bounds1[OF assms, where M = "curry M"] by auto
lemma FW'_out_of_bounds2:
  assumes "j' > n"
  shows "(FW' M n) (i', j') = M (i', j')"
unfolding FW'_def using FW_out_of_bounds2[OF assms, where M = "curry M"] by auto
inductive step_impl ::
  "('a, nat, 't :: linordered_ab_group_add, 's) ta ⇒ 's ⇒ 't DBM'
    ⇒ nat ⇒ 'a action ⇒ 's ⇒ 't DBM' ⇒ bool"
("_ ⊢⇩I ⟨_, _⟩ ↝⇘_,_⇙ ⟨_, _⟩" [61,61,61] 61)
where
  step_t_impl:
    "A ⊢⇩I ⟨l, D⟩ ↝⇘n,τ⇙ ⟨l, FW' (abstr_upd (inv_of A l) (up_canonical_upd D n)) n⟩" |
  step_a_impl:
    "A ⊢ l ⟶⇗g,a,r⇖ l'
    ⟹ A ⊢⇩I ⟨l, D⟩ ↝⇘n,↿a⇙ ⟨l', FW' (abstr_upd (inv_of A l') (reset'_upd (FW' (abstr_upd g D) n) n r 0)) n⟩"
inductive_cases step_impl_cases: "A ⊢⇩I ⟨l, D⟩ ↝⇘n,a⇙ ⟨l', D'⟩"
declare step_impl.intros[intro]
lemma FW'_default:
  assumes "dbm_default (curry M) n"
  shows "dbm_default (curry (FW' M n)) n"
using assms by (simp add: FW'_out_of_bounds1 FW'_out_of_bounds2)
lemma abstr_upd_default:
  assumes "dbm_default (curry M) n" "∀c∈collect_clks cc. c ≤ n"
  shows "dbm_default (curry (abstr_upd cc M)) n"
using assms by (auto simp: abstr_upd_out_of_bounds1 abstr_upd_out_of_bounds2)
lemma up_canonical_upd_default:
  assumes "dbm_default (curry M) n"
  shows "dbm_default (curry (up_canonical_upd M n)) n"
using assms by (auto simp: up_canonical_out_of_bounds1 up_canonical_out_of_bounds2)
lemma reset'_upd_default:
  assumes "dbm_default (curry M) n" "∀c∈set cs. c ≤ n"
  shows "dbm_default (curry (reset'_upd M n cs d)) n"
using assms by (auto simp: reset'_upd_out_of_bounds1 reset'_upd_out_of_bounds2)
lemma FW'_int_preservation:
  assumes "dbm_int (curry M) n"
  shows "dbm_int (curry (FW' M n)) n"
using FW_int_preservation[OF assms] unfolding FW'_def curry_def by auto
lemma step_impl_norm_dbm_default_dbm_int:
  assumes "A ⊢⇩I ⟨l, D⟩ ↝⇘n,a⇙ ⟨l', D'⟩" "dbm_default (curry D) n" "dbm_int (curry D) n"
    "∀ c ∈ clk_set A. c ≤ n ∧ c ≠ 0" "∀ (_, d)  ∈ Timed_Automata.clkp_set A. d ∈ ℤ"
  shows "dbm_default (curry D') n ∧ dbm_int (curry D') n"
  using assms
 apply (cases rule: step_impl.cases)
  subgoal 
  apply standard
  apply standard
  apply standard
      apply safe[]
    apply (simp add: FW'_out_of_bounds1)
    apply (subst abstr_upd_out_of_bounds1[where n = n])
    using collect_clks_inv_clk_set[of A] apply fastforce
    apply assumption
    apply (simp add: up_canonical_out_of_bounds1 FW'_out_of_bounds1; fail)
    apply standard
    apply safe[]
    apply (simp add: FW'_out_of_bounds2)
    apply (subst abstr_upd_out_of_bounds2[where n = n])
    using collect_clks_inv_clk_set[of A] apply fastforce
    apply assumption
    apply (simp add: up_canonical_out_of_bounds2 FW'_out_of_bounds2; fail)
    apply (simp only:)
    apply (rule FW'_int_preservation)
    apply (rule abstr_upd_int_preservation)
    defer
    apply (rule up_canonical_upd_int_preservation)
    apply (simp add: Timed_Automata.clkp_set_def Timed_Automata.collect_clki_def; fast)+
  done
  subgoal for g a r 
  apply standard
  apply standard
  apply standard
  apply safe[]
    apply (simp add: FW'_out_of_bounds1)
    apply (subst abstr_upd_out_of_bounds1[where n = n])
    using collect_clks_inv_clk_set[of A] apply fastforce
    apply assumption
    apply (subst reset'_upd_out_of_bounds1[where n = n])
    apply (fastforce simp: Timed_Automata.collect_clkvt_def)
    apply assumption
    apply (simp add: FW'_out_of_bounds1)
    apply (subst abstr_upd_out_of_bounds1[where n = n])
    using collect_clocks_clk_set apply fast
    apply assumption
    apply (simp; fail)
    apply safe[]
    apply (simp add: FW'_out_of_bounds2)
    apply (subst abstr_upd_out_of_bounds2[where n = n])
    using collect_clks_inv_clk_set[of A] apply fastforce
    apply assumption
    apply (subst reset'_upd_out_of_bounds2[where n = n])
    apply (fastforce simp: Timed_Automata.collect_clkvt_def)
    apply assumption
    apply (simp add: FW'_out_of_bounds2)
    apply (subst abstr_upd_out_of_bounds2[where n = n])
    using collect_clocks_clk_set apply fast
    apply assumption
    apply (simp; fail)
    apply (simp only:)
    apply (rule FW'_int_preservation)
    apply (rule abstr_upd_int_preservation)
    defer
    apply (rule reset'_upd_int_preservation)
    apply (rule FW'_int_preservation)
    apply (rule abstr_upd_int_preservation)
    apply (simp add: Timed_Automata.clkp_set_def Timed_Automata.collect_clkt_def; fast)
    apply assumption
    apply simp
    apply (auto dest!: reset_clk_set; fail)
    apply (simp add: Timed_Automata.clkp_set_def Timed_Automata.collect_clki_def; fast)
 done
done
inductive steps_impl ::
  "('a, nat, 't, 's) ta ⇒ 's ⇒ ('t :: linordered_ab_group_add) DBM'
  ⇒ ('s ⇒ 't list) ⇒ nat ⇒ 's ⇒ 't DBM' ⇒ bool"
("_ ⊢⇩I ⟨_, _⟩ ↝⇘_,_⇙* ⟨_, _⟩" [61,61,61,61] 61)
where
  refl: "A ⊢⇩I ⟨l, Z⟩ ↝⇘k,n⇙* ⟨l, Z⟩" |
  step: "A ⊢⇩I ⟨l, Z⟩ ↝⇘k,n⇙* ⟨l', Z'⟩ ⟹ A ⊢⇩I ⟨l', Z'⟩ ↝⇘n,τ⇙ ⟨l'', Z''⟩
        ⟹ A ⊢⇩I ⟨l'', Z''⟩ ↝⇘n,↿a⇙ ⟨l''', Z'''⟩
        ⟹ A ⊢⇩I ⟨l, Z⟩ ↝⇘k,n⇙* ⟨l''', FW' (norm_upd Z''' (k l''') n) n⟩"
lemma steps_impl_induct[consumes 1, case_names refl step]:
  fixes
    A ::
      "('a ×
        (nat, 'b :: linordered_ab_group_add) acconstraint list ×
        'c × nat list × 'a) set ×
       ('a ⇒ (nat, 'b) acconstraint list)"
  assumes "A ⊢⇩I ⟨x2, x3⟩ ↝⇘k,n⇙* ⟨x6, x7⟩"
    and "⋀l Z. P A l Z k n l Z"
    and
    "⋀l Z l' Z' l'' Z'' a l''' Z'''.
        A ⊢⇩I ⟨l, Z⟩ ↝⇘k,n⇙* ⟨l', Z'⟩ ⟹
        P A l Z k n l' Z' ⟹
        A ⊢⇩I ⟨l', Z'⟩ ↝⇘n,τ⇙ ⟨l'', Z''⟩ ⟹
        A ⊢⇩I ⟨l'', Z''⟩ ↝⇘n,↿a⇙ ⟨l''', Z'''⟩ ⟹
        P A l Z k n l''' (FW' (norm_upd Z''' (k l''') n) n)"
  shows "P A x2 x3 k n x6 x7"
  using assms by (induction A≡A x2 x3 k ≡ k n ≡ n x6 x7; blast)
lemma steps_impl_norm_dbm_default_dbm_int:
  assumes "A ⊢⇩I ⟨l, D⟩ ↝⇘k,n⇙* ⟨l', D'⟩"
    and "dbm_default (curry D) n"
    and "dbm_int (curry D) n"
    and "∀c∈clk_set A. c ≤ n ∧ c ≠ 0"
    and "∀(_, d)∈Timed_Automata.clkp_set A. d ∈ ℤ"
    and "∀ l. ∀c∈set (k l). c ∈ ℤ"
    and "∀ l. length (k l) = Suc n"
  shows "l' = l ∧ D' = D ∨ (∃M. D' = FW' (norm_upd M (k l') n) n ∧
             ((∀i>n. ∀j. curry M i j = 0) ∧ (∀j>n. ∀i. curry M i j = 0)) ∧ dbm_int (curry M) n)"
using assms proof (induction)
  case refl then show ?case by auto
next
  case (step A l Z k n l' Z' l'' Z'' a l''' Z''')
  then have "
    Z' = Z ∨
    (∃M. Z' = FW' (norm_upd M (k l') n) n ∧
         ((∀i>n. ∀j. curry M i j = 0) ∧ (∀j>n. ∀i. curry M i j = 0)) ∧ dbm_int (curry M) n)"
  by auto
  then show ?case
  proof (standard, goal_cases)
    case 1
    with step.prems step.hyps show ?thesis
      apply simp
      apply (drule step_impl_norm_dbm_default_dbm_int; simp)
      apply (drule step_impl_norm_dbm_default_dbm_int; simp)
      by metis
  next
    case 2
    then obtain M where M:
      "Z' = FW' (norm_upd M (k l') n) n" "dbm_default (curry M) n" "dbm_int (curry M) n"
    by auto
    with step.prems(3-) step.hyps show ?case
     apply -
     apply (drule step_impl_norm_dbm_default_dbm_int; simp)
      using FW'_default[OF norm_upd_default, where M2 = M] apply force
      using FW'_int_preservation[OF norm_upd_int_preservation, where M2 = M] apply force
      apply (drule step_impl_norm_dbm_default_dbm_int; simp)
      by metis
  qed
qed
definition valid_dbm where "valid_dbm M n ≡ dbm_int M n ∧ (∀ i ≤ n. M 0 i ≤ 0)"
lemma valid_dbm_pos:
  assumes "valid_dbm M n"
  shows "[M]⇘v,n⇙ ⊆ {u. ∀ c. v c ≤ n ⟶ u c ≥ 0}"
using dbm_positive assms unfolding valid_dbm_def unfolding DBM_zone_repr_def by fast
definition "init_dbm = (λ (x, y). Le 0)"
definition n_eq ("_ =⇩_ _" [51,51,51] 50) where
  "n_eq M n M' ≡ ∀ i ≤ n. ∀ j ≤ n. M i j = M' i j"
lemma canonical_eq_upto:
  fixes A B :: "real DBM"
  assumes
    "clock_numbering' v n" "∀ k ≤ n. k > 0 ⟶ (∃ c. v c = k)"
    "canonical A n" "canonical B n"
    "[A]⇘v,n⇙ ≠ {}" "[A]⇘v,n⇙ = [B]⇘v,n⇙"
    "∀ i ≤ n. A i i = 0" "∀ i ≤ n. B i i = 0"
  shows "A =⇩n B"
unfolding n_eq_def
using assms
apply -
apply standard
apply standard
apply standard
apply standard
subgoal for i j
  apply (cases "i = j")
  apply fastforce
  apply (rule order.antisym)
  apply (rule DBM_canonical_subset_le; auto)
  apply (rule DBM_canonical_subset_le; auto)
done
done
lemma up_canonical_upd_up_canonical':
  shows "curry (up_canonical_upd M n) =⇩n up_canonical (curry M)"
by (auto simp: n_eq_def intro: up_canonical_upd_up_canonical)
lemma And_eqI':
  assumes "A =⇩n A'" "B =⇩n B'"
  shows "And A B =⇩n (And A' B')"
using assms unfolding n_eq_def by auto
lemma n_eq_subst:
  assumes "A =⇩n B"
  shows "(A =⇩n C) = (B =⇩n C)"
using assms unfolding n_eq_def by auto
lemma reset'''_reset'_upd'':
  assumes "∀c∈set cs. c ≠ 0"
  shows "(curry (reset'_upd M n cs d)) =⇩n (reset''' (curry M) n cs d)"
using reset'''_reset'_upd'[OF assms] unfolding n_eq_def by auto
lemma norm_eq_upto:
  assumes "A =⇩n B"
  shows "norm A k n =⇩n norm B k n"
using assms unfolding n_eq_def by (auto simp: norm_def)
lemma FW'_FW:
  "curry (FW' M n) = FW (curry M) n"
unfolding FW'_def by auto
lemma And_eqI:
  assumes "[A]⇘v,n⇙ = [A1]⇘v,n⇙" "[B]⇘v,n⇙ = [B1]⇘v,n⇙"
  shows "[And A B]⇘v,n⇙ = [And A1 B1]⇘v,n⇙"
using assms by (simp only: And_correct[symmetric])
lemma DBM_zone_repr_up_eqI:
  assumes "clock_numbering' v n" "[A]⇘v,n⇙ = [B]⇘v,n⇙"
  shows "[up A]⇘v,n⇙ = [up B]⇘v,n⇙"
using assms DBM_up_complete'[where v = v] DBM_up_sound'[OF assms(1)] by fastforce
lemma reset'_correct:
  assumes "∀c. 0 < v c ∧ (∀x y. v x ≤ n ∧ v y ≤ n ∧ v x = v y ⟶ x = y)"
    and "∀c∈set cs. v c ≤ n"
    and "∀k≤n. 0 < k ⟶ (∃c. v c = k)"
  shows "{[cs→d]u | u. u ∈ [M]⇘v,n⇙} = [reset' M n cs v d]⇘v,n⇙"
proof safe
  fix u
  assume "u ∈ [M]⇘v,n⇙"
  with DBM_reset'_complete[OF _ assms(1,2)] show
    "[cs→d]u ∈ [reset' M n cs v d]⇘v,n⇙"
  by (auto simp: DBM_zone_repr_def)
next
  fix u
  assume "u ∈ [reset' M n cs v d]⇘v,n⇙"
  show "∃u'. u = [cs→d]u' ∧ u' ∈ [M]⇘v,n⇙"
  proof (cases "[M]⇘v,n⇙ = {}")
    case True
    with ‹u ∈ _› DBM_reset'_empty[OF assms(3,1,2)] show ?thesis by auto
  next
    case False
    from DBM_reset'_sound[OF assms(3,1,2) ‹u ∈ _›] obtain ts where
      "set_clocks cs ts u ∈ [M]⇘v,n⇙"
    by blast
    from DBM_reset'_resets'[OF assms(1,2)] ‹u ∈ _› ‹_ ≠ {}›
    have "u = [cs → d](set_clocks cs ts u)" by (fastforce simp: reset_set DBM_zone_repr_def)
    with ‹set_clocks _ _ _ ∈ _› show ?thesis by auto
  qed
qed
lemma DBM_zone_repr_reset'_eqI:
  assumes "∀c. 0 < v c ∧ (∀x y. v x ≤ n ∧ v y ≤ n ∧ v x = v y ⟶ x = y)"
    and "∀c∈set cs. v c ≤ n"
    and "∀k≤n. 0 < k ⟶ (∃c. v c = k)"
    and "[A]⇘v,n⇙ = [B]⇘v,n⇙"
  shows "[reset' A n cs v d]⇘v,n⇙ = [reset' B n cs v d]⇘v,n⇙"
using assms(4) reset'_correct[OF assms(1-3)] by blast
lemma up_canonical_neg_diag:
  assumes "M i i < 0"
  shows "(up_canonical M) i i < 0"
using assms unfolding up_canonical_def by auto
lemma up_neg_diag:
  assumes "M i i < 0"
  shows "(up M) i i < 0"
using assms unfolding up_def by (auto split: split_min)
lemma reset''_neg_diag:
  fixes v :: "'c ⇒ nat"
  assumes "∀ c. v c > 0" "M i i < 0" "i ≤ n"
  shows "(reset'' M n cs v d) i i < 0"
using reset''_diag_preservation[OF assms(1), where M = M and n = n] assms(2-) by auto
lemma FW_canonical':
  assumes "∀ i ≤ n. (FW M n) i i ≥ 0"
  shows "canonical (FW M n) n"
  using FW_neg_cycle_detect assms
  unfolding cycle_free_diag_equiv
  by - (rule fw_canonical[unfolded cycle_free_diag_equiv]; fastforce)
lemma FW_neg_diag_equiv:
  assumes diag: "∃ i ≤ n. (FW A n) i i < 0"
      and surj: "∀ k ≤ n. k > 0 ⟶ (∃ c. v c = k)"
      and cn: "∀ c. v c > 0"
      and equiv: "[A]⇘v,n⇙ = [B]⇘v,n⇙"
  shows "∃ i ≤ n. (FW B n) i i < 0"
proof -
  from assms obtain i where "(FW A n) i i < 0" "i ≤ n" by force
  with neg_diag_empty[OF surj] FW_zone_equiv[OF surj] equiv have "[B]⇘v,n⇙ = {}" by fastforce
  with FW_zone_equiv[OF surj] have "[FW B n]⇘v,n⇙ = {}" by auto
  then obtain i where
    "(FW B n) i i < 0" "i ≤ n"
  using FW_detects_empty_zone[OF surj, where M = B, folded neutral] cn
  by auto
  then show ?thesis by auto
qed
lemma FW_dbm_zone_repr_eqI2:
  assumes f_diag: "⋀ M i. i ≤ n ⟹ M i i < 0 ⟹ (f M) i i < 0"
      and g_diag: "⋀ M i. i ≤ n ⟹ M i i < 0 ⟹ (g M) i i < 0"
      and canonical:
      "⋀ A B. canonical A n ⟹ canonical B n ⟹ ∀ i ≤ n. A i i = 0 ⟹ ∀ i ≤ n. B i i = 0
      ⟹ [A]⇘v,n⇙ = [B]⇘v,n⇙
      ⟹ [f A]⇘v,n⇙ = [g B]⇘v,n⇙"
      and surj: "∀ k ≤ n. k > 0 ⟶ (∃ c. v c = k)"
      and cn: "∀ c. v c > 0"
      and equiv: "[A]⇘v,n⇙ = [B]⇘v,n⇙"
      and diag: "∀ i ≤ n. A i i ≤ 0" "∀ i ≤ n. B i i ≤ 0"
  shows "[f (FW A n)]⇘v,n⇙ = [g (FW B n)]⇘v,n⇙"
proof (cases "∀ i ≤ n. (FW A n) i i ≥ 0")
  case True
  with FW_neg_diag_equiv[OF _ surj cn equiv[symmetric]] have *: "∀i≤n. 0 ≤ (FW B n) i i" by fastforce
  with True FW_diag_preservation[where M = A, OF diag(1)] FW_diag_preservation[where M = B, OF diag(2)]
        FW_zone_equiv[OF surj, of A] FW_zone_equiv[OF surj, of B] equiv
  show ?thesis by - (rule canonical[OF FW_canonical'[OF True] FW_canonical'[OF *]]; fastforce)
next
  case False
  then obtain i where "(FW A n) i i < 0" "i ≤ n" by force
  moreover with FW_neg_diag_equiv[OF _ surj cn equiv] obtain j where
    "(FW B n) j j < 0" "j ≤ n"
  using FW_detects_empty_zone[OF surj, where M = B, folded neutral] cn by auto
  ultimately have "f (FW A n) i i < 0" "g (FW B n) j j < 0" using f_diag g_diag by auto
  with ‹i ≤ n› ‹j ≤ n› neg_diag_empty[OF surj] show ?thesis by auto
qed
lemma FW_dbm_zone_repr_eqI:
  assumes f_diag: "⋀ M i. i ≤ n ⟹ M i i < 0 ⟹ (f M) i i < 0"
      and g_diag: "⋀ M i. i ≤ n ⟹ M i i < 0 ⟹ (g M) i i < 0"
      and canonical: "⋀ M. canonical M n ⟹ [f M]⇘v,n⇙ = [g M]⇘v,n⇙"
      and surj: "∀ k ≤ n. k > 0 ⟶ (∃ c. v c = k)"
  shows "[f (FW M n)]⇘v,n⇙ = [g (FW M n)]⇘v,n⇙"
proof (cases "∀ i ≤ n. (FW M n) i i ≥ 0")
  case True
  from canonical[OF FW_canonical'[OF True]] show ?thesis .
next
  case False
  then obtain i where "(FW M n) i i < 0" "i ≤ n" by force
  with f_diag g_diag have "f (FW M n) i i < 0" "g (FW M n) i i < 0" by auto
  with ‹i ≤ n› neg_diag_empty[OF surj] show ?thesis by auto
qed
lemma FW_dbm_zone_repr_eqI':
  assumes f_diag: "⋀ M i. i ≤ n ⟹ M i i < 0 ⟹ (f M) i i < 0"
      and g_diag: "⋀ M i. i ≤ n ⟹ M i i < 0 ⟹ (g M) i i < 0"
      and canonical: "⋀ M. canonical M n ⟹ ∀ i ≤ n. M i i = 0 ⟹ [f M]⇘v,n⇙ = [g M]⇘v,n⇙"
      and surj: "∀ k ≤ n. k > 0 ⟶ (∃ c. v c = k)"
      and diag: "∀ i ≤ n. M i i ≤ 0"
  shows "[f (FW M n)]⇘v,n⇙ = [g (FW M n)]⇘v,n⇙"
proof (cases "∀ i ≤ n. (FW M n) i i ≥ 0")
  case True
  with FW_diag_preservation[where M = M, OF diag] canonical[OF FW_canonical'[OF True]] show ?thesis
  by fastforce
next
  case False
  then obtain i where "(FW M n) i i < 0" "i ≤ n" by force
  with f_diag g_diag have "f (FW M n) i i < 0" "g (FW M n) i i < 0" by auto
  with ‹i ≤ n› neg_diag_empty[OF surj] show ?thesis by auto
qed
subsubsection ‹Transfer Proofs›
lemma conv_dbm_entry_mono:
  assumes "a ≤ b"
  shows "map_DBMEntry real_of_int a ≤ map_DBMEntry real_of_int b"
using assms by (cases a; cases b) (auto simp: less_eq dbm_le_def elim!: dbm_lt.cases)
lemma conv_dbm_entry_mono_strict:
  assumes "a < b"
  shows "map_DBMEntry real_of_int a < map_DBMEntry real_of_int b"
using assms by (cases a; cases b) (auto simp: less elim!: dbm_lt.cases)
context
  includes lifting_syntax
begin
definition "ri = (λ a b. real_of_int b = a)"
abbreviation "acri ≡ rel_acconstraint (=) ri"
abbreviation "acri' n ≡ rel_acconstraint (eq_onp (λ x. x < Suc n)) ri"
abbreviation
  "RI n ≡ (rel_prod (eq_onp (λ x. x < Suc n)) (eq_onp (λ x. x < Suc n)) ===> rel_DBMEntry ri)"
lemma rel_DBMEntry_map_DBMEntry_ri [simp, intro]:
  "rel_DBMEntry ri (map_DBMEntry real_of_int x) x"
by (cases x) (auto simp: ri_def)
lemma RI_fun_upd[transfer_rule]:
  "(RI n ===> (=) ===> rel_DBMEntry ri ===> RI n) fun_upd fun_upd"
unfolding rel_fun_def eq_onp_def by auto
lemma min_ri_transfer[transfer_rule]:
  "(rel_DBMEntry ri ===> rel_DBMEntry ri ===> rel_DBMEntry ri) min min"
unfolding rel_fun_def
  apply (simp split: split_min)
  apply safe
  subgoal for x y x' y'
    apply (drule not_le_imp_less)
    apply (drule conv_dbm_entry_mono_strict)
  by (cases x; cases x'; cases y; cases y'; auto simp: ri_def; fail)
  subgoal for x y x' y'
    apply (drule not_le_imp_less)
    apply (drule conv_dbm_entry_mono)
  by (cases x; cases x'; cases y; cases y'; auto simp: ri_def; fail)
done
lemma ri_neg[transfer_rule, intro]:
  "ri a b ⟹ ri (-a) (-b)"
unfolding ri_def by auto
lemma abstra_upd_RI[transfer_rule]:
  "(acri' n ===> RI n ===> RI n) abstra_upd abstra_upd"
 apply rule
 apply rule
 subgoal for x y _ _
  apply (cases x; cases y)
 using min_ri_transfer unfolding eq_onp_def rel_fun_def by (auto dest: ri_neg)
done
lemma abstr_upd_RI[transfer_rule]:
  "(list_all2 (acri' n) ===> RI n ===> RI n) abstr_upd abstr_upd"
unfolding abstr_upd_def by transfer_prover
lemma uminus_RI[transfer_rule]:
  "(ri ===> ri) uminus uminus"
unfolding ri_def by auto
lemma add_RI[transfer_rule]:
  "(ri ===> ri ===> ri) ((+) ) (+)"
unfolding ri_def rel_fun_def by auto
lemma add_rel_DBMEntry_transfer[transfer_rule]:
  assumes R: "(A ===> B ===> C) (+) (+)"
  shows "(rel_DBMEntry A ===> rel_DBMEntry B ===> rel_DBMEntry C) (+) (+)"
  using R unfolding rel_fun_def[abs_def] apply safe
  subgoal for x1 y1 x2 y2
    by (cases x1; cases x2; cases y1; cases y2; simp add: add)
  done
lemma add_DBMEntry_RI[transfer_rule]:
  "(rel_DBMEntry ri ===> rel_DBMEntry ri ===> rel_DBMEntry ri) ((+) ) (+)"
  by transfer_prover
lemma norm_upper_RI[transfer_rule]:
  "(rel_DBMEntry ri ===> ri ===> rel_DBMEntry ri) norm_upper norm_upper"
  apply (intro rel_funI)
  subgoal for x y
    by (cases x; cases y; fastforce simp: ri_def less[symmetric])
  done
lemma norm_lower_RI[transfer_rule]:
  "(rel_DBMEntry ri ===> ri ===> rel_DBMEntry ri) norm_lower norm_lower"
  apply (intro rel_funI)
  subgoal for x y
    by (cases x; cases y; fastforce simp: ri_def less[symmetric])
  done
lemma norm_lower_RI':
  "(rel_DBMEntry ri ===> (=) ===> rel_DBMEntry ri) norm_lower norm_lower"
  apply (intro rel_funI)
  subgoal for x y
    by (cases x; cases y; fastforce simp: ri_def less[symmetric])
  done
lemma norm_diag_RI[transfer_rule]:
  "(rel_DBMEntry ri ===> rel_DBMEntry ri) norm_diag norm_diag"
  unfolding norm_diag_def
  apply (intro rel_funI)
  subgoal for x y
    by (cases x; cases y; fastforce simp: ri_def less[symmetric])
  done
lemma zero_RI[transfer_rule]:
  "ri 0 0"
by (simp add: ri_def)
lemma nth_transfer[transfer_rule]:
  fixes n :: nat
  shows "((λ x y. list_all2 A x y ∧ length x = n) ===> eq_onp (λ x. x < n) ===> A) (!) (!)"
by (auto simp: eq_onp_def ri_def rel_fun_def dest: list_all2_nthD)
lemma nth_RI:
  fixes n :: nat
  shows "((λ x y. list_all2 ri x y ∧ length x = n) ===> eq_onp (λ x. x < n) ===> ri) (!) (!)"
by (auto simp: eq_onp_def ri_def rel_fun_def dest: list_all2_nthD)
lemma nth_RI':
  fixes n :: nat
  shows "((λ x y. list_all2 ri x y ∧ length x = n) ===> (λ x y. x = y ∧ x < n) ===> ri) (!) (!)"
by (auto simp: ri_def rel_fun_def dest: list_all2_nthD)
lemma weakening:
  assumes "⋀ x y. B x y ⟹ A x y" "(A ===> C) f g"
  shows "(B ===> C) f g"
using assms by (simp add: rel_fun_def)
lemma weakening':
  assumes "⋀ x y. B x y ⟹ A x y" "(C ===> B) f g"
  shows "(C ===> A) f g"
using assms by (simp add: rel_fun_def)
lemma eq_onp_Suc:
  fixes n :: nat
  shows "(eq_onp (λ x. x = n) ===> eq_onp (λ x. x = Suc n)) Suc Suc"
unfolding rel_fun_def eq_onp_def by auto
lemma upt_transfer_upper_bound[transfer_rule]:
  "((=) ===> eq_onp (λ x. x = n) ===> list_all2 (eq_onp (λ x. x < n))) upt upt"
unfolding rel_fun_def eq_onp_def apply clarsimp
 apply (subst list.rel_eq_onp[unfolded eq_onp_def])
unfolding list_all_iff by auto
lemma zero_nat_transfer:
  "eq_onp (λ x. x < Suc n) 0 0"
by (simp add: eq_onp_def)
lemma [transfer_rule]:
  "bi_unique (rel_prod (eq_onp (λx. x < Suc n)) (eq_onp (λx. x < Suc n)))"
unfolding bi_unique_def eq_onp_def by auto
lemma [transfer_rule]:
  "(eq_onp P ===> (=) ===> (=)) (+) (+)"
unfolding eq_onp_def rel_fun_def by auto
lemma up_canonical_upd_RI2[transfer_rule]:
  "(RI n ===> (eq_onp (λ x. x < Suc n)) ===> RI n) up_canonical_upd up_canonical_upd"
unfolding up_canonical_upd_def[abs_def] by transfer_prover
lemma up_canonical_upd_RI[transfer_rule]:
  "(RI n ===> (eq_onp (λ x. x = n)) ===> RI n) up_canonical_upd up_canonical_upd"
unfolding up_canonical_upd_def[abs_def] by transfer_prover
lemma up_canonical_upd_RI3[transfer_rule]:
  "((rel_prod (=) (=) ===>
   rel_DBMEntry (=)) ===> (eq_onp (λ x. x = n)) ===> (rel_prod (=) (=) ===>
   rel_DBMEntry (=))) up_canonical_upd up_canonical_upd"
unfolding up_canonical_upd_def[abs_def] by transfer_prover
lemma eq_transfer:
  "(eq_onp (λ x. x < Suc n) ===> eq_onp (λ x. x < Suc n) ===> (=)) (=) (=)"
  by (intro rel_funI; simp add: eq_onp_def)
lemma norm_upd_norm:
  "norm_upd = (λM k n (i, j). norm (curry M) (λi. k ! i) n i j)"
  unfolding norm_upd_norm[symmetric] by simp
lemma less_transfer:
  "(eq_onp (λx. x < Suc n) ===> eq_onp (λx. x < Suc n) ===> (=)) (<) (<)"
  by (intro rel_funI; simp add: eq_onp_def)
lemma less_eq_transfer:
  "(eq_onp (λx. x < Suc n) ===> eq_onp (λx. x = n) ===> (=)) (≤) (≤)"
  by (intro rel_funI; simp add: eq_onp_def)
lemma norm_upd_transfer[transfer_rule]:
  fixes n :: nat
  notes eq_onp_Suc[of n, transfer_rule] zero_nat_transfer[transfer_rule] eq_transfer[transfer_rule]
    less_transfer[transfer_rule] less_eq_transfer[transfer_rule]
  shows
    "(RI n ===> (λx y. list_all2 ri x y ∧ length x = Suc n) ===> eq_onp (λx. x = n)  ===> RI n)
    norm_upd norm_upd"
    unfolding norm_upd_norm norm_def by transfer_prover
lemma dbm_entry_val_ri:
  assumes "rel_DBMEntry ri e e'" "dbm_entry_val u c d e"
  shows "dbm_entry_val u c d (map_DBMEntry real_of_int e')"
using assms by (cases e; cases e') (auto simp: ri_def)
lemma dbm_entry_val_ir:
  assumes "rel_DBMEntry ri e e'" "dbm_entry_val u c d (map_DBMEntry real_of_int e')"
  shows "dbm_entry_val u c d e"
using assms by (cases e; cases e') (auto simp: ri_def)
lemma bi_unique_eq_onp_less_Suc[transfer_rule]:
  "bi_unique (eq_onp (λx. x < Suc n))"
by (simp add: eq_onp_def bi_unique_def)
lemma fw_upd_transfer[transfer_rule]:
 "((eq_onp (λ x. x < Suc n) ===> eq_onp (λ x. x < Suc n) ===> rel_DBMEntry ri)
  ===> eq_onp (λ x. x < Suc n) ===> eq_onp (λ x. x < Suc n) ===> eq_onp (λ x. x < Suc n)
  ===> (eq_onp (λ x. x < Suc n) ===> eq_onp (λ x. x < Suc n) ===> rel_DBMEntry ri))
  fw_upd fw_upd"
  unfolding fw_upd_def Floyd_Warshall.upd_def by transfer_prover
lemma fw_upd_transfer'[transfer_rule]:
 "(((=) ===> (=) ===> rel_DBMEntry ri)
  ===> (=) ===> (=) ===> (=)
  ===> ((=) ===> (=) ===> rel_DBMEntry ri))
  fw_upd fw_upd"
  unfolding fw_upd_def Floyd_Warshall.upd_def by transfer_prover
lemma fwi_RI_transfer_aux:
  assumes
    "((λx y. x < Suc n ∧ x = y) ===> (λx y. x < Suc n ∧ x = y) ===> rel_DBMEntry ri) M M'"
    "k < Suc n" "i < Suc n" "j < Suc n"
  shows
  "((λx y. x < Suc n ∧ x = y) ===> (λx y. x < Suc n ∧ x = y) ===> rel_DBMEntry ri)
   (fwi M n k i j) (fwi M' n k i j)"
using assms
apply (induction _ "(i, j)" arbitrary: i j
    rule: wf_induct[of "less_than <*lex*> less_than"]
  )
  apply (auto; fail)
 subgoal for i j
 apply (cases i; cases j; auto simp add: fw_upd_out_of_bounds2)
 unfolding eq_onp_def[symmetric]
 apply (drule rel_funD[OF fw_upd_transfer[of n]])
 apply (auto simp: eq_onp_def dest: rel_funD; fail)
 subgoal premises prems for n'
 proof -
  from prems have
    "(eq_onp (λx. x < Suc n) ===> eq_onp (λx. x < Suc n) ===> rel_DBMEntry ri)
        (fwi M n k 0 n') (fwi M' n k 0 n')"
  by auto
  then show ?thesis
   apply -
   apply (drule rel_funD[OF fw_upd_transfer[of n]])
   apply (drule rel_funD[where x = k and y = k])
   apply (simp add: eq_onp_def ‹k < Suc n›; fail)
   apply (drule rel_funD[where x = 0 and y = 0])
   apply (simp add: eq_onp_def; fail)
   apply (drule rel_funD[where x = "Suc n'" and y = "Suc n'"])
   using prems apply (simp add: eq_onp_def; fail)
   apply assumption
  done
 qed
 subgoal premises prems for n'
 proof -
  from prems have
    "(eq_onp (λx. x < Suc n) ===> eq_onp (λx. x < Suc n) ===> rel_DBMEntry ri)
        (fwi M n k n' n) (fwi M' n k n' n)"
  by auto
  then show ?thesis
   apply -
   apply (drule rel_funD[OF fw_upd_transfer[of n]])
   apply (drule rel_funD[where x = k and y = k])
   apply (simp add: eq_onp_def ‹k < Suc n›; fail)
   apply (drule rel_funD[where x = "Suc n'" and y = "Suc n'"])
   using prems apply (simp add: eq_onp_def; fail)
   apply (drule rel_funD[where x = 0 and y = 0])
   using prems apply (simp add: eq_onp_def; fail)
   apply assumption
  done
 qed
 subgoal premises prems for i j
 proof -
  from prems have
    "(eq_onp (λx. x < Suc n) ===> eq_onp (λx. x < Suc n) ===> rel_DBMEntry ri)
        (fwi M n k (Suc i) j) (fwi M' n k (Suc i) j)"
  by auto
  then show ?thesis
   apply -
   apply (drule rel_funD[OF fw_upd_transfer[of n]])
   apply (drule rel_funD[where x = k and y = k])
   apply (simp add: eq_onp_def ‹k < Suc n›; fail)
   apply (drule rel_funD[where x = "Suc i" and y = "Suc i"])
   using prems apply (simp add: eq_onp_def; fail)
   apply (drule rel_funD[where x = "Suc j" and y = "Suc j"])
   using prems apply (simp add: eq_onp_def; fail)
   apply assumption
  done
 qed
done
done
lemma fw_RI_transfer_aux:
  assumes
    "((λx y. x < Suc n ∧ x = y) ===> (λx y. x < Suc n ∧ x = y) ===> rel_DBMEntry ri) M M'"
    "k < Suc n"
  shows
    "((λx y. x < Suc n ∧ x = y) ===> (λx y. x < Suc n ∧ x = y) ===> rel_DBMEntry ri)
   (fw M n k) (fw M' n k)"
  using assms
  by (induction k) (auto intro: fwi_RI_transfer_aux)
lemma fwi_RI_transfer[transfer_rule]:
  "((eq_onp (λ x. x < Suc n) ===> eq_onp (λ x. x < Suc n) ===> rel_DBMEntry ri)
  ===> eq_onp (λ x. x = n) ===> eq_onp (λ x. x < Suc n) ===> eq_onp (λ x. x < Suc n)
  ===> eq_onp (λ x. x < Suc n) ===> (eq_onp (λ x. x < Suc n) ===> eq_onp (λ x. x < Suc n)
  ===> rel_DBMEntry ri)) fwi fwi"
 apply (rule rel_funI)
 apply (rule rel_funI)
 apply (rule rel_funI)
 apply (rule rel_funI)
 apply (rule rel_funI)
by (auto intro: fwi_RI_transfer_aux simp: eq_onp_def)
lemma fw_RI_transfer[transfer_rule]:
  "((eq_onp (λ x. x < Suc n) ===> eq_onp (λ x. x < Suc n) ===> rel_DBMEntry ri)
  ===> eq_onp (λ x. x = n) ===> eq_onp (λ x. x < Suc n)
  ===> (eq_onp (λ x. x < Suc n) ===> eq_onp (λ x. x < Suc n) ===> rel_DBMEntry ri))
  fw fw"
 apply (rule rel_funI)
 apply (rule rel_funI)
 apply (rule rel_funI)
by (auto intro: fw_RI_transfer_aux simp: eq_onp_def)
lemma FW_RI_transfer[transfer_rule]:
  "((eq_onp (λ x. x < Suc n) ===> eq_onp (λ x. x < Suc n) ===> rel_DBMEntry ri)
  ===> eq_onp (λ x. x = n)
  ===> (eq_onp (λ x. x < Suc n) ===> eq_onp (λ x. x < Suc n) ===> rel_DBMEntry ri)) FW FW"
by standard+ (drule rel_funD[OF fw_RI_transfer]; auto simp: rel_fun_def eq_onp_def)
lemma FW_RI_transfer'[transfer_rule]:
  "(RI n ===> eq_onp (λ x. x = n) ===> RI n) FW' FW'"
using FW_RI_transfer[of n] unfolding FW'_def uncurry_def[abs_def] rel_fun_def by auto
definition RI_I :: "nat ⇒ (nat, real, 's) invassn ⇒ (nat, int, 's) invassn ⇒ bool" where
  "RI_I n ≡ ((=) ===> list_all2 (acri' n))"
definition
  "RI_T n ≡ rel_prod (=) (rel_prod (list_all2 (acri' n)) (rel_prod (=) (rel_prod (list_all2 (eq_onp (λ x. x < Suc n))) (=))))"
definition RI_A :: "nat ⇒ ('a, nat, real, 's) ta ⇒ ('a, nat, int, 's) ta ⇒ bool" where
  "RI_A n ≡ rel_prod (rel_set (RI_T n)) (RI_I n)"
lemma inv_of_transfer [transfer_rule]:
  "(RI_A n ===> RI_I n) inv_of inv_of"
unfolding RI_A_def inv_of_def by transfer_prover
lemma FW'_rsp:
  "((=) ===> (=) ===> (=)) FW' FW'"
unfolding rel_fun_def by auto
lemma [transfer_rule]:
  "(list_all2 (eq_onp (λ x. x < Suc n))) [1..n] [1..n]"
unfolding eq_onp_def
apply (rule list_all2I)
apply auto
apply (metis ab_semigroup_add_class.add.commute atLeastAtMost_iff in_set_zipE int_le_real_less of_int_less_iff of_int_of_nat_eq of_nat_Suc set_upto)
by (simp add: zip_same)
lemmas [transfer_rule] = zero_nat_transfer
definition
  "reset_canonical_upd' n M = reset_canonical_upd M n"
lemma [transfer_rule]:
  "(eq_onp (λx. x < int (Suc n)) ===> eq_onp (λx. x < Suc n)) nat nat"
unfolding eq_onp_def rel_fun_def by auto
lemma reset_canonical_upd_RI_aux:
  "(RI n ===> eq_onp (λ x. x < Suc n) ===> ri ===> RI n)
  (reset_canonical_upd' n) (reset_canonical_upd' n)"
unfolding reset_canonical_upd'_def[abs_def] reset_canonical_upd_def[abs_def] by transfer_prover
lemma reset_canonical_upd_RI[transfer_rule]:
  "(RI n ===> eq_onp (λ x. x = n) ===> eq_onp (λ x. x < Suc n) ===> ri ===> RI n)
  reset_canonical_upd reset_canonical_upd"
using reset_canonical_upd_RI_aux unfolding reset_canonical_upd'_def[abs_def] rel_fun_def eq_onp_def
by auto
lemma reset'_upd_RI[transfer_rule]:
  "(RI n ===> eq_onp (λ x. x = n) ===> list_all2 (eq_onp (λ x. x < Suc n)) ===> ri ===> RI n)
  reset'_upd reset'_upd"
unfolding reset'_upd_def[abs_def] by transfer_prover
definition "ri_len n = (λ x y. list_all2 ri x y ∧ length x = Suc n)"
lemma RI_complete:
  assumes lifts: "RI n D M" "RI_A n A' A"
      and step: "A' ⊢⇩I ⟨l,D⟩ ↝⇘n,a⇙ ⟨l',D'⟩"
  shows "∃ M'. A ⊢⇩I ⟨l,M⟩ ↝⇘n,a⇙ ⟨l',M'⟩ ∧ RI n D' M'"
using step proof cases
  case prems: step_t_impl
  let ?M' =
    "FW' (abstr_upd (inv_of A l) (up_canonical_upd M n)) n"
  note [transfer_rule] = lifts inv_of_transfer[unfolded RI_I_def]
  have [transfer_rule]: "eq_onp (λx. x = n) n n" by (simp add: eq_onp_def)
  have "RI n D' ?M'" unfolding prems by transfer_prover
  with ‹a = τ› ‹l' = l› show ?thesis by auto
next
  case prems: (step_a_impl g' a r')
  obtain T I T' I' where "A = (T, I)" and "A' = (T', I')" by force
  with lifts(2) prems(3) obtain g r where
    "(rel_prod (=) (rel_prod (list_all2 (acri' n)) (rel_prod (=) (rel_prod (list_all2 (eq_onp (λ x. x < Suc n))) (=)))))
     (l, g', a, r', l') (l, g, a, r, l')"
    "(l, g, a, r, l') ∈ T"
  unfolding RI_A_def RI_T_def rel_set_def trans_of_def by (cases rule: rel_prod.cases) fastforce
  with ‹A = _› have g':
    "list_all2 (acri' n) g' g" "A ⊢ l ⟶⇗g,a,r⇖ l'" "(list_all2 (eq_onp (λ x. x < Suc n))) r' r"
  unfolding trans_of_def by (auto dest: rel_prod.cases)
  from this(3) have "r' = r" unfolding eq_onp_def by (simp add: list_all2_eq list_all2_mono)
  let ?M' = "FW' (abstr_upd (inv_of A l') (reset'_upd (FW' (abstr_upd g M) n) n r 0)) n"
  have [transfer_rule]: "eq_onp (λx. x = n) n n" by (simp add: eq_onp_def)
  note [transfer_rule] = g'[unfolded ‹r' = r›] lifts inv_of_transfer[unfolded RI_I_def]
  have "RI n D' ?M'" unfolding prems ‹r' = r› by transfer_prover
  with g' prems(1) show ?thesis unfolding ‹r' = r› by auto
qed
lemma IR_complete:
  assumes lifts: "RI n D M" "RI_A n A' A"
      and step: "A ⊢⇩I ⟨l,M⟩ ↝⇘n,a⇙ ⟨l',M'⟩"
  shows "∃ D'. A' ⊢⇩I ⟨l,D⟩ ↝⇘n,a⇙ ⟨l',D'⟩ ∧ RI n D' M'"
using step proof cases
  case prems: step_t_impl
  let ?D' =
    "FW' (abstr_upd (inv_of A' l) (up_canonical_upd D n)) n"
  note [transfer_rule] = lifts inv_of_transfer[unfolded RI_I_def]
  have [transfer_rule]: "eq_onp (λx. x = n) n n" by (simp add: eq_onp_def)
  have "RI n ?D' M'" unfolding prems by transfer_prover
  with ‹l' = l› ‹a = τ› show ?thesis by auto
next
  case prems: (step_a_impl g a r)
  obtain T I T' I' where "A = (T, I)" and "A' = (T', I')" by force
  with lifts(2) prems(3) obtain g' r' where
    "(rel_prod (=) (rel_prod (list_all2 (acri' n)) (rel_prod (=) (rel_prod (list_all2 (eq_onp (λ x. x < Suc n))) (=)))))
     (l, g', a, r', l') (l, g, a, r, l')"
    "(l, g', a, r', l') ∈ T'"
  unfolding RI_A_def RI_T_def rel_set_def trans_of_def by (cases rule: rel_prod.cases) fastforce
  with ‹A' = _› have g':
    "list_all2 (acri' n) g' g" "A' ⊢ l ⟶⇗g',a,r'⇖ l'" "(list_all2 (eq_onp (λ x. x < Suc n))) r' r"
  unfolding trans_of_def by (auto dest: rel_prod.cases)
  from this(3) have "r' = r" unfolding eq_onp_def by (simp add: list_all2_eq list_all2_mono)
  let ?D' = "FW' (abstr_upd (inv_of A' l') (reset'_upd (FW' (abstr_upd g' D) n) n r 0)) n"
  have [transfer_rule]: "eq_onp (λx. x = n) n n" by (simp add: eq_onp_def)
  note [transfer_rule] = g'[unfolded ‹r' = r›] lifts inv_of_transfer[unfolded RI_I_def]
  have "RI n ?D' M'" unfolding prems by transfer_prover
  with g' prems(1) show ?thesis unfolding ‹r' = r› by auto
qed
lemma RI_norm_step:
  assumes lifts: "RI n D M" "list_all2 ri k' k"
      and len: "length k' = Suc n"
  shows "RI n (FW' (norm_upd (FW' D n) k' n) n) (FW' (norm_upd (FW' M n) k n) n)"
proof -
  note [transfer_rule] = lifts norm_upd_transfer[folded ri_len_def]
  have [transfer_rule]: "eq_onp (λx. x = n) n n" by (simp add: eq_onp_def)
  have [transfer_rule]: "(ri_len n) k' k" using len lifts by (simp add: ri_len_def eq_onp_def)
  show ?thesis by transfer_prover
qed
end 
subsubsection ‹Semantic Equivalence›
lemma delay_step_impl_correct:
  assumes "canonical (curry D) n" 
          "clock_numbering' v n" "∀c∈collect_clks (inv_of A l). v c = c ∧ c > 0 ∧ v c ≤ n"
      and surj: "∀ k ≤ n. k > 0 ⟶ (∃ c. v c = k)"
  assumes D_inv: "D_inv = abstr (inv_of A l) (λi j. ∞) v"
  shows
  "[curry (abstr_upd (inv_of A l) (up_canonical_upd D n))]⇘v,n⇙ =
  [And (up (curry D)) D_inv]⇘v,n⇙"
  apply (subst abstr_upd_abstr')
   defer
   apply (subst abstr_abstr'[symmetric])
    defer
  unfolding D_inv
    apply (subst And_abstr[symmetric])
      defer
      defer
      apply (rule And_eqI)
       apply (subst DBM_up_to_equiv[folded n_eq_def, OF up_canonical_upd_up_canonical'])
  using assms by (fastforce intro!: up_canonical_equiv_up)+
lemma action_step_impl_correct:
  assumes "canonical (curry D) n" 
    "clock_numbering' v n" "∀c∈collect_clks (inv_of A l'). v c = c ∧ c > 0 ∧ v c ≤ n"
    "∀c∈collect_clks g. v c = c ∧ c > 0 ∧ v c ≤ n"
    "∀c∈ set r. v c = c ∧ c > 0 ∧ v c ≤ n"
    "∀ i ≤ n. D (i, i) ≤ 0"
    and surj: "∀ k ≤ n. k > 0 ⟶ (∃ c. v c = k)"
  shows
    "[curry (abstr_upd (inv_of A l') (reset'_upd (FW' (abstr_upd g D) n) n r 0))]⇘v,n⇙ =
   [And (reset' (And (curry D) (abstr g (λi j. ∞) v)) n r v 0)
                               (abstr (inv_of A l') (λi j. ∞) v)]⇘v,n⇙"
  apply (subst abstr_upd_abstr', use assms in fastforce)
  apply (subst abstr_abstr'[symmetric, where v = v], use assms in fastforce)
  apply (subst And_abstr[symmetric], use assms in fastforce, use assms in fastforce)
  apply (rule And_eqI[rotated], rule HOL.refl)
  apply (subst DBM_up_to_equiv[folded n_eq_def, OF reset'''_reset'_upd''],
      use assms in fastforce)
  apply (subst reset''_reset'''[symmetric, where v = v], use assms in fastforce)
  apply (subst FW'_FW)
  apply (subst FW_dbm_zone_repr_eqI'[where g = "λ M. reset' M n r v 0"])
       apply (rule reset''_neg_diag; fastforce simp: assms(2))
      apply (erule DBM_reset'_neg_diag_preservation',
        assumption, use assms(2) in fastforce, use assms in fastforce)
     apply (erule reset'_reset''_equiv[symmetric]; use assms in fastforce)
  using assms apply fastforce
  subgoal
  proof -
    show "∀i≤n. curry (abstr_upd g D) i i ≤ 0"
      using assms(4) abstr_upd_diag_preservation'[OF assms(6)] by fastforce
  qed
  apply (rule DBM_zone_repr_reset'_eqI,
      use assms in fastforce, use assms in fastforce, use assms in fastforce)
  apply (subst FW_zone_equiv[symmetric], use assms in fastforce)
  apply (subst abstr_upd_abstr', use assms in fastforce)
  apply (subst abstr_abstr'[symmetric, where v = v], use assms in fastforce)
  apply (rule And_abstr[symmetric]; use assms in fastforce)
  done
lemma norm_impl_correct:
  fixes k :: "nat list"
  assumes 
          "clock_numbering' v n"
          "∀ i ≤ n. D (i, i) ≤ 0"
          "∀ i ≤ n. M i i ≤ 0"
      and surj: "∀ k ≤ n. k > 0 ⟶ (∃ c. v c = k)"
      and k: "Suc n ≤ length k"
      and equiv: "[curry D]⇘v,n⇙ = [M]⇘v,n⇙"
  shows
    "[curry (FW' (norm_upd (FW' D n) k n) n)]⇘v,n⇙ = [norm (FW M n) (λ i. k ! i) n]⇘v,n⇙"
 apply (subst FW'_FW)
 apply (subst FW_zone_equiv[symmetric, OF surj])
 apply (subst norm_upd_norm'')
  apply (simp add: k; fail)
 apply (subst FW'_FW)
 subgoal
  apply (rule FW_dbm_zone_repr_eqI2)
  apply (rule norm_empty_diag_preservation_real[folded neutral, unfolded comp_def]; assumption)
  apply (rule norm_empty_diag_preservation_real[folded neutral, unfolded comp_def]; assumption)
  subgoal
   apply (rule DBM_up_to_equiv[folded n_eq_def])
   apply (rule norm_eq_upto)
   apply (rule canonical_eq_upto)
   apply (rule assms)
   apply (rule assms)
   apply assumption
   apply assumption
   using ‹clock_numbering' v n›
   apply - apply (rule cyc_free_not_empty[OF canonical_cyc_free])
  by simp+
  using assms by fastforce+
done
lemma norm_action_step_impl_correct:
  fixes k :: "nat list"
  assumes "canonical (curry D) n" 
          "clock_numbering' v n" "∀c∈collect_clks (inv_of A l'). v c = c ∧ c > 0 ∧ v c ≤ n"
          "∀c∈collect_clks g. v c = c ∧ c > 0 ∧ v c ≤ n"
          "∀c∈ set r. v c = c ∧ c > 0 ∧ v c ≤ n"
          "∀ i ≤ n. D (i, i) ≤ 0"
      and surj: "∀ k ≤ n. k > 0 ⟶ (∃ c. v c = k)"
      and k: "Suc n ≤ length k"
  shows
  "[curry (FW' (norm_upd (FW' (abstr_upd (inv_of A l') (reset'_upd (FW' (abstr_upd g D) n) n r 0)) n) k n) n)]⇘v,n⇙ =
   [norm (FW(And (reset' (And (curry D) (abstr g (λi j. ∞) v)) n r v 0)
                               (abstr (inv_of A l') (λi j. ∞) v)) n) (λ i. k ! i) n]⇘v,n⇙"
 apply (rule norm_impl_correct)
 apply (rule assms)
 subgoal
  apply (rule abstr_upd_diag_preservation')
  apply safe[]
  apply (subst reset'_upd_diag_preservation)
  using assms(5) apply fastforce
  apply assumption
  apply (simp add: FW'_def)
  apply (rule FW_diag_preservation[rule_format])
  apply (simp add: curry_def)
  apply (rule abstr_upd_diag_preservation'[rule_format, where n = n])
  using assms(6) apply fastforce
  using assms(4) apply fastforce
  apply assumption
  apply assumption
 using assms(3) by fastforce
 subgoal
  apply safe[]
  apply (rule And_diag1)
  apply (rule DBM_reset'_diag_preservation[rule_format])
  apply (rule And_diag1)
  using assms(6) apply simp
  using assms(2) apply simp
  using assms(5) apply metis
 by assumption
 apply (rule surj; fail)
 apply (rule k; fail)
 apply (rule action_step_impl_correct; rule assms)
done
lemma norm_delay_step_impl_correct:
  fixes k :: "nat list"
  assumes "canonical (curry D) n" 
          "clock_numbering' v n" "∀c∈collect_clks (inv_of A l). v c = c ∧ c > 0 ∧ v c ≤ n"
          "∀ i ≤ n. D (i, i) ≤ 0"
      and surj: "∀ k ≤ n. k > 0 ⟶ (∃ c. v c = k)"
      and k: "Suc n ≤ length k"
  assumes D_inv: "D_inv = abstr (inv_of A l) (λi j. ∞) v"
  shows
  "[curry (FW' (norm_upd (FW' (abstr_upd (inv_of A l) (up_canonical_upd D n)) n) k n) n)]⇘v,n⇙ =
  [norm (FW(And (up (curry D)) D_inv) n) (λ i. k ! i) n]⇘v,n⇙"
 apply (rule norm_impl_correct)
 apply (rule assms)
 subgoal
  apply (rule abstr_upd_diag_preservation')
  apply safe[]
  apply (subst up_canonical_upd_diag_preservation)
  using assms(3,4) by fastforce+
 subgoal
  apply safe[]
  apply (rule And_diag1)
  apply (rule up_diag_preservation)
  using assms(4) apply fastforce
 done
 apply (rule surj; fail)
 apply (rule k; fail)
 apply (rule delay_step_impl_correct; rule assms; fail)
done
lemma step_impl_sound:
  assumes step: "A ⊢⇩I ⟨l,M⟩ ↝⇘n,a⇙ ⟨l',M'⟩"
  assumes canonical: "canonical (curry M) n"
  assumes numbering: "global_clock_numbering A v n" "∀ c ∈ clk_set A. v c = c"
  assumes diag: "∀i≤n. M (i, i) ≤ 0"
  shows "∃ D. A ⊢ ⟨l,curry M⟩ ↝⇘v,n,a⇙ ⟨l',D⟩ ∧ [curry M']⇘v,n⇙ = [D]⇘v,n⇙"
proof -
  have *:
    "∀c. 0 < v c ∧ (∀x y. v x ≤ n ∧ v y ≤ n ∧ v x = v y ⟶ x = y)"
    "∀k≤n. 0 < k ⟶ (∃c. v c = k)" "∀c∈clk_set A. v c ≤ n"
  using numbering by metis+
  from step show ?thesis
  proof cases
    case prems: step_t_impl
    from *(1,3) numbering(2) collect_clks_inv_clk_set have
      "∀c∈collect_clks (inv_of A l). v c = c ∧ 0 < v c ∧ v c ≤ n"
    by fast
    then have **:
      "∀c∈collect_clks (inv_of A l). v c = c ∧ 0 < c ∧ v c ≤ n"
    by fastforce
    let ?D_inv = "abstr (inv_of A l) (λi j. ∞) v"
    from prems delay_step_impl_correct[OF canonical *(1) ** *(2)] have
      "[curry M']⇘v,n⇙ = [And (up (curry M)) ?D_inv]⇘v,n⇙"
      by (simp add: FW'_FW FW_zone_equiv[OF *(2), symmetric])
    moreover have
      "A ⊢ ⟨l,curry M⟩ ↝⇘v,n,a⇙ ⟨l',And (up (curry M)) ?D_inv⟩"
      unfolding ‹a = _› ‹l' = l› by blast
    ultimately show ?thesis by blast
  next
    case prems: (step_a_impl g a' r)
    let ?M =
      "And (reset' (And (curry M) (abstr g (λi j. ∞) v)) n r v 0) (abstr (inv_of A l') (λi j. ∞) v)"
    from prems *(1,3) numbering(2) collect_clks_inv_clk_set collect_clocks_clk_set reset_clk_set have
      "∀c∈collect_clks (inv_of A l'). v c = c ∧ 0 < v c ∧ v c ≤ n"
      "∀c∈collect_clks g. v c = c ∧ 0 < v c ∧ v c ≤ n"
      "∀c∈set r. v c = c ∧ 0 < v c ∧ v c ≤ n"
    by fast+
    then have **:
      "∀c∈collect_clks (inv_of A l'). v c = c ∧ 0 < c ∧ v c ≤ n"
      "∀c∈collect_clks g. v c = c ∧ 0 < c ∧ v c ≤ n"
      "∀c∈set r. v c = c ∧ 0 < c ∧ v c ≤ n"
      by fastforce+
    from prems action_step_impl_correct[OF canonical *(1) ** diag *(2)] have
      "[curry M']⇘v,n⇙ = [?M]⇘v,n⇙"
      by (simp add: FW'_FW FW_zone_equiv[OF *(2), symmetric])
    moreover have
      "A ⊢ ⟨l,curry M⟩ ↝⇘v,n,a⇙ ⟨l',?M⟩" unfolding ‹a = _› by - (intro step_z_norm step_a_z_dbm prems)
    ultimately show ?thesis by auto
  qed
qed
lemma step_impl_complete:
  assumes step: "A ⊢ ⟨l,curry D⟩ ↝⇘v,n,a⇙ ⟨l',curry D'⟩"
  assumes canonical: "canonical (curry D) n"
  assumes numbering: "global_clock_numbering A v n" "∀ c ∈ clk_set A. v c = c"
  assumes diag: "∀i≤n. D (i, i) ≤ 0"
  shows "∃ M'. A ⊢⇩I ⟨l,D⟩ ↝⇘n,a⇙ ⟨l',M'⟩ ∧ [curry M']⇘v,n⇙ = [curry D']⇘v,n⇙"
proof -
  have *:
    "∀c. 0 < v c ∧ (∀x y. v x ≤ n ∧ v y ≤ n ∧ v x = v y ⟶ x = y)"
    "∀k≤n. 0 < k ⟶ (∃c. v c = k)" "∀c∈clk_set A. v c ≤ n"
  using numbering by metis+
  from step show ?thesis
   apply cases
  proof goal_cases
    case prems: (1 D_inv)
    from *(1,3) numbering(2) collect_clks_inv_clk_set have
      "∀c∈collect_clks (inv_of A l). v c = c ∧ 0 < v c ∧ v c ≤ n"
    by fast
    then have **:
      "∀c∈collect_clks (inv_of A l). v c = c ∧ 0 < c ∧ v c ≤ n"
    by fastforce
    let ?D_inv = "abstr (inv_of A l) (λi j. ∞) v"
    let ?M' = "FW' (abstr_upd (inv_of A l) (up_canonical_upd D n)) n"
    have
      "[curry D']⇘v,n⇙ = [And (up (curry D)) ?D_inv]⇘v,n⇙"
      by (simp only: prems)
    also from prems delay_step_impl_correct[OF canonical *(1) ** *(2)] have
      "… = [curry ?M']⇘v,n⇙"
      by (simp only: FW'_FW FW_zone_equiv[OF *(2), symmetric])
    finally moreover have "A ⊢⇩I ⟨l,D⟩ ↝⇘n,a⇙ ⟨l',?M'⟩" by (auto simp only: ‹l' = l› ‹a = _›)
    ultimately show ?thesis by auto
  next
    case prems: (2 g a' r)
    let ?M =
      "FW' (abstr_upd (inv_of A l') (reset'_upd (FW' (abstr_upd g D) n) n r 0)) n"
    from prems *(1,3) numbering(2) collect_clks_inv_clk_set collect_clocks_clk_set reset_clk_set have
      "∀c∈collect_clks (inv_of A l'). v c = c ∧ 0 < v c ∧ v c ≤ n"
      "∀c∈collect_clks g. v c = c ∧ 0 < v c ∧ v c ≤ n"
      "∀c∈set r. v c = c ∧ 0 < v c ∧ v c ≤ n"
    by fast+
    then have **:
      "∀c∈collect_clks (inv_of A l'). v c = c ∧ 0 < c ∧ v c ≤ n"
      "∀c∈collect_clks g. v c = c ∧ 0 < c ∧ v c ≤ n"
      "∀c∈set r. v c = c ∧ 0 < c ∧ v c ≤ n"
    by fastforce+
    from prems action_step_impl_correct[OF canonical *(1) ** diag *(2)] have
      "[curry D']⇘v,n⇙ = [curry ?M]⇘v,n⇙"
    by (simp only: FW'_FW FW_zone_equiv[OF *(2), symmetric])
    moreover have "A ⊢⇩I ⟨l,D⟩ ↝⇘n,a⇙ ⟨l',?M⟩" using prems by auto
    ultimately show ?thesis by auto
  qed
qed
subsection ‹Reachability Checker›
abbreviation conv_M :: "int DBM' ⇒ real DBM'" where "conv_M ≡ (o) (map_DBMEntry real_of_int)"
abbreviation conv_ac :: "('a, int) acconstraint ⇒ ('a, real) acconstraint" where
  "conv_ac ≡ map_acconstraint id real_of_int"
abbreviation conv_cc :: "('a, int) cconstraint ⇒ ('a, real) cconstraint" where
  "conv_cc ≡ map (map_acconstraint id real_of_int)"
abbreviation "conv_t ≡ λ (l,g,a,r,l'). (l,conv_cc g,a,r,l')"
definition "conv_A ≡ λ (T, I). (conv_t ` T, conv_cc o I)"
lemma RI_zone_equiv:
  assumes "RI n M M'"
  shows "[curry M]⇘v,n⇙ = [curry (conv_M M')]⇘v,n⇙"
using assms unfolding DBM_zone_repr_def DBM_val_bounded_def rel_fun_def eq_onp_def
 apply clarsimp
 apply safe
 apply (cases "M (0, 0)"; cases "M' (0, 0)"; fastforce simp: dbm_le_def ri_def; fail)
 subgoal for _ c by (force intro: dbm_entry_val_ri[of "M (0, v c)"])
 subgoal for _ c by (force intro: dbm_entry_val_ri[of "M (v c, 0)"])
 subgoal for _ c1 c2 by (force intro: dbm_entry_val_ri[of "M (v c1, v c2)"])
 apply (cases "M (0, 0)"; cases "M' (0, 0)"; fastforce simp: dbm_le_def ri_def; fail)
 subgoal for _c by (rule dbm_entry_val_ir[of "M (0, v c)"]; auto)
 subgoal for _ c by (rule dbm_entry_val_ir[of "M (v c, 0)"]; auto)
 subgoal for _ c1 c2 by (rule dbm_entry_val_ir[of "M (v c1, v c2)"]; auto)
 done
lemma collect_clkvt_conv_A:
  "collect_clkvt (trans_of A) = collect_clkvt (trans_of (conv_A A))"
proof -
  obtain T I where "A = (T, I)" by force
  have "collect_clkvt (trans_of A) = ⋃((set o fst ∘ snd ∘ snd ∘ snd) ` T)"
  unfolding collect_clkvt_alt_def ‹A = _› trans_of_def by simp
  also have
    "…= ⋃((set o fst ∘ snd ∘ snd ∘ snd) ` (λ(l, g, a, r, l'). (l, map conv_ac g, a, r, l')) ` T)"
  by auto
  also have "… = collect_clkvt ((λ(l, g, a, r, l'). (l, map conv_ac g, a, r, l')) ` T)"
  unfolding collect_clkvt_alt_def[symmetric] ..
  also have "… = collect_clkvt (trans_of (conv_A A))" unfolding ‹A = _› trans_of_def
    by (simp add: conv_A_def)
  finally show ?thesis .
qed
lemma conv_ac_clock_id:
  "constraint_pair (conv_ac ac) = (λ (a, b). (a, real_of_int b)) (constraint_pair ac)"
by (cases ac) auto
lemma collect_clock_pairs_conv_cc:
  "collect_clock_pairs (map conv_ac cc) = (λ (a, b). (a, real_of_int b)) ` collect_clock_pairs cc"
by (auto simp: conv_ac_clock_id collect_clock_pairs_def) (metis conv_ac_clock_id image_eqI prod.simps(2))
lemma collect_clock_pairs_conv_cc':
  fixes S :: "('a, int) acconstraint list set"
  shows
    "(collect_clock_pairs ` map conv_ac ` S)
    = (((`) (λ (a, b). (a, real_of_int b))) ` collect_clock_pairs ` S)"
 apply safe
 apply (auto simp: collect_clock_pairs_conv_cc; fail)
by (auto simp: collect_clock_pairs_conv_cc[symmetric])
lemma collect_clock_pairs_conv_cc'':
  fixes S :: "('a, int) acconstraint list set"
  shows "(⋃x∈collect_clock_pairs ` map conv_ac ` S. x) = (⋃x∈collect_clock_pairs ` S. (λ (a, b). (a, real_of_int b)) ` x)"
by (simp add: collect_clock_pairs_conv_cc')
lemma clkp_set_conv_A:
  "clkp_set (conv_A A) l = (λ (a, b). (a, real_of_int b)) ` clkp_set A l"
  unfolding clkp_set_def collect_clki_def collect_clkt_alt_def inv_of_def trans_of_def conv_A_def
  apply (simp only: image_Un image_Union split: prod.split)
  by (auto simp: collect_clock_pairs_conv_cc' collect_clock_pairs_conv_cc[symmetric])
lemma ta_clkp_set_conv_A:
  "Timed_Automata.clkp_set (conv_A A) = (λ (a, b). (a, real_of_int b)) ` Timed_Automata.clkp_set A"
 apply (simp split: prod.split add: conv_A_def)
 unfolding
   Timed_Automata.clkp_set_def ta_collect_clki_alt_def ta_collect_clkt_alt_def inv_of_def trans_of_def
 apply (simp only: image_Un image_Union)
 apply (subst collect_clock_pairs_conv_cc''[symmetric])
 apply (subst collect_clock_pairs_conv_cc''[symmetric])
 by fastforce
lemma clkp_set_conv_A':
  "fst ` Timed_Automata.clkp_set A = fst ` Timed_Automata.clkp_set (conv_A A)"
by (fastforce simp: ta_clkp_set_conv_A)
lemma clk_set_conv_A:
  "clk_set (conv_A A) = clk_set A"
  unfolding collect_clkvt_conv_A clkp_set_conv_A' ..
lemma global_clock_numbering_conv:
  assumes "global_clock_numbering A v n"
  shows "global_clock_numbering (conv_A A) v n"
using assms clk_set_conv_A by metis
lemma real_of_int_nat:
  assumes "a ∈ ℕ"
  shows "real_of_int a ∈ ℕ"
using assms by (metis Nats_cases of_int_of_nat_eq of_nat_in_Nats)
lemma valid_abstraction_conv':
  assumes "Timed_Automata.valid_abstraction A X' k"
  shows "Timed_Automata.valid_abstraction (conv_A A) X' (λ x. real (k x))"
  using assms
  apply cases
  apply (rule Timed_Automata.valid_abstraction.intros)
    apply (auto intro: real_of_int_nat simp: ta_clkp_set_conv_A; fail)
  using collect_clkvt_conv_A apply fast
  by assumption
lemma valid_abstraction_conv:
  assumes "valid_abstraction A X' k"
  shows "valid_abstraction (conv_A A) X' (λ l x. real (k l x))"
  using assms
  apply cases
  apply (rule valid_abstraction.intros)
     apply (auto 4 3 simp: clkp_set_conv_A intro: real_of_int_nat; fail)
  using collect_clkvt_conv_A apply fast
  by (auto split: prod.split_asm simp: trans_of_def conv_A_def)
text ‹Misc›
lemma map_nth:
  fixes m :: nat
  assumes "i ≤ m"
  shows "map f [0..<Suc m] ! i = f i"
  using assms
by (metis add.left_neutral diff_zero le_imp_less_Suc nth_map_upt)
lemma ints_real_of_int_ex:
  assumes "z ∈ ℤ"
  shows "∃n. z = real_of_int n"
using assms Ints_cases by auto
lemma collect_clock_pairs_trans_clkp_set:
  assumes "A ⊢ l ⟶⇗g,a,r⇖ l'"
  shows "collect_clock_pairs g ⊆ Timed_Automata.clkp_set A"
using assms unfolding Timed_Automata.clkp_set_def Timed_Automata.collect_clkt_def by force
lemma collect_clock_pairs_inv_clkp_set:
  "collect_clock_pairs (inv_of A l) ⊆ Timed_Automata.clkp_set A"
unfolding Timed_Automata.clkp_set_def Timed_Automata.collect_clki_def by force
lemma finite_collect_clock_pairs[intro, simp]:
  "finite (collect_clock_pairs x)"
unfolding collect_clock_pairs_def by auto
lemma finite_collect_clks[intro, simp]:
  "finite (collect_clks x)"
unfolding collect_clks_def by auto
lemma check_diag_subset:
  assumes "¬ check_diag n D" "dbm_subset n D M"
  shows "¬ check_diag n M"
using assms unfolding dbm_subset_def check_diag_def pointwise_cmp_def by fastforce
lemma dbm_int_dbm_default_convD:
  assumes "dbm_int M n" "dbm_default M n"
  shows "∃ M'. curry (conv_M M') = M"
proof -
  let ?unconv = "(o) (map_DBMEntry floor)"
  let ?M' = "?unconv (uncurry M)"
  show ?thesis
   apply (rule exI[where x = ?M'])
   using assms apply (intro ext)
   apply clarsimp
   subgoal for i j
   by (cases "M i j"; cases "i > n"; cases "j > n";
       fastforce dest!: leI intro: ints_real_of_int_ex simp: neutral
     )
  done
qed
lemma dbm_int_all_convD:
  assumes "dbm_int_all M"
  shows "∃ M'. curry (conv_M M') = M"
proof -
  let ?unconv = "(o) (map_DBMEntry floor)"
  let ?M' = "?unconv (uncurry M)"
  show ?thesis
   apply (rule exI[where x = ?M'])
   using assms apply (intro ext)
   apply clarsimp
   subgoal for i j
    apply (cases "M i j")
    apply (auto intro!: ints_real_of_int_ex simp: neutral)
    subgoal premises prems for d
    proof -
      from prems(2) have "M i j ≠ ∞" by auto
      with prems show ?thesis by fastforce
    qed
    subgoal premises prems for d
    proof -
       from prems(2) have "M i j ≠ ∞" by auto
       with prems show ?thesis by fastforce
    qed
    done
  done
qed
lemma acri'_conv_ac:
  assumes "fst (constraint_pair ac) < Suc n"
  shows "acri' n (conv_ac ac) ac"
using assms
by (cases ac) (auto simp: ri_def eq_onp_def)
lemma ri_conv_cc:
  assumes "∀ c ∈ fst ` collect_clock_pairs cc. c < Suc n"
  shows "(list_all2 (acri' n)) (conv_cc cc) cc"
using assms
apply -
apply (rule list_all2I)
apply safe
subgoal premises prems for a b
proof -
  from prems(2) have "a = conv_ac b" by (metis in_set_zip nth_map prod.sel(1) prod.sel(2))
  moreover from prems(1,2) have
    "fst (constraint_pair b) < Suc n"
  unfolding collect_clock_pairs_def by simp (meson set_zip_rightD)
  ultimately show ?thesis by (simp add: acri'_conv_ac)
qed
by simp
lemma canonical_conv_aux:
  assumes "a ≤ b + c"
  shows "map_DBMEntry real_of_int a ≤ map_DBMEntry real_of_int b + map_DBMEntry real_of_int c"
  using assms unfolding less_eq add dbm_le_def
  by (cases a; cases b; cases c) (auto elim!: dbm_lt.cases)
lemma canonical_conv_aux_rev:
  assumes "map_DBMEntry real_of_int a ≤ map_DBMEntry real_of_int b + map_DBMEntry real_of_int c"
  shows "a ≤ b + c"
  using assms unfolding less_eq add dbm_le_def
  by (cases a; cases b; cases c) (auto elim!: dbm_lt.cases)
lemma canonical_conv:
  assumes "canonical (curry M) n"
  shows "canonical (curry (conv_M M)) n"
  using assms by (auto intro: canonical_conv_aux)
lemma canonical_conv_rev:
  assumes "canonical (curry (conv_M M)) n"
  shows "canonical (curry M) n"
  using assms by (auto intro: canonical_conv_aux_rev)
lemma canonical_RI_aux1:
  assumes "rel_DBMEntry ri a1 b1" "rel_DBMEntry ri a2 b2" "rel_DBMEntry ri a3 b3" "a1 ≤ a2 + a3"
  shows "b1 ≤ b2 + b3"
  using assms unfolding ri_def less_eq add dbm_le_def
  by (cases a1; cases a2; cases a3; cases b1; cases b2; cases b3) (auto elim!: dbm_lt.cases)
lemma canonical_RI_aux2:
  assumes "rel_DBMEntry ri a1 b1" "rel_DBMEntry ri a2 b2" "rel_DBMEntry ri a3 b3" "b1 ≤ b2 + b3"
  shows "a1 ≤ a2 + a3"
  using assms unfolding ri_def less_eq add dbm_le_def
  by (cases a1; cases a2; cases a3; cases b1; cases b2; cases b3) (auto elim!: dbm_lt.cases)
lemma canonical_RI:
  assumes "RI n D M"
  shows "canonical (curry D) n = canonical (curry M) n"
  using assms unfolding rel_fun_def eq_onp_def
  apply safe
  subgoal for i j k
    by (rule canonical_RI_aux1[of "D (i, k)" _ "D (i, j)" _ "D (j, k)"]; auto)
  subgoal for i j k
    by (rule canonical_RI_aux2[of _ "M (i, k)" _ "M (i, j)" _ "M (j, k)"]; auto)
  done
lemma RI_conv_M[transfer_rule]:
  "(RI n) (conv_M M) M"
  by (auto simp: rel_fun_def DBMEntry.rel_map(1) ri_def eq_onp_def DBMEntry.rel_refl)
lemma canonical_conv_M_FW':
  "canonical (curry (conv_M (FW' M n))) n = canonical (curry (FW' (conv_M M) n)) n"
proof -
  have [transfer_rule]: "eq_onp (λx. x = n) n n" by (simp add: eq_onp_def)
  note [transfer_rule] = RI_conv_M
  have 1: "RI n (FW' (conv_M M) n) (FW' M n)" by transfer_prover
  have 2: "RI n (conv_M (FW' M n)) (FW' M n)" by (rule RI_conv_M)
  from canonical_RI[OF 1] canonical_RI[OF 2] show ?thesis by simp
qed
lemma diag_conv:
  assumes "∀ i ≤ n. (curry M) i i ≤ 0"
  shows "∀ i ≤ n. (curry (conv_M M)) i i ≤ 0"
  using assms by (auto simp: neutral dest!: conv_dbm_entry_mono)
lemma map_DBMEntry_int_const:
  assumes "get_const (map_DBMEntry real_of_int a) ∈ ℤ"
  shows "get_const a ∈ ℤ"
  using assms by (cases a; auto simp: Ints_def)
lemma map_DBMEntry_not_inf:
  fixes a :: "_ DBMEntry"
  assumes "a ≠ ∞"
  shows "map_DBMEntry real_of_int a ≠ ∞"
  using assms by (cases a; auto simp: Ints_def)
lemma dbm_int_conv_rev:
  assumes "dbm_int (curry (conv_M Z)) n"
  shows "dbm_int (curry Z) n"
  using assms by (auto intro: map_DBMEntry_int_const dest: map_DBMEntry_not_inf)
lemma neutral_RI:
  assumes "rel_DBMEntry ri a b"
  shows "a ≥ 0 ⟷ b ≥ 0"
  using assms by (cases a; cases b; auto simp: neutral ri_def less_eq dbm_le_def elim!: dbm_lt.cases)
lemma diag_RI:
  assumes "RI n D M" "i ≤ n"
  shows "D (i, i) ≥ 0 ⟷ M (i, i) ≥ 0"
  using neutral_RI assms unfolding rel_fun_def eq_onp_def by auto
lemma diag_conv_M:
  assumes "i ≤ n"
  shows "curry (conv_M (FW' M n)) i i ≥ 0 ⟷ curry (FW' (conv_M M) n) i i ≥ 0"
proof -
  have [transfer_rule]: "eq_onp (λx. x = n) n n" by (simp add: eq_onp_def)
  note [transfer_rule] = RI_conv_M
  have 1: "RI n (FW' (conv_M M) n) (FW' M n)" by transfer_prover
  have 2: "RI n (conv_M (FW' M n)) (FW' M n)" by (rule RI_conv_M)
  from ‹i ≤ n› diag_RI[OF 1] diag_RI[OF 2] show ?thesis by simp
qed
lemma conv_dbm_entry_mono_rev:
  assumes "map_DBMEntry real_of_int a ≤ map_DBMEntry real_of_int b"
  shows "a ≤ b"
  using assms
  apply (cases a; cases b)
          apply (auto simp: less_eq dbm_le_def)
     apply (cases rule: dbm_lt.cases, auto; fail)+
  done
lemma conv_dbm_entry_mono_strict_rev:
  assumes "map_DBMEntry real_of_int a < map_DBMEntry real_of_int b"
  shows "a < b"
  using assms
  apply (cases a; cases b)
          apply (auto simp: less)
     apply (cases rule: dbm_lt.cases, auto; fail)+
  done
lemma diag_conv_rev:
  assumes "∀ i ≤ n. (curry (conv_M M)) i i ≤ 0"
  shows "∀ i ≤ n. (curry M) i i ≤ 0"
  using assms by (simp add: conv_dbm_entry_mono_rev neutral)
lemma dbm_subset_conv:
  assumes "dbm_subset n D M"
  shows "dbm_subset n (conv_M D) (conv_M M)"
  using assms unfolding dbm_subset_def pointwise_cmp_def check_diag_def
  by (auto intro: conv_dbm_entry_mono dest!: conv_dbm_entry_mono_strict)
lemma dbm_subset_conv_rev:
  assumes "dbm_subset n (conv_M D) (conv_M M)"
  shows "dbm_subset n D M"
  using assms conv_dbm_entry_mono_strict_rev
  unfolding dbm_subset_def pointwise_cmp_def check_diag_def
  by (auto intro: conv_dbm_entry_mono_rev)
lemma dbm_subset_correct:
  fixes D :: "real DBM'"
  assumes "dbm_subset n D M"
    and "canonical (curry D) n"
    and "∀i≤n. (curry D) i i ≤ 0"
    and "∀i≤n. (curry M) i i ≤ 0"
    and "global_clock_numbering A v n"
  shows "[curry D]⇘v,n⇙ ⊆ [curry M]⇘v,n⇙"
  using assms
  apply (subst subset_eq_dbm_subset[where v= v and M' = M])
       apply (auto; fail)
      apply (auto; fail)
     apply (auto; fail)
  by blast+
lemma dbm_subset_correct':
  fixes D M :: "real DBM'"
  assumes "canonical (curry D) n ∨ check_diag n D"
    and "∀i≤n. (curry D) i i ≤ 0"
    and "∀i≤n. (curry M) i i ≤ 0"
    and "global_clock_numbering A v n"
  shows "[curry D]⇘v,n⇙ ⊆ [curry M]⇘v,n⇙ ⟷ dbm_subset n D M"
  using assms
  apply -
  apply (rule subset_eq_dbm_subset[OF assms(1)])
     apply (auto; fail)
    apply (auto; fail)
  by blast+
lemma step_z_dbm_diag_preservation:
  assumes "step_z_dbm A l D v n a l' D'" "∀ i ≤ n. D i i ≤ 0"
  shows "∀ i ≤ n. D' i i ≤ 0"
  using assms
  apply cases
  using And_diag1 up_diag_preservation apply blast
  by (metis And_diag1 order_mono_setup.refl reset'_diag_preservation)
context AlphaClosure
begin
lemma step_z_dbm_mono:
  "∃ D'. A ⊢ ⟨l, D⟩ ↝⇘v,n,a⇙ ⟨l', D'⟩ ∧ [M']⇘v,n⇙ ⊆ [D']⇘v,n⇙" if
  gcn: "global_clock_numbering A v n" and that: "A ⊢ ⟨l, M⟩ ↝⇘v,n,a⇙ ⟨l', M'⟩" "[M]⇘v,n⇙ ⊆ [D]⇘v,n⇙"
  using step_z_mono[of A] step_z_dbm_sound[OF _ gcn] step_z_dbm_DBM[OF _ gcn] that by metis
end
lemma FW_canonical:
  "canonical (FW M n) n ∨ (∃ i ≤ n. (FW M n) i i < 0)"
  using FW_canonical' leI by blast
lemma FW'_canonical:
  "canonical (curry (FW' M n)) n ∨ (∃ i ≤ n. (FW' M n) (i, i) < 0)"
  by (metis FW'_FW FW_canonical curry_def)
lemma fw_upd_conv_M'':
  "fw_upd (map_DBMEntry real_of_int ∘∘ M) k i j = map_DBMEntry real_of_int ∘∘ fw_upd M k i j"
  unfolding fw_upd_def Floyd_Warshall.upd_def
  by (rule ext; simp split: prod.split; cases "M i j"; cases "M i k"; cases "M k j")
     (force simp: add min_def | force simp: min_inf_r | force simp: min_inf_l)+
lemma fw_upd_conv_M':
  "conv_M (uncurry (fw_upd M k i j)) = uncurry (fw_upd (map_DBMEntry real_of_int ∘∘ M) k i j)"
  unfolding fw_upd_conv_M'' by auto
lemma fw_upd_conv_M:
  "uncurry (fw_upd (curry (conv_M (uncurry M))) h i j)
  = (map_DBMEntry real_of_int ∘∘ uncurry) (fw_upd M h i j)"
  unfolding fw_upd_conv_M' by (auto simp: curry_def comp_def)
lemma fwi_conv_M'':
  "map_DBMEntry real_of_int ∘∘ fwi M n k i j = fwi (map_DBMEntry real_of_int ∘∘ M) n k i j"
  apply (induction _ "(i, j)" arbitrary: i j
    rule: wf_induct[of "less_than <*lex*> less_than"]
  )
  apply (auto; fail)
  subgoal for i j
    by (cases i; cases j; auto simp add: fw_upd_conv_M''[symmetric])
  done
lemma fwi_conv_M':
  "conv_M (uncurry (fwi M n k i j)) = uncurry (fwi (map_DBMEntry real_of_int ∘∘ M) n k i j)"
  unfolding fwi_conv_M''[symmetric] by auto
lemma fwi_conv_M:
  "conv_M (uncurry (fwi (curry M) n k i j)) = uncurry (fwi (curry (conv_M M)) n k i j)"
  unfolding fwi_conv_M' by (auto simp: curry_def comp_def)
lemma fw_conv_M'':
  "map_DBMEntry real_of_int ∘∘ fw M n k = fw (map_DBMEntry real_of_int ∘∘ M) n k"
  by (induction k; simp only: fw.simps fwi_conv_M'')
lemma fw_conv_M':
  "conv_M (uncurry (fw M n k)) = uncurry (fw (map_DBMEntry real_of_int ∘∘ M) n k)"
  unfolding fw_conv_M''[symmetric] by auto
lemma fw_conv_M:
  "conv_M (uncurry (fw (curry M) n k)) = uncurry (fw (curry (conv_M M)) n k)"
  unfolding fw_conv_M' by (auto simp: curry_def comp_def)
lemma FW_conv_M:
  "uncurry (FW (curry (conv_M M)) n) = conv_M (uncurry (FW (curry M) n))"
  using fw_conv_M by metis
lemma FW'_conv_M:
  "FW' (conv_M M) n = conv_M (FW' M n)"
  using FW_conv_M unfolding FW'_def by simp
lemma (in Regions) v_v':
  "∀ c ∈ X. v' (v c) = c"
  using clock_numbering unfolding v'_def by auto
definition
  "subsumes n
  = (λ (l, M) (l', M'). check_diag n M ∨ l = l' ∧ pointwise_cmp (≤) n (curry M) (curry M'))"
lemma subsumes_simp_1:
  "subsumes n (l, M) (l', M') = dbm_subset n M M'" if "l = l'"
  using that unfolding subsumes_def dbm_subset_def by simp
lemma subsumes_simp_2:
  "subsumes n (l, M) (l', M') = check_diag n M" if "l ≠ l'"
  using that unfolding subsumes_def dbm_subset_def by simp
lemma TA_clkp_set_unfold:
  "Timed_Automata.clkp_set A = ⋃ (clkp_set A ` UNIV)"
  unfolding clkp_set_def Timed_Automata.clkp_set_def
  unfolding Timed_Automata.collect_clki_def Timed_Automata.collect_clkt_def
  unfolding collect_clki_def collect_clkt_def
  by blast
locale TA_Start_No_Ceiling_Defs =
  fixes A :: "('a, nat, int, 's) ta"
    and l⇩0 :: 's
    and n :: nat
begin
definition "X ≡ {1..n}"
definition "v i ≡ if i > 0 ∧ i ≤ n then i else (Suc n)"
definition "x ≡ SOME x. x ∉ X"
definition "a⇩0 = (l⇩0, init_dbm)"
abbreviation
  "canonical' D ≡ canonical (curry D) n"
abbreviation
  "canonical_diag' D ≡ canonical' D ∨ check_diag n D"
abbreviation
  "canonical_diag D ≡ canonical' (conv_M D) ∨ check_diag n D"
abbreviation
  "canonical_subs' I M ≡ canonical_subs n I (curry M)"
definition "locations ≡ {l⇩0} ∪ fst ` trans_of A ∪ (snd o snd o snd o snd) ` trans_of A"
lemma X_alt_def:
  "X = {1..<Suc n}"
  unfolding X_def by auto
lemma v_bij:
  "bij_betw v {1..<Suc n} {1..n}"
  unfolding v_def[abs_def] bij_betw_def inj_on_def by auto
lemma triv_numbering:
  "∀ i ∈ {1..n}. v i = i"
  unfolding v_def by auto
lemma canonical_diagI:
  "canonical_diag D"  if "canonical_diag' D"
  using that canonical_conv by auto
lemma v_id:
  "v c = c" if "v c ≤ n"
  by (metis Suc_n_not_le_n that v_def)
end
locale TA_Start_No_Ceiling = TA_Start_No_Ceiling_Defs +
  assumes finite_trans[intro, simp]: "finite (trans_of A)"
      and finite_inv[intro]: "finite (range (inv_of A))"
      and clocks_n: "clk_set A ⊆ {1..n}"
      and consts_nats: "∀(_, d) ∈ Timed_Automata.clkp_set A. d ∈ ℕ"
      and n_gt0[intro, simp]: "n > 0"
begin
lemma clock_range:
  "∀c∈clk_set A. c ≤ n ∧ c ≠ 0"
  using clocks_n by force
lemma clk_set_X:
  "clk_set A ⊆ X"
  unfolding X_def using clocks_n .
lemma finite_X:
  "finite X"
  unfolding X_def by (metis finite_atLeastAtMost)
lemma finite_clkp_set_A[intro, simp]:
  "finite (Timed_Automata.clkp_set A)"
  unfolding Timed_Automata.clkp_set_def ta_collect_clkt_alt_def ta_collect_clki_alt_def by fast
lemma finite_collect_clkvt[intro]:
  "finite (collect_clkvt (trans_of A))"
  unfolding collect_clkvt_def using [[simproc add: finite_Collect]] by auto
lemma consts_ints:
  "∀(_, d) ∈ Timed_Automata.clkp_set A. d ∈ ℤ"
  using consts_nats unfolding Nats_def by auto
lemma n_bounded:
  "∀ c ∈ X. c ≤ n"
  unfolding X_def by auto
lemma finite_locations:
  "finite locations"
  unfolding locations_def using finite_trans by auto
lemma RI_I_conv_cc:
  "RI_I n (conv_cc o snd A) (snd A)"
  using clocks_n
  unfolding RI_I_def rel_fun_def Timed_Automata.clkp_set_def ta_collect_clki_alt_def inv_of_def
  by (force intro: ri_conv_cc)
lemma triv_numbering':
  "∀ c ∈ clk_set A. v c = c"
  using triv_numbering clocks_n by auto
lemma triv_numbering'':
  "∀ c ∈ clk_set (conv_A A). v c = c"
  using triv_numbering' unfolding clk_set_conv_A .
lemma global_clock_numbering:
  "global_clock_numbering A v n"
  using clocks_n unfolding v_def by auto
lemmas global_clock_numbering' = global_clock_numbering_conv[OF global_clock_numbering]
lemma ri_conv_t:
  assumes "t ∈ fst A"
  shows "(RI_T n) (conv_t t) t"
  unfolding RI_T_def apply (auto split: prod.split)
   apply (rule ri_conv_cc)
  using assms clocks_n unfolding Timed_Automata.clkp_set_def ta_collect_clkt_alt_def trans_of_def
   apply fastforce
  using assms using clocks_n unfolding clkp_set_def collect_clkvt_alt_def trans_of_def eq_onp_def
  by (fastforce intro: list_all2I simp: zip_same)
lemma RI_T_conv_t:
  "rel_set (RI_T n) (conv_t ` fst A) (fst A)"
  using ri_conv_t unfolding rel_set_def by (fastforce split: prod.split)
lemma RI_A_conv_A:
  "RI_A n (conv_A A) A"
  using RI_T_conv_t RI_I_conv_cc unfolding RI_A_def conv_A_def by (auto split: prod.split)
lemma step_impl_diag_preservation:
  assumes step: "A ⊢⇩I ⟨l, M⟩ ↝⇘n,a⇙ ⟨l', M'⟩"
    and diag: "∀ i ≤ n. (curry M) i i ≤ 0"
  shows
    "∀ i ≤ n. (curry M') i i ≤ 0"
proof -
  have FW':
    "(FW' M n) (i, i) ≤ 0" if "i ≤ n" "∀ i ≤ n. M (i, i) ≤ 0" for i and M :: "int DBM'"
    using that FW'_diag_preservation[OF that(2)] diag by auto
  have *:
    "∀ c ∈ collect_clks (inv_of A l). c ≠ 0"
    using clock_range collect_clks_inv_clk_set[of A l] by auto
  from step diag * show ?thesis
    apply cases
    subgoal 
      apply simp
      apply (rule FW'_diag_preservation)
      apply (rule abstr_upd_diag_preservation')
       apply (subst up_canonical_upd_diag_preservation)
      by auto
    subgoal 
      apply simp
      apply (rule FW'_diag_preservation)
      apply (rule abstr_upd_diag_preservation')
       apply (standard, standard)
       apply (subst reset'_upd_diag_preservation)
         defer
         apply assumption
        apply (erule FW')
        apply (erule abstr_upd_diag_preservation')
        apply (metis (no_types) clock_range collect_clocks_clk_set subsetCE)
       apply (metis (no_types) clock_range collect_clks_inv_clk_set subsetCE)
      apply (drule reset_clk_set)
      using clock_range by blast
    done
qed
lemma step_impl_neg_diag_preservation:
  assumes step: "A ⊢⇩I ⟨l, M⟩ ↝⇘n,a⇙ ⟨l', M'⟩"
    and le: "i ≤ n"
    and diag: "(curry M) i i < 0"
  shows "(curry M') i i < 0"
  using assms
  apply cases
  subgoal 
    apply simp
    apply (rule FW'_neg_diag_preservation)
     apply (subst abstr_upd_diag_preservation)
       apply assumption
      apply (metis (no_types) clock_range collect_clks_inv_clk_set subsetCE)
     apply (subst up_canonical_upd_diag_preservation)
    by auto
  subgoal 
    apply simp
    apply (rule FW'_neg_diag_preservation)
     apply (subst abstr_upd_diag_preservation)
       apply assumption
      apply (metis (no_types) clock_range collect_clks_inv_clk_set subsetCE)
     apply (subst reset'_upd_diag_preservation)
      defer
      apply assumption
     apply (rule FW'_neg_diag_preservation)
      apply (subst abstr_upd_diag_preservation)
        apply assumption
       apply (metis (no_types) clock_range collect_clocks_clk_set subsetCE)
      apply assumption+
    apply (drule reset_clk_set)
    using clock_range by blast
  done
lemma step_impl_canonical:
  assumes step: "A ⊢⇩I ⟨l, M⟩ ↝⇘n,a⇙ ⟨l', M'⟩"
    and diag: "∀ i ≤ n. (curry M) i i ≤ 0"
  shows
    "canonical (curry (conv_M M')) n ∨ (∃ i ≤ n. M' (i, i) < 0)"
proof -
  from step_impl_diag_preservation[OF assms] have diag: "∀i≤n. curry M' i i ≤ 0" .
  from step obtain M'' where "M' = FW' M'' n" by cases auto
  show ?thesis
  proof (cases "∃ i ≤ n. M' (i, i) < 0")
    case True
    then show ?thesis by auto
  next
    case False
    with diag have "∀i≤n. curry M' i i ≥ 0" by auto
    then have
      "∀i≤n. curry (conv_M M') i i ≥ 0"
      unfolding neutral by (auto dest!: conv_dbm_entry_mono)
    with FW_canonical'[of n "curry (conv_M M'')"] canonical_conv_M_FW' diag_conv_M show ?thesis
      unfolding ‹M' = _› FW'_FW[symmetric] canonical_conv_M_FW' by auto
  qed
qed
lemma step_impl_int_preservation:
  assumes step: "A ⊢⇩I ⟨l,D⟩ ↝⇘n,a⇙ ⟨l',D'⟩"
    and  int: "dbm_int (curry D) n"
  shows "dbm_int (curry D') n"
  using assms
  apply cases
  subgoal premises prems
    unfolding prems
    apply (intro
        FW'_int_preservation norm_upd_int_preservation abstr_upd_int_preservation[rotated]
        up_canonical_upd_int_preservation
        )
    using consts_ints assms unfolding Timed_Automata.clkp_set_def Timed_Automata.collect_clki_def
    by auto
  subgoal premises prems
    unfolding prems
    apply (intro
        FW'_int_preservation norm_upd_int_preservation abstr_upd_int_preservation[rotated]
        reset'_upd_int_preservation
        )
    using assms prems(4) consts_ints collect_clock_pairs_inv_clkp_set[of A l']
        apply (auto dest!: collect_clock_pairs_trans_clkp_set)
    using prems(4) clock_range by (auto dest!: reset_clk_set)
  done
lemma check_diag_empty_spec:
  assumes "check_diag n M"
  shows "[curry M]⇘v,n⇙ = {}"
  apply (rule check_diag_empty)
  using assms global_clock_numbering by fast+
lemma init_dbm_semantics:
  assumes "u ∈ [curry (conv_M init_dbm)]⇘v,n⇙" "0 < c" "c ≤ n"
  shows "u c = 0"
proof -
  from assms(2,3) have "v c ≤ n" unfolding v_def by auto
  with assms(1) show ?thesis unfolding DBM_zone_repr_def init_dbm_def DBM_val_bounded_def v_def
    by auto
qed
lemma dbm_int_conv:
  "dbm_int (curry (conv_M Z)) n"
  apply safe
  subgoal for i j
    by (cases "Z (i, j)") auto
  done
lemma RI_init_dbm:
  "RI n init_dbm init_dbm"
  unfolding init_dbm_def rel_fun_def ri_def by auto
lemma conv_M_init_dbm[simp]:
  "conv_M init_dbm = init_dbm"
  unfolding init_dbm_def by auto
lemmas
  step_z_dbm_equiv' = step_z_dbm_equiv[OF global_clock_numbering']
lemma step_impl_complete':
  assumes step: "step_z_dbm (conv_A A) l (curry (conv_M M)) v n a l' M'"
    and canonical: "canonical (curry (conv_M M)) n"
    and diag: "∀i≤n. conv_M M (i, i) ≤ 0"
  shows "∃ D'. A ⊢⇩I ⟨l, M⟩ ↝⇘n,a⇙ ⟨l', D'⟩ ∧ [curry (conv_M D')]⇘v,n⇙ = [M']⇘v,n⇙"
proof -
  let ?A = "conv_A A"
  have A: "RI_A n ?A A" by (rule RI_A_conv_A)
  have M_conv: "RI n (conv_M M) M" unfolding eq_onp_def by auto
  let ?M' = "uncurry M'"
  from
    step_impl_complete[of ?A l "conv_M M" _ _ a _ ?M',
      OF _ canonical global_clock_numbering' triv_numbering'' diag
      ] step
  obtain M'' where M'':
    "?A ⊢⇩I ⟨l, conv_M M⟩ ↝⇘n,a⇙ ⟨l', M''⟩" "[curry M'']⇘v,n⇙ = [curry ?M']⇘v,n⇙"
    by auto
  from RI_complete[OF M_conv A this(1)] obtain MM where MM:
    "A ⊢⇩I ⟨l, M⟩ ↝⇘n,a⇙ ⟨l', MM⟩" "RI n M'' MM"
    by auto
  moreover from MM(2) M''(2) M''(2) have
    "[M']⇘v,n⇙ = [curry (conv_M MM)]⇘v,n⇙"
    by (auto dest!: RI_zone_equiv[where v = v])
  ultimately show ?thesis by auto
qed
lemma step_impl_check_diag:
  assumes "check_diag n M" "A ⊢⇩I ⟨l, M⟩ ↝⇘n,a⇙ ⟨l', M'⟩"
  shows "check_diag n M'"
  using step_impl_neg_diag_preservation assms unfolding check_diag_def neutral by auto
lemmas
  step_z_dbm_sound = step_z_dbm_sound[OF _ global_clock_numbering']
lemmas
  step_z_dbm_DBM = step_z_dbm_DBM[OF _ global_clock_numbering']
lemmas dbm_subset_correct' = dbm_subset_correct'[OF _ _ _ global_clock_numbering]
subsubsection ‹Bisimilarity›
definition dbm_equiv (infixr "≃" 60) where
  "dbm_equiv M M' ≡ [curry (conv_M M)]⇘v,n⇙ = [curry (conv_M M')]⇘v,n⇙"
definition state_equiv (infixr "∼" 60) where
  "state_equiv ≡ λ (l, M) (l', M'). l = l' ∧ M ≃ M'"
lemma dbm_equiv_trans[intro]:
  "a ≃ c" if "a ≃ b" "b ≃ c"
  using that unfolding dbm_equiv_def by simp
lemma state_equiv_trans[intro]:
  "a ∼ c" if "a ∼ b" "b ∼ c"
  using that unfolding state_equiv_def by blast
lemma dbm_equiv_refl[intro]:
  "a ≃ a"
  unfolding dbm_equiv_def by simp
lemma state_equiv_refl[intro]:
  "a ∼ a"
  unfolding state_equiv_def by blast
lemma dbm_equiv_sym:
  "a ≃ b" if "b ≃ a"
  using that unfolding dbm_equiv_def by simp
lemma state_equiv_sym:
  "a ∼ b" if "b ∼ a"
  using that unfolding state_equiv_def by (auto intro: dbm_equiv_sym)
lemma state_equiv_D:
  "M ≃ M'" if "(l, M) ∼ (l', M')"
  using that unfolding state_equiv_def by auto
lemma finite_trans':
    "finite (trans_of (conv_A A))"
    using finite_trans unfolding trans_of_def conv_A_def by (cases A) auto
lemma init_dbm_semantics':
    assumes "u ∈ [(curry init_dbm :: real DBM)]⇘v,n⇙"
    shows "∀ c ∈ {1..n}. u c = 0"
  using assms init_dbm_semantics by auto
  lemma init_dbm_semantics'':
    assumes "∀ c ∈ {1..n}. u c = 0"
    shows "u ∈ [(curry init_dbm :: real DBM)]⇘v,n⇙"
  unfolding DBM_zone_repr_def DBM_val_bounded_def init_dbm_def using assms by (auto simp: v_def)
end 
locale TA_Start_Defs =
  TA_Start_No_Ceiling_Defs A l⇩0 n for A :: "('a, nat, int, 's) ta" and l⇩0 n +
  fixes k :: "'s ⇒ nat ⇒ nat"
begin
definition "k' l ≡ map (int o k l) [0..<Suc n]"
definition "E = (λ (l, M) (l'', M''').
  ∃ M' l' M'' a.
  A ⊢⇩I ⟨l, M⟩ ↝⇘n,τ⇙ ⟨l', M'⟩
  ∧ A ⊢⇩I ⟨l', M'⟩ ↝⇘n,↿a⇙ ⟨l'', M''⟩
  ∧ M''' = FW' (norm_upd M'' (k' l'') n) n)"
lemma E_alt_def: "E = (λ (l, M) (l', M''').
  ∃ g a r M' M''.
  A ⊢ l ⟶⇗g,a,r⇖ l' ∧
  M' = FW' (abstr_upd (inv_of A l) (up_canonical_upd M n)) n ∧
  M'' = FW' (abstr_upd (inv_of A l') (reset'_upd (FW' (abstr_upd g M') n) n r 0)) n ∧
  M''' = FW' (norm_upd M'' (k' l') n) n
  )"
  unfolding E_def by (force elim!: step_impl.cases)
  lemma length_k':
    "length (k' l) = Suc n"
  unfolding k'_def by auto
end
locale Reachability_Problem_No_Ceiling_Defs =
  TA_Start_No_Ceiling_Defs A l⇩0 n for A :: "('a, nat, int, 's) ta" and l⇩0 n+
  fixes F :: "'s ⇒ bool"
begin
definition "F_rel ≡ λ (l, M). F l ∧ ¬ check_diag n M"
end
locale Reachability_Problem_Defs = TA_Start_Defs + Reachability_Problem_No_Ceiling_Defs
begin
end 
lemma check_diag_conv_M[intro]:
  assumes "check_diag n M"
  shows "check_diag n (conv_M M)"
  using assms unfolding check_diag_def by (auto dest!: conv_dbm_entry_mono_strict)
lemma canonical_variant:
  "canonical (curry (conv_M D)) n ∨ check_diag n D
  ⟷ canonical (curry (conv_M D)) n ∨ (∃i≤n. D (i, i) < 0)"
  unfolding check_diag_def neutral ..
locale TA_Start =
  TA_Start_No_Ceiling A l⇩0 n +
  TA_Start_Defs A l⇩0 n k for A :: "('a, nat, int, 's) ta" and l⇩0 n k +
  assumes k_ceiling:
    "∀ l. ∀(x,m) ∈ clkp_set A l. m ≤ k l x"
    "∀ l g a r l' c. A ⊢ l ⟶⇗g,a,r⇖ l' ∧ c ∉ set r ⟶ k l' c ≤ k l c"
  and k_bound: "∀ l. ∀ i > n. k l i = 0"
  and k_0: "∀ l. k l 0 = 0"
begin
lemma E_closure:
  "E⇧*⇧* a⇩0 (l', M') ⟷ A ⊢⇩I ⟨l⇩0, init_dbm⟩ ↝⇘k',n⇙* ⟨l', M'⟩"
  unfolding a⇩0_def E_def
  apply safe
   apply (drule rtranclp_induct[where P = "λ (l', M'). A ⊢⇩I ⟨l⇩0, init_dbm⟩ ↝⇘k',n⇙* ⟨l', M'⟩"];
      auto dest: step intro: refl; fail
      )
  apply (induction rule: steps_impl.induct; simp add: rtranclp.intros(2))
  by (rule rtranclp.intros(2)) auto
lemma FW'_normalized_integral_dbms_finite':
  "finite {FW' (norm_upd M (k' l) n) n | M. dbm_default (curry M) n}"
  (is "finite ?S")
proof -
  let ?S' = "{norm_upd M (k' l) n | M. dbm_default (curry M) n}"
  have "finite ?S'"
    using normalized_integral_dbms_finite'[where k = "map (k l) [0..<Suc n]"] by (simp add: k'_def)
  moreover have "?S = (λ M. FW' M n) ` ?S'" by auto
  ultimately show ?thesis by auto
qed
lemma E_closure_finite:
  "finite {x. E⇧*⇧* a⇩0 x}"
proof -
  have k': "map int (map (k l) [0..<Suc n]) = k' l" for l unfolding k'_def by auto
  have *: "(l, M) = a⇩0 ∨ (∃M'. M = FW' (norm_upd M' (k' l) n) n ∧ dbm_default (curry M') n)"
    if "E⇧*⇧* a⇩0 (l, M)" for l M
    using that unfolding E_closure
    apply -
    apply (drule steps_impl_norm_dbm_default_dbm_int)
          apply (auto simp: init_dbm_def neutral)[]
         apply (auto simp: init_dbm_def)[]
        defer
        defer
        apply (simp add: k'_def; fail)
       apply (simp add: k'_def; fail)
      apply (simp add: a⇩0_def)
    using clock_range consts_ints by (auto simp: X_def)
  moreover have **: "l ∈ locations" if "E⇧*⇧* a⇩0 (l, M)" for l M
    using that unfolding E_closure locations_def
    apply (induction rule: steps_impl.induct)
     apply (simp; fail)
    by (force elim!: step_impl.cases)
  have
    "{x. E⇧*⇧* a⇩0 x} ⊆
        {a⇩0} ∪ (locations × {FW' (norm_upd M (k' l) n) n | l M. l ∈ locations ∧ dbm_default (curry M) n})"
    (is "_ ⊆ _ ∪ ?S")
    apply safe
     apply (rule **, assumption)
    apply (frule *)
    by (auto intro: **)
  moreover have "finite ?S"
    using FW'_normalized_integral_dbms_finite' finite_locations
    using [[simproc add: finite_Collect]]
    by (auto simp: k'_def finite_project_snd)
  ultimately show ?thesis by (auto intro: finite_subset)
qed
sublocale Regions "{1..<Suc n}" v n k "Suc n"
  apply standard
       apply (simp; fail)
  using default_numbering(2)[OF finite_X] apply (subst (asm) X_def) apply (simp add: v_def; fail)
  using default_numbering(2)[OF finite_X] apply (subst (asm) X_def) apply (simp add: v_def; fail)
  by (auto simp: v_def)
lemma k_simp_1:
  "(λ l i. if i ≤ n then map (k l) [0..<Suc n] ! i else 0) = k"
proof (rule ext)+
  fix l i
  show "(if i ≤ n then map (k l) [0..<Suc n] ! i else 0) = k l i"
  proof (cases "i ≤ n")
    case False
    with k_bound show ?thesis by auto
  next
    case True
    with map_nth[OF this] show ?thesis by auto
  qed
qed
lemma k_simp_2:
  "(λ l. k l ∘ v') = k"
proof (rule ext)+
  fix l i
  show "(λ l. k l o v') l i = k l i"
  proof (cases "i > n")
    case True
    then show ?thesis unfolding v'_def by (auto simp: k_bound)
  next
    case False
    show ?thesis
    proof (cases "i = 0")
      case True
      with k_0 have "k l i = 0" by simp
      moreover have "v' 0 = Suc n" unfolding v'_def by auto
      ultimately show ?thesis using True by (auto simp: k_bound)
    next
      case False
      with ‹¬ n < i› have "v' (v i) = i" using v_v' by auto
      moreover from False ‹¬ n < i› triv_numbering have "v i = i" by auto
      ultimately show ?thesis by auto
    qed
  qed
qed
lemma valid_abstraction:
  "valid_abstraction A X k"
  using k_ceiling consts_nats clk_set_X unfolding X_def
  by (fastforce intro!: valid_abstraction.intros simp: TA_clkp_set_unfold)
lemma norm_upd_diag_preservation:
  assumes "i ≤ n" "M (i, i) ≤ 0"
  shows "(norm_upd M (k' l) n) (i, i) ≤ 0"
  apply (subst norm_upd_norm)
  apply (subst norm_k_cong[where k' = "k l"])
   apply (safe; simp only: k'_def map_nth; simp; fail)
  using norm_diag_preservation_int assms unfolding neutral by auto
lemma norm_upd_neg_diag_preservation:
  assumes "i ≤ n" "M (i, i) < 0"
  shows "(norm_upd M (k' l) n) (i, i) < 0"
  apply (subst norm_upd_norm)
  apply (subst norm_k_cong[where k' = "k l"])
   apply (safe; simp only: k'_def map_nth; simp; fail)
  using norm_empty_diag_preservation_int assms unfolding neutral by auto
lemmas valid_abstraction' = valid_abstraction_conv[OF valid_abstraction, unfolded X_alt_def]
lemma step_impl_V_preservation_canonical:
  assumes step: "A ⊢⇩I ⟨l,D⟩ ↝⇘n,a⇙ ⟨l',D'⟩"
    and canonical: "canonical (curry (conv_M D)) n"
    and diag: "∀i≤n. conv_M D (i, i) ≤ 0"
    and valid: "valid_dbm (curry (conv_M D))"
  shows "[curry (conv_M D')]⇘v,n⇙ ⊆ V"
proof -
  let ?A = "conv_A A"
  have A: "RI_A n ?A A" by (rule RI_A_conv_A)
  have "RI n (conv_M D) D" unfolding eq_onp_def by auto
  from IR_complete[OF this A step] obtain M' where M':
    "?A ⊢⇩I ⟨l, conv_M D⟩ ↝⇘n,a⇙ ⟨l', M'⟩" "RI n M' D'"
    by auto
  from
    step_impl_sound[of ?A l "conv_M D",
      OF this(1) canonical global_clock_numbering' triv_numbering'' diag(1)
      ]
  obtain M'' where M'':
    "?A ⊢ ⟨l, curry (conv_M D)⟩ ↝⇘v,n,a⇙ ⟨l', M''⟩" "[curry M']⇘v,n⇙ = [M'']⇘v,n⇙"
    by (auto simp add: k_simp_1)
  from step_z_valid_dbm[OF M''(1) global_clock_numbering' valid_abstraction'] valid have
    "valid_dbm M''"
    by auto
  then have "[M'']⇘v,n⇙ ⊆ V" by cases
  with M''(2) RI_zone_equiv[OF M'(2)] show ?thesis by auto
qed
lemma step_impl_V_preservation:
  assumes step: "A ⊢⇩I ⟨l,D⟩ ↝⇘n,a⇙ ⟨l',D'⟩"
    and canonical: "canonical (curry (conv_M D)) n ∨ (∃i≤n. D (i, i) < 0)"
    and diag: "∀i≤n. conv_M D (i, i) ≤ 0"
    and valid: "valid_dbm (curry (conv_M D))"
  shows "[curry (conv_M D')]⇘v,n⇙ ⊆ V"
proof -
  from canonical show ?thesis
  proof (standard, goal_cases)
    case 1
    from step_impl_V_preservation_canonical[OF step this diag valid] show ?thesis .
  next
    case 2
    with step_impl_neg_diag_preservation[OF step] have "∃i≤n. D' (i, i) < 0" by auto
    then have
      "[curry (conv_M D')]⇘v,n⇙ = {}"
      by - (rule check_diag_empty_spec;
          auto dest!: conv_dbm_entry_mono_strict simp: check_diag_def neutral
          )
    then show ?thesis by blast
  qed
qed
lemma norm_step_diag_preservation:
  "∀i≤n. curry (FW' (norm_upd D (k' l) n) n) i i ≤ 0" if "∀i≤n. (curry D) i i ≤ 0"
  by (metis FW'_diag_preservation curry_conv norm_upd_diag_preservation that)
lemma norm_step_check_diag_preservation:
  "check_diag n (FW' (norm_upd D (k' l) n) n)" if "check_diag n D"
  by (metis FW'_neg_diag_preservation check_diag_def neutral norm_upd_neg_diag_preservation that)
lemma diag_reachable:
  assumes "E⇧*⇧* a⇩0 (l, M)"
  shows "∀ i ≤ n. (curry M) i i ≤ 0"
  using assms unfolding E_closure
  apply (induction Z ≡ "init_dbm :: int DBM'" _ _ _ _ rule: steps_impl_induct)
   apply (auto simp: init_dbm_def neutral; fail)
  apply (assumption | rule norm_step_diag_preservation step_impl_diag_preservation)+
  done
lemma canonical_norm_step:
  "canonical (curry (conv_M (FW' (norm_upd M (k' l) n) n))) n
   ∨ (∃ i ≤ n. (FW' (norm_upd M (k' l) n) n) (i, i) < 0)"
  by (metis FW'_canonical canonical_conv)
lemma canonical_reachable:
  assumes "E⇧*⇧* a⇩0 (l, M)"
  shows "canonical (curry (conv_M M)) n ∨ (∃i ≤ n. M (i, i) < 0)"
  using assms unfolding E_closure
proof (induction l ≡ l⇩0 Z ≡ "init_dbm :: int DBM'" _ _ _ _ rule: steps_impl_induct)
  case refl
  show ?case unfolding init_dbm_def by simp (simp add: neutral[symmetric])
next
  case step
  show ?case by (rule canonical_norm_step)
qed
lemma diag_reachable':
  assumes "E⇧*⇧* a⇩0 (l, M)"
  shows "∀ i ≤ n. (curry (conv_M M)) i i ≤ 0"
  using diag_reachable[OF assms] by (auto simp: neutral dest!: conv_dbm_entry_mono)
context
    fixes l' :: 's
  begin
  interpretation regions: Regions_global "{1..<Suc n}" v n "k l'" "Suc n"
    by (standard; rule finite clock_numbering not_in_X non_empty)
  lemma v'_v'[simp]:
    "Regions.v' {1..<Suc n} v n (Suc n) = Beta_Regions'.v' {1..<Suc n} v n (Suc n)"
    unfolding v'_def regions.beta_interp.v'_def ..
  lemma k_simp_2': "(λ x. real ((k l' ∘∘∘ Beta_Regions'.v' {1..<Suc n} v) n (Suc n) x)) = k l'"
    using k_simp_2 v'_v' by metis
  lemma norm_upd_V_preservation:
    "[curry (conv_M (norm_upd M (k' l') n))]⇘v,n⇙ ⊆ V"
    if "[curry (conv_M M)]⇘v,n⇙ ⊆ V" "canonical (curry (conv_M M)) n"
  proof -
    from regions.beta_interp.norm_V_preservation[OF that] have
      "[norm (curry (conv_M M)) (λ x. real (k l' x)) n]⇘v,n⇙ ⊆ V"
      by (simp only: k_simp_2')
    then have *: "[norm (curry (conv_M M)) ((!) (map real_of_int (k' l'))) n]⇘v,n⇙ ⊆ V"
      apply (subst norm_k_cong[of _ _ "(λ x. real (k l' x))"])
       apply safe
      
      proof -
        fix i :: nat
        assume "i ≤ n"
        then have "map real_of_int (k' l') ! i = (real_of_int ∘∘∘ (∘)) int (k l') i"
          by (metis (no_types) Normalized_Zone_Semantics_Impl.map_nth k'_def map_map)
        then show "map real_of_int (k' l') ! i = real (k l' i)"
          by simp
      qed
    note [transfer_rule] = norm_upd_transfer[folded ri_len_def]
    have [transfer_rule]: "eq_onp (λx. x = n) n n" by (simp add: eq_onp_def)
    have [transfer_rule]: "(ri_len n) (map real_of_int (k' l')) (k' l')"
      by (simp add: ri_len_def eq_onp_def length_k' list_all2_conv_all_nth ri_def)
    have "RI n (norm_upd (conv_M M) (map real_of_int (k' l')) n) (norm_upd M (k' l') n)"
      by transfer_prover
    from RI_zone_equiv[OF this] * have
      "[curry (conv_M (norm_upd M (k' l') n))]⇘v,n⇙ ⊆ V"
      by (auto simp add: norm_upd_norm'[symmetric])
    then show ?thesis .
  qed
  lemma norm_step_correct:
    assumes diag: "∀i≤n. conv_M D (i, i) ≤ 0" "∀i≤n. M i i ≤ 0"
        and canonical: "canonical (curry (conv_M D)) n ∨ (∃i≤n. D (i, i) < 0)"
        and equiv: "[curry (conv_M D)]⇘v,n⇙ = [M]⇘v,n⇙"
        and valid: "valid_dbm M"
    shows
    "[curry (conv_M (FW' (norm_upd D (k' l') n) n))]⇘v,n⇙
   = [norm (FW M n) (λx. real (k l' x)) n]⇘v,n⇙"
  proof (cases "check_diag n D")
    case False
    let ?k = "map real_of_int (k' l')"
    have k_alt_def: "?k = map (k l') [0..<Suc n]" unfolding k'_def by auto
    have k: "list_all2 ri ?k (k' l')" by (simp add: list_all2_conv_all_nth ri_def)
    from canonical False have "canonical (curry (conv_M D)) n"
      unfolding check_diag_def neutral by simp
    from canonical_conv_rev[OF this] have "canonical (curry D) n"
      by simp
    from FW_canonical_id[OF this] have "FW (curry D) n = curry D" .
    then have "FW' D n = D"
      apply (subst (asm) FW'_FW[symmetric])
      unfolding curry_def by (rule ext, safe, meson)
    have "RI n (FW' (norm_upd (FW' (conv_M D) n) (map real_of_int (k' l')) n) n)
       (FW' (norm_upd (FW' D n) (k' l') n) n)"
      by (intro RI_norm_step RI_conv_M k; simp add: length_k')
    moreover have "[curry (conv_M D)]⇘v,n⇙ = [M]⇘v,n⇙" by (rule equiv)
    moreover have "∀ i ≤ n. (λx. real (map (k l') [0..<Suc n] ! x)) i = (λx. real (k l' x)) i"
      using map_nth by auto
    ultimately show ?thesis
      using diag
      apply -
      apply (subst ‹_ = D›[symmetric])
      apply (subst RI_zone_equiv[symmetric, where M = "FW' (norm_upd (FW' (conv_M D) n) ?k n) n"])
       apply assumption
      unfolding k_alt_def
      using norm_impl_correct[where D = "conv_M D", where M = M]
      apply (subst norm_impl_correct[where M = M])
            prefer 4
      using global_clock_numbering apply satx
      using global_clock_numbering apply satx
          apply (simp; fail)+
      apply (subst norm_k_cong[of _ "(λx. real (k l' x))"])
      by auto
  next
    case True
    then have "check_diag n (conv_M D)" by auto
    from check_diag_empty_spec[OF this] equiv have *:
      "[curry (conv_M D)]⇘v,n⇙ = {}" "[M]⇘v,n⇙ = {}"
      by auto
    from regions.norm_FW_empty[OF valid *(2)] k_simp_2' have **:
      "[norm (FW M n) (λx. real (k l' x)) n]⇘v,n⇙ = {}"
      by auto
    from norm_step_check_diag_preservation[OF True] have
      "check_diag n (FW' (norm_upd D (k' l') n) n)" .
    then have "check_diag n (conv_M (FW' (norm_upd D (k' l') n) n))" by auto
    from check_diag_empty_spec[OF this] have
      "[curry (conv_M (FW' (norm_upd D (k' l') n) n))]⇘v,n⇙ = {}" .
    with ** step' show ?thesis by auto
  qed
  lemma step_z_norm'_empty_preservation:
    assumes "step_z_norm' (conv_A A) l D a l' D'" "valid_dbm D" "[D]⇘v,n⇙ = {}"
    shows "[D']⇘v,n⇙ = {}"
    using assms(1)
    apply cases
    unfolding v'_v'
    apply simp
    apply (rule regions.norm_FW_empty[simplified])
    using assms(2) step_z_valid_dbm[OF _ global_clock_numbering' valid_abstraction'] apply (simp; fail)
    apply (rule step_z_dbm_empty[OF global_clock_numbering'])
    using assms(3) by auto
  lemma canonical_empty_check_diag:
    assumes "canonical (curry (conv_M D')) n ∨ (∃i≤n. D' (i, i) < 0)" "[curry (conv_M D')]⇘v,n⇙ = {}"
    shows "check_diag n D'"
  proof -
    from assms(1)
    show ?thesis
    proof
      assume "canonical (curry (conv_M D')) n"
      from regions.beta_interp.canonical_empty_zone_spec[OF this] assms(2) have
        "∃i≤n. curry (conv_M D') i i < 0"
      by fast
      with conv_dbm_entry_mono_strict_rev show ?thesis unfolding check_diag_def neutral by force
    next
      assume "∃i≤n. D' (i, i) < 0"
      then show ?thesis unfolding check_diag_def neutral by auto
    qed
  qed
  end 
lemma FW'_valid_preservation:
  assumes "valid_dbm (curry (conv_M M))"
  shows "valid_dbm (curry (conv_M (FW' M n)))"
proof -
  from FW_valid_preservation[OF assms]
  show ?thesis by (simp add: FW'_FW[symmetric] FW'_conv_M)
qed
lemma norm_step_valid_dbm:
  "valid_dbm (curry (conv_M (FW' (norm_upd M (k' l') n) n)))" if
  "[curry (conv_M M)]⇘v,n⇙ ⊆ V" "canonical (curry (conv_M M)) n ∨ (∃ i ≤ n. M (i, i) < 0)"
proof -
  let ?M1 = "curry (conv_M (norm_upd M (k' l') n))"
  have "dbm_int ?M1 n" by (rule dbm_int_conv)
  from that(2) have "[?M1]⇘v,n⇙ ⊆ V"
  proof
    assume "canonical (curry (conv_M M)) n"
    from that(1) norm_upd_V_preservation[OF _ this] show "[?M1]⇘v,n⇙ ⊆ V" by auto
  next
    assume "∃i≤n. M (i, i) < 0"
    then have "[?M1]⇘v,n⇙ = {}"
      by (intro check_diag_empty_spec, simp add: check_diag_def)
         (metis DBMEntry.simps(15) conv_dbm_entry_mono_strict neutral norm_upd_neg_diag_preservation
            of_int_0)
    then show ?thesis by simp
  qed
  with ‹dbm_int ?M1 n› have "valid_dbm ?M1" by - rule
  from FW'_valid_preservation[OF this] show ?thesis .
qed
lemma valid_dbm_V:
  "[M]⇘v,n⇙ ⊆ V" if "valid_dbm M"
  using that by cases
lemma step_impl_valid_dbm:
  "valid_dbm (curry (conv_M M'))" if
  "A ⊢⇩I ⟨l, M⟩ ↝⇘n,a⇙ ⟨l', M'⟩" "valid_dbm (curry (conv_M M))"
  "canonical (curry (conv_M M)) n ∨ (∃i≤n. M (i, i) < 0)" "∀i≤n. conv_M M (i, i) ≤ 0"
  apply (rule valid_dbm.intros)
  subgoal
    using that by - (erule step_impl_V_preservation; assumption)
  subgoal
    by (rule dbm_int_conv)
  done
lemma valid_dbm_reachable:
  assumes "E⇧*⇧* a⇩0 (l, M)"
  shows "valid_dbm (curry (conv_M M))"
  using assms unfolding E_closure
  apply (induction l ≡ "l⇩0" Z ≡ "init_dbm :: int DBM'" _ _ _ _ rule: steps_impl_induct)
  subgoal
    apply (rule valid_dbm.intros)
    using init_dbm_semantics unfolding V_def apply force
    by (auto simp: init_dbm_def ; fail)
  subgoal
    apply (rule norm_step_valid_dbm)
     apply (rule valid_dbm_V)
     apply (rule step_impl_valid_dbm, assumption)
       apply (rule step_impl_valid_dbm, assumption)
         apply assumption
        apply (rule canonical_reachable[unfolded E_closure], assumption)
       apply (drule diag_conv[OF diag_reachable, unfolded E_closure]; simp; fail)
      apply (rule step_impl_canonical step_impl_diag_preservation, assumption)
      apply (drule diag_conv[OF diag_reachable, unfolded E_closure],
        simp add: conv_dbm_entry_mono_rev neutral; fail)
    subgoal
      apply (drule step_impl_diag_preservation)
       apply (drule diag_conv[OF diag_reachable, unfolded E_closure])
       apply (auto simp: conv_dbm_entry_mono_rev neutral)[]
      apply (auto simp: conv_dbm_entry_mono_rev neutral)[]
      using conv_dbm_entry_mono by fastforce
    apply (rule step_impl_canonical)
     apply assumption
    apply (rule step_impl_diag_preservation)
     apply assumption
    apply (rule diag_reachable)
    unfolding E_closure .
  done
lemma step_impl_sound':
  assumes step: "A ⊢⇩I ⟨l, D⟩ ↝⇘n,a⇙ ⟨l', D'⟩"
    and canonical: "canonical (curry (conv_M D)) n ∨ check_diag n D"
    and diag: "∀i≤n. conv_M D (i, i) ≤ 0"
    and valid: "valid_dbm (curry (conv_M D))"
  shows
    "∃ M'. step_z_dbm (conv_A A) l (curry (conv_M D)) v n a l' M'
          ∧ [curry (conv_M D')]⇘v,n⇙ = [M']⇘v,n⇙"
proof -
  let ?A = "conv_A A"
  have A: "RI_A n ?A A" by (rule RI_A_conv_A)
  have "RI n (conv_M D) D" unfolding eq_onp_def by auto
  from IR_complete[OF this A step] obtain M' where M':
    "?A ⊢⇩I ⟨l, conv_M D⟩ ↝⇘n,a⇙ ⟨l', M'⟩" "RI n M' D'"
    by auto
  show ?thesis
  proof (cases "check_diag n D")
    case False
    with
      step_impl_sound[of ?A l "conv_M D",
        OF M'(1) _ global_clock_numbering' triv_numbering'' diag
        ] canonical(1)
    obtain M'' where M'':
      "?A ⊢ ⟨l, curry (conv_M D)⟩ ↝⇘v,n,a⇙ ⟨l', M''⟩" "[curry M']⇘v,n⇙ = [M'']⇘v,n⇙"
      by auto
    with M'(2) M''(2) show ?thesis by (auto dest!: RI_zone_equiv[where v = v])
  next
    case True 
    with step_impl_neg_diag_preservation[OF step] have
      "check_diag n D'"
      unfolding check_diag_def neutral by auto
    moreover from step obtain M' where M': "conv_A A ⊢ ⟨l,curry (conv_M D)⟩ ↝⇘v,n,a⇙ ⟨l',M'⟩"
    proof cases
      case prems: step_t_impl
      then show thesis by - (rule that; simp; rule step_t_z_dbm; rule HOL.refl)
    next
      case prems: (step_a_impl g a' r)
      then show thesis
        apply -
        apply (rule that)
        unfolding ‹a = _›
        apply (rule step_a_z_dbm[where A = "conv_A A", of l "map conv_ac g" a' r l'])
        by (fastforce split: prod.split simp: trans_of_def conv_A_def)
    qed
    moreover from step_z_dbm_empty[OF global_clock_numbering' this check_diag_empty_spec] True have
      "[M']⇘v,n⇙ = {}"
      by auto
    ultimately show ?thesis using check_diag_empty_spec[OF check_diag_conv_M] by auto
  qed
qed
lemma step_impl_norm_sound:
  assumes step: "A ⊢⇩I ⟨l, D⟩ ↝⇘n,a⇙ ⟨l', D'⟩"
    and canonical: "canonical (curry (conv_M D)) n ∨ check_diag n D"
    and diag: "∀i≤n. conv_M D (i, i) ≤ 0"
    and valid: "valid_dbm (curry (conv_M D))"
  shows
    "∃ M'. step_z_norm' (conv_A A) l (curry (conv_M D)) a l' M'
           ∧ [curry (conv_M (FW' (norm_upd D' (k' l') n) n))]⇘v,n⇙ = [M']⇘v,n⇙"
proof -
  from step_impl_sound'[OF step canonical diag valid] obtain M' where M':
    "step_z_dbm (conv_A A) l (curry (conv_M D)) v n a l' M'" "[curry (conv_M D')]⇘v,n⇙ = [M']⇘v,n⇙"
    by auto
  from step_z_norm[OF this(1), of k] have
    "conv_A A ⊢ ⟨l, curry (conv_M D)⟩ ↝⇘k,v,n,a⇙ ⟨l', norm (FW M' n) (k l') n⟩" .
  then have step':
    "step_z_norm' (conv_A A) l (curry (conv_M D)) a l' (norm (FW M' n) (k l') n)"
    using k_simp_2 by auto
  from diag have "∀i≤n. curry D i i ≤ 0"
    by (simp add: conv_dbm_entry_mono_rev neutral)
  from step_impl_canonical[OF step this] have
    "canonical (curry (conv_M D')) n ∨ (∃i≤n. D' (i, i) < 0)" .
  moreover have "∀i≤n. conv_M D' (i, i) ≤ 0"
    using step_impl_diag_preservation[OF assms(1) ‹∀i≤n. curry D i i ≤ 0›]
    by (auto dest!: conv_dbm_entry_mono simp: neutral)
  moreover have "∀ i ≤ n. M' i i ≤ 0"
    using step_z_dbm_diag_preservation[OF M'(1)] diag by auto
  moreover from step_z_valid_dbm[OF M'(1) global_clock_numbering' valid_abstraction' valid] have
    "valid_dbm M'" .
  ultimately have "[curry (conv_M (FW' (norm_upd D' (k' l') n) n))]⇘v,n⇙
         = [norm (FW M' n) (λx. real (k l' x)) n]⇘v,n⇙"
    using M'(2) by - (rule norm_step_correct; auto)
  with step' show ?thesis
    by auto
qed
lemmas steps_z_norm'_valid_dbm_preservation =
  steps_z_norm'_valid_dbm_invariant[OF global_clock_numbering' valid_abstraction']
lemma valid_init_dbm[intro]:
  "valid_dbm (curry (init_dbm :: real DBM'))"
  apply rule
  subgoal
    unfolding V_def
    apply safe
    subgoal for u c
      by (auto dest: init_dbm_semantics[unfolded conv_M_init_dbm, where c = c])
    done
  unfolding init_dbm_def by auto
lemmas
  step_z_norm_equiv' = step_z_norm_equiv[OF _ global_clock_numbering' valid_abstraction']
lemmas
  step_z_valid_dbm' = step_z_valid_dbm[OF _ global_clock_numbering' valid_abstraction']
lemma step_impl_norm_complete:
  assumes step: "step_z_norm' (conv_A A) l (curry (conv_M M)) a l' M'"
    and canonical: "canonical (curry (conv_M M)) n"
    and diag: "∀i≤n. conv_M M (i, i) ≤ 0"
    and valid: "valid_dbm (curry (conv_M M))"
  shows
    "∃ D'. A ⊢⇩I ⟨l, M⟩ ↝⇘n,a⇙ ⟨l', D'⟩
    ∧ [curry (conv_M (FW' (norm_upd D' (k' l') n) n))]⇘v,n⇙ = [M']⇘v,n⇙"
proof -
  let ?k = "map real_of_int (k' l')"
  have k_alt_def: "?k = map (k l') [0..<Suc n]" unfolding k'_def by auto
  have k: "list_all2 ri ?k (k' l')" by (simp add: list_all2_conv_all_nth ri_def)
  have "length ?k = Suc n" using length_k' by auto
  let ?A = "conv_A A"
  have A: "RI_A n ?A A" by (rule RI_A_conv_A)
  have M_conv: "RI n (conv_M M) M" unfolding eq_onp_def by auto
  let ?M' = "uncurry M'"
  from step obtain D' where D':
    "M' = norm (FW D' n) (k l') n"
    "conv_A A ⊢ ⟨l, curry (conv_M M)⟩ ↝⇘v,n,a⇙ ⟨l', D'⟩"
    apply cases
    apply (subst (asm) v'_v')
    apply (subst (asm) k_simp_2')
    by (auto simp: k_simp_2')
  from step_impl_complete'[OF D'(2) canonical diag] obtain D'' where D'':
    "A ⊢⇩I ⟨l, M⟩ ↝⇘n,a⇙ ⟨l', D''⟩" "[curry (conv_M D'')]⇘v,n⇙ = [D']⇘v,n⇙"
    by auto
  from diag have "∀i≤n. curry M i i ≤ 0"
    by (simp add: conv_dbm_entry_mono_rev neutral)
  have "∀i≤n. conv_M D'' (i, i) ≤ 0"
    using step_impl_diag_preservation[OF D''(1) ‹∀i≤n. curry M i i ≤ 0›]
    by (auto dest!: conv_dbm_entry_mono simp: neutral)
  moreover have "∀i≤n. D' i i ≤ 0"
    using step_z_dbm_diag_preservation[OF D'(2)] diag by auto
  moreover have "canonical (curry (conv_M D'')) n ∨ (∃i≤n. D'' (i, i) < 0)"
    using step_impl_canonical[OF D''(1) ‹∀i≤n. curry M i i ≤ 0›] .
  moreover have "[curry (conv_M D'')]⇘v,n⇙ = [D']⇘v,n⇙" by fact
  moreover have "valid_dbm D'"
    using step_z_valid_dbm[OF D'(2) global_clock_numbering' valid_abstraction' valid] .
  ultimately have
    "[curry ((map_DBMEntry real_of_int ∘∘∘ FW') (norm_upd D'' (k' l') n) n)]⇘v,n⇙
     = [norm (FW D' n) (λx. real (k l' x)) n]⇘v,n⇙"
    by (rule norm_step_correct)
  with D''(1) show ?thesis by (auto simp add: D'(1))
qed
definition wf_dbm where
  "wf_dbm D ≡
    (canonical (curry (conv_M D)) n ∨ check_diag n D)
    ∧ (∀i≤n. conv_M D (i, i) ≤ 0)
    ∧ valid_dbm (curry (conv_M D))"
lemma wf_dbm_D:
  "canonical (curry (conv_M D)) n ∨ check_diag n D"
  "∀i≤n. conv_M D (i, i) ≤ 0"
  "valid_dbm (curry (conv_M D))"
  if "wf_dbm D"
  using that unfolding wf_dbm_def by auto
lemma wf_dbm_I:
  "wf_dbm D" if
  "canonical (curry (conv_M D)) n ∨ check_diag n D"
  "∀i≤n. conv_M D (i, i) ≤ 0"
  "valid_dbm (curry (conv_M D))"
  using that unfolding wf_dbm_def by auto
lemma reachable_wf_dbm:
  "wf_dbm M" if "E⇧*⇧* a⇩0 (l, M)"
  using canonical_reachable[OF that] diag_reachable[OF that] valid_dbm_reachable[OF that]
  apply (intro wf_dbm_I)
  unfolding check_diag_def neutral[symmetric] using diag_conv by auto
lemma canonical_check_diag_empty_iff:
  "[curry (conv_M D)]⇘v,n⇙ = {} ⟷ check_diag n D" if "canonical_diag' D"
  apply standard
  subgoal
    apply (rule canonical_empty_check_diag)
    using canonical_diagI[OF that] unfolding check_diag_def neutral by auto
  by (intro check_diag_empty_spec check_diag_conv_M)
lemma step_impl_norm_complete'':
  assumes step: "step_z_norm' (conv_A A) l (curry (conv_M M)) a l' D"
    and valid: "valid_dbm (curry (conv_M M))"
    and canonical: "canonical (curry (conv_M M)) n ∨ check_diag n M"
    and diag: "∀i≤n. conv_M M (i, i) ≤ 0"
  shows
    "∃ M'. A ⊢⇩I ⟨l, M⟩ ↝⇘n,a⇙ ⟨l', M'⟩
           ∧ [curry (conv_M (FW' (norm_upd M' (k' l') n) n))]⇘v,n⇙ = [D]⇘v,n⇙"
proof (cases "check_diag n M")
  case True
  then have "[curry (conv_M M)]⇘v,n⇙ = {}" by (intro check_diag_empty_spec check_diag_conv_M)
  with step valid have
    "[D]⇘v,n⇙ = {}"
    by (rule step_z_norm'_empty_preservation)
  from step obtain M' where M': "A ⊢⇩I ⟨l, M⟩ ↝⇘n,a⇙ ⟨l', M'⟩"
    apply cases
    apply (cases rule: step_z_dbm.cases)
      apply assumption
  proof goal_cases
    case 1
    then show ?thesis by - (rule that; simp; rule step_t_impl)
  next
    case prems: (2 _ g a' r)
    obtain g' where "A ⊢ l ⟶⇗g',a',r⇖ l'"
    proof -
      obtain T I where "A = (T, I)" by force
      from prems(6) show ?thesis by (fastforce simp: ‹A = _› trans_of_def conv_A_def intro: that)
    qed
    then show ?thesis
      apply -
      apply (rule that)
      unfolding ‹a = _› by (rule step_a_impl)
  qed
  from step_impl_check_diag[OF ‹check_diag n M› M'] have "check_diag n M'" .
  from norm_step_check_diag_preservation[OF this] have
    "check_diag n (FW' (norm_upd M' (k' l') n) n)"
    by auto
  with M' ‹[D]⇘v,n⇙ = {}› canonical_check_diag_empty_iff show ?thesis
    by blast
next
  case False
  with canonical have
    "canonical (curry (conv_M M)) n"
    unfolding check_diag_def neutral by auto
  then show ?thesis using diag valid step_impl_norm_complete[OF step] by auto
qed
lemmas
  step_z_dbm_mono = step_z_dbm_mono[OF global_clock_numbering']
lemmas
  step_z_norm_mono' = step_z_norm_mono[OF _ global_clock_numbering' valid_abstraction']
lemma dbm_subset_correct'':
  assumes "wf_dbm D" and "wf_dbm M"
  shows "[curry (conv_M D)]⇘v,n⇙ ⊆ [curry (conv_M M)]⇘v,n⇙ ⟷ dbm_subset n (conv_M D) (conv_M M)"
  apply (rule dbm_subset_correct')
  using wf_dbm_D[OF assms(1)] wf_dbm_D[OF assms(2)] by auto
lemma step_impl_wf_dbm:
  assumes step: "A ⊢⇩I ⟨l, D⟩ ↝⇘n,a⇙ ⟨l', D'⟩"
    and wf_dbm: "wf_dbm D"
  shows "wf_dbm D'"
  apply (rule wf_dbm_I)
  using
    step_impl_diag_preservation[OF step] step_impl_canonical[OF step] step_impl_valid_dbm[OF step]
    wf_dbm_D[OF wf_dbm]
  unfolding canonical_variant
  using diag_conv_rev diag_conv by simp+
lemma norm_step_wf_dbm:
  "wf_dbm (FW' (norm_upd D (k' l) n) n)" if "wf_dbm D"
  apply (rule wf_dbm_I)
  subgoal
    using canonical_norm_step
    unfolding canonical_variant .
  subgoal
    using wf_dbm_D[OF that] norm_step_diag_preservation
    using diag_conv[of n "FW' (norm_upd D (k' l) n) n"] diag_conv_rev by simp
  subgoal
    using wf_dbm_D[OF that]
    unfolding canonical_variant
    by (force intro!: valid_dbm_V norm_step_valid_dbm)
  done
lemma step_impl_complete''_improved:
  assumes step: "conv_A A ⊢ ⟨l, curry (conv_M M)⟩ ↝⇘v,n,a⇙ ⟨l', D⟩"
    and wf_dbm: "wf_dbm M"
  shows "∃ M'. A ⊢⇩I ⟨l, M⟩ ↝⇘n,a⇙ ⟨l', M'⟩ ∧ [curry (conv_M M')]⇘v,n⇙ = [D]⇘v,n⇙"
proof -
  note prems = wf_dbm_D[OF wf_dbm]
  show ?thesis
  proof (cases "check_diag n M")
    case True
    then have "[curry (conv_M M)]⇘v,n⇙ = {}" by (intro check_diag_empty_spec check_diag_conv_M)
    with step prems have
      "[D]⇘v,n⇙ = {}"
      by - (rule step_z_dbm_empty[OF global_clock_numbering'])
    moreover from step obtain M' where M': "A ⊢⇩I ⟨l, M⟩ ↝⇘n,a⇙ ⟨l', M'⟩"
    apply cases
    proof goal_cases
      case 1
      then show ?thesis by - (rule that; simp; rule step_t_impl)
    next
      case prems: (2 g a' r)
      obtain g' where "A ⊢ l ⟶⇗g',a',r⇖ l'"
      proof -
        obtain T I where "A = (T, I)" by force
        from prems(4) show ?thesis by (fastforce simp: ‹A = _› trans_of_def conv_A_def intro: that)
      qed
      then show ?thesis
        apply -
        apply (rule that)
        unfolding ‹a = _› by (rule step_a_impl)
    qed
    ultimately show ?thesis
      using step_impl_check_diag[OF True M', THEN check_diag_conv_M, THEN check_diag_empty_spec]
      by auto
  next
    case False
    with prems have
      "canonical (curry (conv_M M)) n"
    unfolding check_diag_def neutral by fast
    moreover from prems have
      "∀i≤n. conv_M M (i, i) ≤ 0" by fast
    ultimately show ?thesis using step_impl_complete'[OF step] by fast
  qed
qed
subsubsection ‹Bisimilarity›
lemma step_impl_equiv:
  assumes "A ⊢⇩I ⟨l,D⟩ ↝⇘n,a⇙ ⟨l',D'⟩"
    and     "wf_dbm D" "wf_dbm M"
    and "D ≃ M"
  shows "∃ M'. A ⊢⇩I ⟨l, M⟩ ↝⇘n,a⇙ ⟨l', M'⟩ ∧ D' ≃ M'"
proof -
  note prems_D = wf_dbm_D[OF assms(2)]
  from step_impl_sound'[OF assms(1) prems_D] diag_conv obtain M' where
    "conv_A A ⊢ ⟨l, curry (conv_M D)⟩ ↝⇘v,n,a⇙ ⟨l', M'⟩"
    "[curry (conv_M D')]⇘v,n⇙ = [M']⇘v,n⇙"
    unfolding check_diag_def neutral by auto
  from step_z_dbm_equiv'[OF this(1) assms(4)[unfolded dbm_equiv_def]] this(2)
  show ?thesis
    by (auto simp: dbm_equiv_def dest!: step_impl_complete''_improved[OF _ assms(3)])
qed
lemma norm_step_correct':
  assumes wf_dbm: "wf_dbm D" "wf_dbm M"
    and equiv:  "D ≃ M"
  shows
    "[curry (conv_M (FW' (norm_upd D (k' l') n) n))]⇘v,n⇙
     = [norm (FW (curry (conv_M M)) n) (λx. real (k l' x)) n]⇘v,n⇙"
  apply (rule norm_step_correct)
  using wf_dbm_D[OF wf_dbm(1)] wf_dbm_D[OF wf_dbm(2)] equiv
  unfolding dbm_equiv_def[symmetric] canonical_variant
  by auto
lemma step_impl_norm_equiv:
  assumes step:
    "A ⊢⇩I ⟨l, D⟩ ↝⇘n,τ⇙ ⟨l', D'⟩"
    "A ⊢⇩I ⟨l', D'⟩ ↝⇘n,↿a⇙ ⟨l'', D''⟩"
    "D''' = FW' (norm_upd D'' (k' l'') n) n"
    and wf_dbm: "wf_dbm D" "wf_dbm M"
    and equiv: "D ≃ M"
  shows "∃ M' M'' M'''. A ⊢⇩I ⟨l, M⟩ ↝⇘n,τ⇙ ⟨l', M'⟩ ∧ A ⊢⇩I ⟨l', M'⟩ ↝⇘n,↿a⇙ ⟨l'', M''⟩
       ∧ M''' = FW' (norm_upd M'' (k' l'') n) n ∧ D''' ≃ M'''"
proof -
  from step_impl_equiv[OF step(1) wf_dbm equiv] obtain M' where M':
    "A ⊢⇩I ⟨l, M⟩ ↝⇘n,τ⇙ ⟨l', M'⟩" "D' ≃ M'"
    by auto
  from wf_dbm step(1) M'(1) have wf_dbm':
    "wf_dbm D'" "wf_dbm M'"
    by (auto intro: step_impl_wf_dbm)
  from step_impl_equiv[OF step(2) this M'(2)] obtain M'' where M'':
    "A ⊢⇩I ⟨l', M'⟩ ↝⇘n,↿a⇙ ⟨l'', M''⟩" "D'' ≃ M''"
    by auto
  with wf_dbm' step(2) have wf_dbm'':
    "wf_dbm D''" "wf_dbm M''"
    by (auto intro: step_impl_wf_dbm)
  let ?M''' = "FW' (norm_upd M'' (k' l'') n) n"
  have "D''' ≃ ?M'''"
    unfolding step(3) dbm_equiv_def
    apply (subst norm_step_correct'[of M'' D''])
       prefer 4
       apply (subst norm_step_correct'[of D'' D''])
    using wf_dbm'' ‹D'' ≃ M''›
    by (auto intro: dbm_equiv_sym)
  with M'(1) M''(1) show ?thesis by auto
qed
definition
  "wf_state ≡ λ (l, M). wf_dbm M"
lemma E_equiv:
  "∃ b'. E b b' ∧ a' ∼ b'" if "E a a'" "wf_state a" "wf_state b" "a ∼ b"
  using that
  unfolding wf_state_def E_def
  apply safe
  by (drule step_impl_norm_equiv; force simp: state_equiv_def dest: state_equiv_D)
lemma E_wf_dbm[intro]:
  "wf_dbm D'" if "E (l, D) (l', D')" "wf_dbm D"
  using that unfolding wf_state_def E_def by (auto simp: norm_step_wf_dbm step_impl_wf_dbm)
lemma E_wf_state[intro]:
  "wf_state b" if "E a b" "wf_state a"
  using that unfolding wf_state_def by auto
lemma E_steps_wf_state[intro]:
  "wf_state b" if "E⇧*⇧* a b" "wf_state a"
  using that by (induction rule: rtranclp_induct) auto
lemma wf_dbm_init_dbm[intro, simp]:
  "wf_dbm init_dbm"
  apply (rule wf_dbm_I)
  using valid_init_dbm
  unfolding conv_M_init_dbm
  unfolding init_dbm_def neutral[symmetric]
  by simp+
lemma wf_state_init[intro, simp]:
  "wf_state a⇩0"
  unfolding wf_state_def a⇩0_def by simp
context
  fixes E⇩1 :: "'s × _ ⇒ 's × _ ⇒ bool" and P
  assumes E_E⇩1_step: "E a b ⟹ wf_state a ⟹ (∃ c. E⇩1 a c ∧ b ∼ c)"
  assumes E⇩1_E_step: "E⇩1 a b ⟹ wf_state a ⟹ (∃ c. E a c ∧ b ∼ c)"
  assumes E⇩1_equiv: "E⇩1 a b ⟹ a ∼ a' ⟹ P a ⟹ P a' ⟹ ∃ b'. E⇩1 a' b' ∧ b ∼ b'"
  assumes E⇩1_P[intro]: "P a ⟹ E⇩1 a b ⟹ P b"
  assumes wf_state_P[intro]: "wf_state a ⟹ P a"
begin
private lemma E⇩1_steps_P:
  "P b" if "E⇩1⇧*⇧* a b" "P a"
  using that by (induction rule: rtranclp_induct) auto
context
  notes [intro] = E⇩1_steps_P
begin
private lemma E_E⇩1_step':
  "(∃ b'. E⇩1 b b' ∧ a' ∼ b')" if "E a a'" "wf_state a" "wf_state b" "a ∼ b"
  using that E_equiv[OF that] by (blast dest: E_E⇩1_step)
private lemma E⇩1_E_step':
  "(∃ b'. E b b' ∧ a' ∼ b')" if "E⇩1 a a'" "wf_state a" "wf_state b" "a ∼ b"
  using that
  apply -
  apply (drule E⇩1_E_step, assumption)
  apply safe
  by (drule E_equiv; blast)
private lemma E_E⇩1_steps:
  "∃ b'. E⇩1⇧*⇧* a b' ∧ a' ∼ b'" if "E⇧*⇧* a a'" "wf_state a"
  using that
proof (induction rule: rtranclp_induct)
  case base
  then show ?case by blast
next
  case (step y z)
  then obtain b where b: "E⇩1⇧*⇧* a b" "y ∼ b" by auto
  from step obtain z' where "E⇩1 y z'" "z ∼ z'"
    by atomize_elim (blast intro: E_E⇩1_step)
  from step b ‹E⇩1 y z'› obtain b' where "E⇩1 b b'" "z' ∼ b'"
    by atomize_elim (blast intro: E⇩1_equiv)
  with b ‹z ∼ z'› show ?case
    by (blast intro: rtranclp.intros(2))
qed
private lemma E⇩1_E_steps:
  "∃ b'. E⇧*⇧* a b' ∧ a' ∼ b'" if "E⇩1⇧*⇧* a a'" "wf_state a"
  using that
proof (induction rule: rtranclp_induct)
  case base
  then show ?case by blast
next
  case (step y z)
  then obtain y' where y': "E⇧*⇧* a y'" "y ∼ y'"
    by auto
  with step obtain z' where "E⇩1 y' z'" "z ∼ z'"
    by atomize_elim (blast intro: E⇩1_equiv)
  with y' ‹wf_state _› obtain z'' where "E y' z''" "z' ∼ z''"
    by atomize_elim (blast intro: E⇩1_E_step)
  with y' ‹z ∼ z'› show ?case
    by (blast intro: rtranclp.intros(2))
qed
lemma E_E⇩1_steps_equiv:
  "(∃ M'. E⇧*⇧* a⇩0 (l', M') ∧ [curry (conv_M M')]⇘v,n⇙ ≠ {}) ⟷
   (∃ M'. E⇩1⇧*⇧* a⇩0 (l', M') ∧ [curry (conv_M M')]⇘v,n⇙ ≠ {})"
  by (auto 4 4 simp: state_equiv_def dbm_equiv_def dest: E_E⇩1_steps E⇩1_E_steps)
end 
end 
definition "E_step ≡
  λ (l, D) (l'', D'').
    ∃ l' M' a M''. conv_A A ⊢ ⟨l, curry (conv_M D)⟩ ↝⇘v,n,τ⇙ ⟨l', M'⟩
           ∧ step_z_norm' (conv_A A) l' M' (↿a) l'' M''
           ∧ [curry (conv_M D'')]⇘v,n⇙ = [M'']⇘v,n⇙"
lemma step_impl_sound_wf:
  assumes step: "A ⊢⇩I ⟨l,D⟩ ↝⇘n,a⇙ ⟨l',D'⟩"
    and     wf: "wf_dbm D"
  shows
    "∃ M'. step_z_norm' (conv_A A) l (curry (conv_M D)) a l' M'
    ∧ [curry (conv_M (FW' (norm_upd D' (k' l') n) n))]⇘v,n⇙ = [M']⇘v,n⇙"
  using step_impl_norm_sound[OF step wf_dbm_D[OF wf]] .
lemmas valid_dbm_convI = valid_dbm.intros[OF _ dbm_int_conv]
lemmas step_z_norm_valid_dbm'_spec =
  step_z_norm_valid_dbm'[OF global_clock_numbering' valid_abstraction']
lemma E_E⇩1_step_aux:
  "∃ l' M' a M''.
    conv_A A ⊢ ⟨l, curry (conv_M D)⟩ ↝⇘v,n,τ⇙ ⟨l', M'⟩ ∧
    step_z_norm' (conv_A A) l' M' (↿a) l'' M'' ∧ [curry (conv_M D'')]⇘v,n⇙ = [M'']⇘v,n⇙"
  if "E (l, D) (l'', D'')" "wf_dbm D"
  using that unfolding E_def
proof (safe, goal_cases)
  case prems: (1 D1 l' D2 a)
  from step_impl_sound'[OF prems(2)] prems(1) obtain M' where M':
    "conv_A A ⊢ ⟨l, curry (conv_M D)⟩ ↝⇘v,n,τ⇙ ⟨l', M'⟩" "[curry (conv_M D1)]⇘v,n⇙ = [M']⇘v,n⇙"
    by (blast dest: wf_dbm_D)
  from prems have "wf_dbm D1"
    by (blast intro: step_impl_wf_dbm)
  from step_impl_sound_wf[OF prems(3) this] obtain M'' where M'':
    "step_z_norm'(conv_A A) l' (curry (conv_M D1)) (↿a) l'' M''"
    "[curry (conv_M (FW' (norm_upd D2 (k' l'') n) n))]⇘v,n⇙ = [M'']⇘v,n⇙"
    by blast
  from ‹wf_dbm D1› ‹wf_dbm D› M'(1) have "valid_dbm (curry (conv_M D1))" "valid_dbm M'"
    by (blast intro: wf_dbm_D step_z_valid_dbm')+
  with step_z_norm_equiv'[OF M''(1) _ _ M'(2)] obtain M''' where
    "step_z_norm'(conv_A A) l' M' (↿a) l'' M'''" "[M'']⇘v,n⇙ = [M''']⇘v,n⇙"
    by blast
  with M'(1) M''(2) show ?case by auto
qed
lemma E_E⇩1_step: "∃ c. E_step a c ∧ b ∼ c" if "E a b" "wf_state a"
  apply (cases a)
  apply (cases b)
  using that
  apply simp
  unfolding wf_state_def
  apply (drule E_E⇩1_step_aux)
  unfolding E_step_def
  unfolding state_equiv_def dbm_equiv_def
  by blast+
lemma E⇩1_E_step: "∃ c. E a c ∧ b ∼ c" if "E_step a b" "wf_state a"
  using that
  unfolding wf_state_def
  unfolding E_step_def
proof (safe del: step_z_norm'_elims, goal_cases)
  case prems: (1 l D l'' D'' l' M' a M'')
  from step_impl_complete''_improved[OF prems(4,1)] obtain M1 where M1:
    "A ⊢⇩I ⟨l, D⟩ ↝⇘n,τ⇙ ⟨l', M1⟩" "[curry (conv_M M1)]⇘v,n⇙ = [M']⇘v,n⇙"
    by blast
  with prems have "valid_dbm M'" "wf_dbm M1" "valid_dbm (curry (conv_M M1))"
    by (blast intro: wf_dbm_D step_z_valid_dbm' step_impl_wf_dbm)+
  with step_z_norm_equiv'[OF prems(5) _ _ M1(2)[symmetric]] obtain M2 where M2:
    "step_z_norm' (conv_A A) l' (curry (conv_M M1)) (↿a) l'' M2" "[M'']⇘v,n⇙ = [M2]⇘v,n⇙"
    by blast
  from wf_dbm_D(1)[OF ‹wf_dbm M1›] have "canonical (curry (conv_M M1)) n ∨ check_diag n M1" .
  then show ?case
  proof standard
    assume "canonical (curry (conv_M M1)) n"
    with ‹wf_dbm M1› step_impl_norm_complete[OF M2(1)] obtain D3 where
      "A ⊢⇩I ⟨l', M1⟩ ↝⇘n,↿a⇙ ⟨l'', D3⟩"
      "[curry (conv_M (FW' (norm_upd D3 (k' l'') n) n))]⇘v,n⇙ = [M2]⇘v,n⇙"
      by (blast dest: wf_dbm_D)
    with M1(1) M2(2) prems(6) show ?case
      unfolding E_def state_equiv_def dbm_equiv_def by blast
  next
    assume "check_diag n M1"
    with M1(2) have "[M']⇘v,n⇙ = {}" by (auto dest: check_diag_conv_M simp: check_diag_empty_spec)
    with prems(5) ‹valid_dbm M'› have "[M'']⇘v,n⇙ = {}"
      by - (drule step_z_norm'_empty_preservation; assumption)
    from step_impl_norm_complete''[OF M2(1)] ‹wf_dbm M1› obtain D3 where D3:
      "A ⊢⇩I ⟨l', M1⟩ ↝⇘n,↿a⇙ ⟨l'', D3⟩"
      "[M2]⇘v,n⇙ ⊆ [curry (conv_M (FW' (norm_upd D3 (k' l'') n) n))]⇘v,n⇙"
      by (blast dest: wf_dbm_D)
    from step_impl_check_diag[OF ‹check_diag n M1› D3(1)] have "check_diag n D3" .
    then have "[curry (conv_M (FW' (norm_upd D3 (k' l'') n) n))]⇘v,n⇙ = {}"
      using check_diag_empty_spec norm_step_check_diag_preservation by blast
    with D3 ‹[M'']⇘v,n⇙ = {}› M1(1) prems(6) show ?case
      unfolding E_def state_equiv_def dbm_equiv_def by blast
  qed
qed
lemma E⇩1_equiv:
  defines "P ≡ valid_dbm o curry o conv_M o snd"
  assumes that: "E_step a b" "a ∼ a'" "P a" "P a'"
  shows "∃ b'. E_step a' b' ∧ b ∼ b'"
  using that
  unfolding E_step_def state_equiv_def dbm_equiv_def
  apply (safe del: step_z_norm'_elims)
  apply (frule step_z_dbm_equiv', assumption)
  apply (erule exE conjE)+
  unfolding P_def
  by (drule step_z_norm_equiv'; fastforce dest: step_z_valid_dbm')
lemma E⇩1_P:
  defines "P ≡ valid_dbm o curry o conv_M o snd"
  assumes that: "E_step a b" "P a"
  shows "P b"
  using that
  unfolding E_step_def P_def
  apply (safe del: step_z_norm'_elims)
  apply (drule step_z_valid_dbm', (simp; fail))
  apply (drule step_z_norm_valid_dbm'_spec, (simp; fail))
  using valid_dbm_convI by (fastforce dest: valid_dbm_V)
lemma E_steps_equiv:
  "(∃ M'. E⇧*⇧* a⇩0 (l', M') ∧ [curry (conv_M M')]⇘v,n⇙ ≠ {}) ⟷
   (∃ M'. E_step⇧*⇧* a⇩0 (l', M') ∧ [curry (conv_M M')]⇘v,n⇙ ≠ {})"
proof -
  define P :: "'s × _ ⇒ bool" where "P ≡ valid_dbm o curry o conv_M o snd"
  show ?thesis
    apply (rule E_E⇩1_steps_equiv[where P = P])
        apply (rule E_E⇩1_step; assumption)
       apply (rule E⇩1_E_step; assumption)
      apply (rule E⇩1_equiv; simp add: P_def)
     apply (drule E⇩1_P; simp add: P_def)
    unfolding P_def wf_state_def by (auto dest: wf_dbm_D)
qed
subsubsection ‹First Layer of Simulation›
lemma E_step_valid_dbm[intro]:
  assumes "valid_dbm (curry (conv_M D))" "E_step (l, D) (l', D')"
  shows "valid_dbm (curry (conv_M D'))"
  using assms unfolding E_step_def
  proof (safe del: step_z_norm'_elims, goal_cases)
    case prems: (1 l' M' a M'')
    then have "valid_dbm M''"
      by (elim step_z_norm_valid_dbm'_spec step_z_valid_dbm')
    with prems(4) show ?case
      by (blast elim!: valid_dbm.cases intro: valid_dbm_convI)
  qed
lemma E_steps_valid_dbm[intro]:
  assumes "E_step⇧*⇧* a⇩0 (l, D)"
  shows "valid_dbm (curry (conv_M D))"
  using assms
  apply (induction "(l, D)" arbitrary: l D rule: rtranclp_induct)
  subgoal
    using valid_init_dbm by (auto simp only: conv_M_init_dbm a⇩0_def)
  subgoal for x l D
    by (cases x) blast
  done
lemma E_steps_steps_z_norm':
  "∃ M'. steps_z_norm' (conv_A A) l⇩0 (curry init_dbm) l' M' ∧ [M']⇘v,n⇙ = [curry (conv_M D')]⇘v,n⇙"
  if "E_step⇧*⇧* a⇩0 (l', D')"
using that proof (induction "(l', D')" arbitrary: l' D' rule: rtranclp_induct)
  case base
  then show ?case by (auto simp: a⇩0_def; fail)
next
  case (step y)
  obtain l D where [simp]: "y = (l, D)"
    by force
  from step(3-)[OF this] obtain M' where M':
    "steps_z_norm' (conv_A A) l⇩0 (curry init_dbm) l M'" "[M']⇘v,n⇙ = [curry (conv_M D)]⇘v,n⇙"
    by atomize_elim
  from step(2) obtain Mi li a M'' where step':
    "conv_A A ⊢ ⟨l, curry (conv_M D)⟩ ↝⇘v,n,τ⇙ ⟨li, Mi⟩"
    "step_z_norm' (conv_A A) li Mi (↿a) l' M''"
    "[curry (conv_M D')]⇘v,n⇙ = [M'']⇘v,n⇙"
    unfolding E_step_def by auto
  from step_z_dbm_equiv'[OF this(1) M'(2)[symmetric]] obtain Mi' where Mi':
    "conv_A A ⊢ ⟨l, M'⟩ ↝⇘v,n,τ⇙ ⟨li, Mi'⟩" "[Mi]⇘v,n⇙ = [Mi']⇘v,n⇙"
    by atomize_elim
  with step'(1) M'(1) step(1) have "valid_dbm Mi" "valid_dbm Mi'"
    unfolding ‹y = _› by (blast intro: steps_z_norm'_valid_dbm_preservation step_z_valid_dbm')+
  from step_z_norm_equiv'[OF step'(2) this Mi'(2)] obtain M''' where
    "step_z_norm' (conv_A A) li Mi' (↿a) l' M'''" "[M'']⇘v,n⇙ = [M''']⇘v,n⇙"
    by atomize_elim
  with Mi'(1) M'(1) step'(3) show ?case
    unfolding step_z_norm''_def by (auto 4 5 elim: rtranclp.intros(2))
qed
lemma dbm_int_conv_M_equiv:
  "∃ D. [M]⇘v,n⇙ = [curry (conv_M D)]⇘v,n⇙" if "dbm_int M n"
proof -
  define D where "D ≡ λ (i, j). map_DBMEntry floor (M i j)"
  have "rel_DBMEntry ri (M i j) (D (i, j))" if "i ≤ n" "j ≤ n" for i j
  proof -
    from that ‹dbm_int M n› have "M i j ≠ ∞ ⟶ get_const (M i j) ∈ ℤ" by blast
    then show ?thesis
      unfolding D_def ri_def by (cases "M i j") (auto elim: Ints_cases)
  qed
  then have "RI n (uncurry M) D"
    unfolding eq_onp_def by auto
  from RI_zone_equiv[OF this] show ?thesis
    by auto
qed
definition "A' = conv_A A"
interpretation Bisimulation_Invariant
  "λ (l, Z) (l', Z'). ∃ a. step_z_norm'' (conv_A A) l Z a l' Z'"
  E_step
  "λ (l, M) (l', D). l' = l ∧ [M]⇘v,n⇙ = [curry (conv_M D)]⇘v,n⇙"
  "λ (l, M). valid_dbm M" "λ (l, D). valid_dbm (curry (conv_M D))"
  apply standard
  subgoal premises prems for a b a'
  proof -
    from prems(2) obtain l M D l' M' where [simp]: "a = (l, M)" "a' = (l, D)" "b = (l', M')"
      by force
    from prems[unfolded step_z_norm''_def, simplified, folded A'_def] obtain l1 M1 a where steps:
      "A' ⊢ ⟨l, M⟩ ↝⇘v,n,τ⇙ ⟨l1, M1⟩"
      "A' ⊢ ⟨l1, M1⟩ ↝⇘(λl. (k l ∘∘∘ Regions.v' {Suc 0..<Suc n} v) n (Suc n)),v,n,↿a⇙ ⟨l', M'⟩"
      by auto
    from step_z_dbm_equiv'[folded A'_def, OF this(1), of "curry (conv_M D)"] prems(2) obtain M2
      where M2: "A' ⊢ ⟨l, curry (conv_M D)⟩ ↝⇘v,n,τ⇙ ⟨l1, M2⟩" "[M1]⇘v,n⇙ = [M2]⇘v,n⇙"
      by auto
    from steps(2) obtain M3
      where M3:
        "A' ⊢ ⟨l1, M2⟩ ↝⇘(λl. (k l ∘∘∘ Regions.v' {Suc 0..<Suc n} v) n (Suc n)),v,n,↿a⇙ ⟨l', M3⟩"
        "[M']⇘v,n⇙ = [M3]⇘v,n⇙"
      using prems(3,4) steps(1) M2(1)
      by atomize_elim (auto
          intro!: step_z_norm_equiv'[folded A'_def, simplified, OF steps(2) _ _ M2(2)]
          dest: step_z_valid_dbm'[folded A'_def]
          )
    from prems(4) M2(1) M3(1) have "valid_dbm M3"
      by - (rule step_z_norm_valid_dbm'_spec[folded A'_def], fastforce,
          fastforce dest: step_z_valid_dbm'[folded A'_def]
          )
    then have "dbm_int M3 n"
      by - (erule valid_dbm_cases)
    from dbm_int_conv_M_equiv[OF this] obtain D' where "[M3]⇘v,n⇙ = [curry (conv_M D')]⇘v,n⇙"
      by auto
    with ‹[M']⇘v,n⇙ = _› show ?thesis
      unfolding E_step_def A'_def[symmetric] by (fastforce intro: M2(1) M3(1))
  qed
  subgoal for a a' b'
    unfolding step_z_norm''_def E_step_def A'_def[symmetric]
    apply clarsimp
    apply (frule step_z_dbm_equiv'[folded A'_def], rule sym, assumption)
    apply clarify
    by (frule step_z_norm_equiv'[folded A'_def, simplified];
        auto elim: step_z_valid_dbm'[folded A'_def, simplified]
        ) auto
  subgoal for a b
    by (blast intro: steps_z_norm'_valid_dbm_preservation)
  subgoal for a b
    by blast
  done
lemma steps_z_norm'_E_steps:
  "∃ D'. E_step⇧*⇧* a⇩0 (l', D') ∧ [M']⇘v,n⇙ = [curry (conv_M D')]⇘v,n⇙" if
  "steps_z_norm' (conv_A A) l⇩0 (curry init_dbm) l' M'"
  using bisim.A_B_reaches[OF that, of a⇩0] valid_init_dbm unfolding a⇩0_def equiv'_def by auto
lemma E_steps_steps_z_norm'_iff:
  "(∃ D'. E_step⇧*⇧* a⇩0 (l', D') ∧ [curry (conv_M D')]⇘v,n⇙ ≠ {})
  ⟷ (∃ M'. steps_z_norm' (conv_A A) l⇩0 (curry init_dbm) l' M' ∧ [M']⇘v,n⇙ ≠ {})"
  using steps_z_norm'_E_steps E_steps_steps_z_norm' by fast
subsubsection ‹Monotonicity›
lemma E_step_mono_reachable:
  assumes "E_step (l,D) (l',D')"
    and   "wf_dbm D" "wf_dbm M"
    and "[curry (conv_M D)]⇘v,n⇙ ⊆ [curry (conv_M M)]⇘v,n⇙"
  shows "∃ M'. E_step (l,M) (l',M') ∧ [curry (conv_M D')]⇘v,n⇙ ⊆ [curry (conv_M M')]⇘v,n⇙"
  using assms
  unfolding E_step_def
  apply (safe del: step_z_norm'_elims)
  apply (frule step_z_dbm_mono, assumption)
  apply (safe del: step_z_norm'_elims)
  apply (drule step_z_norm_mono')
     prefer 3
     apply assumption
    apply (blast intro: step_z_valid_dbm' wf_dbm_D)
   apply (blast intro: step_z_valid_dbm' wf_dbm_D)
  apply (safe del: step_z_norm'_elims)
  subgoal premises prems for l'' M' a M'' D'' M'''
  proof -
    from prems have "valid_dbm M'''"
      by (fast intro: step_z_valid_dbm' wf_dbm_D step_z_norm_valid_dbm'_spec)
    with dbm_int_conv_M_equiv[of M'''] obtain D''' where D''':
      "[M''']⇘v,n⇙ = [curry (conv_M D''')]⇘v,n⇙"
      by (blast elim!: valid_dbm.cases)
    with prems show ?thesis
      by fastforce
  qed
  done
lemma E_mono:
  assumes "E (l,D) (l',D')"
    and   "wf_dbm D" "wf_dbm M"
    and "[curry (conv_M D)]⇘v,n⇙ ⊆ [curry (conv_M M)]⇘v,n⇙"
  shows "∃ M'. E (l,M) (l',M') ∧ [curry (conv_M D')]⇘v,n⇙ ⊆ [curry (conv_M M')]⇘v,n⇙"
  using assms
  apply -
  apply (drule E_E⇩1_step)
   apply (simp add: wf_state_def; fail)
  apply safe
  apply (drule E_step_mono_reachable[where M = M], assumption+)
  by (force simp: wf_state_def state_equiv_def dbm_equiv_def dest: E⇩1_E_step)
lemma E_mono':
  assumes "E (l,D) (l',D')"
    and   "wf_dbm D" "wf_dbm M"
    and "dbm_subset n D M"
  shows "∃ M'. E (l,M) (l',M') ∧ dbm_subset n D' M'"
  using assms
  apply -
  apply (frule E_mono[where M = M], assumption+)
   apply (subst dbm_subset_correct'', assumption+)
   apply (rule dbm_subset_conv, assumption)
  apply safe
  apply (subst (asm) dbm_subset_correct'')
    apply (blast intro: dbm_subset_conv_rev)+
  done
lemma reachable_steps_z_norm':
  "(∃ D'. E⇧*⇧* a⇩0 (l', D') ∧ [curry (conv_M D')]⇘v,n⇙ ≠ {})
    ⟷ (∃ M'. steps_z_norm' (conv_A A) l⇩0 (curry init_dbm) l' M' ∧ [M']⇘v,n⇙ ≠ {})"
  unfolding E_steps_steps_z_norm'_iff E_steps_equiv ..
subsubsection ‹Instantiating the Reachability Problem›
theorem reachable_decides_emptiness:
  "(∃ D'. E⇧*⇧* a⇩0 (l', D') ∧ [curry (conv_M D')]⇘v,n⇙ ≠ {})
    ⟷ (∃ u ∈ [curry init_dbm]⇘v,n⇙. ∃ u'. conv_A A ⊢' ⟨l⇩0, u⟩ →* ⟨l', u'⟩)"
  by (subst reachable_steps_z_norm')
    (subst
      steps_z_norm_decides_emptiness
      [OF global_clock_numbering' valid_abstraction'[unfolded X_alt_def]
        finite_trans_of_finite_state_set[OF finite_trans'] valid_init_dbm
        ],
      rule HOL.refl
      )
lemma reachable_decides_emptiness':
  "(∃ D'. E⇧*⇧* a⇩0 (l', D') ∧ [curry (conv_M D')]⇘v,n⇙ ≠ {})
    ⟷ (∃ u u'. conv_A A ⊢' ⟨l⇩0, u⟩ →* ⟨l', u'⟩ ∧ (∀ c ∈ {1..n}. u c = 0))"
  using reachable_decides_emptiness init_dbm_semantics' init_dbm_semantics'' by blast
lemma reachable_empty_check_diag:
  assumes "E⇧*⇧* a⇩0 (l', D')" "[curry (conv_M D')]⇘v,n⇙ = {}"
  shows "check_diag n D'"
  using canonical_reachable[OF assms(1)] canonical_empty_check_diag assms(2) by simp
lemma check_diag_E_preservation:
  "check_diag n M'" if "check_diag n M" "E (l, M) (l', M')"
  using that unfolding E_def check_diag_def neutral[symmetric]
  by (fastforce
      simp: curry_def dest: step_impl_neg_diag_preservation
      intro: FW'_neg_diag_preservation norm_upd_neg_diag_preservation
      )
lemma cn_weak:
  "∀c. 0 < v c"
  using clock_numbering(1) by auto
end 
locale Reachability_Problem =
  TA_Start A l⇩0 n k +
  Reachability_Problem_Defs A l⇩0 n k F for A :: "('a, nat, int, 's) ta" and l⇩0 n k F
begin
theorem reachability_check:
  "(∃ D'. E⇧*⇧* a⇩0 (l', D') ∧ F_rel (l', D'))
    ⟷ (∃ u u'. conv_A A ⊢' ⟨l⇩0, u⟩ →* ⟨l', u'⟩ ∧ (∀ c ∈ {1..n}. u c = 0) ∧ F l')"
  using reachable_decides_emptiness'[of l'] check_diag_empty_spec reachable_empty_check_diag
  unfolding F_rel_def by auto
sublocale Standard_Search_Space: Search_Space E a⇩0 F_rel "subsumes n" "λ (l, M). check_diag n M"
  apply standard
  subgoal for a
    apply (rule prod.exhaust[of a])
    by (auto simp add: subsumes_simp_1 dbm_subset_refl)
  subgoal for a b c
    apply (rule prod.exhaust[of a], rule prod.exhaust[of b], rule prod.exhaust[of c])
    subgoal for l1 M1 l2 M2 l3 M3
      apply simp
      apply (cases "check_diag n M1")
       apply (simp add: subsumes_def; fail)
      apply (simp add: subsumes_def)
      by (meson check_diag_subset dbm_subset_def dbm_subset_trans)
    done
  subgoal for a b a'
    apply (rule prod.exhaust[of a], rule prod.exhaust[of b], rule prod.exhaust[of a'])
    apply safe
    by (drule E_mono';
        fastforce simp: E_def subsumes_def dbm_subset_def Graph_Start_Defs.reachable_def
        intro!: reachable_wf_dbm)
  subgoal
    unfolding F_rel_def subsumes_def by auto
  subgoal
    using check_diag_subset unfolding subsumes_def dbm_subset_def by auto
  subgoal
    using check_diag_E_preservation by auto
  unfolding F_rel_def subsumes_def
  unfolding check_diag_def pointwise_cmp_def
  by fastforce
sublocale Standard_Search_Space_finite_strict:
  Search_Space_finite_strict E a⇩0 F_rel "subsumes n" "λ (l, M). check_diag n M"
  apply standard
  using E_closure_finite unfolding Graph_Start_Defs.reachable_def by (auto; fail)
paragraph ‹Nice to have›
lemma V_I:
  assumes "∀ i ∈ {1..<Suc n}. M 0 i ≤ 0"
  shows "[M]⇘v,n⇙ ⊆ V"
  unfolding V_def DBM_zone_repr_def
proof (safe, goal_cases)
  case prems: (1 u i)
  then have "v i = i"
    using X_alt_def X_def triv_numbering by blast
  with prems have "v i > 0" "v i ≤ n" by auto
  with prems have "dbm_entry_val u None (Some i) (M 0 (v i))"
    unfolding DBM_val_bounded_def by auto
  moreover from assms ‹v i > 0› ‹v i ≤ n› have "M 0 (v i) ≤ 0" by auto
  ultimately
  show ?case
    apply (cases "M 0 (v i)")
    unfolding neutral less_eq dbm_le_def
    by (auto elim!: dbm_lt.cases simp: ‹v i = i›)
qed
lemma DBM_val_bounded_cong':
  "∀c∈{Suc 0..n}. u c = u' c ⟹ u ⊢⇘v,n⇙ M ⟹ u' ⊢⇘v,n⇙ M"
  unfolding DBM_val_bounded_def
proof (safe, goal_cases)
  case prems: (1 c)
  from prems have v: "v c = c" "v c > 0" by (auto simp: cn_weak intro: v_id)
  show ?case
  proof (cases "M 0 (v c)")
    case (Le d)
    with prems have "- u c ≤ d" by auto
    with Le v prems show ?thesis by auto
  next
    case (Lt d)
    with prems have "- u c < d" by auto
    with Lt v prems show ?thesis by auto
  qed auto
next
  case prems: (2 c)
  from prems have v: "v c = c" "v c > 0" by (auto simp: cn_weak intro: v_id)
  show ?case
  proof (cases "M (v c) 0")
    case (Le d)
    with prems have "u c ≤ d" by auto
    with Le v prems show ?thesis by auto
  next
    case (Lt d)
    with prems have "u c < d" by auto
    with Lt v prems show ?thesis by auto
  qed auto
next
  case prems: (3 c1 c2)
  from prems have v: "v c1 = c1" "v c1 > 0" "v c2 = c2" "v c2 > 0"
    by (auto simp: cn_weak intro: v_id)
  show ?case
  proof (cases "M (v c1) (v c2)")
    case (Le d)
    with prems have "u c1 - u c2 ≤ d" by (auto 4 3)
    with Le v prems show ?thesis by auto
  next
    case (Lt d)
    with prems have "u c1 - u c2 < d" by (auto 4 3)
    with Lt v prems show ?thesis by auto
  qed auto
qed
lemma DBM_val_bounded_cong:
  "∀c∈{Suc 0..n}. u c = d ⟹ ∀c∈{Suc 0..n}. u' c = d ⟹ u ⊢⇘v,n⇙ M ⟹ u' ⊢⇘v,n⇙ M"
  using DBM_val_bounded_cong' by force
end 
lemma abstr_upd_correct:
  assumes gcn: "∀c. 0 < v c ∧ (∀x y. v x ≤ n ∧ v y ≤ n ∧ v x = v y ⟶ x = y)"
      and surj: "∀ k ≤ n. k > 0 ⟶ (∃ c. v c = k)"
      and clocks: "∀c∈collect_clks (inv_of A l). v c = c ∧ 0 < c ∧ v c ≤ n"
  shows
  "[curry (abstr_upd (inv_of A l) D)]⇘v,n⇙ = [And (curry D) (abstr (inv_of A l) (λi j. ∞) v)]⇘v,n⇙"
  apply (subst abstr_upd_abstr')
   defer
   apply (subst abstr_abstr'[symmetric])
    defer
    apply (subst And_abstr[symmetric])
  using assms by fastforce+
locale Reachability_Problem_start =
  Reachability_Problem +
  assumes start_inv: "[(curry init_dbm :: real DBM)]⇘v,n⇙ ⊆ {u. u ⊢ conv_cc (inv_of A l⇩0)}"
begin
lemma start_inv':
  "[(curry init_dbm :: real DBM)]⇘v,n⇙ ⊆ {u. u ⊢ inv_of (conv_A A) l⇩0}"
  using start_inv unfolding conv_A_def
  by (smt case_prod_conv comp_apply inv_of_def prod.collapse snd_conv subset_Collect_conv)
lemma start_vals:
  "u ⊢ inv_of (conv_A A) l⇩0" if "∀ c ∈ {1..n}. u c = 0"
  using that start_inv' init_dbm_semantics'' by auto
lemma steps_steps'_equiv':
  "(∃ u u'. conv_A A ⊢' ⟨l⇩0, u⟩ →* ⟨l', u'⟩ ∧ (∀ c ∈ {1..n}. u c = 0))
  ⟷ (∃ u u'. conv_A A ⊢ ⟨l⇩0, u⟩ →* ⟨l', u'⟩ ∧ (∀ c ∈ {1..n}. u c = 0))"
  using steps_steps'_equiv[of _ ‹conv_A A› l⇩0] start_vals by fast
corollary reachability_check_start:
  "(∃ D'. E⇧*⇧* a⇩0 (l', D') ∧ F_rel (l', D'))
  ⟷ (∃ u u'. conv_A A ⊢ ⟨l⇩0, u⟩ →* ⟨l', u'⟩ ∧ (∀ c ∈ {1..n}. u c = 0) ∧ F l')"
  using reachability_check steps_steps'_equiv' by fast
end 
lemma check_diag_conv_M_rev:
  assumes "check_diag n (conv_M M)"
  shows "check_diag n M"
using assms unfolding check_diag_def by (auto intro!: conv_dbm_entry_mono_strict_rev)
context TA_Start_No_Ceiling
begin
lemma surj_numbering:
  "∀k≤n. 0 < k ⟶ (∃c. v c = k)"
  using global_clock_numbering[THEN conjunct2, THEN conjunct2] .
lemma collect_clks_numbering:
  "∀c∈collect_clks (inv_of A l⇩0). v c = c ∧ 0 < c ∧ v c ≤ n"
  by (metis collect_clks_inv_clk_set global_clock_numbering subsetCE v_id)
lemma collect_clks_numbering':
  "∀c∈collect_clks (inv_of (conv_A A) l⇩0). v c = c ∧ 0 < c ∧ v c ≤ n"
  by (metis (no_types, lifting) collect_clks_inv_clk_set global_clock_numbering' subsetCE triv_numbering'')
lemmas dbm_abstr_zone_eq' = dbm_abstr_zone_eq[OF global_clock_numbering[THEN conjunct1]]
end
context TA_Start
begin
lemmas abstr_upd_correct' =
  abstr_upd_correct[OF clock_numbering(1) surj_numbering collect_clks_numbering']
lemma FW'_zone_equiv:
  "[curry (conv_M (FW' M n))]⇘v,n⇙ = [curry (conv_M M)]⇘v,n⇙"
  by (metis FW'_FW FW'_conv_M FW_zone_equiv_spec)
lemma unbounded_dbm_semantics:
  "[curry (unbounded_dbm n)]⇘v,n⇙ = UNIV"
  unfolding unbounded_dbm_def
  unfolding DBM_zone_repr_def apply auto
  unfolding DBM_val_bounded_def
  apply auto
  
  using cn_weak gr_implies_not_zero apply blast
  using cn_weak gr_implies_not_zero apply blast
  
  by (metis dbm_entry_val.intros(5) diff_self order_mono_setup.refl v_id)
lemma And_unbounded_dbm:
  "[And (curry (unbounded_dbm n)) M]⇘v,n⇙ = [M]⇘v,n⇙"
  by (subst And_correct[symmetric]) (simp add: unbounded_dbm_semantics)
definition
  "start_inv_check ≡ dbm_subset n init_dbm (FW' (abstr_upd (inv_of A l⇩0) (unbounded_dbm n)) n)"
lemma conv_inv:
  "conv_cc (inv_of A l⇩0) = inv_of (conv_A A) l⇩0"
  unfolding inv_of_def conv_A_def by (simp split!: prod.split)
lemma start_inv_check:
  "start_inv_check ⟷ [(curry init_dbm :: real DBM)]⇘v,n⇙ ⊆ {u. u ⊢ conv_cc (inv_of A l⇩0)}"
proof -
  let ?M = "FW' (abstr_upd (inv_of A l⇩0) (unbounded_dbm n)) n"
  have canonical: "canonical (curry init_dbm) n ∨ check_diag n init_dbm"
    unfolding init_dbm_def by (auto simp: neutral[symmetric])
  have diag: "∀i≤n. curry (conv_M ?M) i i ≤ 0"
    apply (rule diag_conv)
    unfolding curry_def unbounded_dbm_def
    apply (rule FW'_diag_preservation)
    apply (rule abstr_upd_diag_preservation')
    using collect_clks_numbering unfolding neutral by auto
  have "∀i≤n. curry init_dbm i i ≤ 0" unfolding init_dbm_def neutral by auto
  from dbm_subset_correct'[OF canonical this diag] canonical have
    "dbm_subset n init_dbm (conv_M ?M)
    ⟷ [(curry init_dbm :: real DBM)]⇘v,n⇙ ⊆ [curry (conv_M ?M)]⇘v,n⇙"
    by auto
  moreover have "[curry (conv_M ?M)]⇘v,n⇙ = {u. u ⊢ inv_of (conv_A A) l⇩0}"
    apply (subst FW'_zone_equiv)
    apply (subst RI_zone_equiv[symmetric,
           where M = "abstr_upd (inv_of (conv_A A) l⇩0) (unbounded_dbm n)"])
     defer
     apply (subst abstr_upd_correct')
     apply (subst And_unbounded_dbm)
     apply (subst dbm_abstr_zone_eq')
    using collect_clks_numbering' apply force
     apply simp
    subgoal
    proof -
      have [transfer_rule]: "rel_DBMEntry ri 0 0" "rel_DBMEntry ri ∞ ∞"
        unfolding ri_def neutral by auto
      have [transfer_rule]: "eq_onp (λx. x < Suc n) n n"
        by (simp add: eq_onp_def)
      have [transfer_rule]:
        "rel_fun (eq_onp (λx. x < Suc n)) (rel_fun (eq_onp (λx. x < Suc n)) (=)) (<) (<)"
        unfolding rel_fun_def eq_onp_def by simp
      have [transfer_rule]: "(RI n) (unbounded_dbm n) (unbounded_dbm n)"
        unfolding unbounded_dbm_def by transfer_prover
      have [transfer_rule]: "RI_A n (conv_A A) A" by (rule RI_A_conv_A)
      note [transfer_rule] = inv_of_transfer[unfolded RI_I_def]
      show ?thesis by transfer_prover
    qed
    done
  ultimately show ?thesis
    unfolding start_inv_check_def conv_inv using dbm_subset_conv_rev dbm_subset_conv conv_M_init_dbm
    by metis
qed
lemma start_inv_check_correct:
  "start_inv_check ⟷ (∀ u. (∀ c ∈ {1..n}. u c = 0) ⟶ u ⊢ inv_of (conv_A A) l⇩0)"
  unfolding start_inv_check conv_inv using  init_dbm_semantics'' init_dbm_semantics' by fast
end
context Reachability_Problem
begin
lemma F_reachable_equiv':
  "(∃ l' u u'. conv_A A ⊢' ⟨l⇩0, u⟩ →* ⟨l', u'⟩ ∧ (∀ c ∈ {1..n}. u c = 0))
    ⟷ (∃ l' u u'. conv_A A ⊢ ⟨l⇩0, u⟩ →* ⟨l', u'⟩ ∧ (∀ c ∈ {1..n}. u c = 0))"
  if start_inv_check
proof -
  interpret start: Reachability_Problem_start A l⇩0 n k
    apply standard
    using that unfolding start_inv_check .
  from start.steps_steps'_equiv' show ?thesis by fast
qed
lemma F_reachable_equiv:
  "(∃ l' u u'. conv_A A ⊢' ⟨l⇩0, u⟩ →* ⟨l', u'⟩ ∧ (∀ c ∈ {1..n}. u c = 0) ∧ F l')
    ⟷ (∃ l' u u'. conv_A A ⊢ ⟨l⇩0, u⟩ →* ⟨l', u'⟩ ∧ (∀ c ∈ {1..n}. u c = 0) ∧ F l')"
  if start_inv_check
proof -
  interpret start: Reachability_Problem_start A l⇩0 n k
    apply standard
    using that unfolding start_inv_check .
  from start.steps_steps'_equiv' show ?thesis by fast
qed
end
context TA_Start_No_Ceiling
begin
  context
  begin
  private abbreviation "k ≡ default_ceiling A"
  lemma k_bound:
    assumes "i > n"
    shows "k i = 0"
  proof -
    from ‹i > n› have "i ∉ {1..n}" by auto
    then have
      "i ∉ clk_set A"
      using clocks_n by fast
    then have "{m. (i, m) ∈ Timed_Automata.clkp_set A} = {}" by auto
    then show ?thesis unfolding default_ceiling_def by auto
  qed
  lemma k_0:
   "k 0 = 0"
    using clock_range unfolding default_ceiling_def by auto
  lemma valid_abstraction:
     "Timed_Automata.valid_abstraction A X k"
    by (rule standard_abstraction_int;
        force intro!: consts_nats clk_set_X[unfolded X_def] clk_set_X finite_X)
  
  lemma k_ge:
    "∀(x,m) ∈ Timed_Automata.clkp_set A. m ≤ k x"
    using valid_abstraction by (auto elim!: Timed_Automata.valid_abstraction.cases)
  end
end
lemma mem_nth:
  "x ∈ set xs ⟹ ∃ i < length xs. xs ! i = x"
by (metis index_less_size_conv nth_index)
lemma collect_clock_pairs_empty[simp]:
  "collect_clock_pairs [] = {}"
unfolding collect_clock_pairs_def by auto
subsubsection ‹Pre-compiled automata with states and clocks as natural numbers›
locale Reachability_Problem_precompiled_defs =
  fixes n :: nat 
    and m :: nat 
    and k :: "nat list list"
    
    and inv :: "(nat, int) cconstraint list" 
    and trans :: "((nat, int) cconstraint * nat list * nat) list list"
        
    and final :: "nat list" 
begin
  definition "clkp_set' l ≡
    collect_clock_pairs (inv ! l) ∪ ⋃ ((λ (g, _). collect_clock_pairs g) ` set (trans ! l))"
  definition clk_set'_def: "clk_set' =
    (⋃ ((λ l. fst ` clkp_set' l) ` {0..<n}) ∪ ⋃ ((λ (g, r, _). set r) ` set (concat trans)))"
  text ‹Definition of the corresponding automaton›
  definition "label a ≡ λ (g, r, l'). (g, a, r, l')"
  definition "I l ≡ if l < n then inv ! l else []"
  definition "T ≡ {(l, label i (trans ! l ! i)) | l i. l < n ∧ i < length (trans ! l)}"
  definition "A ≡ (T, I)"
  definition "F ≡ λ x. x ∈ set final"
end
locale Reachability_Problem_precompiled = Reachability_Problem_precompiled_defs +
  assumes inv_length: "length inv = n"
    and trans_length: "length trans = n" 
    and state_set: "∀ xs ∈ set trans. ∀ (_, _, l) ∈ set xs. l < n"
    and k_length: "length k = n" "∀ l ∈ set k. length l = m + 1"
    
  assumes k_ceiling:
    "∀ l < n. ∀ (c, d) ∈ clkp_set' l. k ! l ! c ≥ nat d" "∀ l < n. ∀ c ∈ {1..m}. k ! l ! c ≥ 0"
    "∀ l < n. k ! l ! 0 = 0"
    "∀ l < n. ∀ (_, r, l') ∈ set (trans ! l). ∀ c ≤ m. c ∉ set r
        ⟶ k ! l ! c ≥ k ! l' ! c"
  assumes consts_nats: "∀ l < n. snd ` clkp_set' l ⊆ ℕ"
  assumes clock_set: "clk_set' = {1..m}"
    and m_gt_0: "m > 0"
    and n_gt_0: "n > 0" and start_has_trans: "trans ! 0 ≠ []" 
begin
lemma consts_nats':
  "∀ cc ∈ set inv. ∀ (c, d) ∈ collect_clock_pairs cc. d ∈ ℕ"
  "∀ xs ∈ set trans. ∀ (g, _) ∈ set xs. ∀ (c, d) ∈ collect_clock_pairs g. d ∈ ℕ"
  using consts_nats unfolding clkp_set'_def
   apply safe
  using inv_length apply (auto dest!: mem_nth; fail)
  using trans_length by - (drule mem_nth, auto)
lemma clkp_set_simp_1:
  "collect_clock_pairs (inv ! l) = collect_clki (inv_of A) l" if "l < n"
  unfolding A_def inv_of_def collect_clki_def I_def[abs_def] using inv_length that by auto
lemma clkp_set_simp_1':
  "⋃ (collect_clock_pairs ` set inv) = Timed_Automata.collect_clki (inv_of A)"
  unfolding A_def inv_of_def Timed_Automata.collect_clki_def I_def[abs_def] using inv_length
  by (auto simp add: collect_clks_id image_Union dest: nth_mem dest!: mem_nth)
lemma clk_set_simp_2:
  "⋃ ((λ (g, r, _). set r) ` set (concat trans)) = collect_clkvt (trans_of A)"
  unfolding A_def trans_of_def collect_clkvt_def T_def[abs_def] label_def using trans_length
  apply (clarsimp simp add: image_Union dest: nth_mem)
  apply safe
   apply ((drule mem_nth)+, force)
  apply (drule nth_mem, simp)
  subgoal for _ a _ _ _ _ i
    apply (rule bexI[where x = "trans ! a"])
    apply (rule bexI[where x = "trans ! a ! i"])
    by auto
  done
lemma clkp_set_simp_3:
  "⋃ ((λ (g, _). collect_clock_pairs g) ` set (trans ! l))
      = collect_clkt (trans_of A) l" if "l < n"
  unfolding A_def trans_of_def collect_clkt_def T_def[abs_def] label_def
  using trans_length that
  apply (auto simp add: collect_clks_id image_Union dest: nth_mem)
  by (force dest!: mem_nth)
lemma clkp_set_simp_3':
  "⋃ ((λ (g, _). Timed_Automata.collect_clock_pairs g) ` set (concat trans))
      = Timed_Automata.collect_clkt (trans_of A)"
  unfolding A_def trans_of_def Timed_Automata.collect_clkt_def T_def[abs_def] label_def
  using trans_length
  apply (auto simp add: collect_clks_id image_Union dest: nth_mem)
   apply (auto dest!: mem_nth)[]
   apply force
  apply (auto dest!: nth_mem)
  apply (rule_tac x = "trans ! aa" in bexI)
   apply (rule_tac x = "trans ! aa ! i" in bexI)
  by auto
lemma clkp_set'_eq:
  "clkp_set A l = clkp_set' l" if "l < n"
  using that
  by (fastforce simp: clkp_set'_def clkp_set_def image_Un image_Union
      clkp_set_simp_1[symmetric] clkp_set_simp_3[symmetric]
      )
lemma clkp_set_out_of_bounds:
  "clkp_set A l = {}" if "l ≥ n" for l
  using that
  unfolding clkp_set_def collect_clki_def collect_clkt_def
  unfolding inv_of_def A_def I_def T_def trans_of_def
  by simp
lemma clkp_setD:
  "(x, d) ∈ clkp_set' l" if "(x, d) ∈ Closure.clkp_set A l"
  using that by (cases "l < n") (auto simp: clkp_set'_eq clkp_set_out_of_bounds)
lemma clkp_set_boundD:
  "l < n" if "pair ∈ clkp_set A l" for pair
  using that by - (rule ccontr, auto simp: clkp_set_out_of_bounds)
lemma clkp_set'_eq':
  "Timed_Automata.clkp_set A = ⋃ (clkp_set' ` {0..<n})"
proof -
  have "⋃ (clkp_set A ` UNIV) = ⋃ (clkp_set A ` {0..<n})"
    apply safe
    subgoal for _ _ x
      by (cases "x < n") (auto simp: clkp_set_out_of_bounds)
    by auto
  then show ?thesis by (simp add: TA_clkp_set_unfold clkp_set'_eq)
qed
lemma clk_set'_eq[simp]:
  "clk_set A = clk_set'"
  unfolding clkp_set'_eq' clk_set'_def clk_set_simp_2 by auto
lemma Collect_fold_pair:
  "{f a b | a b. P a b} = (λ (a, b). f a b) ` {(a, b). P a b}"
  by auto
lemma finite_T[intro, simp]:
  "finite (trans_of A)"
proof -
  have
    "{(l, i). l < n ∧ i < length (trans ! l)}
      = ⋃ ((λ l. {(l, i) | i. i < length (trans ! l)}) ` {l. l < n})"
    by auto
  then show ?thesis unfolding trans_of_def A_def T_def by (auto simp: Collect_fold_pair)
qed
lemma has_clock[intro]:
  "clk_set A ≠ {}"
  using clock_set m_gt_0 by simp
lemma clk_set:
  "clk_set A = {1..m}"
  using clock_set m_gt_0 by auto
lemma clk_set_eq:
  "clk_set A = {1..card (clk_set A)}"
  using clk_set by auto
lemma
  "∀c∈clk_set A. c ≤ card (clk_set A) ∧ c ≠ 0"
  using clock_set by auto
lemma clkp_set_consts_nat:
  "∀(_, d)∈Timed_Automata.clkp_set A. d ∈ ℕ"
  unfolding
    Timed_Automata.clkp_set_def ta_collect_clki_alt_def ta_collect_clkt_alt_def
    A_def trans_of_def inv_of_def
    T_def I_def[abs_def] label_def
  using consts_nats' inv_length trans_length by (force dest: nth_mem)
lemma finite_range_inv_of_A[intro, simp]:
  "finite (range (inv_of A))"
  unfolding inv_of_def A_def I_def[abs_def] by (auto intro: finite_subset[where B = "{[]}"])
lemma transD:
  "(g, r, l') ∈ set (trans ! l) ∧ l < n" if "A ⊢ l ⟶⇗g,a,r⇖ l'"
  using that
  unfolding trans_of_def A_def T_def label_def
  by (auto dest!: nth_mem; simp split: prod.split_asm)
lemma clkp_set_clk_bound:
  "a ≤ m" if "(a, b) ∈ clkp_set' l" "l < n"
  using that clock_set clk_set'_def
  by fastforce
sublocale TA_Start_No_Ceiling A 0 m
  using has_clock clkp_set_consts_nat clk_set m_gt_0 by - (standard, auto)
sublocale Reachability_Problem A 0 m "λ l i. if l < n ∧ i ≤ m then k ! l ! i else 0" "PR_CONST F"
  apply standard
  subgoal
    apply safe
    apply (frule clkp_set_boundD)
    unfolding clkp_set'_eq
    using k_ceiling
    by (auto 4 10 dest: clkp_set_clk_bound)
  subgoal
    using k_ceiling(4) by (fastforce dest!: transD)
  subgoal
    using k_ceiling(2,3) by simp
  subgoal
    using k_ceiling(3) by simp
  done
end 
end