diff --git a/thys/Progress_Tracking/Auxiliary.thy b/thys/Progress_Tracking/Auxiliary.thy --- a/thys/Progress_Tracking/Auxiliary.thy +++ b/thys/Progress_Tracking/Auxiliary.thy @@ -1,406 +1,406 @@ section \Auxiliary Lemmas\ (*<*) theory Auxiliary imports "HOL-Library.Multiset" "Nested_Multisets_Ordinals.Signed_Multiset" "HOL-Library.Linear_Temporal_Logic_on_Streams" begin (*>*) subsection\General\ lemma sum_list_hd_tl: fixes xs :: "(_ :: group_add) list" shows "xs \ [] \ sum_list (tl xs) = (- hd xs) + sum_list xs" by (cases xs) simp_all lemma finite_distinct_bounded: "finite A \ finite {xs. distinct xs \ set xs \ A}" apply (rule finite_subset[of _ "\n \ {0 .. card A}. {xs. length xs = n \ distinct xs \ set xs \ A}"]) subgoal by clarsimp (metis card_mono distinct_card) subgoal by auto done subsection\Sums\ lemma Sum_eq_pick_changed_elem: assumes "finite M" and "m \ M" "f m = g m + \" and "\n. n \ m \ n \ M \ f n = g n" shows "(\x\M. f x) = (\x\M. g x) + \" using assms proof (induct M arbitrary: m rule: finite_induct) case empty then show ?case by simp next case (insert x F) then show ?case proof (cases "x=m") case True with insert have "sum f F = sum g F" by (intro sum.cong[OF refl]) force with insert True show ?thesis by (auto simp: add.commute add.left_commute) next case False with insert show ?thesis by (auto simp: add.assoc) qed qed lemma sum_pos_ex_elem_pos: "(0::int) < (\m\M. f m) \ \m\M. 0 < f m" by (meson not_le sum_nonpos) lemma sum_if_distrib_add: "finite A \ b \ A \ (\a\A. if a=b then X b + Y a else X a) = (\a\A. X a) + Y b" by (simp add: Sum_eq_pick_changed_elem) subsection\Partial Orders\ lemma (in order) order_finite_set_exists_foundation: fixes t :: 'a assumes "finite M" and "t \ M" shows "\s\M. s \ t \ (\u\M. \ u < s)" using assms by (simp add: dual_order.strict_iff_order finite_has_minimal2) lemma order_finite_set_obtain_foundation: fixes t :: "_ :: order" assumes "finite M" and "t \ M" obtains s where "s \ M" "s \ t" "\u\M. \ u < s" using assms order_finite_set_exists_foundation by blast subsection\Multisets\ lemma finite_nonzero_count: "finite {t. count M t > 0}" - using count unfolding multiset_def by auto + using count by auto lemma finite_count[simp]: "finite {t. count M t > i}" by (rule finite_subset[OF _ finite_nonzero_count[of M]]) (auto simp only: set_mset_def) subsection\Signed Multisets\ lemma zcount_zmset_of_nonneg[simp]: "0 \ zcount (zmset_of M) t" by simp lemma finite_zcount_pos[simp]: "finite {t. zcount M t > 0}" apply transfer subgoal for M apply (rule finite_subset[OF _ finite_Un[THEN iffD2, OF conjI[OF finite_nonzero_count finite_nonzero_count]], of _ "fst M" "snd M"]) apply (auto simp only: set_mset_def fst_conv snd_conv split: prod.splits) done done lemma finite_zcount_neg[simp]: "finite {t. zcount M t < 0}" apply transfer subgoal for M apply (rule finite_subset[OF _ finite_Un[THEN iffD2, OF conjI[OF finite_nonzero_count finite_nonzero_count]], of _ "fst M" "snd M"]) apply (auto simp only: set_mset_def fst_conv snd_conv split: prod.splits) done done lemma pos_zcount_in_zmset: "0 < zcount M x \ x \#\<^sub>z M" by (simp add: zcount_inI) lemma zmset_elem_nonneg: "x \#\<^sub>z M \ (\x. x \#\<^sub>z M \ 0 \ zcount M x) \ 0 < zcount M x" by (simp add: order.order_iff_strict zcount_eq_zero_iff) lemma zero_le_sum_single: "0 \ zcount (\x\M. {#f x#}\<^sub>z) t" by (induct M rule: infinite_finite_induct) auto lemma mem_zmset_of[simp]: "x \#\<^sub>z zmset_of M \ x \# M" by (simp add: set_zmset_def) lemma mset_neg_minus: "mset_neg (abs_zmultiset (Mp,Mn)) = Mn-Mp" by (simp add: mset_neg.abs_eq) lemma mset_pos_minus: "mset_pos (abs_zmultiset (Mp,Mn)) = Mp-Mn" by (simp add: mset_pos.abs_eq) lemma mset_neg_sum_set: "(\m. m \ M \ mset_neg (f m) = {#}) \ mset_neg (\m\M. f m) = {#}" by (induct M rule: infinite_finite_induct) auto lemma mset_neg_empty_iff: "mset_neg M = {#} \ (\t. 0 \ zcount M t)" apply rule subgoal by (metis add.commute add.right_neutral mset_pos_as_neg zcount_zmset_of_nonneg zmset_of_empty) subgoal apply (induct rule: zmultiset.abs_induct) subgoal for y apply (induct y) apply (subst mset_neg_minus) apply transfer apply (simp add: Diff_eq_empty_iff_mset mset_subset_eqI) done done done lemma mset_neg_zcount_nonneg: "mset_neg M = {#} \ 0 \ zcount M t" by (subst (asm) mset_neg_empty_iff) simp lemma in_zmset_conv_pos_neg_disj: "x \#\<^sub>z M \ x \# mset_pos M \ x \# mset_neg M" by (metis count_mset_pos in_diff_zcount mem_zmset_of mset_pos_neg_partition nat_code(2) not_in_iff zcount_ne_zero_iff) lemma in_zmset_notin_mset_pos[simp]: "x \#\<^sub>z M \ x \# mset_pos M \ x \# mset_neg M" by (auto simp: in_zmset_conv_pos_neg_disj) lemma in_zmset_notin_mset_neg[simp]: "x \#\<^sub>z M \ x \# mset_neg M \ x \# mset_pos M" by (auto simp: in_zmset_conv_pos_neg_disj) lemma in_mset_pos_in_zmset: "x \# mset_pos M \ x \#\<^sub>z M" by (auto intro: iffD2[OF in_zmset_conv_pos_neg_disj]) lemma in_mset_neg_in_zmset: "x \# mset_neg M \ x \#\<^sub>z M" by (auto intro: iffD2[OF in_zmset_conv_pos_neg_disj]) lemma set_zmset_eq_set_mset_union: "set_zmset M = set_mset (mset_pos M) \ set_mset (mset_neg M)" by (auto dest: in_mset_pos_in_zmset in_mset_neg_in_zmset) lemma member_mset_pos_iff_zcount: "x \# mset_pos M \ 0 < zcount M x" using not_in_iff pos_zcount_in_zmset by force lemma member_mset_neg_iff_zcount: "x \# mset_neg M \ zcount M x < 0" by (metis member_mset_pos_iff_zcount mset_pos_uminus neg_le_0_iff_le not_le zcount_uminus) lemma mset_pos_mset_neg_disjoint[simp]: "set_mset (mset_pos \) \ set_mset (mset_neg \) = {}" by (auto simp: member_mset_pos_iff_zcount member_mset_neg_iff_zcount) lemma zcount_sum: "zcount (\M\MM. f M) t = (\M\MM. zcount (f M) t)" by (induct MM rule: infinite_finite_induct) auto lemma zcount_filter_invariant: "zcount {# t'\#\<^sub>zM. t'=t #} t = zcount M t" by auto lemma in_filter_zmset_in_zmset[simp]: "x \#\<^sub>z filter_zmset P M \ x \#\<^sub>z M" by (metis count_filter_zmset zcount_ne_zero_iff) lemma pos_filter_zmset_pos_zmset[simp]: "0 < zcount (filter_zmset P M) x \ 0 < zcount M x" by (metis (full_types) count_filter_zmset less_irrefl) lemma neg_filter_zmset_neg_zmset[simp]: "0 > zcount (filter_zmset P M) x \ 0 > zcount M x" by (metis (full_types) count_filter_zmset less_irrefl) lift_definition update_zmultiset :: "'t zmultiset \ 't \ int \ 't zmultiset" is "\(A,B) T D.(if D>0 then (A + replicate_mset (nat D) T, B) else (A,B + replicate_mset (nat (-D)) T))" by (auto simp: equiv_zmset_def if_split) lemma zcount_update_zmultiset: "zcount (update_zmultiset M t n) t' = zcount M t' + (if t = t' then n else 0)" by transfer auto lemma (in order) order_zmset_exists_foundation: fixes t :: 'a assumes "0 < zcount M t" shows "\s. s \ t \ 0 < zcount M s \ (\u. 0 < zcount M u \ \ u < s)" using assms proof - let ?M = "{t. 0 < zcount M t}" from assms have "t \ ?M" by simp then have "\s\?M. s \ t \ (\u\?M. \ u < s)" by - (drule order_finite_set_exists_foundation[rotated 1], auto) then show ?thesis by auto qed lemma (in order) order_zmset_exists_foundation': fixes t :: 'a assumes "0 < zcount M t" shows "\s. s \ t \ 0 < zcount M s \ (\u 0)" using assms order_zmset_exists_foundation by (meson le_less_linear) lemma (in order) order_zmset_exists_foundation_neg: fixes t :: 'a assumes "zcount M t < 0" shows "\s. s \ t \ zcount M s < 0 \ (\u. zcount M u < 0 \ \ u < s)" using assms proof - let ?M = "{t. zcount M t < 0}" from assms have "t \ ?M" by simp then have "\s\?M. s \ t \ (\u\?M. \ u < s)" by - (drule order_finite_set_exists_foundation[rotated 1], auto) then show ?thesis by auto qed lemma (in order) order_zmset_exists_foundation_neg': fixes t :: 'a assumes "zcount M t < 0" shows "\s. s \ t \ zcount M s < 0 \ (\u zcount M u)" using assms order_zmset_exists_foundation_neg by (meson le_less_linear) lemma (in order) elem_order_zmset_exists_foundation: fixes x :: 'a assumes "x \#\<^sub>z M" shows "\s\#\<^sub>zM. s \ x \ (\u\#\<^sub>zM. \ u < s)" by (rule order_finite_set_exists_foundation[OF finite_set_zmset, OF assms(1)]) subsubsection\Image of a Signed Multiset\ lift_definition image_zmset :: "('a \ 'b) \ 'a zmultiset \ 'b zmultiset" is "\f (M, N). (image_mset f M, image_mset f N)" by (auto simp: equiv_zmset_def simp flip: image_mset_union) syntax (ASCII) "_comprehension_zmset" :: "'a \ 'b \ 'b zmultiset \ 'a zmultiset" ("({#_/. _ :#z _#})") syntax "_comprehension_zmset" :: "'a \ 'b \ 'b zmultiset \ 'a zmultiset" ("({#_/. _ \#\<^sub>z _#})") translations "{#e. x \#\<^sub>z M#}" \ "CONST image_zmset (\x. e) M" lemma image_zmset_empty[simp]: "image_zmset f {#}\<^sub>z = {#}\<^sub>z" by transfer (auto simp: equiv_zmset_def) lemma image_zmset_single[simp]: "image_zmset f {#x#}\<^sub>z = {#f x#}\<^sub>z" by transfer (simp add: equiv_zmset_def) lemma image_zmset_union[simp]: "image_zmset f (M + N) = image_zmset f M + image_zmset f N" by transfer (auto simp: equiv_zmset_def) lemma image_zmset_Diff[simp]: "image_zmset f (A - B) = image_zmset f A - image_zmset f B" proof - have "image_zmset f (A - B + B) = image_zmset f (A - B) + image_zmset f B" using image_zmset_union by blast then show ?thesis by simp qed lemma mset_neg_image_zmset: "mset_neg M = {#} \ mset_neg (image_zmset f M) = {#}" unfolding multiset_eq_iff count_empty by transfer (auto simp add: image_mset_subseteq_mono mset_subset_eqI mset_subset_eq_count) lemma nonneg_zcount_image_zmset[simp]: "(\t. 0 \ zcount M t) \ 0 \ zcount (image_zmset f M) t" by (meson mset_neg_empty_iff mset_neg_image_zmset) lemma image_zmset_add_zmset[simp]: "image_zmset f (add_zmset t M) = add_zmset (f t) (image_zmset f M)" by transfer (auto simp: equiv_zmset_def) lemma pos_zcount_image_zmset[simp]: "(\t. 0 \ zcount M t) \ 0 < zcount M t \ 0 < zcount (image_zmset f M) (f t)" apply transfer subgoal for M t f apply (induct M) subgoal for Mp Mn apply simp apply (metis count_diff count_image_mset_ge_count image_mset_Diff less_le_trans subseteq_mset_def zero_less_diff) done done done lemma set_zmset_transfer[transfer_rule]: "(rel_fun (pcr_zmultiset (=)) (rel_set (=))) (\(Mp, Mn). set_mset Mp \ set_mset Mn - {x. count Mp x = count Mn x}) set_zmset" by (auto simp: rel_fun_def pcr_zmultiset_def cr_zmultiset_def rel_set_eq multiset.rel_eq set_zmset_def zcount.abs_eq count_eq_zero_iff[symmetric] simp del: zcount_ne_zero_iff) lemma zcount_image_zmset: "zcount (image_zmset f M) x = (\y \ f -` {x} \ set_zmset M. zcount M y)" apply (transfer fixing: f x) subgoal for M apply (cases M; clarify) subgoal for Mp Mn unfolding count_image_mset int_sum proof - have "(\x\f -` {x} \ set_mset Mp. int (count Mp x)) = (\x\f -` {x} \ (set_mset Mp \ set_mset Mn). int (count Mp x))" (is "?S1 = _") by (subst sum.same_carrier[where C="f -` {x} \ (set_mset Mp \ set_mset Mn)"]) (auto simp: count_eq_zero_iff) moreover have "(\x\f -` {x} \ set_mset Mn. int (count Mn x)) = (\x\f -` {x} \ (set_mset Mp \ set_mset Mn). int (count Mn x))"(is "?S2 = _") by (subst sum.same_carrier[where C="f -` {x} \ (set_mset Mp \ set_mset Mn)"]) (auto simp: count_eq_zero_iff) moreover have "(\x\f -` {x} \ (set_mset Mp \ set_mset Mn - {x. count Mp x = count Mn x}). int (count Mp x) - int (count Mn x)) = (\x\f -` {x} \ (set_mset Mp \ set_mset Mn). int (count Mp x) - int (count Mn x))" (is "?S = _") by (subst sum.same_carrier[where C="f -` {x} \ (set_mset Mp \ set_mset Mn)"]) auto ultimately show "?S1 - ?S2 = ?S" by (auto simp: sum_subtractf) qed done done lemma zmset_empty_image_zmset_empty: "(\t. zcount M t = 0) \ zcount (image_zmset f M) t = 0" by (auto simp: zcount_image_zmset) lemma in_image_zmset_in_zmset: "t \#\<^sub>z image_zmset f M \ \t. t \#\<^sub>z M" by (rule ccontr) simp lemma zcount_image_zmset_zero: "(\m. m \#\<^sub>z M \ f m \ x) \ x \#\<^sub>z image_zmset f M" unfolding set_zmset_def by (simp add: zcount_image_zmset) (metis Int_emptyI sum.empty vimage_singleton_eq) lemma image_zmset_pre: "t \#\<^sub>z image_zmset f M \ \m. m \#\<^sub>z M \ f m = t" proof (rule ccontr) assume t: "t \#\<^sub>z image_zmset f M" assume "\m. m \#\<^sub>z M \ f m = t" then have "m \#\<^sub>z M \ \ f m = t" for m by blast then have "zcount (image_zmset f M) t = 0" by (meson t zcount_image_zmset_zero) with t show False by (meson zcount_ne_zero_iff) qed lemma pos_image_zmset_obtain_pre: "(\t. 0 \ zcount M t) \ 0 < zcount (image_zmset f M) t \ \m. 0 < zcount M m \ f m = t" proof - assume nonneg: "0 \ zcount M t" for t assume "0 < zcount (image_zmset f M) t" then have "t \#\<^sub>z image_zmset f M" by (simp add: pos_zcount_in_zmset) then obtain x where x: "x \#\<^sub>z M" "f x = t" by (auto dest: image_zmset_pre) with nonneg have "0 < zcount M x" by (meson zmset_elem_nonneg) with x show ?thesis by auto qed subsection\Streams\ definition relates :: "('a \ 'a \ bool) \ 'a stream \ bool" where "relates \ s = \ (shd s) (shd (stl s))" lemma relatesD[dest]: "relates P s \ P (shd s) (shd (stl s))" unfolding relates_def by simp lemma alw_relatesD[dest]: "alw (relates P) s \ P (shd s) (shd (stl s))" by auto lemma relatesI[intro]: "P (shd s) (shd (stl s)) \ relates P s" by (auto simp: relates_def) lemma alw_holds_smap_conv_comp: "alw (holds P) (smap f s) = alw (\s. (P o f) (shd s)) s" apply (rule iffI) apply (coinduction arbitrary: s) apply auto [] apply (coinduction arbitrary: s) apply auto done lemma alw_relates: "alw (relates P) s \ P (shd s) (shd (stl s)) \ alw (relates P) (stl s)" apply (rule iffI) apply (auto simp: relates_def dest: alwD) [] apply (coinduction arbitrary: s) apply (auto simp: relates_def) done subsection\Notation\ no_notation AND (infix "aand" 60) no_notation OR (infix "or" 60) no_notation IMPL (infix "imp" 60) notation AND (infixr "aand" 70) notation OR (infixr "or" 65) notation IMPL (infixr "imp" 60) (*<*) end (*>*) \ No newline at end of file diff --git a/thys/Progress_Tracking/Exchange.thy b/thys/Progress_Tracking/Exchange.thy --- a/thys/Progress_Tracking/Exchange.thy +++ b/thys/Progress_Tracking/Exchange.thy @@ -1,3193 +1,3193 @@ section \Exchange Protocol\label{sec:exchange}\ (*<*) theory Exchange imports "HOL-Library.While_Combinator" Auxiliary begin (*>*) subsection\Specification\ record ('p, 't) configuration = c_temp :: "'p \ 't zmultiset" c_msg :: "'p \ 'p \ 't zmultiset list" c_glob :: "'p \ 't zmultiset" c_caps :: "'p \ 't zmultiset" c_data_msg :: "('p \ 't) multiset" text\ Description of the configuration: @{term "c_msg c p q"} global, all progress messages currently in-flight from p to q @{term "c_data_msg c"} global, capabilities carried by in-flight data messages @{term "c_temp c p"} local, aggregated progress updates of worker p that haven't been sent yet @{term "c_glob c p"} local, worker p's conservative approximation of all capabilities in the system @{term "c_caps c p"} local, worker p's capabilities global = state of the whole system to which no worker has access local = state that is kept locally by each worker and which it can access \ type_synonym ('p, 't) computation = "('p, 't) configuration stream" context order begin abbreviation "timestamps M \ {# t. (x,t) \#\<^sub>z M #}" definition vacant_upto :: "'a zmultiset \ 'a \ bool" where "vacant_upto a t \ (\s. s \ t \ zcount a s = 0)" definition nonpos_upto :: "'a zmultiset \ 'a \ bool" where "nonpos_upto a t = (\s. s \ t \ zcount a s \ 0)" definition supported :: "'a zmultiset \ 'a \ bool" where "supported a t \ (\s. s < t \ zcount a s < 0)" definition supported_strong :: "'a zmultiset \ 'a \ bool" where "supported_strong a t \ (\s. s < t \ zcount a s < 0 \ nonpos_upto a s)" definition justified where "justified C M = (\t. 0 < zcount M t \ supported M t \ (\t' zcount M t < zcount C t)" lemma justified_alt: "justified C M = (\t. 0 < zcount M t \ supported_strong M t \ (\t' zcount M t < zcount C t)" unfolding justified_def supported_def supported_strong_def apply (rule iffI) apply clarsimp apply (drule order_zmset_exists_foundation') apply clarsimp subgoal for t s apply (drule spec[of _ s]) apply safe apply (meson le_less_trans less_le_trans nonpos_upto_def) using order.strict_trans2 apply blast using order.order_iff_strict apply auto done apply blast done definition justified_with where "justified_with C M N = (\t. 0 < zcount M t \ (\s zcount N s < 0)) \ (\s zcount (M+N) t < zcount C t)" lemma justified_with_alt: "justified_with C M N = (\t. 0 < zcount M t \ (\s zcount N s < 0) \ (\s' 0)) \ (\s zcount (M+N) t < zcount C t)" unfolding justified_with_def apply (rule iffI) apply clarsimp apply (drule order_zmset_exists_foundation') apply clarsimp subgoal for t s apply (drule spec[of _ s]) apply safe using order.strict_trans order.strict_trans2 apply blast+ - apply (metis add_less_zeroD order.order_iff_strict not_less_iff_gr_or_eq order_class.order.strict_trans) + apply (simp add: not_less not_le) + apply (smt (verit, best) antisym_conv2) done apply blast done definition PositiveImplies where "PositiveImplies v w \ \t. zcount v t > 0 \ zcount w t > 0" \ \A worker can mint capabilities greater or equal to any owned capability\ definition minting_self where "minting_self C M = (\t\#M. \t'\t. 0 < zcount C t')" \ \Sending messages mints a capability at a strictly greater pointstamp\ definition minting_msg where "minting_msg C M = (\(p,t)\#M. \t'p\UNIV. c_caps c p) + timestamps (zmset_of (c_data_msg c))" definition InfoAt where "InfoAt c k p q = (if 0 \ k \ k < length (c_msg c p q) then (c_msg c p q) ! k else {#}\<^sub>z)" definition IncomingInfo :: "('p, 'a) configuration \ nat \ 'p \ 'p \ 'a zmultiset" where "IncomingInfo c k p q \ sum_list (drop k (c_msg c p q)) + c_temp c p" definition GlobalIncomingInfo :: "('p :: finite, 'a) configuration \ nat \ 'p \ 'p \ 'a zmultiset" where "GlobalIncomingInfo c k p q \ \p' \ UNIV. IncomingInfo c (if p' = p then k else 0) p' q" (* (GlobalIncomingInfo c 0 q q) sums up all info incoming at q *) abbreviation GlobalIncomingInfoAt where "GlobalIncomingInfoAt c q \ GlobalIncomingInfo c 0 q q" definition init_config :: "('p :: finite, 'a) configuration \ bool" where "init_config c \ (\p. c_temp c p = {#}\<^sub>z) \ (\p1 p2. c_msg c p1 p2 = []) \ \ \Capabilities have non-negative multiplicities\ (\p t. 0 \ zcount (c_caps c p) t) \ \ \The pointstamps in glob are exactly those in @{term records}\ (\p. c_glob c p = records c) \ \ \All capabilities are being tracked\ c_data_msg c = {#}" (* Processor receives a capability, i.e. in timely: receives a data message *) definition next_recvcap' :: "('p :: finite, 'a) configuration \ ('p, 'a) configuration \ 'p \ 'a \ bool" where "next_recvcap' c0 c1 p t = ( (p,t) \# c_data_msg c0 \ c1 = c0\c_caps := (c_caps c0)(p := c_caps c0 p + {#t#}\<^sub>z), c_data_msg := c_data_msg c0 - {#(p,t)#}\)" abbreviation next_recvcap where "next_recvcap c0 c1 \ \p t. next_recvcap' c0 c1 p t" text\ Can minting of capabilities be described as a refinement of the Abadi model? Short answer: No, not in general. Long answer: Could slightly modify Abadi model, such that a capability always comes with a multiplicity $2^64$ (or similar, could be parametrized over arbitrarily large constant). In that case minting new capabilities can be described as an upright change, dropping one of the capabilities, to make the change upright. This only works as long as no capability is required more than the constant number of times. Issues: - Not fully general, due to the arbitrary constant - Not clear whether refinement proofs would be easier than simply altering the model to support the operations \ text\Rationale for the condition on @{term "c_caps c0 p"}: In Abadi, the operation @{term next_performop'} has the premise @{term "\t. int (count \neg t) \ zcount (records c0) t"}, (records corresponds to the global field @{term nrec} in that model) which means the processor performing the transition must verify that this condition is met. Since @{term "records c"} is "global" state, which no processor can know, an implementation of this protocol has to include some other protocol or reasoning for when it is safe to do this transition. Naively using a processor's @{term "c_glob c p"} to approximate @{term "records c"} and justify transitions can cause a race condition, where a processor drops a pointstamp, e.g., @{term "\neg = {#t#}"}, after which @{term "zcount (records c) t = 0"} but other processors might still use the pointstamp to justify the creation of pointstamps that violate the safety property. Instead we model ownership of pointstamps, calling "owned pointstamps" \<^bold>\capabilities\, which are tracked in @{term "c_caps c"}. In place of @{term nrec} we define @{term "records c"}, which is the sum of all capabilities, as well as @{term "c_data_msg c"}, which contains the capabilities carried by data messages. Since @{term "\p t. zcount (c_caps c p) t \ zcount (records c) t"}, our condition @{term "\t. int (count \neg t) \ zcount (c_caps c0 p) t"} implies the one on @{term nrec} in Abadi's model. \ text\Conditions in performop: The performop transition takes three msets of pointstamps, @{term \neg}, @{term \mint_msg}, and @{term \mint_self} @{term \neg} contains dropped capabilities (a subset of @{term c_caps}) @{term \mint_msg} contains pairs @{term "(p,t)"}, where a data message is sent (i.e. capability added to the pool), creating a capability at t, owned by p @{term \mint_self} contains pointstamps minted and owned by worker @{term p} @{term \neg} in combination with @{term \mint_msg} also allows any upright updates to be made as in the Abadi model, meaning this definition allows strictly more behaviors. The @{term "\mint_msg \ {#} \ zmset_of \mint_self - zmset_of \neg \ {#}\<^sub>z"} condition ensures that no-ops aren't possible. However, it's still possible that the combined @{term \} is empty. E.g. a processor has capabilities 1 and 2, uses cap 1 to send a message, minting capability 2. Simultaneously it drops a capability 2 (for unrelated reasons), cancelling out the overall change but shifting a capability to the pool, possibly with a different owner than itself.\ definition next_performop' :: "('p::finite, 'a) configuration \ ('p, 'a) configuration \ 'p \ 'a multiset \ ('p \ 'a) multiset \ 'a multiset \ bool" where "next_performop' c0 c1 p \neg \mint_msg \mint_self = \ \@{term \pos} contains all positive changes, @{term \} the combined positive and negative changes\ (let \pos = timestamps (zmset_of \mint_msg) + zmset_of \mint_self; \ = \pos - zmset_of \neg in (\mint_msg \ {#} \ zmset_of \mint_self - zmset_of \neg \ {#}\<^sub>z) \ (\t. int (count \neg t) \ zcount (c_caps c0 p) t) \ \Pointstamps added in @{term \mint_self} are minted at p\ \ minting_self (c_caps c0 p) \mint_self \ \Pointstamps added in @{term \mint_msg} correspond to sent data messages\ \ minting_msg (c_caps c0 p) \mint_msg \ \Worker immediately knows about dropped and minted capabilities\ \ c1 = c0\c_caps := (c_caps c0)(p := c_caps c0 p + zmset_of \mint_self - zmset_of \neg), \ \Sending a data message creates a capability, once that message arrives. This is modelled as a pool of capabilities that may (will) appear at processors at some point.\ c_data_msg := c_data_msg c0 + \mint_msg, c_temp := (c_temp c0)(p := c_temp c0 p + \)\)" abbreviation next_performop where "next_performop c0 c1 \ (\p \neg \mint_msg \mint_self. next_performop' c0 c1 p \neg \mint_msg \mint_self)" definition next_sendupd' :: "('p::finite, 'a) configuration \ ('p, 'a) configuration \ 'p \ 'a set \ bool" where "next_sendupd' c0 c1 p tt = (let \ = {#t \#\<^sub>z c_temp c0 p. t \ tt#} in \ \ 0 \ justified (c_caps c0 p) (c_temp c0 p - \) \ c1 = c0\c_msg := (c_msg c0)(p := \q. c_msg c0 p q @ [\]), c_temp := (c_temp c0)(p := c_temp c0 p - \)\)" abbreviation next_sendupd where "next_sendupd c0 c1 \ (\p tt. next_sendupd' c0 c1 p tt)" definition next_recvupd' :: "('p::finite, 'a) configuration \ ('p, 'a) configuration \ 'p \ 'p \ bool" where "next_recvupd' c0 c1 p q \ c_msg c0 p q \ [] \ c1 = c0\c_msg := (c_msg c0)(p := (c_msg c0 p)(q := tl (c_msg c0 p q))), c_glob := (c_glob c0)(q := c_glob c0 q + hd (c_msg c0 p q))\" abbreviation next_recvupd where "next_recvupd c0 c1 \ (\p q. next_recvupd' c0 c1 p q)" definition "next'" where "next' c0 c1 = (next_performop c0 c1 \ next_sendupd c0 c1 \ next_recvupd c0 c1 \ next_recvcap c0 c1 \ c1 = c0)" abbreviation "next" where "next s \ next' (shd s) (shd (stl s))" definition spec :: "('p :: finite, 'a) computation \ bool" where "spec s \ holds init_config s \ alw next s" abbreviation GlobVacantUpto where "GlobVacantUpto c q t \ vacant_upto (c_glob c q) t" abbreviation GlobNonposUpto where "GlobNonposUpto c q t \ nonpos_upto (c_glob c q) t" abbreviation RecordsVacantUpto where "RecordsVacantUpto c t \ vacant_upto (records c) t" definition SafeGlobVacantUptoImpliesStickyNrec :: "('p :: finite, 'a) computation \ bool" where "SafeGlobVacantUptoImpliesStickyNrec s = (let c = shd s in \t q. GlobVacantUpto c q t \ alw (holds (\c. RecordsVacantUpto c t)) s)" subsection\Auxiliary Lemmas\ lemma finite_induct_select [consumes 1, case_names empty select]: assumes "finite S" and empty: "P {}" and select: "\T. finite T \ T \ S \ P T \ \s\S - T. P (insert s T)" shows "P S" proof - from assms(1) have "P S \ finite S" by (induct S rule: finite_induct_select) (auto intro: empty select) then show ?thesis by blast qed lemma finite_induct_decompose_sum: fixes f :: "'c \ ('b :: comm_monoid_add)" assumes "finite X" and "x\X" and "A (f x)" and "\Z. B (sum f Z)" and "\x Z. A (f x) \ B (sum f Z) \ A (f x + sum f Z)" and "\x Z. B (f x) \ A (sum f Z) \ A (f x + sum f Z)" shows "A (\x\X. f x)" using assms(1,2,3) apply (induct X rule: finite_induct_select) apply simp apply (simp add: sum.insert_remove) subgoal for T apply (cases "x \ T"; simp add: assms(3)) apply (drule psubset_imp_ex_mem) apply clarsimp subgoal for z apply (rule bexI[of _ z]) apply (rule conjI) apply clarsimp apply (rule assms(6)[of z T]) apply (rule assms(4)[THEN spec, of "{z}", simplified]) apply simp apply simp done apply clarsimp apply (drule bspec[of _ _ x]) apply safe apply (rule assms(2)) using assms(4) assms(5) apply blast done done lemma minting_msg_add_records: "minting_msg C1 M \ \t. 0 \ zcount C2 t \ minting_msg (C1+C2) M" by (auto simp: minting_msg_def intro: add_strict_increasing dest!: bspec) lemma add_less: "(a::int) < c \ b \ 0 \ a + b < c" by linarith lemma disj3_split: "P \ Q \ R \ (P \ thesis) \ (\ P \ Q \ thesis) \ (\ P \ \ Q \ R \ thesis) \ thesis" by blast lemma filter_zmset_conclude_predicate: "0 < zcount {# x \#\<^sub>z M. P x #} x \ 0 < zcount M x \ P x" by (auto split: if_splits) lemma alw_holds2: "alw (holds P) ss = (P (shd ss) \ alw (holds P) (stl ss))" by (meson alw.simps holds.elims(2) holds.elims(3)) lemma zmset_of_remove1_mset: "x \# M \ zmset_of (remove1_mset x M) = zmset_of M - {#x#}\<^sub>z" by (induct M) auto lemma timestamps_zmset_of_pair_image[simp]: "timestamps (zmset_of {# (c,t). t \# M #}) = zmset_of M" by (induct M) auto lemma timestamps_image_zmset_fst[simp]: "timestamps {# (f x, t). (x, t) \#\<^sub>z M #} = timestamps M" apply transfer apply (clarsimp simp: equiv_zmset_def) apply (metis (no_types, lifting) case_prod_unfold image_mset_cong prod.collapse prod.inject) done lemma lift_invariant_to_spec: assumes "(\c. init_config c \ P c)" and "(\s. holds P s \ next s \ nxt (holds P) s)" shows "spec s \ alw (holds P) s" unfolding spec_def apply (elim conjE) apply (coinduction arbitrary: s) apply clarsimp apply (intro conjI assms(1)) apply safe subgoal proof - fix sa :: "('b, 'a) configuration stream" assume a1: "init_config (shd sa)" assume a2: "alw next sa" assume "\ alw (holds P) (stl sa)" then have "\ alw (\s. holds P s \ nxt (holds P) s) sa" using a1 by (metis (no_types) alw.cases alw_invar assms(1) holds.elims(3)) then show "init_config (shd (stl sa))" using a2 by (metis (lifting) alw_iff_sdrop assms(2)) qed apply auto done lemma timestamps_sum_distrib[simp]: "(\p \ A. timestamps (f p)) = timestamps (\p \ A. f p)" by (induction A rule: infinite_finite_induct) auto lemma timestamps_zmset_of[simp]: "timestamps (zmset_of M) = zmset_of {# t. (p,t) \# M #}" by (induct M) auto lemma vacant_upto_add: "vacant_upto a t \ vacant_upto b t \ vacant_upto (a+b) t" by (simp add: vacant_upto_def) lemma nonpos_upto_add: "nonpos_upto a t \ nonpos_upto b t \ nonpos_upto (a+b) t" by (auto intro: add_nonpos_nonpos simp: nonpos_upto_def) lemma nonzero_lt_gtD: "(n::_::linorder) \ 0 \ 0 < n \ n < 0" by auto lemma zero_lt_diff: "(0::int) < a - b \ b \ 0 \ 0 < a" by auto lemma zero_lt_add_disj: "0 < (a::int) + b \ 0 \ a \ 0 \ b \ 0 < a \ 0 < b" by auto subsubsection\Transition lemmas\ lemma next_performopD: assumes "next_performop' c0 c1 p \neg \mint_msg \mint_self" shows "\mint_msg \ {#} \ zmset_of \mint_self - zmset_of \neg \ {#}\<^sub>z" "\t. int (count \neg t) \ zcount (c_caps c0 p) t" "minting_self (c_caps c0 p) \mint_self" "minting_msg (c_caps c0 p) \mint_msg" "c_temp c1 = (c_temp c0)(p := c_temp c0 p + (timestamps (zmset_of \mint_msg) + zmset_of \mint_self - zmset_of \neg))" "c_msg c1 = c_msg c0" "c_glob c1 = c_glob c0" "c_data_msg c1 = c_data_msg c0 + \mint_msg" "c_caps c1 = (c_caps c0)(p := c_caps c0 p + (zmset_of \mint_self - zmset_of \neg))" using assms by (simp_all add: next_performop'_def Let_def algebra_simps) lemma next_performop_complexD: assumes "next_performop' c0 c1 p \neg \mint_msg \mint_self" shows "records c1 = records c0 + (timestamps (zmset_of \mint_msg) + zmset_of \mint_self - zmset_of \neg)" "GlobalIncomingInfoAt c1 q = GlobalIncomingInfoAt c0 q + (timestamps (zmset_of \mint_msg) + zmset_of \mint_self - zmset_of \neg)" "IncomingInfo c1 k p' q = (if p' = p then IncomingInfo c0 k p' q + (timestamps (zmset_of \mint_msg) + zmset_of \mint_self - zmset_of \neg) else IncomingInfo c0 k p' q)" "\t' zcount (timestamps (zmset_of \mint_msg)) t = 0" "InfoAt c1 k p' q = InfoAt c0 k p' q" proof - let ?\ = "timestamps (zmset_of \mint_msg) + zmset_of \mint_self - zmset_of \neg" note change = next_performopD[OF assms(1)] show "records c1 = records c0 + ?\" unfolding records_def change apply simp apply (subst add_diff_eq[symmetric]) apply (subst sum_if_distrib_add) apply (simp_all add: algebra_simps zmset_of_plus) done show "IncomingInfo c1 k p' q = (if p' = p then IncomingInfo c0 k p' q + (timestamps (zmset_of \mint_msg) + zmset_of \mint_self - zmset_of \neg) else IncomingInfo c0 k p' q)" unfolding IncomingInfo_def change by (auto simp: algebra_simps) show "GlobalIncomingInfoAt c1 q = GlobalIncomingInfoAt c0 q + ?\" for q unfolding GlobalIncomingInfo_def IncomingInfo_def by (rule Sum_eq_pick_changed_elem[where m = p]) (simp_all add: change algebra_simps) show "\t' zcount (timestamps (zmset_of \mint_msg)) t = 0" for t by (rule ccontr) (clarsimp dest!: image_zmset_pre change(4)[unfolded minting_msg_def, rule_format]) show "InfoAt c1 k p' q = InfoAt c0 k p' q" unfolding InfoAt_def change by simp qed lemma next_sendupdD: assumes "next_sendupd' c0 c1 p tt" shows "{#t \#\<^sub>z c_temp c0 p. t \ tt#} \ {#}\<^sub>z" "justified (c_caps c0 p) (c_temp c0 p - {#t \#\<^sub>z c_temp c0 p. t \ tt#})" "c_temp c1 p' = (if p' = p then c_temp c0 p - {#t \#\<^sub>z c_temp c0 p. t \ tt#} else c_temp c0 p')" "c_msg c1 = (\p' q. if p' = p then c_msg c0 p q @ [{#t \#\<^sub>z c_temp c0 p. t \ tt#}] else c_msg c0 p' q)" "c_glob c1 = c_glob c0" "c_caps c1 = c_caps c0" "c_data_msg c1 = c_data_msg c0" using assms by (simp_all add: next_sendupd'_def Let_def fun_eq_iff) lemma next_sendupd_complexD: assumes "next_sendupd' c0 c1 p tt" shows "records c1 = records c0" "IncomingInfo c1 0 = IncomingInfo c0 0" "IncomingInfo c1 k p' q = (if p' = p \ length (c_msg c0 p q) < k then IncomingInfo c0 k p' q - {#t \#\<^sub>z c_temp c0 p'. t \ tt#} else IncomingInfo c0 k p' q)" "k \ length (c_msg c0 p q) \ IncomingInfo c1 k p' q = IncomingInfo c0 k p' q" "length (c_msg c0 p q) < k \ IncomingInfo c1 k p' q = (if p' = p then IncomingInfo c0 k p' q - {#t \#\<^sub>z c_temp c0 p'. t \ tt#} else IncomingInfo c0 k p' q)" "GlobalIncomingInfoAt c1 q = GlobalIncomingInfoAt c0 q" "InfoAt c1 k p' q = (if p' = p \ k = length (c_msg c0 p q) then {#t \#\<^sub>z c_temp c0 p'. t \ tt#} else InfoAt c0 k p' q)" proof - note change = next_sendupdD[OF assms] show "records c1 = records c0" by (simp add: records_def change) show ii: "IncomingInfo c1 k p' q = (if p' = p \ length (c_msg c0 p q) < k then IncomingInfo c0 k p' q - {#t \#\<^sub>z c_temp c0 p'. t \ tt#} else IncomingInfo c0 k p' q)" by (simp add: algebra_simps IncomingInfo_def change) then show "k \ length (c_msg c0 p q) \ IncomingInfo c1 k p' q = IncomingInfo c0 k p' q" by auto from ii show "length (c_msg c0 p q) < k \ IncomingInfo c1 k p' q = (if p' = p then IncomingInfo c0 k p' q - {#t \#\<^sub>z c_temp c0 p'. t \ tt#} else IncomingInfo c0 k p' q)" by auto have "IncomingInfo c1 0 p q = IncomingInfo c0 0 p q" for p q by (simp add: algebra_simps IncomingInfo_def change) then show "IncomingInfo c1 0 = IncomingInfo c0 0" by auto then show "GlobalIncomingInfoAt c1 q = GlobalIncomingInfoAt c0 q" unfolding GlobalIncomingInfo_def by auto show "InfoAt c1 k p' q = (if p' = p \ k = length (c_msg c0 p q) then {#t \#\<^sub>z c_temp c0 p'. t \ tt#} else InfoAt c0 k p' q)" unfolding InfoAt_def change by (auto simp: nth_append) qed lemma next_recvupdD: assumes "next_recvupd' c0 c1 p q" shows "c_msg c0 p q \ []" "c_temp c1 = c_temp c0" "c_msg c1 = (\p' q'. if p' = p \ q' = q then tl (c_msg c0 p q) else c_msg c0 p' q')" "c_glob c1 = (c_glob c0)(q := c_glob c0 q + hd (c_msg c0 p q))" "c_caps c1 = c_caps c0" "c_data_msg c1 = c_data_msg c0" using assms by (simp_all add: next_recvupd'_def fun_eq_iff) lemma next_recvupd_complexD: assumes "next_recvupd' c0 c1 p q" shows "records c1 = records c0" "IncomingInfo c1 0 p' q' = (if p' = p \ q' = q then IncomingInfo c0 0 p' q' - hd (c_msg c0 p q) else IncomingInfo c0 0 p' q')" "IncomingInfo c1 k p' q' = (if p' = p \ q' = q then IncomingInfo c0 (k+1) p' q' else IncomingInfo c0 k p' q')" "GlobalIncomingInfoAt c1 q' = (if q' = q then GlobalIncomingInfoAt c0 q' - hd (c_msg c0 p q) else GlobalIncomingInfoAt c0 q')" "InfoAt c1 k p q = InfoAt c0 (k+1) p q" "InfoAt c1 k p' q' = (if p' = p \ q' = q then InfoAt c0 (k+1) p q else InfoAt c0 k p' q')" proof - note change = next_recvupdD[OF assms] show "records c1 = records c0" by (simp add: records_def change) show ii: "IncomingInfo c1 0 p' q' = (if p' = p \ q' = q then IncomingInfo c0 0 p' q' - hd (c_msg c0 p q) else IncomingInfo c0 0 p' q')" for p' q' by (auto simp: IncomingInfo_def change algebra_simps sum_list_hd_tl) show "IncomingInfo c1 k p' q' = (if p' = p \ q' = q then IncomingInfo c0 (k+1) p' q' else IncomingInfo c0 k p' q')" by (auto simp: IncomingInfo_def change algebra_simps sum_list_hd_tl drop_Suc) show "GlobalIncomingInfoAt c1 q' = (if q' = q then GlobalIncomingInfoAt c0 q' - hd (c_msg c0 p q) else GlobalIncomingInfoAt c0 q')" unfolding GlobalIncomingInfo_def apply (cases "q'=q") apply simp apply (subst diff_conv_add_uminus) apply (intro Sum_eq_pick_changed_elem[where m = p]) apply (simp_all add: ii) done show "InfoAt c1 k p q = InfoAt c0 (k+1) p q" unfolding InfoAt_def change by (auto simp: nth_tl) show "InfoAt c1 k p' q' = (if p' = p \ q' = q then InfoAt c0 (k+1) p q else InfoAt c0 k p' q')" unfolding InfoAt_def change by (auto simp: nth_tl) qed lemma next_recvcapD: assumes "next_recvcap' c0 c1 p t" shows "(p,t) \# c_data_msg c0" "c_temp c1 = c_temp c0" "c_msg c1 = c_msg c0" "c_glob c1 = c_glob c0" "c_caps c1 = (c_caps c0)(p := c_caps c0 p + {#t#}\<^sub>z)" "c_data_msg c1 = c_data_msg c0 - {#(p,t)#}" using assms by (simp_all add: next_recvcap'_def) lemma next_recvcap_complexD: assumes "next_recvcap' c0 c1 p t" shows "records c1 = records c0" "IncomingInfo c1 = IncomingInfo c0" "GlobalIncomingInfo c1 = GlobalIncomingInfo c0" "InfoAt c1 k p' q = InfoAt c0 k p' q" proof - note change = next_recvcapD[OF assms] show "records c1 = records c0" unfolding records_def change fun_upd_apply apply (subst sum_if_distrib_add) using change(1) apply (simp_all add: zmset_of_remove1_mset algebra_simps records_def change) done show "IncomingInfo c1 = IncomingInfo c0" unfolding IncomingInfo_def change by simp then show "GlobalIncomingInfo c1 = GlobalIncomingInfo c0" unfolding GlobalIncomingInfo_def by simp show "InfoAt c1 k p' q = InfoAt c0 k p' q" unfolding InfoAt_def change by simp qed lemma ex_next_recvupd: assumes "c_msg c0 p q \ []" shows "\c1. next_recvupd' c0 c1 p q" using assms unfolding next_recvupd'_def by (intro exI[of _ "c0\c_msg := (\p' q'. if p' = p \ q' = q then tl (c_msg c0 p q) else c_msg c0 p' q'), c_glob := (\q'. if q' = q then c_glob c0 q + hd (c_msg c0 p q) else c_glob c0 q')\"]) (auto simp: fun_eq_iff) subsubsection\Facts about @{term justified}'ness\ lemma justified_empty[simp]: "justified {#}\<^sub>z {#}\<^sub>z" by (simp add: justified_def) text\It's sufficient to show justified for least pointstamps in M.\ lemma justified_leastI: assumes "\t. 0 < zcount M t \ (\t' 0) \ supported_strong M t \ (\t' (zcount M t < zcount C t)" shows "justified C M" unfolding justified_alt supported_strong_def apply (intro allI impI) apply (drule order_zmset_exists_foundation) apply (elim exE conjE) subgoal for t' s apply (drule assms(1)[unfolded supported_strong_def, rule_format]) apply (auto intro: ccontr) [] apply (elim disj3_split) apply (rule disjI1) using order.strict_trans2 apply blast apply (rule disjI2, rule disjI1) using order.strict_trans2 apply blast apply (clarsimp simp: nonpos_upto_def) apply (metis le_less_linear linear le_imp_less_or_eq preorder_class.le_less_trans) done done lemma justified_add: assumes "justified C1 M1" and "justified C2 M2" and "\t. 0 \ zcount C1 t" and "\t. 0 \ zcount C2 t" shows "justified (C1+C2) (M1+M2)" apply (rule justified_leastI) apply (intro allI impI) subgoal for t apply (cases "0 < zcount M1 t") (* symmetric cases *) subgoal apply (drule assms(1)[unfolded justified_alt supported_strong_def, rule_format]) apply (elim disj3_split) subgoal apply (elim exE conjE) apply (drule order_zmset_exists_foundation_neg) apply (elim exE conjE) subgoal for s s' (* anything less than s' is 0 in M1 *) apply (cases "zcount (M1 + M2) s' < 0") subgoal apply (rule disjI1) apply (auto intro!: exI[of _ s'] simp: nonpos_upto_def supported_strong_def) [] done subgoal apply (subst (asm) not_less) apply (cases "0 < zcount M2 s'") prefer 2 subgoal by auto (* trivial contradiction *) subgoal apply (drule assms(2)[unfolded justified_alt supported_strong_def, rule_format]) apply (elim disj3_split) subgoal apply (rule disjI1) apply (elim exE) subgoal for s'' by (auto intro!: exI[of _ s''] simp: nonpos_upto_def supported_strong_def add_nonpos_neg) done subgoal apply (rule disjI2, rule disjI1) apply (elim exE conjE) subgoal for s'' using assms(3) by (auto simp: add_nonneg_pos intro!: exI[of _ s'']) done subgoal by (metis add.right_neutral add_strict_increasing2 assms(3) less_add_same_cancel1 order.strict_trans1 pos_add_strict zcount_union) done done done done subgoal by (metis add.commute add_mono_thms_linordered_field(4) assms(4) add_0 zcount_union) subgoal apply (cases "supported_strong M2 t") subgoal apply (rule disjI1) using assms(1)[unfolded justified_alt] apply (subst supported_strong_def) apply (subst (asm) supported_strong_def) apply (elim exE conjE) unfolding not_ex subgoal for s apply clarsimp apply (rule exI[of _ s]) apply (intro conjI) apply blast apply (rule add_nonpos_neg) - apply (metis order.strict_trans1 less_imp_le not_le not_less_iff_gr_or_eq order_class.order.strict_trans2 supported_strong_def) + apply (metis assms(3) le_less_linear less_trans order_class.le_less supported_strong_def) apply simp apply (clarsimp simp: nonpos_upto_def) done done subgoal apply (cases "\t't'p\P. justified (f p) (g p)" and "\p\P. \t. 0 \ zcount (f p) t" shows "justified (\p\P. f p) (\p\P. g p)" using assms by (induct P rule: infinite_finite_induct) (auto intro!: justified_add sum_nonneg simp: zcount_sum) lemma justified_add_records: assumes "justified C M" and "\t. 0 \ zcount C' t" shows "justified (C+C') M" using assms unfolding justified_def apply (clarsimp intro: add_pos_nonneg) apply (metis add.commute add_strict_increasing2 assms(2)) done lemma justified_add_zmset_records: assumes "justified C M" shows "justified (add_zmset t C) M" using assms apply (subst add_zmset_add_single) apply (rule justified_add_records) apply simp_all done lemma justified_diff: assumes "justified C M" and "\t. 0 \ zcount C t" and "\t. count \ t \ zcount C t" shows "justified (C - zmset_of \) (M - zmset_of \)" proof (intro justified_leastI allI impI) fix t assume least: "\t') t' \ 0" assume "0 < zcount (M - zmset_of \) t" then have Mt: "0 < zcount M t" by auto note assms(1)[unfolded justified_alt, rule_format, OF Mt] then consider "supported_strong M t" | "\t') t \ (\t') t') \ zcount (M - zmset_of \) t < zcount (C - zmset_of \) t" proof cases case 1 then show ?thesis unfolding supported_strong_def apply (elim exE) subgoal for s by (auto intro!: disjI1 exI[of _ s] simp: nonpos_upto_def) done next case 2 then obtain s where s: "s < t" "0 < zcount C s" "\s') s" | "zcount (C - zmset_of \) s = 0" "zcount (M - zmset_of \) s < 0" | "zcount (C - zmset_of \) s = 0" "zcount (M - zmset_of \) s = 0" | "zcount (C - zmset_of \) s = 0" "zcount (M - zmset_of \) s > 0" using assms(3)[rule_format, of s] by atomize_elim auto then show ?thesis proof cases case 1 then show ?thesis using s by auto next case 2 then show ?thesis using s least by (auto simp: nonpos_upto_def supported_strong_def) next case 3 note case3 = 3 with s(2) have Ms: "0 < zcount M s" by - (rule ccontr, auto simp: not_less) note assms(1)[unfolded justified_alt, rule_format, OF Ms] then consider "supported_strong M s" | "\t'" and "\t. 0 \ zcount C t" shows "justified C (M + timestamps (zmset_of \))" proof (intro allI impI justified_leastI) fix t assume t: "0 < zcount (M + timestamps (zmset_of \)) t" assume least: "\t')) t' \ 0" have \t: "0 < zcount (timestamps (zmset_of \)) t \ supported_strong (M + timestamps (zmset_of \)) t \ (\t' zcount (M + timestamps (zmset_of \)) t < zcount C t" by (auto dest: pos_image_zmset_obtain_pre[rotated] assms(2)[unfolded minting_msg_def, rule_format]) { assume \t: "zcount (timestamps (zmset_of \)) t \ 0" with t have Mt: "0 < zcount M t" by auto note assms(1)[unfolded justified_alt, rule_format, OF Mt] then consider "supported_strong M t" "\t' zcount C t" | "\t')) t \ (\t' zcount (M + timestamps (zmset_of \)) t < zcount C t" proof cases case 1 then obtain s where s: "s < t" "zcount M s < 0" "\s's'\s. zcount (timestamps (zmset_of \)) s' > 0") apply (elim exE conjE) subgoal for s' apply (drule pos_image_zmset_obtain_pre[rotated]) apply simp apply (elim exE conjE) apply simp apply (drule assms(2)[unfolded minting_msg_def, rule_format]) apply (auto simp: supported_strong_def) done subgoal apply (intro disjI1 exI[of _ s]) unfolding not_less apply (metis (full_types) le_less_linear least eq_refl order.strict_trans1 nonpos_upto_def supported_strong_def sublist_order.add_less zcount_union) done done next case 2 then show ?thesis by auto next case 3 then show ?thesis using \t by auto qed } then show "supported_strong (M + timestamps (zmset_of \)) t \ (\t' zcount (M + timestamps (zmset_of \)) t < zcount C t" apply (cases "zcount (timestamps (zmset_of \)) t \ 0") apply blast apply (rule \t) apply auto done qed lemma justified_add_same: assumes "justified C M" and "minting_self C \" and "\t. 0 \ zcount C t" shows "justified (C + zmset_of \) (M + zmset_of \)" proof (intro allI impI justified_leastI) fix t assume t: "0 < zcount (M + zmset_of \) t" assume least: "\t') t' \ 0" from t consider "0 < zcount M t" | "0 < zcount (zmset_of \) t" "zcount M t \ 0" by atomize_elim (auto simp: not_less count_inI) then show "supported_strong (M + zmset_of \) t \ (\t') t') \ zcount (M + zmset_of \) t < zcount (C + zmset_of \) t" proof cases case 1 note assms(1)[unfolded justified_alt, rule_format, OF 1] then consider "supported_strong M t" | "\t's\t'. zcount (zmset_of \) s > 0") apply (elim exE conjE) subgoal for s apply simp apply (drule assms(2)[unfolded minting_self_def, rule_format]) - apply (meson add_pos_nonneg order.ordering_axioms of_nat_0_le_iff ordering.strict_trans1) + apply (smt (z3) order.strict_trans1 of_nat_0_le_iff) done subgoal apply (intro disjI1 exI[of _ t'] conjI) apply simp apply simp apply (metis add_cancel_right_left add_mono_thms_linordered_field(1) count_eq_zero_iff order.order_iff_strict of_nat_eq_0_iff) using least nonpos_upto_def apply auto done done done next case 2 then show ?thesis by auto next case 3 then show ?thesis by auto qed next case 2 then obtain t' where t': "t' \ t" "0 < zcount C t'" using assms(2)[unfolded minting_self_def] by auto then show ?thesis apply (cases "t'=t") subgoal using 2(2) by auto subgoal apply (rule disjI2, rule disjI1) using assms(3) order.not_eq_order_implies_strict apply fastforce done done qed qed subsubsection\Facts about @{term justified_with}'ness\ lemma justified_with_add_records: assumes "justified_with C1 M N" and "\t. 0 \ zcount C2 t" shows "justified_with (C1+C2) M N" unfolding justified_with_def apply (intro allI impI) subgoal for t apply (drule assms(1)[unfolded justified_with_def, rule_format]) apply (elim disjE) subgoal by blast subgoal apply (elim exE) subgoal for s apply (rule disjI2, rule disjI1) using assms(2)[rule_format, of s] by auto done subgoal apply (intro disjI2) using assms(2)[rule_format, of t] by auto done done lemma justified_with_leastI: assumes "(\t. 0 < zcount M t \ (\t' 0) \ (\s zcount N s < 0) \ (\s' 0)) \ (\s zcount (M+N) t < zcount C t)" shows "justified_with C M N" unfolding justified_with_alt proof (intro allI impI) fix t assume t: "0 < zcount M t" from t obtain t' where t': "t' \ t" "0 < zcount M t'" "\s 0" by atomize_elim (drule order_zmset_exists_foundation') note assms[rule_format, OF t'(2)] with t'(3) consider "\s zcount N s < 0) \ (\s' 0)" | "\ss zcount N s < 0) \ (\s' 0)) \ (\s zcount (M+N) t < zcount C t" proof cases case 1 then show ?thesis using order.strict_trans2 t'(1) by blast next case 2 then show ?thesis using order.strict_trans2 t'(1) by blast next case 3 then consider "zcount (M+N) t' < 0" | "zcount C t' > 0" by atomize_elim auto then show ?thesis proof cases case 1 then have "zcount N t' < 0" using t'(2) by auto with t'(3) show ?thesis apply (cases "t'=t") subgoal using 3(1) by blast subgoal using t'(1) by (auto intro: exI[of _ t']) done next case 2 then show ?thesis apply (cases "t'=t") subgoal apply (intro disjI2) using 3(1) apply blast done subgoal apply (rule disjI2) apply (rule disjI1) using order.not_eq_order_implies_strict t'(1) apply blast done done qed qed qed lemma justified_with_add: assumes "justified_with C1 M N1" and "justified C1 N1" and "justified C2 N2" and "\t. 0 \ zcount C1 t" and "\t. 0 \ zcount C2 t" shows "justified_with (C1+C2) M (N1+N2)" proof (intro justified_with_leastI allI impI) fix t assume count_t: "0 < zcount M t" assume least: "\t' 0" note assms(1)[unfolded justified_with_alt, rule_format, OF count_t] then consider "\s (\s' 0)" | "\s (\s' 0)" | "\ss zcount (N1 + N2) s < 0) \ (\s' 0)) \ (\s zcount (M + (N1 + N2)) t < zcount (C1 + C2) t" proof cases case 1 then show ?thesis by blast next case 2 then obtain s where s: "s < t" "zcount N1 s < 0" "\s' zcount N1 s'" apply atomize_elim apply (elim exE conjE) apply (drule order_zmset_exists_foundation_neg') using order.strict_trans order.strict_trans1 apply blast done then consider "zcount (N1 + N2) s < 0" | "0 < zcount N2 s" "zcount (N1 + N2) s \ 0" by atomize_elim auto then show ?thesis proof cases case 1 then show ?thesis using least order.strict_trans s(1) by blast next case 2 note assms(3)[unfolded justified_alt, rule_format, OF 2(1)] then consider "supported_strong N2 s" "\t' 0" "zcount N2 s \ zcount C2 s" | "\t' zcount (N1+N2) s' \ 0 < zcount N1 s'" by auto show ?thesis apply (cases "zcount (N1 + N2) s' < 0") subgoal using least order.strict_trans s'(1) s(1) by (intro disjI1) blast subgoal unfolding not_less apply (drule nonneg) apply (drule assms(2)[unfolded justified_alt supported_strong_def, rule_format]) apply (elim disjE exE conjE) subgoal for u - by (metis (full_types) order.ordering_axioms order_class.order.irrefl order_class.order.strict_trans2 ordering.strict_trans s'(1) s(3)) + by (meson local.order.strict_trans not_less s'(1) s(3)) subgoal for u - by (metis (no_types, hide_lams) 1(2) add_cancel_left_right assms(5) less_trans order_class.antisym s'(1) s(1) zcount_union) + by (metis add_strict_increasing assms(5) local.less_trans s'(1) s(1) zcount_union) subgoal - by (metis (no_types, hide_lams) 1(2) add_cancel_left_right assms(5) less_trans not_le order_class.antisym order_class.order.strict_trans2 s'(1) s(1) s(3) zcount_union) + by (smt (verit, ccfv_threshold) assms(5) local.dual_order.strict_trans s'(1) s(1) s(3) zcount_union) done done next case 2 then show ?thesis apply (elim exE conjE) subgoal for s' apply (rule disjI2) apply (rule disjI1) using assms(4)[rule_format, of s'] s(1) apply (auto intro!: exI[of _ s']) done done next case 3 then show ?thesis - by (metis 2(1) add_cancel_left_right add_strict_increasing2 assms(4) not_le order_class.order.irrefl order_class.order.strict_trans2 pos_add_strict s(1) zcount_union) + by (smt (verit) "2"(1) assms(4) s(1) zcount_union) qed qed next case 3 then show ?thesis using assms(5) apply - apply (rule disjI2) apply (rule disjI1) - apply (metis order_class.order.strict_trans2 subset_zmset.le_add_same_cancel1 subseteq_zmset_def zcount_empty) + apply (metis add_strict_increasing zcount_union) done next case 4 then show ?thesis proof (cases "zcount (M + (N1 + N2)) t < zcount (C1 + C2) t") case True then show ?thesis by blast next case False then have N2t: "0 < zcount N2 t" using 4 assms(5)[rule_format, of t] unfolding not_less zcount_union by linarith then show ?thesis using assms(3)[unfolded justified_alt supported_strong_def, rule_format, OF N2t] apply (elim exE conjE disjE) subgoal for s apply (cases "0 < zcount N1 s") subgoal apply (drule assms(2)[unfolded justified_alt supported_strong_def, rule_format]) apply (elim exE conjE disjE) subgoal for s' apply (rule disjI1) apply (rule exI[of _ s']) apply (intro conjI) apply simp apply (metis add_cancel_right_right add_neg_neg order.strict_implies_order nonpos_upto_def order_class.order.not_eq_order_implies_strict zcount_union) apply (meson least less_trans) done subgoal for s' by (metis add_pos_nonneg assms(5) less_trans zcount_union) subgoal apply (rule ccontr) apply (clarsimp simp: not_le not_less) apply (metis (no_types, hide_lams) add_cancel_right_right add_neg_neg assms(4) assms(5) least less_trans not_less order_class.order.not_eq_order_implies_strict pos_add_strict) done done subgoal apply (intro disjI1 exI[of _ s]) apply (intro disjI2 conjI) apply simp apply simp using least apply simp done done subgoal for s by (metis add.comm_neutral add_mono_thms_linordered_field(4) assms(4) zcount_union) subgoal using 4 by auto done qed qed qed lemma justified_with_sum': assumes "finite X" "X\{}" and "\x\X. justified_with (C x) M (N x)" and "\x\X. justified (C x) (N x)" and "\x\X. \t. 0 \ zcount (C x) t" shows "justified_with (\x\X. C x) M (\x\X. N x)" using assms proof (induct X rule: finite_induct) case empty then show ?case by simp next case (insert x F) show ?case apply (cases "F={}") subgoal using insert(5) by simp subgoal apply (subst (1 2) sum.insert_remove) using insert(1) apply simp using insert(2) apply simp apply (rule justified_with_add) using insert(5) apply simp using insert(6) apply simp apply (rule justified_sum) using insert(6) apply simp using insert(7) apply simp using insert(7) apply simp apply (intro allI) unfolding zcount_sum apply (rule sum_nonneg) using insert(7) apply simp done done qed lemma justified_with_sum: assumes "finite X" "X\{}" and "x \ X" and "justified_with (C x) M (N x)" and "\x\X. justified (C x) (N x)" and "\x\X. \t. 0 \ zcount (C x) t" shows "justified_with (\x\X. C x) M (\x\X. N x)" using assms proof (induct X rule: finite_induct) case empty then show ?case by simp next case (insert y F) thm insert show ?case apply (cases "F={}") subgoal using insert by simp subgoal apply (subst (1 2) sum.insert_remove) using insert(1) apply simp using insert(2) apply simp apply (cases "y=x") subgoal apply (rule justified_with_add) using insert(6) apply simp using insert(7) apply simp apply (rule justified_sum) using insert(7) apply simp using insert(8) apply simp using insert(8) apply simp apply (intro allI) unfolding zcount_sum apply (rule sum_nonneg) using insert(8) apply simp done subgoal apply (subst (1 2) add.commute) apply (rule justified_with_add) apply (rule insert(3)) apply simp using insert(5) apply simp using insert(6) apply simp using insert(7) apply simp using insert(8) apply simp apply (rule justified_sum) using insert(7) apply simp using insert(8) apply simp using insert(7) apply simp apply (intro allI) unfolding zcount_sum apply (rule sum_nonneg) using insert(8) apply simp using insert(8) apply simp done done done qed lemma justified_with_add_same: assumes "justified_with C M N" and "\t. 0 \ zcount C t" shows "justified_with (C + zmset_of \) M (N + zmset_of \)" unfolding justified_with_def proof (intro allI impI) fix t assume Mt: "0 < zcount M t" note assms(1)[unfolded justified_with_alt, rule_format, OF Mt] with Mt consider "\s (\s' 0)" | "\s (\s' 0)" | "\ss zcount (N + zmset_of \) s < 0)) \ (\s) s) \ zcount (M + (N + zmset_of \)) t < zcount (C + zmset_of \) t" proof cases case 1 then show ?thesis by blast next case 2 then show ?thesis by (metis add_less_same_cancel2 assms(2) not_less preorder_class.le_less_trans zcount_union) next case 3 then show ?thesis by fastforce next case 4 then show ?thesis by auto qed qed lemma justified_with_add_msg_delta: assumes "justified_with C M N" and "minting_msg C \" and "\t. 0 \ zcount C t" shows "justified_with C M (N + timestamps (zmset_of \))" unfolding justified_with_def proof (intro allI impI) fix t assume Mt: "0 < zcount M t" note assms(1)[unfolded justified_with_alt, rule_format, OF Mt] with Mt consider "\s (\s' 0)" | "\s (\s' 0)" | "\ss zcount (N + timestamps (zmset_of \)) s < 0)) \ (\s zcount (M + (N + timestamps (zmset_of \))) t < zcount C t" proof cases case 1 then show ?thesis by blast next case 2 then obtain s where s: "s < t" "zcount N s < 0" by blast then show ?thesis proof (cases "\(p,s')\#\. s' \ s") case True then show ?thesis apply - apply clarsimp apply (drule assms(2)[unfolded minting_msg_def, rule_format]) using order.strict_trans order.strict_trans1 s(1) not_less apply blast done next case False then have "zcount (timestamps (zmset_of \)) s = 0" by (force intro: ccontr dest: pos_image_zmset_obtain_pre[rotated]) then show ?thesis by (metis plus_int_code(1) s(1,2) zcount_union) qed next case 3 then show ?thesis by blast next case 4 then show ?thesis apply (cases "zcount (timestamps (zmset_of \)) t > 0") apply (auto dest: pos_image_zmset_obtain_pre[rotated] assms(2)[unfolded minting_msg_def, rule_format]) [] unfolding not_less apply auto done qed qed lemma justified_with_diff: assumes "justified_with C M N" and "\t. 0 \ zcount C t" and "\t. count \ t \ zcount C t" and "justified C N" shows "justified_with (C - zmset_of \) M (N - zmset_of \)" proof (intro allI impI justified_with_leastI) fix t assume Mt: "0 < zcount M t" assume least: "\t' 0" note assms(1)[unfolded justified_with_alt, rule_format, OF Mt] with Mt consider "\s (\s' 0)" | "\s (\s' 0)" | "\ss 0 \ \ (\s' 0)" "\s 0 \ \ (\s' 0)" "zcount (M + N) t \ zcount C t" | "zcount (M + N) t < zcount C t" using not_less by blast then show "(\s zcount (N - zmset_of \) s < 0) \ (\s' 0)) \ (\s) s) \ zcount (M + (N - zmset_of \)) t < zcount (C - zmset_of \) t" proof cases case 1 then show ?thesis by blast next case 2 then show ?thesis using diff_add_cancel zcount_union zcount_zmset_of_nonneg by auto next case 3 then obtain s where s: "s < t" "0 < zcount C s" "\s') s" | "zcount (C - zmset_of \) s = 0" "zcount (N - zmset_of \) s < 0" | "zcount (C - zmset_of \) s = 0" "zcount (N - zmset_of \) s = 0" | "zcount (C - zmset_of \) s = 0" "zcount (N - zmset_of \) s > 0" using assms(3)[rule_format, of s] by atomize_elim auto then show ?thesis proof cases case 1 then show ?thesis using s by auto next case 2 then show ?thesis using s least by (auto simp: nonpos_upto_def) next case 3 note case3 = 3 with s(2) have Ns: "0 < zcount N s" by (auto intro: ccontr simp: not_less) note assms(4)[unfolded justified_alt, rule_format, OF Ns] then consider "supported_strong N s" | "\t't'neg \mint_msg \mint_self" and "\t. 0 \ zcount (c_caps c0 p) t" and "justified (c_caps c0 p) N" shows "justified_with (c_caps c0 p + zmset_of \mint_self - zmset_of \neg) M (N + zmset_of \mint_self + timestamps (zmset_of \mint_msg) - zmset_of \neg)" apply (rule justified_with_diff) apply (rule justified_with_add_msg_delta) apply (rule justified_with_add_same) using assms(1) apply simp using assms(3) apply simp apply (rule minting_msg_add_records) using next_performopD(4)[OF assms(2)] apply simp apply simp using assms(3) apply simp using assms(3) apply simp using next_performopD(2)[OF assms(2)] apply (simp add: add.commute add_increasing) apply (rule justified_add_msg_delta) apply (rule justified_add_same) using assms(4) apply simp using next_performopD(3)[OF assms(2)] apply simp using assms(3) apply simp apply (rule minting_msg_add_records) using next_performopD(4)[OF assms(2)] apply simp apply simp using assms(3) apply (simp add: add.commute add_increasing) done subsection\Invariants\ subsubsection\InvRecordCount\ text\InvRecordCount states that for every processor, its local approximation @{text "c_glob c q"} and the sum of all incoming progress updates @{text "GlobalIncomingInfoAt c q"} together are equal to the sum of all capabilities in the system.\ definition InvRecordCount where "InvRecordCount c \ \q. records c = GlobalIncomingInfoAt c q + c_glob c q" lemma init_config_implies_InvRecordCount: "init_config c \ InvRecordCount c" by (simp add: InvRecordCount_def init_config_def GlobalIncomingInfo_def IncomingInfo_def) lemma performop_preserves_InvRecordCount: assumes "InvRecordCount c0" and "next_performop' c0 c1 p \neg \mint_msg \mint_self" shows "InvRecordCount c1" proof - note change = next_performopD[OF assms(2)] note complex_change = next_performop_complexD[OF assms(2)] show "InvRecordCount c1" unfolding InvRecordCount_def complex_change by (auto intro: assms(1)[unfolded InvRecordCount_def, rule_format] simp: change) qed lemma sendupd_preserves_InvRecordCount: assumes "InvRecordCount c0" and "next_sendupd' c0 c1 p tt" shows "InvRecordCount c1" proof - note change = next_sendupdD[OF assms(2)] note complex_change = next_sendupd_complexD[OF assms(2)] from assms(1) show "InvRecordCount c1" unfolding InvRecordCount_def complex_change change(5) by assumption qed lemma recvupd_preserves_InvRecordCount: assumes "InvRecordCount c0" and "next_recvupd' c0 c1 p q" shows "InvRecordCount c1" proof - note change = next_recvupdD[OF assms(2)] note complex_change = next_recvupd_complexD[OF assms(2)] show "InvRecordCount c1" unfolding InvRecordCount_def complex_change change(4) by (auto simp: assms(1)[unfolded InvRecordCount_def, rule_format]) qed lemma recvcap_preserves_InvRecordCount: assumes "InvRecordCount c0" and "next_recvcap' c0 c1 p t" shows "InvRecordCount c1" proof - note change = next_recvcapD[OF assms(2)] note complex_change = next_recvcap_complexD[OF assms(2)] show "InvRecordCount c1" unfolding InvRecordCount_def complex_change change(4) by (auto simp: assms(1)[unfolded InvRecordCount_def, rule_format]) qed lemma next_preserves_InvRecordCount: "InvRecordCount c0 \ next' c0 c1 \ InvRecordCount c1" unfolding next'_def apply (elim disjE) subgoal using performop_preserves_InvRecordCount by auto subgoal using sendupd_preserves_InvRecordCount by auto subgoal using recvupd_preserves_InvRecordCount by auto subgoal using recvcap_preserves_InvRecordCount by auto subgoal by simp done lemma alw_InvRecordCount: "spec s \ alw (holds InvRecordCount) s" using lift_invariant_to_spec init_config_implies_InvRecordCount next_preserves_InvRecordCount by (metis (mono_tags, lifting) holds.elims(2) holds.elims(3) nxt.simps) subsubsection\InvCapsNonneg and InvRecordsNonneg\ text\InvCapsNonneg states that elements in a processor's @{text "c_caps c p"} always have non-negative cardinality. InvRecordsNonneg lifts this result to @{text "records c"}\ definition InvCapsNonneg :: "('p :: finite, 'a) configuration \ bool" where "InvCapsNonneg c = (\p t. 0 \ zcount (c_caps c p) t)" definition InvRecordsNonneg where "InvRecordsNonneg c = (\t. 0 \ zcount (records c) t)" lemma init_config_implies_InvCapsNonneg: "init_config c \ InvCapsNonneg c" unfolding init_config_def InvCapsNonneg_def by simp lemma performop_preserves_InvCapsNonneg: assumes "InvCapsNonneg c0" and "next_performop' c0 c1 p \\<^sub>m \\<^sub>p\<^sub>1 \\<^sub>p\<^sub>2" shows "InvCapsNonneg c1" using assms unfolding InvCapsNonneg_def next_performop'_def Let_def by clarsimp (metis add.right_neutral add_mono_thms_linordered_semiring(1) of_nat_0_le_iff) lemma sendupd_performs_InvCapsNonneg: assumes "InvCapsNonneg c0" and "next_sendupd' c0 c1 p tt" shows "InvCapsNonneg c1" using assms by (simp add: InvCapsNonneg_def next_sendupd'_def Let_def) lemma recvupd_preserves_InvCapsNonneg: assumes "InvCapsNonneg c0" and "next_recvupd' c0 c1 p q" shows "InvCapsNonneg c1" using assms unfolding InvCapsNonneg_def next_recvupd'_def by simp lemma recvcap_preserves_InvCapsNonneg: assumes "InvCapsNonneg c0" and "next_recvcap' c0 c1 p t" shows "InvCapsNonneg c1" using assms unfolding InvCapsNonneg_def next_recvcap'_def by simp lemma next_preserves_InvCapsNonneg: "holds InvCapsNonneg s \ next s \ nxt (holds InvCapsNonneg) s" unfolding next'_def apply (elim disjE) subgoal using performop_preserves_InvCapsNonneg by auto subgoal using sendupd_performs_InvCapsNonneg by auto subgoal using recvupd_preserves_InvCapsNonneg by auto subgoal using recvcap_preserves_InvCapsNonneg by auto subgoal by simp done lemma alw_InvCapsNonneg: "spec s \ alw (holds InvCapsNonneg) s" using lift_invariant_to_spec next_preserves_InvCapsNonneg init_config_implies_InvCapsNonneg by blast lemma alw_InvRecordsNonneg: "spec s \ alw (holds InvRecordsNonneg) s" apply (rule alw_mp[where \ = "holds InvCapsNonneg"]) using alw_InvCapsNonneg apply assumption apply (rule all_imp_alw) unfolding InvCapsNonneg_def InvRecordsNonneg_def records_def apply (auto intro!: add_nonneg_nonneg sum_nonneg simp: zcount_sum) done subsubsection\Resulting lemmas\ lemma pos_caps_pos_records: assumes "InvCapsNonneg c" shows "0 < zcount (c_caps c p) x \ 0 < zcount (records c) x" proof - fix x assume "0 < zcount (c_caps c p) x" then have "0 < zcount (\p\UNIV. c_caps c p) x" using assms(1)[unfolded InvCapsNonneg_def] by (auto intro!: sum_pos2 simp: zcount_sum) then show "0 < zcount (records c) x" unfolding records_def by simp qed subsubsection\SafeRecordsMono\ text\The records in the system are monotonic, i.e. once @{text "records c"} contains no records up to some timestamp t, then it will stay that way forever.\ definition SafeRecordsMono :: "('p :: finite, 'a) computation \ bool" where "SafeRecordsMono s = (\t. RecordsVacantUpto (shd s) t \ alw (holds (\c. RecordsVacantUpto c t)) s)" lemma performop_preserves_RecordsVacantUpto: assumes "RecordsVacantUpto c0 t" and "next_performop' c0 c1 p \neg \mint_msg \mint_self" and "InvRecordsNonneg c1" and "InvCapsNonneg c0" shows "RecordsVacantUpto c1 t" proof - note InvRecordsNonneg = assms(3)[rule_format] { fix s let ?\pos = "timestamps (zmset_of \mint_msg) + zmset_of \mint_self" let ?\ = "?\pos - zmset_of \neg" note change = next_performopD[OF assms(2)] note complex_change = next_performop_complexD[OF assms(2)] assume s: "s \ t" "zcount (records c1) s \ 0" then have s_pos: "0 < zcount (records c1) s" using InvRecordsNonneg by (simp add: order_class.order.not_eq_order_implies_strict InvRecordsNonneg_def) have \_in_nrec: "0 < zcount ?\ t \ \t'\t. 0 < zcount (records c0) t'" for t apply (subst (asm) zcount_diff) apply (subst (asm) zcount_union) apply (drule zero_lt_diff) apply simp apply (drule zero_lt_add_disj) apply simp apply simp apply (erule disjE) subgoal apply (drule pos_image_zmset_obtain_pre[rotated]) apply (auto dest!: change(4)[unfolded minting_msg_def, rule_format] pos_caps_pos_records[OF assms(4)] less_imp_le) done subgoal by (auto dest!: change(3)[unfolded minting_self_def, rule_format] pos_caps_pos_records[OF assms(4)]) done have nrec0s: "zcount (records c0) s = 0" by (rule assms(1)[unfolded vacant_upto_def, rule_format, OF s(1)]) have "zcount (records c1) s \ 0" unfolding complex_change apply (subst zcount_union) apply (subst nrec0s) apply (subst add_0) apply (rule ccontr) unfolding not_le apply (drule \_in_nrec[of s]) apply (meson assms(1) order_trans pos_zcount_in_zmset s(1) vacant_upto_def zcount_ne_zero_iff) done with s_pos have False by linarith } note r = this from assms show ?thesis unfolding next_performop'_def Let_def vacant_upto_def apply clarify apply (rule ccontr) apply (rule r) apply auto done qed lemma next'_preserves_RecordsVacantUpto: fixes c0 :: "('p::finite, 'a) configuration" shows "InvCapsNonneg c0 \ InvRecordsNonneg c1 \ RecordsVacantUpto c0 t \ next' c0 c1 \ RecordsVacantUpto c1 t" unfolding next'_def apply (elim disjE) subgoal by (auto intro: performop_preserves_RecordsVacantUpto) subgoal by (auto simp: next_sendupd'_def Let_def records_def) subgoal by (auto simp: next_recvupd'_def records_def) subgoal by (auto dest: next_recvcap_complexD) subgoal by simp done lemma alw_next_implies_alw_SafeRecordsMono: "alw next s \ alw (holds InvCapsNonneg) s \ alw (holds InvRecordsNonneg) s \ alw SafeRecordsMono s" apply (coinduction arbitrary: s) subgoal for s unfolding spec_def next'_def SafeRecordsMono_def Let_def apply (rule exI[of _ s]) apply safe subgoal for t apply (coinduction arbitrary: s rule: alw.coinduct) apply clarsimp apply (rule conjI) apply blast apply (erule alw.cases) apply clarsimp apply (metis (no_types, lifting) next'_def next'_preserves_RecordsVacantUpto alw_holds2) done apply blast done done lemma alw_SafeRecordsMono: "spec s \ alw SafeRecordsMono s" by (auto intro!: alw_next_implies_alw_SafeRecordsMono alw_InvRecordsNonneg alw_InvCapsNonneg simp: spec_def) subsubsection\InvJustifiedII and InvJustifiedGII\ text\These two invariants state that any net-positive change in the sum of incoming progress updates is "justified" by one of several statements being true.\ definition InvJustifiedII where "InvJustifiedII c = (\k p q. justified (c_caps c p) (IncomingInfo c k p q))" definition InvJustifiedGII where "InvJustifiedGII c = (\k p q. justified (records c) (GlobalIncomingInfo c k p q))" text\Given some zmset @{term M} justified wrt to @{term "caps c0 p"}, after a performop @{term "M + \"} is justified wrt to @{term "c_caps c1 p"}. This lemma captures the identical argument used for preservation of InvTempJustified and InvJustifiedII.\ lemma next_performop'_preserves_justified: assumes "justified (c_caps c0 p) M" and "next_performop' c0 c1 p \neg \mint_msg \mint_self" and "InvCapsNonneg c0" shows "justified (c_caps c1 p) (M + (timestamps (zmset_of \mint_msg) + zmset_of \mint_self - zmset_of \neg))" proof - let ?\pos = "timestamps (zmset_of \mint_msg) + zmset_of \mint_self" let ?\ = "?\pos - zmset_of \neg" let ?M1 = "M + ?\" note change = next_performopD[OF assms(2)] note complex_change = next_performop_complexD[OF assms(2)] note inv0 = assms(1)[unfolded InvJustifiedII_def justified_alt, rule_format] { fix k q t assume t: "0 < zcount ?M1 t" assume least: "\t' 0" from t consider "0 < zcount M t" | "zcount M t \ 0" "0 < zcount ?\ t" by atomize_elim (auto simp: complex_change) then have "supported_strong ?M1 t \ (\t' zcount ?M1 t < zcount (c_caps c1 p) t" proof cases case 1 \ \@{term M} was already positive at @{term t} in @{term c0}\ note Mcount = 1 note assms(1)[unfolded InvJustifiedII_def justified_alt, rule_format, OF Mcount] then consider "supported_strong M t" | "\ supported_strong M t" "\t't' 0" "zcount M t < zcount (c_caps c0 p) t" by atomize_elim auto then show ?thesis proof cases case 1 { assume nosupp: "\ supported_strong ?M1 t" assume "\t' 0 < zcount (c_caps c1 p) t'" then have nocaps: "\t's'. s' < s \ zcount M s' = 0" unfolding nonpos_upto_def supported_strong_def apply atomize_elim apply (elim exE conjE) apply (drule order_zmset_exists_foundation_neg) apply (elim exE) subgoal for _ s apply (rule exI[of _ s]) apply (rule conjI) using le_less_trans apply blast using less_imp_le order_trans order_class.order.not_eq_order_implies_strict apply blast done done have count1s: "0 \ zcount ?M1 s" apply (rule ccontr) apply (subst (asm) not_le) using nosupp[unfolded nonpos_upto_def supported_strong_def, simplified, rule_format] using least order.strict_trans2 s(1) apply fastforce done have \inc: "0 < zcount ?\ s" using complex_change(3) count1s s(2) by auto have "0 < zcount (timestamps (zmset_of \mint_msg)) s" using \inc change(2)[rule_format, of s] nocaps[rule_format, OF s(1)] unfolding change(9) fun_upd_same zcount_union zcount_diff by linarith then obtain u where u: "u < s" "0 < zcount (c_caps c0 p) u" "\u' supported_strong ?M1 t" assume "\t' 0 < zcount (c_caps c1 p) t'" then have nocaps: "\t's'counts: "\s. s < t \ count \neg s = zcount (c_caps c0 p) s" "\s. s < t \ count \mint_self s = 0" "\p s'. s' \ s \ count \mint_msg (p,s') = 0" subgoal for s' using change(2) nocaps s(1) order_class.order.not_eq_order_implies_strict by (fastforce simp: change(9)) subgoal for s' using nocaps[rule_format, of s'] by (simp add: change(9) \\s'. s' < t \ int (count \neg s') = zcount (c_caps c0 p) s'\) subgoal for p s' using change(4)[unfolded minting_msg_def, rule_format, of "(p,s')"] s(3) by (force intro: ccontr) done have caps_le_ii0: "zcount (c_caps c0 p) s \ zcount M s" proof (rule ccontr) assume nle: "\ zcount (c_caps c0 p) s \ zcount M s" have "zcount ?M1 s < 0" unfolding complex_change(3) using complex_change(4) nle s(3) by (auto simp: \counts(1,2)[OF s(1)]) then show False using s(1) least by (force dest!: nosupp[unfolded supported_strong_def, simplified, rule_format] simp: nonpos_upto_def) qed with s(2) have count0s: "0 < zcount M s" by auto have False using inv0[OF count0s] apply (elim disj3_split) using 2(1) order.strict_trans s(1) supported_strong_def apply blast using s(3) apply auto [] using caps_le_ii0 apply linarith done } then show ?thesis by blast next case 3 then show ?thesis (* positive and negative changes to a least pointstamp are directly reflected in c_caps *) apply (intro disjI2) unfolding complex_change(3) change(9) apply (simp only: if_P zcount_union zcount_diff) apply (subst complex_change(4)[of t]) using assms(3)[unfolded InvCapsNonneg_def, rule_format] - apply (simp add: order_class.antisym) - apply simp + apply (simp_all add: antisym) done qed next \ \Adding @{term \} made @{term M} positive at @{term t} in @{term c}1\ case 2 { assume nosupp: "\ supported_strong ?M1 t" assume "\t' 0 < zcount (c_caps c1 p) t'" then have nocaps: "\t' zcount ?M1 t < zcount (c_caps c1 p) t" then have caps_le: "zcount (c_caps c1 p) t \ zcount ?M1 t" by linarith from 2 have "count \neg t < zcount ?\pos t" by auto then have "0 < count \mint_self t \ 0 < zcount (timestamps (zmset_of \mint_msg)) t" by (metis 2(2) add.commute add.left_neutral not_gr_zero of_nat_0 zero_lt_diff zcount_diff zcount_of_mset zcount_union zcount_zmset_of_nonneg) then obtain s where s: "s \ t" "0 < zcount (c_caps c0 p) s" "\s'counts: "\s. s < t \ count \neg s = zcount (c_caps c0 p) s" "\s. s < t \ count \mint_self s = 0" "\p s'. s' \ s \ count \mint_msg (p,s') = 0" subgoal for s' using change(2) nocaps s(1) order_class.order.not_eq_order_implies_strict by (fastforce simp: change(9)) subgoal for s' using nocaps[rule_format, of s'] by (simp add: change(9) \\s'. s' < t \ int (count \neg s') = zcount (c_caps c0 p) s'\) subgoal for p s' using change(4)[unfolded minting_msg_def, rule_format, of "(p,s')"] s(3) by (force intro: ccontr) done { assume less: "s < t" have caps_le_ii0: "zcount (c_caps c0 p) s \ zcount M s" proof (rule ccontr) assume nle: "\ zcount (c_caps c0 p) s \ zcount M s" have "zcount ?M1 s < 0" unfolding complex_change(3) using complex_change(4) nle s(3) by (auto simp: \counts(1,2)[OF less]) then show False using less least order.strict_trans2 by (force dest!: nosupp[unfolded supported_strong_def, simplified, rule_format] simp: nonpos_upto_def) qed with s(2) have count0s: "0 < zcount M s" by auto have False using inv0[OF count0s] apply (elim disj3_split) subgoal proof - assume "supported_strong M s" then obtain u where u: "u < s" "zcount M u < 0" unfolding supported_strong_def by blast have "0 \ zcount ?M1 u" using least nosupp[unfolded supported_strong_def nonpos_upto_def, simplified, rule_format, of u] order.strict_trans[OF u(1) less] by fastforce then have "0 < zcount ?\pos u" using \counts(1)[of u] s(3) u(1) u(2) less by force then have "0 < count \mint_self u \ 0 < zcount (timestamps (zmset_of \mint_msg)) u" using gr0I by fastforce then obtain u' where "u' \ u" "0 < zcount (c_caps c0 p) u'" apply atomize_elim apply (elim disjE) subgoal apply simp apply (drule change(3)[unfolded minting_self_def, rule_format]) using s(1) s(2) apply blast done subgoal apply (drule pos_image_zmset_obtain_pre[rotated]) apply simp apply clarify subgoal for p' apply simp apply (drule change(4)[unfolded minting_msg_def, rule_format]) using order.strict_iff_order apply auto done done done then show False using s(3) u(1) by auto qed using s(3) apply auto [] using caps_le_ii0 apply linarith done } moreover { assume eq: "s = t" have count0t: "0 < zcount M t" using eq caps_le change(9) complex_change(3,4) s(2,3) by auto have False using 2(1) count0t by auto } ultimately have False using order.not_eq_order_implies_strict s(1) by blast } then show ?thesis by blast qed } then show "justified (c_caps c1 p) ?M1" by (intro justified_leastI) blast qed lemma InvJustifiedII_implies_InvJustifiedGII: assumes "InvJustifiedII c" and "InvCapsNonneg c" shows "InvJustifiedGII c" using assms unfolding InvJustifiedGII_def InvJustifiedII_def GlobalIncomingInfo_def records_def InvCapsNonneg_def by (auto intro!: justified_add_records justified_sum) lemma init_config_implies_InvJustifiedII: "init_config c \ InvJustifiedII c" by (simp add: init_config_def InvJustifiedII_def justified_alt IncomingInfo_def) lemma performop_preserves_InvJustifiedII: assumes "InvJustifiedII c0" and "next_performop' c0 c1 p \neg \mint_msg \mint_self" and "InvCapsNonneg c0" shows "InvJustifiedII c1" unfolding InvJustifiedII_def apply clarify subgoal for k p' q apply (cases "p'=p") subgoal unfolding next_performop_complexD[OF assms(2)] apply (simp only: if_P) apply (rule next_performop'_preserves_justified[ OF assms(1)[unfolded InvJustifiedII_def, rule_format, of p k q], OF assms(2,3)]) done subgoal unfolding next_performopD[OF assms(2)] next_performop_complexD[OF assms(2)] using assms(1)[unfolded InvJustifiedII_def] by simp done done lemma sendupd_preserves_InvJustifiedII: assumes "InvJustifiedII c0" and "next_sendupd' c0 c1 p tt" shows "InvJustifiedII c1" unfolding InvJustifiedII_def justified_alt supported_strong_def next_sendupdD(6)[OF assms(2)] apply (intro allI) subgoal for k p' q t apply (cases "k \ length (c_msg c0 p q)") subgoal apply (drule next_sendupd_complexD(4)[OF assms(2), of _ _ p']) apply (auto dest: assms(1)[unfolded InvJustifiedII_def justified_alt supported_strong_def, rule_format]) done subgoal apply (subst (asm) not_le) apply (subst (1 2 3 4) next_sendupd_complexD(5)[OF assms(2), of _ _ p']) apply simp apply simp apply (cases "p'=p") subgoal apply rule apply (subst (asm) if_P) apply simp apply (subst (1 2 3) if_P) apply simp apply simp unfolding IncomingInfo_def apply (subst (asm) drop_all) apply simp apply (subst drop_all) apply simp apply (simp del: zcount_diff) \ \The justified condition ensures that anything remaining in temp satisfies this invariant\ apply (drule next_sendupdD(2)[OF assms(2), unfolded justified_alt supported_strong_def, rule_format]) apply simp done subgoal by (auto intro!: assms(1)[unfolded InvJustifiedII_def justified_alt supported_strong_def, rule_format]) done done done lemma recvupd_preserves_InvJustifiedII: assumes "InvJustifiedII c0" and "next_recvupd' c0 c1 p q" shows "InvJustifiedII c1" using assms(1) unfolding InvJustifiedII_def next_recvupd_complexD[OF assms(2)] next_recvupdD[OF assms(2)] by auto lemma recvcap_preserves_InvJustifiedII: assumes "InvJustifiedII c0" and "next_recvcap' c0 c1 p t" shows "InvJustifiedII c1" unfolding InvJustifiedII_def justified_alt supported_strong_def next_recvcap_complexD[OF assms(2)] next_recvcapD(5)[OF assms(2)] by (auto dest!: assms(1)[unfolded InvJustifiedII_def justified_alt supported_strong_def, rule_format]) lemma next'_preserves_InvJustifiedII: "InvCapsNonneg c0 \ InvJustifiedII c0 \ next' c0 c1 \ InvJustifiedII c1" using next'_def performop_preserves_InvJustifiedII recvcap_preserves_InvJustifiedII recvupd_preserves_InvJustifiedII sendupd_preserves_InvJustifiedII by blast lemma alw_InvJustifiedII: "spec s \ alw (holds InvJustifiedII) s" apply (frule alw_InvCapsNonneg) unfolding spec_def apply (elim conjE) apply (subst (asm) holds.simps) apply (drule init_config_implies_InvJustifiedII) apply (coinduction arbitrary: s rule: alw.coinduct) apply (subst (asm) (1 2) alw_nxt) apply clarsimp using next'_preserves_InvJustifiedII apply blast done lemma alw_InvJustifiedGII: "spec s \ alw (holds InvJustifiedGII) s" apply (frule alw_InvJustifiedII) apply (drule alw_InvCapsNonneg) apply (simp add: alw_iff_sdrop InvJustifiedII_implies_InvJustifiedGII) done subsubsection\InvTempJustified\ definition InvTempJustified where "InvTempJustified c = (\p. justified (c_caps c p) (c_temp c p))" lemma init_config_implies_InvTempJustified: "init_config c \ InvTempJustified c" unfolding init_config_def InvTempJustified_def using justified_add_records[OF justified_empty] by auto lemma recvcap_preserves_InvTempJustified: assumes "InvTempJustified c0" and "next_recvcap' c0 c1 p t" shows "InvTempJustified c1" using assms(1)[unfolded InvTempJustified_def, rule_format, of p] assms(1) unfolding next_recvcapD[OF assms(2)] InvTempJustified_def by - (frule justified_add_records[of _ _ "{#t#}\<^sub>z"], auto) lemma recvupd_preserves_InvTempJustified: assumes "InvTempJustified c0" and "next_recvupd' c0 c1 p t" shows "InvTempJustified c1" using assms(1) unfolding next_recvupdD[OF assms(2)] InvTempJustified_def by assumption lemma sendupd_preserves_InvTempJustified: assumes "InvTempJustified c0" and "next_sendupd' c0 c1 p tt" shows "InvTempJustified c1" using assms(1) unfolding next_sendupdD[OF assms(2)] InvTempJustified_def using next_sendupdD(2)[OF assms(2)] by auto lemma performop_preserves_InvTempJustified: assumes "InvTempJustified c0" and "next_performop' c0 c1 p \neg \mint_msg \mint_self" and "InvCapsNonneg c0" shows "InvTempJustified c1" unfolding InvTempJustified_def apply clarify subgoal for p' apply (cases "p'=p") subgoal unfolding next_performopD(5)[OF assms(2)] fun_upd_apply apply (simp only: if_P) apply (rule next_performop'_preserves_justified[ OF assms(1)[unfolded InvTempJustified_def, rule_format, of p], OF assms(2,3)]) done subgoal unfolding next_performopD[OF assms(2)] next_performop_complexD[OF assms(2)] using assms(1)[unfolded InvTempJustified_def] by simp done done lemma next'_preserves_InvTempJustified: "InvCapsNonneg c0 \ InvTempJustified c0 \ next' c0 c1 \ InvTempJustified c1" using next'_def performop_preserves_InvTempJustified recvcap_preserves_InvTempJustified recvupd_preserves_InvTempJustified sendupd_preserves_InvTempJustified by blast lemma alw_InvTempJustified: "spec s \ alw (holds InvTempJustified) s" apply (frule alw_InvCapsNonneg) unfolding spec_def apply (elim conjE) apply (subst (asm) holds.simps) apply (drule init_config_implies_InvTempJustified) apply (coinduction arbitrary: s rule: alw.coinduct) apply (subst (asm) (1 2) alw_nxt) apply clarsimp using next'_preserves_InvTempJustified apply blast done subsubsection\InvGlobNonposImpRecordsNonpos\ text\InvGlobNonposImpRecordsNonpos states that each processor's @{term "c_glob c q"} is a conservative approximation of @{term "records c"}.\ definition InvGlobNonposImpRecordsNonpos :: "('p :: finite, 'a) configuration \ bool" where "InvGlobNonposImpRecordsNonpos c = (\t q. nonpos_upto (c_glob c q) t \ nonpos_upto (records c) t)" definition InvGlobVacantImpRecordsVacant :: "('p :: finite, 'a) configuration \ bool" where "InvGlobVacantImpRecordsVacant c = (\t q. GlobVacantUpto c q t \ RecordsVacantUpto c t)" lemma invs_imp_InvGlobNonposImpRecordsNonpos: assumes "InvJustifiedGII c" and "InvRecordCount c" and "InvRecordsNonneg c" shows "InvGlobNonposImpRecordsNonpos c" unfolding InvGlobNonposImpRecordsNonpos_def apply (rule ccontr) apply (clarsimp simp: nonpos_upto_def) subgoal for t q u proof - let ?GII = "GlobalIncomingInfoAt c q" assume gvu: "\sa\t. zcount (c_glob c q) sa \ 0" assume uleqt: "u \ t" assume "\ zcount (records c) u \ 0" \ \u is pointstamp that violates @{term InvGlobNonposImpRecordsNonpos}\ with assms(2) have u: "0 < zcount (records c) u" by linarith \ \u' is the least pointstamp with positive @{term records}\ with uleqt obtain u' where u': "0 < zcount (records c) u'" "\u. 0 < zcount (records c) u \ \ u < u'" "u' \ t" using order_zmset_exists_foundation[OF u] by auto \ \from the @{term records} count we know that GII also has positive count\ from u'(1,3) assms(2) gvu have pos_gii: "0 < zcount ?GII u'" - unfolding InvRecordCount_def - by (metis add_diff_cancel_left' diff_eq_eq less_add_same_cancel1 order_class.order.strict_trans1 zcount_diff) + by (simp add: InvRecordCount_def) (smt (verit) zcount_union) \ \Case distinction on which part of the partition GII's u is in\ { \ \Original proof from Abadi paper, change is justified by uprightness\ assume "supported_strong ?GII u'" \ \uprightness gives us a lesser pointstamp with negative count in GII..\ then obtain v where v: "v \ u'" "zcount ?GII v < 0" using order.strict_implies_order supported_strong_def by blast \ \..which is also negative in @{term records}..\ with u'(3) v(1) assms(2) have "zcount (records c) v < 0" - by (metis (no_types, hide_lams) InvRecordCount_def add.commute gvu less_add_same_cancel2 order.trans not_le order_class.order.strict_trans2 zcount_union) + by (smt (verit, ccfv_threshold) InvRecordCount_def gvu local.order_trans zcount_union) \ \..contradicting InvNrecNonneg\ with assms(3) have "False" unfolding InvRecordsNonneg_def using order_class.leD by blast } moreover { \ \Change is justified by strictly lesser pointstamp in @{term records}\ assume "\t' \v is a strictly lesser positive count in @{term records}..\ then obtain v where v: "v < u'" "0 < zcount (records c) v" by auto \ \..which contradicts the fact that we obtained @{term u'} as the least, positive pointstamp in @{term records}\ with u'(2) have "False" by auto } moreover { \ \Change is justified by records\ assume "zcount ?GII u' < zcount (records c) u'" then have "0 < zcount (c_glob c q) u'" by (simp add: assms(2)[unfolded InvRecordCount_def, rule_format, of q]) then have False using gvu u'(3) by auto } ultimately show False using pos_gii assms(1)[unfolded InvJustifiedGII_def justified_alt, rule_format, OF pos_gii] by auto qed done text\InvGlobVacantImpRecordsVacant is the one proved in the Abadi paper. We prove InvGlobNonposImpRecordsNonpos, which implies this.\ lemma invs_imp_InvGlobVacantImpRecordsVacant: assumes "InvJustifiedGII c" and "InvRecordCount c" and "InvRecordsNonneg c" shows "InvGlobVacantImpRecordsVacant c" proof - { fix p x assume "GlobVacantUpto c p x" then have "GlobNonposUpto c p x" unfolding nonpos_upto_def vacant_upto_def by simp note invs_imp_InvGlobNonposImpRecordsNonpos[OF assms, unfolded InvGlobNonposImpRecordsNonpos_def, rule_format, OF this] then have "RecordsVacantUpto c x" using assms(3) unfolding nonpos_upto_def vacant_upto_def InvRecordsNonneg_def by (simp add: order_class.order.antisym) } then show ?thesis unfolding InvGlobVacantImpRecordsVacant_def by simp qed lemma alw_InvGlobNonposImpRecordsNonpos: "spec s \ alw (holds InvGlobNonposImpRecordsNonpos) s" apply (frule alw_InvJustifiedGII) apply (frule alw_InvRecordCount) apply (drule alw_InvRecordsNonneg) apply (simp add: alw_iff_sdrop invs_imp_InvGlobNonposImpRecordsNonpos) done lemma alw_InvGlobVacantImpRecordsVacant: "spec s \ alw (holds InvGlobVacantImpRecordsVacant) s" apply (frule alw_InvGlobNonposImpRecordsNonpos) apply (frule alw_InvJustifiedGII) apply (frule alw_InvRecordCount) apply (drule alw_InvRecordsNonneg) apply (simp add: alw_iff_sdrop invs_imp_InvGlobVacantImpRecordsVacant) done subsubsection\SafeGlobVacantUptoImpliesStickyNrec\ text\This is the main safety property proved in the Abadi paper.\ lemma invs_imp_SafeGlobVacantUptoImpliesStickyNrec: "SafeRecordsMono s \ holds InvGlobVacantImpRecordsVacant s \ SafeGlobVacantUptoImpliesStickyNrec s" by (simp add: InvGlobVacantImpRecordsVacant_def SafeRecordsMono_def SafeGlobVacantUptoImpliesStickyNrec_def) lemma alw_SafeGlobVacantUptoImpliesStickyNrec: "spec s \ alw SafeGlobVacantUptoImpliesStickyNrec s" by (meson alw_iff_sdrop invs_imp_SafeGlobVacantUptoImpliesStickyNrec alw_SafeRecordsMono alw_InvGlobVacantImpRecordsVacant) subsubsection\InvGlobNonposEqVacant\ text\The least pointstamps in glob are always positive, i.e. @{term nonpos_upto} and @{term vacant_upto} on glob are equivalent.\ definition InvGlobNonposEqVacant where "InvGlobNonposEqVacant c = (\q t. GlobVacantUpto c q t = GlobNonposUpto c q t)" lemma invs_imp_InvGlobNonposEqVacant: assumes "InvRecordCount c" and "InvJustifiedGII c" and "InvRecordsNonneg c" shows "InvGlobNonposEqVacant c" proof - note safe = invs_imp_InvGlobNonposImpRecordsNonpos[OF assms(2,1,3), unfolded InvGlobNonposImpRecordsNonpos_def nonpos_upto_def, THEN spec2, THEN mp] note nonneg = assms(3)[unfolded InvRecordsNonneg_def, rule_format] note count = assms(1)[unfolded InvRecordCount_def, rule_format] { fix q t assume np: "GlobNonposUpto c q t" assume nv: "\ GlobVacantUpto c q t" \ \Obtain the least, negative pointstamp in glob\ obtain s where s: "s \ t" "zcount (c_glob c q) s < 0" "\s' \No records @{term "s' \ s"} can exist, since that would violate InvGlobNonposImpRecordsNonpos\ have norec: "s' \ s \ zcount (records c) s' = 0" for s' - by (metis (full_types) order.ordering_axioms nonneg nonpos_upto_def np order_class.antisym_conv ordering.trans s(1) safe) + by (metis (full_types) nonneg nonpos_upto_def np order_class.antisym_conv order_trans s(1) safe) \ \Hence GII must be positive at @{term s}\ have gii: "zcount (GlobalIncomingInfoAt c q) s > 0" using count[of q] s(2) norec[of s, simplified] by (metis add.commute less_add_same_cancel1 zcount_union) \ \which means it must be justified by one of these three disjuncts\ then consider "supported_strong (GlobalIncomingInfoAt c q) s" | "\t' \@{term s} can't be @{term supported_strong}, since either glob or records would have to be positive at the support\ then show False using norec count s(3) unfolding supported_strong_def by (metis (full_types) add.commute add.left_neutral less_le preorder_class.less_irrefl zcount_union) next case 2 \ \no lesser capabilities exist\ then show ?thesis using norec s(2,3) order.order_iff_strict by auto next case 3 \ \no capabilities at @{term s} exist\ then show ?thesis unfolding norec[of s, simplified] using gii by auto qed } then show ?thesis unfolding InvGlobNonposEqVacant_def apply (intro allI) apply (rule iffI) apply (simp add: nonpos_upto_def vacant_upto_def) apply auto done qed lemma alw_InvGlobNonposEqVacant: "spec s \ alw (holds InvGlobNonposEqVacant) s" using alw_InvJustifiedGII alw_InvRecordCount alw_InvRecordsNonneg invs_imp_InvGlobNonposEqVacant by (metis alw_iff_sdrop holds.elims(2) holds.elims(3)) subsubsection\InvInfoJustifiedWithII and InvInfoJustifiedWithGII\ definition InvInfoJustifiedWithII where "InvInfoJustifiedWithII c = (\k p q. justified_with (c_caps c p) (InfoAt c k p q) (IncomingInfo c (k+1) p q))" definition InvInfoJustifiedWithGII where "InvInfoJustifiedWithGII c = (\k p q. justified_with (records c) (InfoAt c k p q) (GlobalIncomingInfo c (k+1) p q))" lemma init_config_implies_InvInfoJustifiedWithII: "init_config c \ InvInfoJustifiedWithII c" unfolding init_config_def InvInfoJustifiedWithII_def justified_with_def InfoAt_def by auto text\This proof relies heavily on the addition properties summarized in the lemma @{thm "next_performop'_preserves_justified_with"}\ lemma performop_preserves_InvInfoJustifiedWithII: assumes "InvInfoJustifiedWithII c0" and "next_performop' c0 c1 p' \neg \mint_msg \mint_self" and "InvJustifiedII c0" and "InvCapsNonneg c0" shows "InvInfoJustifiedWithII c1" unfolding InvInfoJustifiedWithII_def apply (intro allI) subgoal for k p q apply (cases "p'=p") subgoal unfolding next_performop_complexD[OF assms(2)] next_performopD[OF assms(2)] apply (simp only: add_diff_eq if_P fun_upd_same) apply (subst (4) add.commute) apply (subst add.assoc[symmetric]) apply (rule next_performop'_preserves_justified_with) using assms(1)[unfolded InvInfoJustifiedWithII_def] apply simp using assms(2) apply simp using assms(4)[unfolded InvCapsNonneg_def] apply simp using assms(3)[unfolded InvJustifiedII_def] apply simp done subgoal using assms(1)[unfolded InvInfoJustifiedWithII_def justified_with_def] by (auto simp: not_less justified_with_def next_performop_complexD[OF assms(2)] next_performopD[OF assms(2)]) done done lemma sendupd_preserves_InvInfoJustifiedWithII: assumes "InvInfoJustifiedWithII c0" and "next_sendupd' c0 c1 p' tt" and "InvTempJustified c0" shows "InvInfoJustifiedWithII c1" unfolding InvInfoJustifiedWithII_def proof (intro allI impI) fix k :: nat and p q note complex = next_sendupd_complexD[OF assms(2)] note change = next_sendupdD[OF assms(2)] consider "p' = p" "k < length (c_msg c0 p q)" | "p' = p" "k = length (c_msg c0 p q)" | "p' = p" "k > length (c_msg c0 p q)" | "p' \ p" by atomize_elim auto then show "justified_with (c_caps c1 p) (InfoAt c1 k p q) (IncomingInfo c1 (k+1) p q)" proof cases case 1 then show ?thesis by (metis InvInfoJustifiedWithII_def Suc_eq_plus1 Suc_le_eq assms(1) change(6) complex(4,7) order_class.less_le) next case 2 have temp0: "c_temp c0 p = InfoAt c1 k p q + c_temp c1 p" unfolding complex change using 2 by (auto simp: algebra_simps) have pi: "PositiveImplies (InfoAt c1 k p q) (c_temp c0 p)" unfolding PositiveImplies_def complex using 2(2) by (auto simp: InfoAt_def) have iitemp: "IncomingInfo c1 (k+1) p q = c_temp c1 p" unfolding IncomingInfo_def using 2 by (simp add: change) show ?thesis apply (rule PositiveImplies_justified_with) unfolding iitemp temp0[symmetric] unfolding change using assms(3) apply (simp add: InvTempJustified_def) using pi apply simp done next case 3 then show ?thesis by (metis add_cancel_right_left complex(7) justified_with_leastI preorder_class.less_asym InfoAt_def zcount_union) next case 4 then show ?thesis unfolding complex change using assms(1)[unfolded InvInfoJustifiedWithII_def, rule_format] apply simp done qed qed lemma recvupd_preserves_InvInfoJustifiedWithII: assumes "InvInfoJustifiedWithII c0" and "next_recvupd' c0 c1 p q" shows "InvInfoJustifiedWithII c1" using assms(1) unfolding InvInfoJustifiedWithII_def next_recvupd_complexD[OF assms(2)] next_recvupdD[OF assms(2)] by auto lemma recvcap_preserves_InvInfoJustifiedWithII: assumes "InvInfoJustifiedWithII c0" and "next_recvcap' c0 c1 p t" shows "InvInfoJustifiedWithII c1" using assms(1) unfolding InvInfoJustifiedWithII_def next_recvcap_complexD[OF assms(2)] next_recvcapD[OF assms(2)] by simp lemma invs_imp_InvInfoJustifiedWithGII: assumes "InvInfoJustifiedWithII c" and "InvJustifiedII c" and "InvCapsNonneg c" shows "InvInfoJustifiedWithGII c" unfolding InvInfoJustifiedWithGII_def apply clarify subgoal for k p q unfolding GlobalIncomingInfo_def records_def apply (rule justified_with_add_records) subgoal apply (rule justified_with_sum[where x = p]) apply simp apply simp apply simp using assms(1)[unfolded InvInfoJustifiedWithII_def, rule_format, of p k q] apply simp using assms(2)[unfolded InvJustifiedII_def] apply simp using assms(3)[unfolded InvCapsNonneg_def] apply simp done apply simp done done lemma next'_preserves_InvInfoJustifiedWithII: assumes "InvInfoJustifiedWithII c0" and "next' c0 c1" and "InvCapsNonneg c0" and "InvJustifiedII c0" and "InvTempJustified c0" shows "InvInfoJustifiedWithII c1" using assms unfolding next'_def apply (elim disjE exE) apply (drule (4) performop_preserves_InvInfoJustifiedWithII) apply (drule (3) sendupd_preserves_InvInfoJustifiedWithII) apply (drule (2) recvupd_preserves_InvInfoJustifiedWithII) apply (drule (2) recvcap_preserves_InvInfoJustifiedWithII) apply simp done lemma alw_InvInfoJustifiedWithII: "spec s \ alw (holds InvInfoJustifiedWithII) s" apply (frule alw_InvCapsNonneg) apply (frule alw_InvJustifiedII) apply (frule alw_InvTempJustified) unfolding spec_def apply (elim conjE) apply (subst (asm) holds.simps) apply (drule init_config_implies_InvInfoJustifiedWithII) apply (coinduction arbitrary: s rule: alw.coinduct) apply (subst (asm) (1 2 3) alw_nxt) apply clarsimp using next'_preserves_InvInfoJustifiedWithII apply blast done lemma alw_InvInfoJustifiedWithGII: "spec s \ alw (holds InvInfoJustifiedWithGII) s" by (metis alw_InvCapsNonneg alw_InvInfoJustifiedWithII alw_InvJustifiedII alw_iff_sdrop holds.elims(2,3) invs_imp_InvInfoJustifiedWithGII) subsubsection\SafeGlobMono and InvMsgInGlob\ text\The records in glob are monotonic. This implies the corollary InvMsgInGlob; No incoming message carries a timestamp change that would cause glob to regress.\ definition SafeGlobMono where "SafeGlobMono c0 c1 = (\p t. GlobVacantUpto c0 p t \ GlobVacantUpto c1 p t)" definition InvMsgInGlob where "InvMsgInGlob c = (\p q t. c_msg c p q \ [] \ t \#\<^sub>z hd (c_msg c p q) \ (\t'\t. 0 < zcount (c_glob c q) t'))" lemma not_InvMsgInGlob_imp_not_SafeGlobMono: assumes "\ InvMsgInGlob c0" and "InvGlobNonposEqVacant c0" shows "\c1. next_recvupd c0 c1 \ \ SafeGlobMono c0 c1" proof - note npeq0 = assms(2)[unfolded InvGlobNonposEqVacant_def, rule_format] from assms(1) obtain p q t c1 where t: "next_recvupd' c0 c1 p q" "t \#\<^sub>z hd (c_msg c0 p q)" "GlobVacantUpto c0 q t" by (auto simp: InvMsgInGlob_def npeq0 nonpos_upto_def not_less dest!: ex_next_recvupd) have nvu: "\ GlobVacantUpto c1 q t" unfolding vacant_upto_def next_recvupdD(4)[OF t(1)] using t(2) t(3) vacant_upto_def by auto from t(3) nvu have "\ SafeGlobMono c0 c1" by (auto simp: SafeGlobMono_def) with t(1) show ?thesis by blast qed lemma GII_eq_GIA: "GlobalIncomingInfo c 1 p q = (if c_msg c p q = [] then GlobalIncomingInfoAt c q else GlobalIncomingInfoAt c q - hd (c_msg c p q))" unfolding GlobalIncomingInfo_def apply (cases "c_msg c p q = []") apply (simp add: IncomingInfo_def) apply (rule sum.cong[OF refl], simp) apply simp apply (subst diff_conv_add_uminus) apply (rule Sum_eq_pick_changed_elem) apply (auto simp: IncomingInfo_def drop_Suc sum_list_hd_tl algebra_simps) done lemma recvupd_preserves_GlobVacantUpto: assumes "GlobVacantUpto c0 q t" and "next_recvupd' c0 c1 p q" and "InvInfoJustifiedWithGII c0" and "InvGlobNonposEqVacant c1" and "InvGlobVacantImpRecordsVacant c0" and "InvRecordCount c0" shows "GlobVacantUpto c1 q t" proof (rule ccontr) note npeq1 = assms(4)[unfolded InvGlobNonposEqVacant_def, rule_format] note gvu0 = assms(1)[unfolded vacant_upto_def, rule_format] note nvu0 = assms(5)[unfolded InvGlobVacantImpRecordsVacant_def, rule_format, OF assms(1)] note recordcount = assms(6)[unfolded InvRecordCount_def zmultiset_eq_iff zcount_union, rule_format] note change = next_recvupdD[OF assms(2)] let ?\ = "hd (c_msg c0 p q)" from change(1) have ia\: "?\ = InfoAt c0 0 p q" unfolding InfoAt_def by (simp add: hd_conv_nth) assume gvu1: "\ GlobVacantUpto c1 q t" obtain t' where t': "t' \ t" "0 < zcount (c_glob c1 q) t'" "zcount (c_glob c0 q) t' = 0" "\s 0" apply atomize_elim using gvu1 unfolding npeq1 nonpos_upto_def apply (simp add: not_le) apply (elim exE conjE) apply (drule order_zmset_exists_foundation') apply (elim exE conjE) subgoal for s s' apply (rule exI[of _ s']) using gvu0 apply auto done done from t'(2,3) have count\: "0 < zcount ?\ t'" by (auto simp: change(4)) note assms(3)[unfolded InvInfoJustifiedWithGII_def justified_with_alt, rule_format, OF count\[unfolded ia\]] then consider "\s (\s' 0)" | "\s (\s' 0)" "\s. s < t' \ zcount (InfoAt c0 0 p q) s < 0 \ (\s' 0)" | "\ss' 0" by blast then have globs: "zcount (c_glob c1 q) s < 0" using gvu0 ia\ t'(1) by (auto simp: change(4)) have "zcount (c_glob c1 q) s \ 0" using globs by linarith then have "\s'\s. zcount (c_glob c1 q) s' \ 0" using s(1) t'(4) by auto with globs show False by (auto simp: npeq1[unfolded nonpos_upto_def, symmetric] vacant_upto_def) next case 2 then obtain s where s: "s < t'" "zcount (GlobalIncomingInfo c0 1 p q) s < 0" "\s' 0" by blast from 2(2) s(1,3) have "zcount (InfoAt c0 0 p q) s \ 0" by force have rc: "zcount (records c0) s = 0" using order.strict_implies_order order.strict_trans2 nvu0 s(1) t'(1) vacant_upto_def by blast show False using assms(6) change(1,4) s(1,2) rc t'(4) unfolding GII_eq_GIA InvRecordCount_def fun_upd_same by clarsimp (metis add.commute add_mono_thms_linordered_field(1) not_le recordcount) next case 3 with nvu0 t'(1) show False unfolding vacant_upto_def by auto next case 4 then have "0 < zcount (c_glob c0 q) t'" using change(1) ia\ t'(3) unfolding GII_eq_GIA apply clarsimp apply (metis add.right_neutral preorder_class.less_irrefl recordcount) done then show False by (simp add: t'(3)) qed qed lemma recvupd_imp_SafeGlobMono: assumes "next_recvupd' c0 c1 p q" and "InvInfoJustifiedWithGII c0" and "InvGlobNonposEqVacant c1" and "InvGlobVacantImpRecordsVacant c0" and "InvRecordCount c0" shows "SafeGlobMono c0 c1" unfolding SafeGlobMono_def apply clarify subgoal for q' t apply (cases "q'=q") subgoal apply (rule recvupd_preserves_GlobVacantUpto) using assms apply simp_all done subgoal unfolding next_recvupdD[OF assms(1)] by simp done done lemma next'_imp_SafeGlobMono: assumes "next' c0 c1" and "InvInfoJustifiedWithGII c0" and "InvGlobNonposEqVacant c1" and "InvGlobVacantImpRecordsVacant c0" and "InvRecordCount c0" shows "SafeGlobMono c0 c1" using assms unfolding next'_def apply (elim disjE exE) subgoal by (auto simp: SafeGlobMono_def dest: next_performopD(7)) subgoal by (auto simp: SafeGlobMono_def dest: next_sendupdD(5)) subgoal by (auto intro: recvupd_imp_SafeGlobMono) subgoal by (auto simp: SafeGlobMono_def dest: next_recvcapD(4)) subgoal by (simp add: SafeGlobMono_def) done lemma invs_imp_InvMsgInGlob: fixes c0 :: "('p::finite, 'a) configuration" assumes "InvInfoJustifiedWithGII c0" and "InvGlobNonposEqVacant c0" and "InvGlobVacantImpRecordsVacant c0" and "InvRecordCount c0" and "InvJustifiedII c0" and "InvCapsNonneg c0" and "InvRecordsNonneg c0" shows "InvMsgInGlob c0" using assms apply - apply (rule ccontr) apply (drule not_InvMsgInGlob_imp_not_SafeGlobMono[rotated, OF assms(2)]) apply (elim exE conjE) apply (frule recvupd_imp_SafeGlobMono) apply simp subgoal apply (rule invs_imp_InvGlobNonposEqVacant) apply (drule (2) recvupd_preserves_InvRecordCount) apply (drule (1) recvupd_preserves_InvJustifiedII) apply (rule InvJustifiedII_implies_InvJustifiedGII) apply simp apply (drule (2) recvupd_preserves_InvCapsNonneg) apply (simp add: InvRecordsNonneg_def next_recvupd_complexD) done apply simp apply simp apply simp done lemma alw_SafeGlobMono: "spec s \ alw (relates SafeGlobMono) s" proof - assume spec: "spec s" { assume "alw next s" moreover assume "alw (holds InvGlobNonposEqVacant) s" moreover assume "alw (holds InvGlobVacantImpRecordsVacant) s" moreover assume "alw (holds InvInfoJustifiedWithGII) s" moreover assume "alw (holds InvRecordCount) s" ultimately have "alw (relates SafeGlobMono) s" apply (coinduction arbitrary: s) apply (clarsimp simp: relates_def intro!: next'_imp_SafeGlobMono) apply (rule conjI) apply (rule next'_imp_SafeGlobMono) apply auto [2] apply (subst (asm) alw_holds2) apply clarify apply (drule alwD)+ apply simp apply auto done } with spec show ?thesis apply - apply (frule alw_InvGlobNonposEqVacant) apply (frule alw_InvGlobVacantImpRecordsVacant) apply (frule alw_InvInfoJustifiedWithGII) apply (frule alw_InvRecordCount) unfolding spec_def apply auto done qed lemma alw_InvMsgInGlob: "spec s \ alw (holds InvMsgInGlob) s" apply (frule alw_InvInfoJustifiedWithGII) apply (frule alw_InvGlobNonposEqVacant) apply (frule alw_InvGlobVacantImpRecordsVacant) apply (frule alw_InvRecordCount) apply (frule alw_InvJustifiedII) apply (frule alw_InvCapsNonneg) apply (drule alw_InvRecordsNonneg) apply (simp add: alw_iff_sdrop invs_imp_InvMsgInGlob) done lemma SafeGlobMono_preserves_vacant: assumes "\t'\t. zcount (c_glob c0 q) t' = 0" and "(\c0 c1. SafeGlobMono c0 c1)\<^sup>*\<^sup>* c0 c1" shows "\t'\t. zcount (c_glob c1 q) t' = 0" using assms(2,1) by (induct rule: rtranclp_induct)(auto simp: SafeGlobMono_def vacant_upto_def) lemma rtranclp_all_imp_rel: "r\<^sup>*\<^sup>* x y \ \a b. r a b \ r' a b \ r'\<^sup>*\<^sup>* x y" by (metis mono_rtranclp) lemma rtranclp_rel_and_invar: "r\<^sup>*\<^sup>* x y \ Q x \ \a b. Q a \ r a b \ P a b \ Q b \ (\x y. P x y \ Q y)\<^sup>*\<^sup>* x y" apply (induct rule: rtranclp_induct) apply auto [] apply (metis (no_types, lifting) rtranclp.simps) done lemma rtranclp_invar_conclude_last: "(\x y. P x y \ Q y)\<^sup>*\<^sup>* x y \ Q x \ Q y" using rtranclp.cases by fastforce lemma InvCapsNonneg_imp_InvRecordsNonneg: "InvCapsNonneg c \ InvRecordsNonneg c" unfolding InvCapsNonneg_def InvRecordsNonneg_def records_def by (auto simp: zcount_sum intro!: sum_nonneg add_nonneg_nonneg) lemma invs_imp_msg_in_glob: fixes c :: "('p::finite, 'a) configuration" assumes "M \ set (c_msg c p q)" and "t \#\<^sub>z M" and "InvGlobNonposEqVacant c" and "InvJustifiedII c" and "InvInfoJustifiedWithII c" and "InvGlobVacantImpRecordsVacant c" and "InvRecordCount c" and "InvCapsNonneg c" and "InvMsgInGlob c" shows "\t'\t. 0 < zcount (c_glob c q) t'" proof (rule ccontr) assume "\ (\t'\t. 0 < zcount (c_glob c q) t')" then have vacant: "\t'\t. zcount (c_glob c q) t' = 0" using assms(3) by (auto simp: InvGlobNonposEqVacant_def vacant_upto_def nonpos_upto_def) let ?c1 = "the (while_option (\c. hd (c_msg c p q) \ M) (\c. SOME c'. next_recvupd' c c' p q) c)" have r[simp]: "M \ set (c_msg c p q) \ c_msg (SOME c'. next_recvupd' c c' p q) p q = tl (c_msg c p q)" for c by (rule someI2_ex[OF ex_next_recvupd]) (auto simp: next_recvupd'_def) obtain c1 where while_some: "while_option (\c. hd (c_msg c p q) \ M) (\c. SOME c'. next_recvupd' c c' p q) c = Some c1" apply atomize_elim apply (rule measure_while_option_Some[where P="\c. M \ set (c_msg c p q)" and f="\c. Min {i. i < length (c_msg c p q) \ M = c_msg c p q ! i}"]) apply clarsimp apply safe apply (metis list.exhaust_sel list.sel(2) set_ConsD) apply (subst Min_gr_iff) apply (auto simp: in_set_conv_nth nth_tl) [2] apply clarsimp apply (subst Min_less_iff) apply (auto simp: in_set_conv_nth nth_tl) [] apply (clarsimp simp: in_set_conv_nth nth_tl) apply (metis (no_types, hide_lams) Suc_less_eq Suc_pred hd_conv_nth list.size(3) not_gr_zero not_less_zero) apply (clarsimp simp: in_set_conv_nth nth_tl) subgoal for s x by (rule exI[of _ "x-1"]) (metis One_nat_def Suc_le_eq Suc_pred' diff_less diff_less_mono hd_conv_nth length_tl list.size(3) not_gr_zero nth_tl zero_less_Suc) apply (meson assms(1) in_set_conv_nth) done have c1: "(\c0 c1. next_recvupd' c0 c1 p q)\<^sup>*\<^sup>* c c1" "c_msg c1 p q \ []" "hd (c_msg c1 p q) = M" subgoal apply (rule conjunct2) apply (rule while_option_rule[OF _ while_some, where P="\d. M \ set (c_msg d p q) \ (\c0 c1. next_recvupd' c0 c1 p q)\<^sup>*\<^sup>* c d"]) apply (rule conjI) apply (metis list.sel(1) list.sel(3) list.set_cases r) apply (auto simp: assms(1) elim!: rtrancl_into_rtrancl[to_pred] intro: someI2_ex[OF ex_next_recvupd]) done subgoal using while_option_rule[OF _ while_some, where P="\c. M \ set (c_msg c p q)"] by (metis assms(1) empty_iff hd_Cons_tl list.set(1) r set_ConsD) subgoal using while_option_stop[OF while_some] by simp done have invs_trancl: "(\c0 c1. SafeGlobMono c0 c1 \ InvJustifiedII c1 \ InvGlobNonposEqVacant c1 \ InvInfoJustifiedWithII c1 \ InvGlobVacantImpRecordsVacant c1 \ InvRecordCount c1 \ InvCapsNonneg c1)\<^sup>*\<^sup>* c c1" apply (rule rtranclp_rel_and_invar) using c1(1) apply simp using assms(3-8) apply simp apply clarsimp subgoal for a b apply (frule InvJustifiedII_implies_InvJustifiedGII) apply simp apply (frule recvupd_preserves_InvJustifiedII) apply simp apply (frule InvCapsNonneg_imp_InvRecordsNonneg) apply (frule next_preserves_InvRecordCount[of _ b]) apply (auto simp: next'_def) [] apply (frule recvupd_preserves_InvCapsNonneg[of _ b]) apply simp apply (frule InvJustifiedII_implies_InvJustifiedGII[of b]) apply simp apply (frule InvCapsNonneg_imp_InvRecordsNonneg[of b]) apply (frule invs_imp_InvGlobNonposEqVacant[of b]) apply simp_all [2] apply (frule recvupd_preserves_InvInfoJustifiedWithII[of _ b]) apply simp apply (frule invs_imp_InvInfoJustifiedWithGII) apply simp_all [2] apply (frule invs_imp_InvInfoJustifiedWithGII[of b]) apply simp_all [2] apply (intro conjI) apply (rule next'_imp_SafeGlobMono) apply (clarsimp simp: next'_def) apply simp_all [7] apply (rule invs_imp_InvGlobVacantImpRecordsVacant) apply simp_all done done then have trancl_mono: "(\c0 c1. SafeGlobMono c0 c1)\<^sup>*\<^sup>* c c1" by (metis (no_types, lifting) rtranclp_all_imp_rel) then have vacant_c1: "\t'\t. zcount (c_glob c1 q) t' = 0" by (rule SafeGlobMono_preserves_vacant[OF vacant]) have InvMsgInGlob: "InvMsgInGlob c1" using rtranclp_invar_conclude_last[OF invs_trancl] assms apply (intro invs_imp_InvMsgInGlob) using invs_imp_InvInfoJustifiedWithGII InvCapsNonneg_imp_InvRecordsNonneg apply blast+ done have "\t'\t. 0 < zcount (c_glob c1 q) t'" using InvMsgInGlob[unfolded InvMsgInGlob_def] c1(2,3) assms(1,2) by auto then show False using vacant_c1 by auto qed lemma alw_msg_glob: "spec s \ alw (holds (\c. \p q t. (\M \ set (c_msg c p q). t \#\<^sub>z M) \ (\t'\t. 0 < zcount (c_glob c q) t'))) s" apply (frule alw_InvGlobNonposEqVacant) apply (frule alw_InvJustifiedII) apply (frule alw_InvInfoJustifiedWithII) apply (frule alw_InvGlobVacantImpRecordsVacant) apply (frule alw_InvRecordCount) apply (frule alw_InvCapsNonneg) apply (drule alw_InvMsgInGlob) apply (coinduction arbitrary: s) apply clarsimp apply (rule conjI) apply clarify apply (rule invs_imp_msg_in_glob) apply auto [2] using holds.elims(2) apply blast+ done end (*<*) end (*>*) \ No newline at end of file diff --git a/thys/Progress_Tracking/Graph.thy b/thys/Progress_Tracking/Graph.thy --- a/thys/Progress_Tracking/Graph.thy +++ b/thys/Progress_Tracking/Graph.thy @@ -1,384 +1,384 @@ section\Multigraphs with Partially Ordered Weights\ (*<*) theory Graph imports "HOL-Library.Sublist" Antichain begin (*>*) abbreviation (input) FROM where "FROM \ \(s, l, t). s" abbreviation (input) LBL where "LBL \ \(s, l, t). l" abbreviation (input) TO where "TO \ \(s, l, t). t" notation subseq (infix "\" 50) locale graph = fixes weights :: "'vtx :: finite \ 'vtx \ 'lbl :: {order, monoid_add} antichain" assumes zero_le[simp]: "0 \ (s::'lbl)" and plus_mono: "(s1::'lbl) \ s2 \ s3 \ s4 \ s1 + s3 \ s2 + s4" and summary_self: "weights loc loc = {}\<^sub>A" begin lemma le_plus: "(s::'lbl) \ s + s'" "(s'::'lbl) \ s + s'" by (intro plus_mono[of s s 0 s', simplified] plus_mono[of 0 s s' s', simplified])+ subsection\Paths\ inductive path :: "'vtx \ 'vtx \ ('vtx \ 'lbl \ 'vtx) list \ bool" where path0: "l1 = l2 \ path l1 l2 []" | path: "path l1 l2 xs \ lbl \\<^sub>A weights l2 l3 \ path l1 l3 (xs @ [(l2, lbl, l3)])" inductive_cases path0E: "path l1 l2 []" inductive_cases path_AppendE: "path l1 l3 (xs @ [(l2,s,l2')])" lemma path_trans: "path l1 l2 xs \ path l2 l3 ys \ path l1 l3 (xs @ ys)" by (rotate_tac, induct l2 l3 ys rule: path.induct) (auto intro: path.path simp flip: append_assoc) lemma path_take_from: "path l1 l2 xs \ m < length xs \ FROM (xs ! m) = l2' \ path l1 l2' (take m xs)" proof (induct l1 l2 xs rule: path.induct) case (path l1 l2 xs lbl l3) then show ?case apply (unfold take_append) apply simp apply (cases "l2=l2'") apply (metis linorder_not_less nth_append take_all) apply (metis case_prod_conv less_Suc_eq nth_append nth_append_length) done qed simp lemma path_take_to: "path l1 l2 xs \ m < length xs \ TO (xs ! m) = l2' \ path l1 l2' (take (m+1) xs)" proof (induct l1 l2 xs rule: path.induct) case (path l1 l2 xs lbl l3) then show ?case apply (cases "m < length xs") apply (simp add: nth_append) apply clarsimp apply (metis case_prod_conv less_antisym nth_append_length path.path) done qed simp lemma path_determines_loc: "path l1 l2 xs \ path l1 l3 xs \ l2 = l3" by (induct l1 l2 xs rule: path.induct) (auto elim: path.cases) lemma path_first_loc: "path loc loc' xs \ xs \ [] \ FROM (xs ! 0) = loc" proof (induct rule: path.induct) case (path l1 l2 xs lbl l3) then show ?case by (auto elim: path0E simp: nth_append) qed simp lemma path_to_eq_from: "path loc1 loc2 xs \ i + 1 < length xs \ FROM (xs ! (i+1)) = TO (xs ! i)" proof (induct rule: path.induct) case (path l1 l2 xs lbl l3) then show ?case apply (cases "i + 1 < length xs") apply (simp add: nth_append) apply (simp add: nth_append) apply (metis add.commute drop_eq_Nil hd_drop_conv_nth id_take_nth_drop linorder_not_less path_determines_loc path_take_to plus_1_eq_Suc take_hd_drop) done qed simp lemma path_singleton[intro, simp]: "s \\<^sub>A weights l1 l2 \ path l1 l2 [(l1,s,l2)]" by (subst path.simps) (auto simp: path.intros) lemma path_appendE: "path l1 l3 (xs @ ys) \ \l2. path l2 l3 ys \ path l1 l2 xs" proof (induct l1 l3 "xs@ys" arbitrary: xs ys rule: path.induct) case (path0 l1 l2) then show ?case by (auto intro: path.intros) next case (path l1 l2 xs lbl l3 xs' ys') from path(1,3-) show ?case apply - apply (subst (asm) append_eq_append_conv2[of xs "[(l2,lbl,l3)]" xs' ys']) apply (elim exE conjE disjE) subgoal for us using path(2)[of xs' us] by (auto intro: path.intros) subgoal for us by (cases "us=[]") (auto intro: path.intros simp: Cons_eq_append_conv) done qed lemma path_replace_prefix: "path l1 l3 (xs @ zs) \ path l1 l2 ys \ path l1 l2 xs \ path l1 l3 (ys @ zs)" by (drule path_appendE) (auto elim!: path_trans dest: path_determines_loc) lemma drop_subseq: "n \ length xs \ drop n xs \ xs" by (auto simp: suffix_def intro!: exI[of _ "take n xs"]) lemma take_subseq[simp, intro]: "take n xs \ xs" by (induct xs) auto lemma map_take_subseq[simp, intro]: "map f (take n xs) \ map f xs" by (rule subseq_map, induct xs) auto lemma path_distinct: "path l1 l2 xs \ \xs'. distinct xs' \ path l1 l2 xs' \ map LBL xs' \ map LBL xs" proof (induct rule: path.induct) case (path0 l1 l2) then show ?case by (intro exI[of _ "[]"]) (auto intro: path.intros) next case (path l1 l2 xs lbl l3) then obtain xs' where ih: "path l1 l2 xs'" "distinct xs'" "map LBL xs' \ map LBL xs" by blast then show ?case proof (cases "(l2, lbl, l3) \ set xs'") case True then obtain m where m: "m < length xs'" "xs' ! m = (l2, lbl, l3)" unfolding in_set_conv_nth by blast from m ih have "path l1 l2 (take m xs')" by (auto intro: path_take_from) with m ih path show ?thesis apply (intro exI[of _ "take m xs' @ [(l2, lbl, l3)]"]) apply (rule conjI) apply (metis distinct_take take_Suc_conv_app_nth) apply (rule conjI) apply (rule path.intros) apply simp apply simp apply simp - apply (metis ih(3) subseq_order.order.trans take_map take_subseq) + apply (metis ih(3) subseq_order.trans take_map take_subseq) done next case False with ih path(3) show ?thesis by (auto intro!: exI[of _ "xs' @ [(l2, lbl, l3)]"] intro: path.intros) qed qed lemma path_edge: "(l1', lbl, l2') \ set xs \ path l1 l2 xs \ lbl \\<^sub>A weights l1' l2'" by (rotate_tac, induct rule: path.induct) auto subsection\Path Weights\ abbreviation sum_weights :: "'lbl list \ 'lbl" where "sum_weights xs \ foldr (+) xs 0" abbreviation "sum_path_weights xs \ sum_weights (map LBL xs)" definition "path_weightp l1 l2 s \ (\xs. path l1 l2 xs \ s = sum_path_weights xs)" lemma sum_not_less_zero[simp, dest]: "(s::'lbl) < 0 \ False" by (simp add: less_le_not_le) lemma sum_le_zero[simp]: "(s::'lbl) \ 0 \ s = 0" by (simp add: eq_iff) lemma sum_le_zeroD[dest]: "(x::'lbl) \ 0 \ x = 0" by simp lemma foldr_plus_mono: "(n::'lbl) \ m \ foldr (+) xs n \ foldr (+) xs m" by (induct xs) (auto simp: plus_mono) lemma sum_weights_append: "sum_weights (ys @ xs) = sum_weights ys + sum_weights xs" by (induct ys) (auto simp: add.assoc) lemma sum_summary_prepend_le: "sum_path_weights ys \ sum_path_weights xs \ sum_path_weights (zs @ ys) \ sum_path_weights (zs @ xs)" by (induct zs arbitrary: xs ys) (auto intro: plus_mono) lemma sum_summary_append_le: "sum_path_weights ys \ sum_path_weights xs \ sum_path_weights (ys @ zs) \ sum_path_weights (xs @ zs)" proof (induct zs arbitrary: xs ys) case (Cons a zs) then show ?case by (metis plus_mono map_append order_refl sum_weights_append) qed simp lemma foldr_plus_zero_le: "foldr (+) xs (0::'lbl) \ foldr (+) xs a" by (induct xs) (simp_all add: plus_mono) lemma subseq_sum_weights_le: assumes "xs \ ys" shows "sum_weights xs \ sum_weights ys" using assms proof (induct rule: list_emb.induct) case (list_emb_Nil ys) then show ?case by auto next case (list_emb_Cons xs ys y) then show ?case by (auto elim!: order_trans simp: le_plus) next case (list_emb_Cons2 x y xs ys) then show ?case by (auto elim!: order_trans simp: plus_mono) qed lemma subseq_sum_path_weights_le: "map LBL xs \ map LBL ys \ sum_path_weights xs \ sum_path_weights ys" by (rule subseq_sum_weights_le) lemma sum_path_weights_take_le[simp, intro]: "sum_path_weights (take i xs) \ sum_path_weights xs" by (auto intro!: subseq_sum_path_weights_le) lemma sum_weights_append_singleton: "sum_weights (xs @ [x]) = sum_weights xs + x" by (induct xs) (simp_all add: add.assoc) lemma sum_path_weights_append_singleton: "sum_path_weights (xs @ [(l,x,l')]) = sum_path_weights xs + x" by (induct xs) (simp_all add: add.assoc) lemma path_weightp_ex_path: "path_weightp l1 l2 s \ \xs. (let s' = sum_path_weights xs in s' \ s \ path_weightp l1 l2 s' \ distinct xs \ (\(l1,s,l2) \ set xs. s \\<^sub>A weights l1 l2))" unfolding path_weightp_def apply (erule exE conjE)+ apply (drule path_distinct) apply (erule exE conjE)+ subgoal for xs xs' apply (rule exI[of _ xs']) apply (auto simp: Let_def dest!: path_edge intro: subseq_sum_path_weights_le) done done lemma finite_set_summaries: "finite ((\((l1,l2),s). (l1,s,l2)) ` (Sigma UNIV (\(l1,l2). set_antichain (weights l1 l2))))" by force lemma finite_summaries: "finite {xs. distinct xs \ (\(l1, s, l2) \ set xs. s \\<^sub>A weights l1 l2)}" apply (rule finite_subset[OF _ finite_distinct_bounded[of "((\((l1,l2),s). (l1,s,l2)) ` (Sigma UNIV (\(l1,l2). set_antichain (weights l1 l2))))"]]) apply (force simp: finite_set_summaries)+ done lemma finite_minimal_antichain_path_weightp: "finite (minimal_antichain {x. path_weightp l1 l2 x})" apply (rule finite_surj[OF finite_summaries, where f = sum_path_weights]) apply (clarsimp simp: minimal_antichain_def image_iff dest!: path_weightp_ex_path) apply (fastforce simp: Let_def) done (* antichain of summaries along cycles-less paths (cycle-less = no edge repeated) *) lift_definition path_weight :: "'vtx \ 'vtx \ 'lbl antichain" is "\l1 l2. minimal_antichain {x. path_weightp l1 l2 x}" using finite_minimal_antichain_path_weightp by auto definition "reachable l1 l2 \ path_weight l1 l2 \ {}\<^sub>A" lemma in_path_weight: "s \\<^sub>A path_weight loc1 loc2 \ s \ minimal_antichain {s. path_weightp loc1 loc2 s}" by transfer simp lemma path_weight_refl[simp]: "0 \\<^sub>A path_weight loc loc" proof - have *: "path loc loc []" by (simp add: path0) then have "0 = sum_path_weights []" by auto with * have "path_weightp loc loc 0" using path_weightp_def by blast then show ?thesis by (auto simp: in_path_weight in_minimal_antichain) qed lemma zero_in_minimal_antichain[simp]: "(0::'lbl) \ S \ 0 \ minimal_antichain S" by (auto simp: in_minimal_antichain intro: sum_not_less_zero) definition "path_weightp_distinct l1 l2 s \ (\xs. distinct xs \ path l1 l2 xs \ s = sum_path_weights xs)" lemma minimal_antichain_path_weightp_distinct: "minimal_antichain {xs. path_weightp l1 l2 xs} = minimal_antichain {xs. path_weightp_distinct l1 l2 xs}" unfolding path_weightp_def path_weightp_distinct_def minimal_antichain_def apply safe apply clarsimp apply (metis path_distinct order.strict_iff_order subseq_sum_path_weights_le) apply (blast+) [2] apply clarsimp apply (metis (no_types, lifting) le_less_trans path_distinct subseq_sum_weights_le) done lemma finite_path_weightp_distinct[simp, intro]: "finite {xs. path_weightp_distinct l1 l2 xs}" unfolding path_weightp_distinct_def apply (rule finite_subset[where B = "sum_path_weights ` {xs. distinct xs \ path l1 l2 xs}"]) apply clarsimp apply (rule finite_imageI) apply (rule finite_subset[OF _ finite_summaries]) apply (clarsimp simp: path_edge) done lemma path_weightp_distinct_nonempty: "{xs. path_weightp l1 l2 xs} \ {} \ {xs. path_weightp_distinct l1 l2 xs} \ {}" by (auto dest: path_distinct simp: path_weightp_def path_weightp_distinct_def) lemma path_weightp_distinct_member: "s \ {s. path_weightp l1 l2 s} \ \u. u \ {s. path_weightp_distinct l1 l2 s} \ u \ s" apply (clarsimp simp: path_weightp_def path_weightp_distinct_def) apply (drule path_distinct) apply (auto dest: subseq_sum_path_weights_le) done lemma minimal_antichain_path_weightp_member: "s \ {xs. path_weightp l1 l2 xs} \ \u. u \ minimal_antichain {xs. path_weightp l1 l2 xs} \ u \ s" proof - assume "s \ {xs. path_weightp l1 l2 xs}" then obtain u where u: "u \ {s. path_weightp_distinct l1 l2 s} \ u \ s" using path_weightp_distinct_member by blast have finite: "finite {xs. path_weightp_distinct l1 l2 xs}" .. from u finite obtain v where "v \ minimal_antichain {xs. path_weightp_distinct l1 l2 xs} \ v \ u" by atomize_elim (auto intro: minimal_antichain_member) with u show ?thesis by (auto simp: minimal_antichain_path_weightp_distinct) qed lemma path_path_weight: "path l1 l2 xs \ \s. s \\<^sub>A path_weight l1 l2 \ s \ sum_path_weights xs" proof - assume "path l1 l2 xs" then have "sum_path_weights xs \ {x. path_weightp l1 l2 x}" by (auto simp: path_weightp_def) then obtain u where "u \ minimal_antichain {x. path_weightp l1 l2 x} \ u \ sum_path_weights xs" apply atomize_elim apply (drule minimal_antichain_path_weightp_member) apply auto done then show ?thesis by transfer auto qed lemma path_weight_conv_path: "s \\<^sub>A path_weight l1 l2 \ \xs. path l1 l2 xs \ s = sum_path_weights xs \ (\ys. path l1 l2 ys \ \ sum_path_weights ys < sum_path_weights xs)" by transfer (auto simp: in_minimal_antichain path_weightp_def) abbreviation "optimal_path loc1 loc2 xs \ path loc1 loc2 xs \ (\ys. path loc1 loc2 ys \ \ sum_path_weights ys < sum_path_weights xs)" lemma path_weight_path: "s \\<^sub>A path_weight loc1 loc2 \ (\xs. optimal_path loc1 loc2 xs \ distinct xs \ sum_path_weights xs = s \ P) \ P" apply atomize_elim apply transfer apply (clarsimp simp: in_minimal_antichain path_weightp_def) apply (drule path_distinct) apply (erule exE) subgoal for loc1 loc2 xs xs' apply (rule exI[of _ xs']) apply safe using order.strict_iff_order subseq_sum_path_weights_le apply metis using less_le subseq_sum_path_weights_le apply fastforce done done lemma path_weight_elem_trans: "s \\<^sub>A path_weight l1 l2 \ s' \\<^sub>A path_weight l2 l3 \ \u. u \\<^sub>A path_weight l1 l3 \ u \ s + s'" proof - assume ps1: "s \\<^sub>A path_weight l1 l2" assume ps2: "s' \\<^sub>A path_weight l2 l3" from ps1 obtain xs where path1: "path l1 l2 xs" "s = sum_path_weights xs" by (auto intro: path_weight_path) from ps2 obtain ys where path2: "path l2 l3 ys" "s' = sum_path_weights ys" by (auto intro: path_weight_path) from path1(1) path2(1) have "path l1 l3 (xs @ ys)" by (rule path_trans) with path1(2) path2(2) have "s + s' \ {s. path_weightp l1 l3 s}" by (auto simp: path_weightp_def sum_weights_append[symmetric]) then show "\u. u \\<^sub>A path_weight l1 l3 \ u \ s + s'" by transfer (simp add: minimal_antichain_path_weightp_member) qed end (*<*) end (*>*) \ No newline at end of file