diff --git a/thys/CryptHOL/Cyclic_Group.thy b/thys/CryptHOL/Cyclic_Group.thy --- a/thys/CryptHOL/Cyclic_Group.thy +++ b/thys/CryptHOL/Cyclic_Group.thy @@ -1,107 +1,107 @@ (* Title: Cyclic_Group.thy Author: Andreas Lochbihler, ETH Zurich *) section \Cyclic groups\ theory Cyclic_Group imports "HOL-Algebra.Coset" begin record 'a cyclic_group = "'a monoid" + generator :: 'a ("\<^bold>g\") locale cyclic_group = group G for G :: "('a, 'b) cyclic_group_scheme" (structure) + assumes generator_closed [intro, simp]: "generator G \ carrier G" and generator: "carrier G \ range (\n :: nat. generator G [^]\<^bsub>G\<^esub> n)" begin lemma generatorE [elim?]: assumes "x \ carrier G" obtains n :: nat where "x = generator G [^] n" using generator assms by auto lemma inj_on_generator: "inj_on (([^]) \<^bold>g) {.. {.. {..g [^] n = \<^bold>g [^] m" ultimately show "n = m" proof(induction n m rule: linorder_wlog) case sym thus ?case by simp next case (le n m) let ?d = "m - n" have "\<^bold>g [^] (int m - int n) = \<^bold>g [^] int m \ inv (\<^bold>g [^] int n)" by(simp add: int_pow_diff) also have "\<^bold>g [^] int m = \<^bold>g [^] int n" by(simp add: le.prems int_pow_int) also have "\ \ inv (\<^bold>g [^] (int n)) = \" by simp finally have "\<^bold>g [^] ?d = \" using le.hyps by(simp add: of_nat_diff[symmetric] int_pow_int) { assume "n < m" have "carrier G \ (\n. \<^bold>g [^] n) ` {.. carrier G" then obtain k :: nat where "x = \<^bold>g [^] k" .. also have "\ = (\<^bold>g [^] ?d) [^] (k div ?d) \ \<^bold>g [^] (k mod ?d)" by(simp add: nat_pow_pow nat_pow_mult div_mult_mod_eq) also have "\ = \<^bold>g [^] (k mod ?d)" using \\<^bold>g [^] ?d = \\ by simp finally show "x \ (\n. \<^bold>g [^] n) ` {..n < m\ by auto qed hence "order G \ card ((\n. \<^bold>g [^] n) ` {.. \ card {.. < order G" using \m < order G\ by simp finally have False by simp } with \n \ m\ show "n = m" by(auto simp add: order.order_iff_strict) qed qed lemma finite_carrier: "finite (carrier G)" (* contributed by Dominique Unruh *) proof - from generator obtain n :: nat where "\<^bold>g [^] n = inv \<^bold>g" by(metis generatorE generator_closed inv_closed) then have g1: "\<^bold>g [^] (Suc n) = \" by auto have mod: "\<^bold>g [^] m = \<^bold>g [^] (m mod Suc n)" for m proof - obtain k where "m mod Suc n + Suc n * k = m" - by (metis mod_less_eq_dividend mod_mod_trivial nat_mod_eq_lemma) + using mod_mult_div_eq by blast then have "\<^bold>g [^] m = \<^bold>g [^] (m mod Suc n + Suc n * k)" by simp also have "\ = \<^bold>g [^] (m mod Suc n)" unfolding nat_pow_mult[symmetric, OF generator_closed] nat_pow_pow[symmetric, OF generator_closed] g1 by simp finally show ?thesis . qed have "\<^bold>g [^] x \ ([^]) \<^bold>g ` {..g :: nat \ _) \ (([^]) \<^bold>g) ` {..g :: nat \ _))" by(rule finite_surj[rotated]) simp with generator show ?thesis by(rule finite_subset) qed lemma carrier_conv_generator: "carrier G = (\n. \<^bold>g [^] n) ` {..n. \<^bold>g [^] n) ` {.. carrier G" by auto moreover have "card ((\n. \<^bold>g [^] n) ` {.. order G" using inj_on_generator by(simp add: card_image) ultimately show ?thesis using finite_carrier unfolding order_def by(rule card_seteq[symmetric, rotated]) qed lemma bij_betw_generator_carrier: "bij_betw (\n :: nat. \<^bold>g [^] n) {.. 0" using order_gt_0_iff_finite by(simp add: finite_carrier) end lemma (in monoid) order_in_range_Suc: "order G \ range Suc \ finite (carrier G)" by(cases "order G")(auto simp add: order_def carrier_not_empty intro: card_ge_0_finite) end diff --git a/thys/Diophantine_Eqns_Lin_Hom/List_Vector.thy b/thys/Diophantine_Eqns_Lin_Hom/List_Vector.thy --- a/thys/Diophantine_Eqns_Lin_Hom/List_Vector.thy +++ b/thys/Diophantine_Eqns_Lin_Hom/List_Vector.thy @@ -1,662 +1,661 @@ (* Author: Florian Messner Author: Julian Parsert Author: Jonas Schöpf Author: Christian Sternagel License: LGPL *) section \Vectors as Lists of Naturals\ theory List_Vector imports Main begin (*TODO: move*) lemma lex_lengthD: "(x, y) \ lex P \ length x = length y" by (auto simp: lexord_lex) (*TODO: move*) lemma lex_take_index: assumes "(xs, ys) \ lex r" obtains i where "length ys = length xs" and "i < length xs" and "take i xs = take i ys" and "(xs ! i, ys ! i) \ r" proof - obtain n us x xs' y ys' where "(xs, ys) \ lexn r n" and "length xs = n" and "length ys = n" and "xs = us @ x # xs'" and "ys = us @ y # ys'" and "(x, y) \ r" using assms by (fastforce simp: lex_def lexn_conv) then show ?thesis by (intro that [of "length us"]) auto qed (*TODO: move*) lemma mods_with_nats: assumes "(v::nat) > w" and "(v * b) mod a = (w * b) mod a" shows "((v - w) * b) mod a = 0" - by (metis add_diff_cancel_left' assms left_diff_distrib' - less_imp_le mod_mult_self1_is_0 mult_le_mono1 nat_mod_eq_lemma) + using assms by (simp add: mod_eq_dvd_iff_nat algebra_simps) \ \The 0-vector of length \n\.\ abbreviation zeroes :: "nat \ nat list" where "zeroes n \ replicate n 0" lemma rep_upd_unit: assumes "x = (zeroes n)[i := a]" shows "\j < length x. (j \ i \ x ! j = 0) \ (j = i \ x ! j = a)" using assms by simp definition nonzero_iff: "nonzero xs \ (\x\set xs. x \ 0)" lemma nonzero_append [simp]: "nonzero (xs @ ys) \ nonzero xs \ nonzero ys" by (auto simp: nonzero_iff) subsection \The Inner Product\ definition dotprod :: "nat list \ nat list \ nat" (infixl "\" 70) where "xs \ ys = (\i ys = sum_list (map (\(x, y). x * y) (zip xs ys))" by (auto simp: dotprod_def sum_list_sum_nth lessThan_atLeast0) lemma dotprod_commute: assumes "length xs = length ys" shows "xs \ ys = ys \ xs" using assms by (auto simp: dotprod_def mult.commute) lemma dotprod_Nil [simp]: "[] \ [] = 0" by (simp add: dotprod_def) lemma dotprod_Cons [simp]: "(x # xs) \ (y # ys) = x * y + xs \ ys" unfolding dotprod_def and length_Cons and min_Suc_Suc and sum.lessThan_Suc_shift by auto lemma dotprod_1_right [simp]: "xs \ replicate (length xs) 1 = sum_list xs" by (induct xs) (simp_all) lemma dotprod_0_right [simp]: "xs \ zeroes (length xs) = 0" by (induct xs) (simp_all) lemma dotprod_unit [simp]: assumes "length a = n" and "k < n" shows "a \ (zeroes n)[k := zk] = a ! k * zk" using assms by (induct a arbitrary: k n) (auto split: nat.splits) lemma dotprod_gt0: assumes "length x = length y" and "\i 0 \ y ! i > 0" shows "x \ y > 0" using assms by (induct x y rule: list_induct2) (fastforce simp: nth_Cons split: nat.splits)+ lemma dotprod_gt0D: assumes "length x = length y" and "x \ y > 0" shows "\i 0 \ y ! i > 0" using assms by (induct x y rule: list_induct2) (auto simp: Ex_less_Suc2) lemma dotprod_gt0_iff [iff]: assumes "length x = length y" shows "x \ y > 0 \ (\i 0 \ y ! i > 0)" using assms and dotprod_gt0D and dotprod_gt0 by blast lemma dotprod_append: assumes "length a = length b" shows"(a @ x) \ (b @ y) = a \ b + x \ y" using assms by (induct a b rule: list_induct2) auto lemma dotprod_le_take: assumes "length a = length b" and "k \ length a" shows"take k a \ take k b \ a \ b" using assms and append_take_drop_id [of k a] and append_take_drop_id [of k b] by (metis add_right_cancel leI length_append length_drop not_add_less1 dotprod_append) lemma dotprod_le_drop: assumes "length a = length b" and "k \ length a" shows "drop k a \ drop k b \ a \ b" using assms and append_take_drop_id [of k a] and append_take_drop_id [of k b] by (metis dotprod_append length_take order_refl trans_le_add2) lemma dotprod_is_0 [simp]: assumes "length x = length y" shows "x \ y = 0 \ (\i y ! i = 0)" using assms by (metis dotprod_gt0_iff neq0_conv) lemma dotprod_eq_0_iff: assumes "length x = length a" and "0 \ set a" shows "x \ a = 0 \ (\e \ set x. e = 0)" using assms by (fastforce simp: in_set_conv_nth) lemma dotprod_eq_nonzero_iff: assumes "a \ x = b \ y" and "length x = length a" and "length y = length b" and "0 \ set a" and "0 \ set b" shows "nonzero x \ nonzero y" using assms by (auto simp: nonzero_iff) (metis dotprod_commute dotprod_eq_0_iff neq0_conv)+ lemma eq_0_iff: "xs = zeroes n \ length xs = n \ (\x\set xs. x = 0)" using in_set_replicate [of _ n 0] and replicate_eqI [of xs n 0] by auto lemma not_nonzero_iff: "\ nonzero x \ x = zeroes (length x)" by (auto simp: nonzero_iff replicate_length_same eq_0_iff) lemma neq_0_iff': "xs \ zeroes n \ length xs \ n \ (\x\set xs. x > 0)" by (auto simp: eq_0_iff) lemma dotprod_pointwise_le: assumes "length as = length xs" and "i < length as" shows "as ! i * xs ! i \ as \ xs" proof - have "as \ xs = (\i y = x * sum_list y" proof - have "x * (\iiThe Pointwise Order on Vectors\ definition less_eq :: "nat list \ nat list \ bool" ("_/ \\<^sub>v _" [51, 51] 50) where "xs \\<^sub>v ys \ length xs = length ys \ (\i ys ! i)" definition less :: "nat list \ nat list \ bool" ("_/ <\<^sub>v _" [51, 51] 50) where "xs <\<^sub>v ys \ xs \\<^sub>v ys \ \ ys \\<^sub>v xs" interpretation order_vec: order less_eq less by (standard, auto simp add: less_def less_eq_def dual_order.antisym nth_equalityI) (force) lemma less_eqI [intro?]: "length xs = length ys \ \i ys ! i \ xs \\<^sub>v ys" by (auto simp: less_eq_def) lemma le0 [simp, intro]: "zeroes (length xs) \\<^sub>v xs" by (simp add: less_eq_def) lemma le_list_update [simp]: assumes "xs \\<^sub>v ys" and "i < length ys" and "z \ ys ! i" shows "xs[i := z] \\<^sub>v ys" using assms by (auto simp: less_eq_def nth_list_update) lemma le_Cons: "x # xs \\<^sub>v y # ys \ x \ y \ xs \\<^sub>v ys" by (auto simp add: less_eq_def nth_Cons split: nat.splits) lemma zero_less: assumes "nonzero x" shows "zeroes (length x) <\<^sub>v x" using assms and eq_0_iff order_vec.dual_order.strict_iff_order by (auto simp: nonzero_iff) lemma le_append: assumes "length xs = length vs" shows "xs @ ys \\<^sub>v vs @ ws \ xs \\<^sub>v vs \ ys \\<^sub>v ws" using assms by (auto simp: less_eq_def nth_append) (metis add.commute add_diff_cancel_left' nat_add_left_cancel_less not_add_less2) lemma less_Cons: "(x # xs) <\<^sub>v (y # ys) \ length xs = length ys \ (x \ y \ xs <\<^sub>v ys \ x < y \ xs \\<^sub>v ys)" by (simp add: less_def less_eq_def All_less_Suc2) (auto dest: leD) lemma le_length [dest]: assumes "xs \\<^sub>v ys" shows "length xs = length ys" using assms by (simp add: less_eq_def) lemma less_length [dest]: assumes "x <\<^sub>v y" shows "length x = length y" using assms by (auto simp: less_def) lemma less_append: assumes "xs <\<^sub>v vs " and "ys \\<^sub>v ws" shows "xs @ ys <\<^sub>v vs @ ws" proof - have "length xs = length vs" using assms by blast then show ?thesis using assms by (induct xs vs rule: list_induct2) (auto simp: less_Cons le_append le_length) qed lemma less_appendD: assumes "xs @ ys <\<^sub>v vs @ ws" and "length xs = length vs" shows "xs <\<^sub>v vs \ ys <\<^sub>v ws" by (auto) (metis (no_types, lifting) assms le_append order_vec.order.strict_iff_order) lemma less_append_cases: assumes "xs @ ys <\<^sub>v vs @ ws" and "length xs = length vs" obtains "xs <\<^sub>v vs" and "ys \\<^sub>v ws" | "xs \\<^sub>v vs" and "ys <\<^sub>v ws" using assms and that by (metis le_append less_appendD order_vec.order.strict_implies_order) lemma less_append_swap: assumes "x @ y <\<^sub>v u @ v" and "length x = length u" shows "y @ x <\<^sub>v v @ u" using assms(2, 1) by (induct x u rule: list_induct2) (auto simp: order_vec.order.strict_iff_order le_Cons le_append le_length) lemma le_sum_list_less: assumes "xs \\<^sub>v ys" and "sum_list xs < sum_list ys" shows "xs <\<^sub>v ys" proof - have "length xs = length ys" and "\i ys ! i" using assms by (auto simp: less_eq_def) then show ?thesis using \sum_list xs < sum_list ys\ by (induct xs ys rule: list_induct2) (auto simp: less_Cons All_less_Suc2 less_eq_def) qed lemma dotprod_le_right: assumes "v \\<^sub>v w" and "length b = length w" shows "b \ v \ b \ w" using assms by (auto simp: dotprod_def less_eq_def intro: sum_mono) lemma dotprod_pointwise_le_right: assumes "length z = length u" and "length u = length v" and "\i v ! i" shows "z \ u \ z \ v" using assms by (intro dotprod_le_right) (auto intro: less_eqI) lemma dotprod_le_left: assumes "v \\<^sub>v w" and "length b = length w" shows "v \ b \ w \ b " using assms by (simp add: dotprod_le_right dotprod_commute le_length) lemma dotprod_le: assumes "x \\<^sub>v u" and "y \\<^sub>v v" and "length y = length x" and "length v = length u" shows "x \ y \ u \ v" using assms by (metis dotprod_le_left dotprod_le_right le_length le_trans) lemma dotprod_less_left: assumes "length b = length w" and "0 \ set b" and "v <\<^sub>v w" shows "v \ b < w \ b" proof - have "length v = length w" using assms using less_eq_def order_vec.order.strict_implies_order by blast then show ?thesis using assms proof (induct v w arbitrary: b rule: list_induct2) case (Cons x xs y ys) then show ?case by (cases b) (auto simp: less_Cons add_mono_thms_linordered_field dotprod_le_left) qed simp qed lemma le_append_swap: assumes "length y = length v" and "x @ y \\<^sub>v w @ v" shows "y @ x \\<^sub>v v @ w" proof - have "length w = length x" using assms by auto with assms show ?thesis by (induct y v arbitrary: x w rule: list_induct2) (auto simp: le_Cons le_append) qed lemma le_append_swap_iff: assumes "length y = length v" shows "y @ x \\<^sub>v v @ w \ x @ y \\<^sub>v w @ v" using assms and le_append_swap by (auto) (metis (no_types, lifting) add_left_imp_eq le_length length_append) lemma unit_less: assumes "i < n" and "x <\<^sub>v (zeroes n)[i := b]" shows "x ! i < b \ (\j i \ x ! j = 0)" proof show "x ! i < b" using assms less_def by fastforce next have "x \\<^sub>v (zeroes n)[i := b]" by (simp add: assms order_vec.less_imp_le) then show "\j i \ x ! j = 0" by (auto simp: less_eq_def) qed lemma le_sum_list_mono: assumes "xs \\<^sub>v ys" shows "sum_list xs \ sum_list ys" using assms and sum_list_mono [of "[0..\<^sub>v y" and "sum_list u < sum_list y" shows "\ii y ! i" using \u \\<^sub>v y\ by (auto simp: less_eq_def) then show ?thesis using \sum_list u < sum_list y\ by (induct u y rule: list_induct2) (force simp: Ex_less_Suc2 All_less_Suc2)+ qed lemma less_vec_sum_list_less: assumes "v <\<^sub>v w" shows "sum_list v < sum_list w" using assms proof - have "length v = length w" using assms less_eq_def less_imp_le by blast then show ?thesis using assms proof (induct v w rule: list_induct2) case (Cons x xs y ys) then show ?case using length_replicate less_Cons order_vec.order.strict_iff_order by force qed simp qed definition maxne0 :: "nat list \ nat list \ nat" where "maxne0 x a = (if length x = length a \ (\i 0) then Max {a ! i | i. i < length a \ x ! i \ 0} else 0)" lemma maxne0_le_Max: "maxne0 x a \ Max (set a)" by (auto simp: maxne0_def nonzero_iff in_set_conv_nth) simp lemma maxne0_Nil [simp]: "maxne0 [] as = 0" "maxne0 xs [] = 0" by (auto simp: maxne0_def) lemma maxne0_Cons [simp]: "maxne0 (x # xs) (a # as) = (if length xs = length as then (if x = 0 then maxne0 xs as else max a (maxne0 xs as)) else 0)" proof - let ?a = "a # as" and ?x = "x # xs" have eq: "{?a ! i | i. i < length ?a \ ?x ! i \ 0} = (if x > 0 then {a} else {}) \ {as ! i | i. i < length as \ xs ! i \ 0}" by (auto simp: nth_Cons split: nat.splits) (metis Suc_pred)+ show ?thesis unfolding maxne0_def and eq by (auto simp: less_Suc_eq_0_disj nth_Cons' intro: Max_insert2) qed lemma maxne0_times_sum_list_gt_dotprod: assumes "length b = length ys" shows "maxne0 ys b * sum_list ys \ b \ ys" using assms apply (induct b ys rule: list_induct2) apply (auto simp: max_def ring_distribs add_mono_thms_linordered_semiring(1)) by (meson leI le_trans mult_less_cancel2 nat_less_le) lemma max_times_sum_list_gt_dotprod: assumes "length b = length ys" shows "Max (set b) * sum_list ys \ b \ ys" proof - have "\ e \ set b . Max (set b) \ e" by simp then have "replicate (length ys) (Max (set b)) \ ys \ b \ ys" (is "?rep \ _") by (metis assms dotprod_pointwise_le_right dotprod_commute length_replicate nth_mem nth_replicate) moreover have "Max (set b) * sum_list ys = ?rep" using replicate_dotprod [of ys _ "Max (set b)"] by auto ultimately show ?thesis by (simp add: assms) qed lemma maxne0_mono: assumes "y \\<^sub>v x" shows "maxne0 y a \ maxne0 x a" proof (cases "length y = length a") case True have "length y = length x" using assms by (auto) then show ?thesis using assms and True proof (induct y x arbitrary: a rule: list_induct2) case (Cons x xs y ys) then show ?case by (cases a) (force simp: less_eq_def All_less_Suc2 le_max_iff_disj)+ qed simp next case False then show ?thesis using assms by (auto simp: maxne0_def) qed lemma all_leq_Max: assumes "x \\<^sub>v y" and "x \ []" shows "\xi \ set x. xi \ Max (set y)" by (metis (no_types, lifting) List.finite_set Max_ge_iff assms in_set_conv_nth length_0_conv less_eq_def set_empty) lemma le_not_less_replicate: "\x\set xs. x \ b \ \ xs <\<^sub>v replicate (length xs) b \ xs = replicate (length xs) b" by (induct xs) (auto simp: less_Cons) lemma le_replicateI: "\x\set xs. x \ b \ xs \\<^sub>v replicate (length xs) b" by (induct xs) (auto simp: le_Cons) lemma le_take: assumes "x \\<^sub>v y" and "i \ length x" shows "take i x \\<^sub>v take i y" using assms by (auto simp: less_eq_def) lemma wf_less: "wf {(x, y). x <\<^sub>v y}" proof - have "wf (measure sum_list)" .. moreover have "{(x, y). x <\<^sub>v y} \ measure sum_list" by (auto simp: less_vec_sum_list_less) ultimately show "wf {(x, y). x <\<^sub>v y}" by (rule wf_subset) qed subsection \Pointwise Subtraction\ definition vdiff :: "nat list \ nat list \ nat list" (infixl "-\<^sub>v" 65) where "w -\<^sub>v v = map (\i. w ! i - v ! i) [0 ..< length w]" lemma vdiff_Nil [simp]: "[] -\<^sub>v [] = []" by (simp add: vdiff_def) lemma upt_Cons_conv: assumes "j < n" shows "[j.. Suc) [m ..< n]" by (fold list.map_comp [of f "Suc" "[m ..< n]"]) (simp add: map_Suc_upt) lemma vdiff_Cons [simp]: "(x # xs) -\<^sub>v (y # ys) = (x - y) # (xs -\<^sub>v ys)" by (simp add: vdiff_def upt_Cons_conv [OF zero_less_Suc] map_upt_Suc del: upt_Suc) lemma vdiff_alt_def: assumes "length w = length v" shows "w -\<^sub>v v = map (\(x, y). x - y) (zip w v)" using assms by (induct rule: list_induct2) simp_all lemma vdiff_dotprod_distr: assumes "length b = length w" and "v \\<^sub>v w" shows "(w -\<^sub>v v) \ b = w \ b - v \ b" proof - have "length v = length w" and "\i w ! i" using assms less_eq_def by auto then show ?thesis using \length b = length w\ proof (induct v w arbitrary: b rule: list_induct2) case (Cons x xs y ys) then show ?case by (cases b) (auto simp: All_less_Suc2 diff_mult_distrib dotprod_commute dotprod_pointwise_le_right) qed simp qed lemma sum_list_vdiff_distr [simp]: assumes "v \\<^sub>v u" shows "sum_list (u -\<^sub>v v) = sum_list u - sum_list v" by (metis (no_types, lifting) assms diff_zero dotprod_1_right length_map length_replicate length_upt less_eq_def vdiff_def vdiff_dotprod_distr) lemma vdiff_le: assumes "v \\<^sub>v w" and "length v = length x" shows "v -\<^sub>v x \\<^sub>v w" using assms by (auto simp add: less_eq_def vdiff_def) lemma mods_with_vec: assumes "v <\<^sub>v w" and "0 \ set b" and "length b = length w" and "(v \ b) mod a = (w \ b) mod a" shows "((w -\<^sub>v v) \ b) mod a = 0" proof - have *: "v \ b < w \ b" using dotprod_less_left and assms by blast have "v \\<^sub>v w" using assms by auto from vdiff_dotprod_distr [OF assms(3) this] have "((w -\<^sub>v v) \ b) mod a = (w \ b - v \ b) mod a " by simp also have "... = 0 mod a" using mods_with_nats [of "v \ b" "w \ b" "1" a, OF *] assms by auto finally show ?thesis by simp qed lemma mods_with_vec_2: assumes "v <\<^sub>v w" and "0 \ set b" and "length b = length w" and "(b \ v) mod a = (b \ w) mod a" shows "(b \ (w -\<^sub>v v)) mod a = 0" by (metis (no_types, lifting) assms diff_zero dotprod_commute length_map length_upt less_eq_def order_vec.less_imp_le mods_with_vec vdiff_def) subsection \The Lexicographic Order on Vectors\ abbreviation lex_less_than ("_/ <\<^sub>l\<^sub>e\<^sub>x _" [51, 51] 50) where "xs <\<^sub>l\<^sub>e\<^sub>x ys \ (xs, ys) \ lex less_than" definition rlex (infix "<\<^sub>r\<^sub>l\<^sub>e\<^sub>x" 50) where "xs <\<^sub>r\<^sub>l\<^sub>e\<^sub>x ys \ rev xs <\<^sub>l\<^sub>e\<^sub>x rev ys" lemma rev_le [simp]: "rev xs \\<^sub>v rev ys \ xs \\<^sub>v ys" proof - { fix i assume i: "i < length ys" and [simp]: "length xs = length ys" and "\i < length ys. rev xs ! i \ rev ys ! i" then have "rev xs ! (length ys - i - 1) \ rev ys ! (length ys - i - 1)" by auto then have "xs ! i \ ys ! i" using i by (auto simp: rev_nth) } then show ?thesis by (auto simp: less_eq_def rev_nth) qed lemma rev_less [simp]: "rev xs <\<^sub>v rev ys \ xs <\<^sub>v ys" by (simp add: less_def) lemma less_imp_lex: assumes "xs <\<^sub>v ys" shows "xs <\<^sub>l\<^sub>e\<^sub>x ys" proof - have "length ys = length xs" using assms by auto then show ?thesis using assms by (induct rule: list_induct2) (auto simp: less_Cons) qed lemma less_imp_rlex: assumes "xs <\<^sub>v ys" shows "xs <\<^sub>r\<^sub>l\<^sub>e\<^sub>x ys" using assms and less_imp_lex [of "rev xs" "rev ys"] by (simp add: rlex_def) lemma lex_not_sym: assumes "xs <\<^sub>l\<^sub>e\<^sub>x ys" shows "\ ys <\<^sub>l\<^sub>e\<^sub>x xs" proof assume "ys <\<^sub>l\<^sub>e\<^sub>x xs" then obtain i where "i < length xs" and "take i xs = take i ys" and "ys ! i < xs ! i" by (elim lex_take_index) auto moreover obtain j where "j < length xs" and "length ys = length xs" and "take j xs = take j ys" and "xs ! j < ys ! j" using assms by (elim lex_take_index) auto ultimately show False by (metis le_antisym nat_less_le nat_neq_iff nth_take) qed lemma rlex_not_sym: assumes "xs <\<^sub>r\<^sub>l\<^sub>e\<^sub>x ys" shows "\ ys <\<^sub>r\<^sub>l\<^sub>e\<^sub>x xs" proof assume ass: "ys <\<^sub>r\<^sub>l\<^sub>e\<^sub>x xs" then obtain i where "i < length xs" and "take i xs = take i ys" and "ys ! i > xs ! i" using assms lex_not_sym rlex_def by blast moreover obtain j where "j < length xs" and "length ys = length xs" and "take j xs = take j ys" and "xs ! j > ys ! j" using assms rlex_def ass lex_not_sym by blast ultimately show False by (metis leD nat_less_le nat_neq_iff nth_take) qed lemma lex_trans: assumes "x <\<^sub>l\<^sub>e\<^sub>x y" and "y <\<^sub>l\<^sub>e\<^sub>x z" shows "x <\<^sub>l\<^sub>e\<^sub>x z" using assms by (auto simp: antisym_def intro: transD [OF lex_transI]) lemma rlex_trans: assumes "x <\<^sub>r\<^sub>l\<^sub>e\<^sub>x y" and "y <\<^sub>r\<^sub>l\<^sub>e\<^sub>x z" shows "x <\<^sub>r\<^sub>l\<^sub>e\<^sub>x z" using assms lex_trans rlex_def by blast lemma lex_append_rightD: assumes "xs @ us <\<^sub>l\<^sub>e\<^sub>x ys @ vs" and "length xs = length ys" and "\ xs <\<^sub>l\<^sub>e\<^sub>x ys" shows "ys = xs \ us <\<^sub>l\<^sub>e\<^sub>x vs" using assms(2,1,3) by (induct xs ys rule: list_induct2) auto lemma rlex_Cons: "x # xs <\<^sub>r\<^sub>l\<^sub>e\<^sub>x y # ys \ xs <\<^sub>r\<^sub>l\<^sub>e\<^sub>x ys \ ys = xs \ x < y" (is "?A = ?B") by (cases "length ys = length xs") (auto simp: rlex_def intro: lex_append_rightI lex_append_leftI dest: lex_append_rightD lex_lengthD) lemma rlex_irrefl: "\ x <\<^sub>r\<^sub>l\<^sub>e\<^sub>x x" by (induct x) (auto simp: rlex_def dest: lex_append_rightD) subsection \Code Equations\ fun exists2 where "exists2 d P [] [] \ False" | "exists2 d P (x#xs) (y#ys) \ P x y \ exists2 d P xs ys" | "exists2 d P _ _ \ d" lemma not_le_code [code_unfold]: "\ xs \\<^sub>v ys \ exists2 True (>) xs ys" by (induct "True" "(>) :: nat \ nat \ bool" xs ys rule: exists2.induct) (auto simp: le_Cons) end diff --git a/thys/TortoiseHare/Basis.thy b/thys/TortoiseHare/Basis.thy --- a/thys/TortoiseHare/Basis.thy +++ b/thys/TortoiseHare/Basis.thy @@ -1,297 +1,297 @@ (*<*) theory Basis imports "HOL-Library.While_Combinator" begin (*>*) section\ Point-free notation \ text\ We adopt point-free notation for our assertions over program states. \ abbreviation (input) pred_K :: "'b \ 'a \ 'b" ("\_\") where "\f\ \ \s. f" abbreviation (input) pred_not :: "('a \ bool) \ 'a \ bool" ("\<^bold>\") where "\<^bold>\a \ \s. \a s" abbreviation (input) pred_conj :: "('a \ bool) \ ('a \ bool) \ 'a \ bool" (infixr "\<^bold>\" 35) where "a \<^bold>\ b \ \s. a s \ b s" abbreviation (input) pred_implies :: "('a \ bool) \ ('a \ bool) \ 'a \ bool" (infixr "\<^bold>\" 25) where "a \<^bold>\ b \ \s. a s \ b s" abbreviation (input) pred_eq :: "('a \ 'b) \ ('a \ 'b) \ 'a \ bool" (infix "\<^bold>=" 40) where "a \<^bold>= b \ \s. a s = b s" abbreviation (input) pred_member :: "('a \ 'b) \ ('a \ 'b set) \ 'a \ bool" (infix "\<^bold>\" 40) where "a \<^bold>\ b \ \s. a s \ b s" abbreviation (input) pred_neq :: "('a \ 'b) \ ('a \ 'b) \ 'a \ bool" (infix "\<^bold>\" 40) where "a \<^bold>\ b \ \s. a s \ b s" abbreviation (input) pred_If :: "('a \ bool) \ ('a \ 'b) \ ('a \ 'b) \ 'a \ 'b" ("(\<^bold>if (_)/ \<^bold>then (_)/ \<^bold>else (_))" [0, 0, 10] 10) where "\<^bold>if P \<^bold>then x \<^bold>else y \ \s. if P s then x s else y s" abbreviation (input) pred_less :: "('a \ 'b::ord) \ ('a \ 'b) \ 'a \ bool" (infix "\<^bold><" 40) where "a \<^bold>< b \ \s. a s < b s" abbreviation (input) pred_le :: "('a \ 'b::ord) \ ('a \ 'b) \ 'a \ bool" (infix "\<^bold>\" 40) where "a \<^bold>\ b \ \s. a s \ b s" abbreviation (input) pred_plus :: "('a \ 'b::plus) \ ('a \ 'b) \ 'a \ 'b" (infixl "\<^bold>+" 65) where "a \<^bold>+ b \ \s. a s + b s" abbreviation (input) pred_minus :: "('a \ 'b::minus) \ ('a \ 'b) \ 'a \ 'b" (infixl "\<^bold>-" 65) where "a \<^bold>- b \ \s. a s - b s" abbreviation (input) fun_fanout :: "('a \ 'b) \ ('a \ 'c) \ 'a \ 'b \ 'c" (infix "\<^bold>\" 35) where "f \<^bold>\ g \ \x. (f x, g x)" abbreviation (input) pred_all :: "('b \ 'a \ bool) \ 'a \ bool" (binder "\<^bold>\" 10) where "\<^bold>\x. P x \ \s. \x. P x s" abbreviation (input) pred_ex :: "('b \ 'a \ bool) \ 'a \ bool" (binder "\<^bold>\" 10) where "\<^bold>\x. P x \ \s. \x. P x s" section\ ``Monoidal'' Hoare logic \ text\ In the absence of a general-purpose development of Hoare Logic for total correctness in Isabelle/HOL\footnote{At the time of writing the distribution contains several for partial correctness, and one for total correctness over a language with restricted expressions. SIMPL (@{cite "DBLP:journals/afp/Schirmer08"}) is overkill for our present purposes.}, we adopt the following syntactic contrivance that eases making multiple assertions about function results. ``Programs'' consist of the state-transformer semantics of statements. \ definition valid :: "('s \ bool) \ ('s \ 's) \ ('s \ bool) \ bool" ("\_\/ _/ \_\") where "\P\ c \Q\ \ \s. P s \ Q (c s)" notation (input) id ("SKIP") notation fcomp (infixl ";;" 60) named_theorems wp_intro "weakest precondition intro rules" lemma seqI[wp_intro]: assumes "\Q\ d \R\" assumes "\P\ c \Q\" shows "\P\ c ;; d \R\" using assms by (simp add: valid_def) lemma iteI[wp_intro]: assumes "\P'\ x \Q\" assumes "\P''\ y \Q\" shows "\\<^bold>if b \<^bold>then P' \<^bold>else P''\ \<^bold>if b \<^bold>then x \<^bold>else y \Q\" using assms by (simp add: valid_def) lemma assignI[wp_intro]: shows "\Q \ f\ f \Q\" by (simp add: valid_def) lemma whileI: assumes "\I'\ c \I\" assumes "\s. I s \ if b s then I' s else Q s" assumes "wf r" assumes "\s. \ I s; b s \ \ (c s, s) \ r" shows "\I\ while b c \Q\" using assms by (simp add: while_rule valid_def) lemma hoare_pre: assumes "\R\ f \Q\" assumes "\s. P s \ R s" shows "\P\ f \Q\" using assms by (simp add: valid_def) lemma hoare_post_imp: assumes "\P\ a \Q\" assumes "\s. Q s \ R s" shows "\P\ a \R\" using assms by (simp add: valid_def) text\ Note that the @{thm[source] assignI} rule applies to all state transformers, and therefore the order in which we attempt to use the @{thm[source] wp_intro} rules matters. \ section\ Properties of iterated functions on finite sets \ text\ We begin by fixing the @{term "f"} and @{term "x0"} under consideration in a locale, and establishing Knuth's properties. The sequence is modelled as a function \seq :: nat \ 'a\ in the obvious way. \ locale fx0 = fixes f :: "'a::finite \ 'a" fixes x0 :: "'a" begin definition seq' :: "'a \ nat \ 'a" where "seq' x i \ (f ^^ i) x" abbreviation "seq \ seq' x0" (*<*) declare (in -) fx0.seq'_def[code] lemma seq'_simps[simp]: "seq' x 0 = x" "seq' x (Suc i) = f (seq' x i)" "seq' (f x) i \ range (seq' x)" by (auto intro: range_eqI[where x="Suc i"] simp: seq'_def funpow_swap1) lemma seq_inj: "\ seq' x i = seq' x j; p = i + n; q = j + n \ \ seq' x p = seq' x q" apply hypsubst_thin by (induct n) simp_all (*>*) text\ The parameters \lambda\ and \mu\ must exist by the pigeonhole principle. \ lemma seq'_not_inj_on_card_UNIV: shows "\inj_on (seq' x) {0 .. card (UNIV::'a set)}" by (simp add: inj_on_iff_eq_card) (metis UNIV_I card_mono finite lessI not_less subsetI) definition properties :: "nat \ nat \ bool" where "properties lambda mu \ 0 < lambda \ inj_on seq {0 ..< mu + lambda} \ (\i\mu. \j. seq (i + j * lambda) = seq i)" lemma properties_existence: obtains lambda mu where "properties lambda mu" proof - obtain l where l: "inj_on seq {0..l} \ \inj_on seq {0..Suc l}" using ex_least_nat_less[where P="\ub. \inj_on seq {0..ub}" and n="card (UNIV :: 'a set)"] seq'_not_inj_on_card_UNIV by fastforce moreover from l obtain mu where mu: "mu \ l \ seq (Suc l) = seq mu" by (fastforce simp: atLeastAtMostSuc_conv) moreover define lambda where "lambda = l - mu + 1" have "seq (i + j * lambda) = seq i" if "mu \ i" for i j using that proof (induct j) case (Suc j) from l mu have F: "seq (l + j + 1) = seq (mu + j)" for j by (fastforce elim: seq_inj) from mu Suc F[where j="i + j * lambda - mu"] show ?case by (simp add: lambda_def field_simps) qed simp ultimately have "properties lambda mu" by (auto simp: properties_def lambda_def atLeastLessThanSuc_atLeastAtMost) then show thesis .. qed end text\ To ease further reasoning, we define a new locale that fixes @{term "lambda"} and @{term "mu"}, and assume these properties hold. We then derive further rules that are easy to apply. \ locale properties = fx0 + fixes lambda mu :: "nat" assumes P: "properties lambda mu" begin lemma properties_lambda_gt_0: shows "0 < lambda" using P by (simp add: properties_def) lemma properties_loop: assumes "mu \ i" shows "seq (i + j * lambda) = seq i" using P assms by (simp add: properties_def) lemma properties_mod_lambda: assumes "mu \ i" shows "seq i = seq (mu + (i - mu) mod lambda)" using properties_loop[where i="mu + (i - mu) mod lambda" and j="(i - mu) div lambda"] assms by simp lemma properties_distinct: assumes "j \ {0 <..< lambda}" shows "seq (i + j) \ seq i" proof(cases "mu \ i") case True from assms have A: "(i + j) mod lambda \ i mod lambda" for i - using nat_mod_eq_lemma by fastforce + by (auto simp add: mod_eq_dvd_iff_nat) from \mu \ i\ have "seq (i + j) = seq (mu + (i + j - mu) mod lambda)" "seq i = seq (mu + (i - mu) mod lambda)" by (auto intro: properties_mod_lambda) with P \mu \ i\ assms A[where i="i-mu"] show ?thesis by (clarsimp simp: properties_def inj_on_eq_iff) next case False with P assms show ?thesis by (clarsimp simp: properties_def inj_on_eq_iff) qed lemma properties_distinct_contrapos: assumes "seq (i + j) = seq i" shows "j \ {0 <..< lambda}" using assms by (rule contrapos_pp) (simp add: properties_distinct) lemma properties_loops_ge_mu: assumes "seq (i + j) = seq i" assumes "0 < j" shows "mu \ i" proof(rule classical) assume X: "\?thesis" show ?thesis proof(cases "mu \ i + j") case True with P X assms show ?thesis by (fastforce simp: properties_def inj_on_eq_iff dest: properties_mod_lambda) next case False with P assms show ?thesis by (fastforce simp add: properties_def inj_on_eq_iff) qed qed end (*<*) end (*>*) diff --git a/thys/VerifyThis2018/Challenge3.thy b/thys/VerifyThis2018/Challenge3.thy --- a/thys/VerifyThis2018/Challenge3.thy +++ b/thys/VerifyThis2018/Challenge3.thy @@ -1,1312 +1,1312 @@ chapter \Array-Based Queuing Lock\ section \Challenge\ text_raw \{\upshape Array-Based Queuing Lock (ABQL) is a variation of the Ticket Lock algorithm with a bounded number of concurrent threads and improved scalability due to better cache behaviour. %(\url{https://en.wikipedia.org/wiki/Array_Based_Queuing_Locks}) We assume that there are \texttt{N} threads and we allocate a shared Boolean array \texttt{pass[]} of length \texttt{N}. We also allocate a shared integer value \texttt{next}. In practice, \texttt{next} is an unsigned bounded integer that wraps to 0 on overflow, and we assume that the maximal value of \texttt{next} is of the form $k\mathtt{N} - 1$. Finally, we assume at our disposal an atomic \texttt{fetch\_and\_add} instruction, such that \texttt{fetch\_and\_add(next,1)} increments the value of \texttt{next} by 1 and returns the original value of \texttt{next}. The elements of \texttt{pass[]} are spinlocks, assigned individually to each thread in the waiting queue. Initially, each element of \texttt{pass[]} is set to \texttt{false}, except \texttt{pass[0]} which is set to \texttt{true}, allowing the first coming thread to acquire the lock. Variable \texttt{next} contains the number of the first available place in the waiting queue and is initialized to 0. Here is an implementation of the locking algorithm in pseudocode: \begin{lstlisting}[language=C,morekeywords={procedure,function,end,to,in,var,then,not,mod}] procedure abql_init() for i = 1 to N - 1 do pass[i] := false end-for pass[0] := true next := 0 end-procedure function abql_acquire() var my_ticket := fetch_and_add(next,1) mod N while not pass[my_ticket] do end-while return my_ticket end-function procedure abql_release(my_ticket) pass[my_ticket] := false pass[(my_ticket + 1) mod N] := true end-procedure \end{lstlisting} Each thread that acquires the lock must eventually release it by calling \texttt{abql\_release(my\_ticket)}, where \texttt{my\_ticket} is the return value of the earlier call of \texttt{abql\_acquire()}. We assume that no thread tries to re-acquire the lock while already holding it, neither it attempts to release the lock which it does not possess. Notice that the first assignment in \texttt{abql\_release()} can be moved at the end of \texttt{abql\_acquire()}. \bigskip \noindent\textbf{Verification task~1.}~Verify the safety of ABQL under the given assumptions. Specifically, you should prove that no two threads can hold the lock at any given time. \bigskip \noindent\textbf{Verification task~2.}~Verify the fairness, namely that the threads acquire the lock in order of request. \bigskip \noindent\textbf{Verification task~3.}~Verify the liveness under a fair scheduler, namely that each thread requesting the lock will eventually acquire it. \bigskip You have liberty of adapting the implementation and specification of the concurrent setting as best suited for your verification tool. In particular, solutions with a fixed value of \texttt{N} are acceptable. We expect, however, that the general idea of the algorithm and the non-deterministic behaviour of the scheduler shall be preserved. } \clearpage \ section \Solution\ theory Challenge3 imports "lib/VTcomp" "lib/DF_System" begin text \The Isabelle Refinement Framework does not support concurrency. However, Isabelle is a general purpose theorem prover, thus we can model the problem as a state machine, and prove properties over runs. For this polished solution, we make use of a small library for transition systems and simulations: @{theory VerifyThis2018.DF_System}. Note, however, that our definitions are still quite ad-hoc, and there are lots of opportunities to define libraries that make similar proofs simpler and more canonical. We approach the final ABQL with three refinement steps: \<^enum> We model a ticket lock with unbounded counters, and prove safety, fairness, and liveness. \<^enum> We bound the counters by \mod N\ and \mod (k*N) respectively\ \<^enum> We implement the current counter by an array, yielding exactly the algorithm described in the challenge. With a simulation argument, we transfer the properties of the abstract system over the refinements. The final theorems proving safety, fairness, and liveness can be found at the end of this chapter, in Subsection~\ref{sec:main_theorems}. \ subsection \General Definitions\ text \We fix a positive number \N\ of threads\ consts N :: nat specification (N) N_not0[simp, intro!]: "N\0" by auto lemma N_gt0[simp, intro!]: "0A thread's state, representing the sequence points in the given algorithm. This will not change over the refinements.\ datatype thread = INIT | is_WAIT: WAIT (ticket: nat) | is_HOLD: HOLD (ticket: nat) | is_REL: REL (ticket: nat) subsection \Refinement 1: Ticket Lock with Unbounded Counters\ text \System's state: Current ticket, next ticket, thread states\ type_synonym astate = "nat \ nat \ (nat \ thread)" abbreviation "cc \ fst" abbreviation "nn s \ fst (snd s)" abbreviation "tts s \ snd (snd s)" text \The step relation of a single thread\ inductive astep_sng where enter_wait: "astep_sng (c,n,INIT) (c,(n+1),WAIT n)" | loop_wait: "c\k \ astep_sng (c,n,WAIT k) (c,n,WAIT k)" | exit_wait: "astep_sng (c,n,WAIT c) (c,n,HOLD c)" | start_release: "astep_sng (c,n,HOLD k) (c,n,REL k)" | release: "astep_sng (c,n,REL k) (k+1,n,INIT)" text \The step relation of the system\ inductive alstep for t where "\ t \ alstep t (c,n,ts) (c',n',ts(t:=s'))" text \Initial state of the system\ definition "as\<^sub>0 \ (0, 0, \_. INIT)" interpretation A: system as\<^sub>0 alstep . text \In our system, each thread can always perform a step\ lemma never_blocked: "A.can_step l s \ lThus, our system is in particular deadlock free\ interpretation A: df_system as\<^sub>0 alstep apply unfold_locales subgoal for s using never_blocked[of 0 s] unfolding A.can_step_def by auto done subsubsection \Safety: Mutual Exclusion\ text \Predicates to express that a thread uses or holds a ticket\ definition "has_ticket s k \ s=WAIT k \ s=HOLD k \ s=REL k" lemma has_ticket_simps[simp]: "\has_ticket INIT k" "has_ticket (WAIT k) k'\ k'=k" "has_ticket (HOLD k) k'\ k'=k" "has_ticket (REL k) k'\ k'=k" unfolding has_ticket_def by auto definition "locks_ticket s k \ s=HOLD k \ s=REL k" lemma locks_ticket_simps[simp]: "\locks_ticket INIT k" "\locks_ticket (WAIT k) k'" "locks_ticket (HOLD k) k'\ k'=k" "locks_ticket (REL k) k'\ k'=k" unfolding locks_ticket_def by auto lemma holds_imp_uses: "locks_ticket s k \ has_ticket s k" unfolding locks_ticket_def by auto text \We show the following invariant. Intuitively, it can be read as follows: \<^item> Current lock is less than or equal next lock \<^item> For all threads that use a ticket (i.e., are waiting, holding, or releasing): \<^item> The ticket is in between current and next \<^item> No other thread has the same ticket \<^item> Only the current ticket can be held (or released) \ definition "invar1 \ \(c,n,ts). c \ n \ (\t k. t has_ticket (ts t) k \ c \ k \ k < n \ (\t' k'. t' has_ticket (ts t') k' \ t\t' \ k\k') \ (\k. k\c \ \locks_ticket (ts t) k) ) " lemma is_invar1: "A.is_invar invar1" apply rule subgoal by (auto simp: invar1_def as\<^sub>0_def) subgoal for s s' apply (clarify) apply (erule alstep.cases) apply (erule astep_sng.cases) apply (clarsimp_all simp: invar1_def) apply fastforce apply fastforce apply fastforce apply fastforce by (metis Suc_le_eq holds_imp_uses locks_ticket_def le_neq_implies_less) done text \From the above invariant, it's straightforward to show mutual exclusion\ theorem mutual_exclusion: "\A.reachable s; tt'; is_HOLD (tts s t); is_HOLD (tts s t') \ \ False" apply (cases "tts s t"; simp) apply (cases "tts s t'"; simp) using A.invar_reachable[OF is_invar1, of s] apply (auto simp: invar1_def) by (metis locks_ticket_simps(3) has_ticket_simps(3)) lemma mutual_exclusion': "\A.reachable s; tt'; locks_ticket (tts s t) tk; locks_ticket (tts s t') tk' \ \ False" apply (cases "tts s t"; simp; cases "tts s t'"; simp) apply (cases "tts s t'"; simp) using A.invar_reachable[OF is_invar1, of s] apply (clarsimp_all simp: invar1_def) unfolding locks_ticket_def has_ticket_def apply metis+ done subsubsection \Fairness: Ordered Lock Acquisition\ text \We first show an auxiliary lemma: Consider a segment of a run from \i\ to \j\. Every thread that waits for a ticket in between the current ticket at \i\ and the current ticket at \j\ will be granted the lock in between \i\ and \j\. \ lemma fair_aux: assumes R: "A.is_run s" assumes A: "i k" "k < cc (s j)" "tl. i\l \ l tts (s l) t = HOLD k" proof - interpret A: run as\<^sub>0 alstep s by unfold_locales fact from A show ?thesis proof (induction "j-i" arbitrary: i) case 0 then show ?case by auto next case (Suc i') hence [simp]: "i'=j - Suc i" by auto note IH=Suc.hyps(1)[OF this] obtain t' where "alstep t' (s i) (s (Suc i))" by (rule A.stepE) then show ?case using Suc.prems proof cases case (1 c n ts c' n' s') note [simp] = "1"(1,2,3) from A.run_invar[OF is_invar1, of i] have "invar1 (c,n,ts)" by auto note I1 = this[unfolded invar1_def, simplified] from "1"(4) show ?thesis proof (cases rule: astep_sng.cases) case enter_wait then show ?thesis using IH Suc.prems apply (auto) by (metis "1"(2) Suc_leD Suc_lessI fst_conv leD thread.distinct(1)) next case (loop_wait k) then show ?thesis using IH Suc.prems apply (auto) by (metis "1"(2) Suc_leD Suc_lessI fst_conv leD) next case exit_wait then show ?thesis apply (cases "t'=t") subgoal using Suc.prems apply clarsimp by (metis "1"(2) Suc_leD Suc_lessI fst_conv fun_upd_same leD less_or_eq_imp_le snd_conv) subgoal using Suc.prems IH apply auto by (metis "1"(2) Suc_leD Suc_lessI fst_conv leD) done next case (start_release k) then show ?thesis using IH Suc.prems apply (auto) by (metis "1"(2) Suc_leD Suc_lessI fst_conv leD thread.distinct(7)) next case (release k) then show ?thesis apply (cases "t'=t") using I1 IH Suc.prems apply (auto) by (metis "1"(2) "1"(3) Suc_leD Suc_leI Suc_lessI fst_conv locks_ticket_simps(4) le_antisym not_less_eq_eq has_ticket_simps(2) has_ticket_simps(4)) qed qed qed qed lemma s_case_expand: "(case s of (c, n, ts) \ P c n ts) = P (cc s) (nn s) (tts s)" by (auto split: prod.splits) text \ A version of the fairness lemma which is very detailed on the actual ticket numbers. We will weaken this later. \ lemma fair_aux2: assumes RUN: "A.is_run s" assumes ACQ: "t0 alstep s by unfold_locales fact from ACQ WAIT have [simp]: "t\t'" "t'\t" by auto from ACQ have [simp]: "nn (s i) = k \ nn (s (Suc i)) = Suc k \ cc (s (Suc i)) = cc (s i) \ tts (s (Suc i)) = (tts (s i))(t:=WAIT k)" apply (rule_tac A.stepE[of i]) apply (erule alstep.cases) apply (erule astep_sng.cases) by (auto simp: nth_list_update split: if_splits) from A.run_invar[OF is_invar1, of i] have "invar1 (s i)" by auto note I1 = this[unfolded invar1_def, unfolded s_case_expand, simplified] from WAIT I1 have "k' < k" by fastforce from ACQ HOLD have "Suc i \ j" by auto with HOLD have "Suc i < j" by auto have X1: "cc (s i) \ k'" using I1 WAIT by fastforce have X2: "k' < cc (s j)" using A.run_invar[OF is_invar1, of j, unfolded invar1_def s_case_expand] using \k' < k\ \t HOLD(2) apply clarsimp by (metis locks_ticket_simps(3) has_ticket_simps(3)) from fair_aux[OF RUN \Suc i < j\, of k' t', simplified] obtain l where "l\Suc i" "l WAIT tk" obtains l where "ij" "tts (s l) t = HOLD tk" proof - interpret A: run as\<^sub>0 alstep s by unfold_locales fact from WAIT(2) NWAIT have "\l. i l\j \ tts (s l) t = HOLD tk" proof (induction "j-i" arbitrary: i) case 0 then show ?case by auto next case (Suc i') hence [simp]: "i'=j - Suc i" by auto note IH=Suc.hyps(1)[OF this] obtain t' where "alstep t' (s i) (s (Suc i))" by (rule A.stepE) then show ?case apply - apply (cases "t=t'";erule alstep.cases; erule astep_sng.cases) apply auto using IH Suc.prems Suc.hyps(2) apply (auto) apply (metis Suc_lessD Suc_lessI fun_upd_same snd_conv) apply (metis Suc_lessD Suc_lessI fun_upd_other snd_conv) apply (metis Suc.prems(1) Suc_lessD Suc_lessI fun_upd_triv) apply (metis Suc_lessD Suc_lessI fun_upd_other snd_conv) apply (metis Suc_lessD Suc_lessI fun_upd_other snd_conv) apply (metis Suc_lessD Suc_lessI fun_upd_other snd_conv) done qed thus ?thesis using that by blast qed text \ Finally we can show fairness, which we state as follows: Whenever a thread \t\ gets a ticket, all other threads \t'\ waiting for the lock will be granted the lock before \t\. \ theorem fair: assumes RUN: "A.is_run s" assumes ACQ: "t \Thread \t\ calls \acquire\ in step \i\\ assumes HOLD: "i \Thread \t\ holds lock in step \j\\ assumes WAIT: "t' \Thread \t'\ waits for lock at step \i\\ obtains l where "i \Then, \t'\ gets lock earlier\ proof - obtain k where Wk: "tts (s (Suc i)) t = WAIT k" using ACQ by (cases "tts (s (Suc i)) t") auto obtain k' where Wk': "tts (s i) t' = WAIT k'" using WAIT by (cases "tts (s i) t'") auto from ACQ HOLD have "Suc i \ j" by auto with HOLD have "Suc i < j" by auto obtain j' where H': "Suc i < j'" "j' \ j" "tts (s j') t = HOLD k" apply (rule find_hold_position[OF RUN \t Wk \Suc i < j\]) using HOLD(2) by auto show ?thesis apply (rule fair_aux2[OF RUN ACQ(1,2) Wk _ H'(3) WAIT(1) Wk']) subgoal using H'(1) by simp subgoal apply (erule that) using H'(2) by auto done qed subsubsection \Liveness\ text \For all tickets in between the current and the next ticket, there is a thread that has this ticket\ definition "invar2 \ \(c,n,ts). \k. c\k \ k (\t0_def) subgoal for s s' apply (clarsimp simp: invar2_def) apply (erule alstep.cases; erule astep_sng.cases; clarsimp) apply (metis less_antisym has_ticket_simps(1)) subgoal by (metis has_ticket_simps(2)) subgoal by (metis has_ticket_simps(2)) subgoal by (metis has_ticket_simps(3)) subgoal apply (frule A.invar_reachable[OF is_invar1]) unfolding invar1_def apply clarsimp by (metis Suc_leD locks_ticket_simps(4) not_less_eq_eq has_ticket_simps(4)) done done text \If a thread t is waiting for a lock, the current lock is also used by a thread\ corollary current_lock_used: assumes R: "A.reachable (c,n,ts)" assumes WAIT: "tUsed tickets are unique (Corollary from invariant 1)\ lemma has_ticket_unique: "\A.reachable (c,n,ts); t \ t'=t" apply (drule A.invar_reachable[OF is_invar1]) by (auto simp: invar1_def) text \We define the thread that holds a specified ticket\ definition "tkt_thread \ \ts k. THE t. t has_ticket (ts t) k" lemma tkt_thread_eq: assumes R: "A.reachable (c,n,ts)" assumes A: "tFor the inductive argument, we will use this measure, that decreases as a single thread progresses through its phases. \ definition "tweight s \ case s of WAIT _ \ 3::nat | HOLD _ \ 2 | REL _ \ 1 | INIT \ 0" text \We show progress: Every thread that waits for the lock will eventually hold the lock.\ theorem progress: assumes FRUN: "A.is_fair_run s" assumes A: "tj>i. is_HOLD (tts (s j) t)" proof - interpret A: fair_run as\<^sub>0 alstep s by unfold_locales fact from A obtain k where Wk: "tts (s i) t = WAIT k" by (cases "tts (s i) t") auto text \We use the following induction scheme: \<^item> Either the current thread increases (if we reach \k\, we are done) \<^item> (lex) the thread using the current ticket makes a step \<^item> (lex) another thread makes a step \ define lrel where "lrel \ inv_image (measure id <*lex*> measure id <*lex*> measure id) (\i. ( k-cc (s i), tweight (tts (s i) (tkt_thread (tts (s i)) (cc (s i)))), A.dist_step (tkt_thread (tts (s i)) (cc (s i))) i ))" have "wf lrel" unfolding lrel_def by auto then show ?thesis using A(1) Wk proof (induction i) case (less i) text \We name the components of this and the next state\ obtain c n ts where [simp]: "s i = (c,n,ts)" by (cases "s i") from A.run_reachable[of i] have R: "A.reachable (c,n,ts)" by simp obtain c' n' ts' where [simp]: "s (Suc i) = (c',n',ts')" by (cases "s (Suc i)") from A.run_reachable[of "Suc i"] have R': "A.reachable (c',n',ts')" by simp from less.prems have WAIT[simp]: "ts t = WAIT k" by simp { text \If thread \t\ left waiting state, we are done\ assume "ts' t \ WAIT k" hence "ts' t = HOLD k" using less.prems apply (rule_tac A.stepE[of i]) apply (auto elim!: alstep.cases astep_sng.cases split: if_splits) done hence ?case by auto } moreover { assume [simp]: "ts' t = WAIT k" text \Otherwise, we obtain the thread \tt\ that holds the current lock\ obtain tt where UTT: "tttt have "A.can_step tt (s i)" by (simp add: never_blocked) hence ?case proof (cases rule: A.rstep_cases) case (other t') \ \Another thread than \tt\ makes a step.\ text \The current ticket and \tt\'s state remain the same\ hence [simp]: "c' = c \ ts' tt = ts tt" using has_ticket_unique[OF R UTT, of t'] unfolding A.rstep_def using holds_only_current[OF R, of t'] by (force elim!: alstep.cases astep_sng.cases) text \Thus, \tt\ is still using the current ticket\ have [simp]: "tkt_thread ts' c = tt" using UTT tkt_thread_eq[OF R', of tt c] by auto text \Only the distance to \tt\'s next step has decreased\ have "(Suc i, i) \ lrel" unfolding lrel_def tweight_def by (simp add: other) text \And we can apply the induction hypothesis\ with less.IH[of "Suc i"] \t show ?thesis apply (auto) using Suc_lessD by blast next case THIS: this \ \The thread \tt\ that uses the current ticket makes a step\ show ?thesis proof (cases "\k'. ts tt = REL k'") case True \ \\tt\ has finished releasing the lock \ then have [simp]: "ts tt = REL c" using UTT by auto text \Thus, current increases\ have [simp]: "c' = Suc c" using THIS apply - unfolding A.rstep_def apply (erule alstep.cases, erule astep_sng.cases) by auto text \But is still less than \k\\ from A.invar_reachable[OF is_invar1 R] have "k>c" apply (auto simp: invar1_def) by (metis UTT WAIT \ts tt = REL c\ le_neq_implies_less less.prems(1) thread.distinct(9) has_ticket_simps(2)) text \And we can apply the induction hypothesis\ hence "(Suc i, i) \ lrel" unfolding lrel_def by auto with less.IH[of "Suc i"] \t show ?thesis apply (auto) using Suc_lessD by blast next case False \ \\tt\ has acquired the lock, or started releasing it\ text \Hence, current remains the same, but the weight of \tt\ decreases\ hence [simp]: " c' = c \ tweight (ts tt) > tweight (ts' tt) \ has_ticket (ts' tt) c" using THIS UTT apply - unfolding A.rstep_def apply (erule alstep.cases, erule astep_sng.cases) by (auto simp: has_ticket_def tweight_def) text \\tt\ still holds the current lock\ have [simp]: "tkt_thread ts' c = tt" using tkt_thread_eq[OF R' \tt, of c] by simp text \And we can apply the IH\ have "(Suc i, i) \ lrel" unfolding lrel_def by auto with less.IH[of "Suc i"] \t show ?thesis apply (auto) using Suc_lessD by blast qed qed } ultimately show ?case by blast qed qed subsection \Refinement 2: Bounding the Counters\ text \We fix the \k\ from the task description, which must be positive\ consts k::nat specification (k) k_not0[simp]: "k\0" by auto lemma k_gt0[simp]: "0System's state: Current ticket, next ticket, thread states\ type_synonym bstate = "nat \ nat \ (nat \ thread)" text \The step relation of a single thread\ inductive bstep_sng where enter_wait: "bstep_sng (c,n,INIT) (c,(n+1) mod (k*N),WAIT (n mod N))" | loop_wait: "c\tk \ bstep_sng (c,n,WAIT tk) (c,n,WAIT tk)" | exit_wait: "bstep_sng (c,n,WAIT c) (c,n,HOLD c)" | start_release: "bstep_sng (c,n,HOLD tk) (c,n,REL tk)" | release: "bstep_sng (c,n,REL tk) ((tk+1) mod N,n,INIT)" text \The step relation of the system, labeled with the thread \t\ that performs the step\ inductive blstep for t where "\ t \ blstep t (c,n,ts) (c',n',ts(t:=s'))" text \Initial state of the system\ definition "bs\<^sub>0 \ (0, 0, \_. INIT)" interpretation B: system bs\<^sub>0 blstep . lemma b_never_blocked: "B.can_step l s \ l0 blstep apply unfold_locales subgoal for s using b_never_blocked[of 0 s] unfolding B.can_step_def by auto done subsubsection \Simulation\ text \We show that the abstract system simulates the concrete one.\ text \A few lemmas to ease the automation further below\ lemma nat_sum_gtZ_iff[simp]: "finite s \ sum f s \ (0::nat) \ (\x\s. f x \ 0)" by simp lemma n_eq_Suc_sub1_conv[simp]: "n = Suc (n - Suc 0) \ n\0" by auto lemma mod_mult_mod_eq[mod_simps]: "x mod (k * N) mod N = x mod N" by (meson dvd_eq_mod_eq_0 mod_mod_cancel mod_mult_self2_is_0) -lemma mod_eq_imp_eq_aux: "b mod N = (a::nat) mod N \ a\b \ b b=a" - using nat_mod_eq_lemma by force + lemma mod_eq_imp_eq_aux: "b mod N = (a::nat) mod N \ a\b \ b b=a" + by (auto simp add: mod_eq_dvd_iff_nat le_imp_diff_is_add) lemma mod_eq_imp_eq: "\b \ x; x < b + N; b \ y; y < b + N; x mod N = y mod N \ \ x=y" proof - assume a1: "b \ y" assume a2: "y < b + N" assume a3: "b \ x" assume a4: "x < b + N" assume a5: "x mod N = y mod N" have f6: "x < y + N" using a4 a1 by linarith have "y < x + N" using a3 a2 by linarith then show ?thesis using f6 a5 by (metis (no_types) mod_eq_imp_eq_aux nat_le_linear) qed text \Map the ticket of a thread\ fun map_ticket where "map_ticket f INIT = INIT" | "map_ticket f (WAIT tk) = WAIT (f tk)" | "map_ticket f (HOLD tk) = HOLD (f tk)" | "map_ticket f (REL tk) = REL (f tk)" lemma map_ticket_addsimps[simp]: "map_ticket f t = INIT \ t=INIT" "map_ticket f t = WAIT tk \ (\tk'. tk=f tk' \ t=WAIT tk')" "map_ticket f t = HOLD tk \ (\tk'. tk=f tk' \ t=HOLD tk')" "map_ticket f t = REL tk \ (\tk'. tk=f tk' \ t=REL tk')" by (cases t; auto)+ text \We define the number of threads that use a ticket\ fun ni_weight :: "thread \ nat" where "ni_weight INIT = 0" | "ni_weight _ = 1" lemma ni_weight_le1[simp]: "ni_weight s \ Suc 0" by (cases s) auto definition "num_ni ts \ \ i=0.._. INIT) = 0" by (auto simp: num_ni_def) lemma num_ni_upd: "t num_ni (ts(t:=s)) = num_ni ts - ni_weight (ts t) + ni_weight s" by (auto simp: num_ni_def if_distrib[of ni_weight] sum.If_cases algebra_simps simp: sum_diff1_nat ) lemma num_ni_nz_if[simp]: "\t < N; ts t \ INIT\ \ num_ni ts \ 0" apply (cases "ts t") by (simp_all add: num_ni_def) force+ lemma num_ni_leN: "num_ni ts \ N" apply (clarsimp simp: num_ni_def) apply (rule order_trans) apply (rule sum_bounded_above[where K=1]) apply auto done text \We provide an additional invariant, considering the distance of \c\ and \n\. Although we could probably get this from the previous invariants, it is easy enough to prove directly. \ definition "invar3 \ \(c,n,ts). n = c + num_ni ts" lemma is_invar3: "A.is_invar invar3" apply (rule) subgoal by (auto simp: invar3_def as\<^sub>0_def) subgoal for s s' apply clarify apply (erule alstep.cases, erule astep_sng.cases) apply (auto simp: invar3_def num_ni_upd) using holds_only_current by fastforce done text \We establish a simulation relation: The concrete counters are the abstract ones, wrapped around. \ definition "sim_rel1 \ \(c,n,ts) (ci,ni,tsi). ci = c mod N \ ni = n mod (k*N) \ tsi = (map_ticket (\t. t mod N)) o ts " lemma sraux: "sim_rel1 (c,n,ts) (ci,ni,tsi) \ ci = c mod N \ ni = n mod (k*N)" by (auto simp: sim_rel1_def Let_def) lemma sraux2: "\sim_rel1 (c,n,ts) (ci,ni,tsi); t \ tsi t = map_ticket (\x. x mod N) (ts t)" by (auto simp: sim_rel1_def Let_def) interpretation sim1: simulationI as\<^sub>0 alstep bs\<^sub>0 blstep sim_rel1 proof unfold_locales show "sim_rel1 as\<^sub>0 bs\<^sub>0" by (auto simp: sim_rel1_def as\<^sub>0_def bs\<^sub>0_def) next fix as bs t bs' assume Ra_aux: "A.reachable as" and Rc_aux: "B.reachable bs" and SIM: "sim_rel1 as bs" and CS: "blstep t bs bs'" obtain c n ts where [simp]: "as=(c,n,ts)" by (cases as) obtain ci ni tsi where [simp]: "bs=(ci,ni,tsi)" by (cases bs) obtain ci' ni' tsi' where [simp]: "bs'=(ci',ni',tsi')" by (cases bs') from Ra_aux have Ra: "A.reachable (c,n,ts)" by simp from Rc_aux have Rc: "B.reachable (ci,ni,tsi)" by simp from CS have "ttk" "tkt apply fastforce using \t num_ni_leN[of ts] by fastforce from SIM CS have "\as'. alstep t as as' \ sim_rel1 as' bs'" apply simp apply (erule blstep.cases) apply (erule bstep_sng.cases) apply clarsimp_all subgoal apply (intro exI conjI) apply (rule alstep.intros) apply (simp add: sim_rel1_def Let_def) apply (simp add: sraux sraux2) apply (rule astep_sng.enter_wait) apply (simp add: sim_rel1_def; intro conjI ext) apply (auto simp: sim_rel1_def Let_def mod_simps) done subgoal apply (clarsimp simp: sraux sraux2) apply (intro exI conjI) apply (rule alstep.intros) apply (simp add: sim_rel1_def Let_def) apply clarsimp apply (rule astep_sng.loop_wait) apply (auto simp: sim_rel1_def Let_def mod_simps) done subgoal apply (clarsimp simp: sraux sraux2) subgoal for tk' apply (subgoal_tac "tk'=c") apply (intro exI conjI) apply (rule alstep.intros) apply (simp add: sim_rel1_def Let_def) apply clarsimp apply (rule astep_sng.exit_wait) apply (auto simp: sim_rel1_def Let_def mod_simps) [] apply (clarsimp simp: sim_rel1_def) apply (erule mod_eq_imp_eq_aux) apply (auto simp: AUX1) done done subgoal apply (clarsimp simp: sraux sraux2) apply (intro exI conjI) apply (rule alstep.intros) apply (simp add: sim_rel1_def Let_def) apply clarsimp apply (rule astep_sng.start_release) apply (auto simp: sim_rel1_def Let_def mod_simps) done subgoal apply (clarsimp simp: sraux sraux2) apply (intro exI conjI) apply (rule alstep.intros) apply (simp add: sim_rel1_def Let_def) apply clarsimp apply (rule astep_sng.release) apply (auto simp: sim_rel1_def Let_def mod_simps) done done then show "\as'. sim_rel1 as' bs' \ alstep t as as'" by blast next fix as bs l assume "A.reachable as" "B.reachable bs" "sim_rel1 as bs" "A.can_step l as" then show "B.can_step l bs" using b_never_blocked never_blocked by simp qed subsubsection \Transfer of Properties\ text \We transfer a few properties over the simulation, which we need for the next refinement step. \ lemma xfer_locks_ticket: assumes "locks_ticket (map_ticket (\t. t mod N) (ts t)) tki" obtains tk where "tki=tk mod N" "locks_ticket (ts t) tk" using assms unfolding locks_ticket_def by auto lemma b_holds_only_current: "\B.reachable (c, n, ts); t < N; locks_ticket (ts t) tk\ \ tk = c" apply (rule sim1.xfer_reachable, assumption) apply (clarsimp simp: sim_rel1_def) apply (erule xfer_locks_ticket)+ using holds_only_current by blast lemma b_mutual_exclusion': "\B.reachable s; tt'; locks_ticket (tts s t) tk; locks_ticket (tts s t') tk' \ \ False" apply (rule sim1.xfer_reachable, assumption) apply (clarsimp simp: sim_rel1_def) apply (erule xfer_locks_ticket)+ apply (drule (3) mutual_exclusion'; simp) done lemma xfer_has_ticket: assumes "has_ticket (map_ticket (\t. t mod N) (ts t)) tki" obtains tk where "tki=tk mod N" "has_ticket (ts t) tk" using assms unfolding has_ticket_def by auto lemma has_ticket_in_range: assumes Ra: "A.reachable (c,n,ts)" and "ttk \ tktk \ tkt apply fastforce using \t num_ni_leN[of ts] by fastforce qed lemma b_has_ticket_unique: "\B.reachable (ci,ni,tsi); t \ t'=t" apply (rule sim1.xfer_reachable, assumption) apply (auto simp: sim_rel1_def) subgoal for c n ts apply (erule xfer_has_ticket)+ apply simp subgoal for tk tk' apply (subgoal_tac "tk=tk'") apply simp apply (frule (4) has_ticket_unique, assumption) apply (frule (2) has_ticket_in_range[where tk=tk]) apply (frule (2) has_ticket_in_range[where tk=tk']) apply (auto simp: mod_simps) apply (rule mod_eq_imp_eq; (assumption|simp)) done done done subsection \Refinement 3: Using an Array\text_raw \\label{sec:refine3}\ text \Finally, we use an array instead of a counter, thus obtaining the exact data structures from the challenge assignment. Note that we model the array by a list of Booleans here. \ text \System's state: Current ticket array, next ticket, thread states\ type_synonym cstate = "bool list \ nat \ (nat \ thread)" text \The step relation of a single thread\ inductive cstep_sng where enter_wait: "cstep_sng (p,n,INIT) (p,(n+1) mod (k*N),WAIT (n mod N))" | loop_wait: "\p!tk \ cstep_sng (p,n,WAIT tk) (p,n,WAIT tk)" | exit_wait: "p!tk \ cstep_sng (p,n,WAIT tk) (p,n,HOLD tk)" | start_release: "cstep_sng (p,n,HOLD tk) (p[tk:=False],n,REL tk)" | release: "cstep_sng (p,n,REL tk) (p[(tk+1) mod N := True],n,INIT)" text \The step relation of the system, labeled with the thread \t\ that performs the step\ inductive clstep for t where "\ t \ clstep t (c,n,ts) (c',n',ts(t:=s'))" text \Initial state of the system\ definition "cs\<^sub>0 \ ((replicate N False)[0:=True], 0, \_. INIT)" interpretation C: system cs\<^sub>0 clstep . lemma c_never_blocked: "C.can_step l s \ l0 clstep apply unfold_locales subgoal for s using c_never_blocked[of 0 s] unfolding C.can_step_def by auto done text \We establish another invariant that states that the ticket numbers are bounded.\ definition "invar4 \ \(c,n,ts). c (\ttk. has_ticket (ts t) tk \ tk0_def) subgoal for s s' apply clarify apply (erule blstep.cases, erule bstep_sng.cases) unfolding invar4_def apply safe apply (metis N_gt0 fun_upd_apply has_ticket_simps(2) mod_less_divisor) apply (metis fun_upd_triv) apply (metis fun_upd_other fun_upd_same has_ticket_simps(3)) apply (metis fun_upd_other fun_upd_same has_ticket_def has_ticket_simps(4)) using mod_less_divisor apply blast apply (metis fun_upd_apply thread.distinct(1) thread.distinct(3) thread.distinct(5) has_ticket_def) done done text \We define a predicate that describes that a thread of the system is at the release sequence point --- in this case, the array does not have a set bit, otherwise, the set bit corresponds to the current ticket.\ definition "is_REL_state \ \ts. \ttk. ts t = REL tk" lemma is_REL_state_simps[simp]: "t is_REL_state (ts(t:=REL tk))" "t \is_REL (ts t) \ \is_REL s' \ is_REL_state (ts(t:=s')) \ is_REL_state ts" unfolding is_REL_state_def apply (auto; fail) [] apply auto [] by (metis thread.disc(12)) lemma is_REL_state_aux1: assumes R: "B.reachable (c,n,ts)" assumes REL: "is_REL_state ts" assumes "tc" using REL unfolding is_REL_state_def apply clarify subgoal for t' tk' using b_has_ticket_unique[OF R \t, of tk t'] using b_holds_only_current[OF R, of t' tk'] by (auto) done lemma is_REL_state_aux2: assumes R: "B.reachable (c,n,ts)" assumes A: "tis_REL_state (ts(t:=INIT))" using b_holds_only_current[OF R] A using b_mutual_exclusion'[OF R] apply (clarsimp simp: is_REL_state_def) by fastforce text \Simulation relation that implements current ticket by array\ definition "sim_rel2 \ \(c,n,ts) (ci,ni,tsi). (if is_REL_state ts then ci = replicate N False else ci = (replicate N False)[c:=True] ) \ ni = n \ tsi = ts " interpretation sim2: simulationI bs\<^sub>0 blstep cs\<^sub>0 clstep sim_rel2 proof unfold_locales show "sim_rel2 bs\<^sub>0 cs\<^sub>0" by (auto simp: sim_rel2_def bs\<^sub>0_def cs\<^sub>0_def is_REL_state_def) next fix bs cs t cs' assume Rc_aux: "B.reachable bs" and Rd_aux: "C.reachable cs" and SIM: "sim_rel2 bs cs" and CS: "clstep t cs cs'" obtain c n ts where [simp]: "bs=(c,n,ts)" by (cases bs) obtain ci ni tsi where [simp]: "cs=(ci,ni,tsi)" by (cases cs) obtain ci' ni' tsi' where [simp]: "cs'=(ci',ni',tsi')" by (cases cs') from Rc_aux have Rc: "B.reachable (c,n,ts)" by simp from Rd_aux have Rd: "C.reachable (ci,ni,tsi)" by simp from CS have "tt by (auto simp: invar4_def) have HOLD_AUX: "tk=c" if "ts t = HOLD tk" for tk using b_holds_only_current[OF Rc \t, of tk] that by auto have REL_AUX: "tk=c" if "ts t = REL tk" "tt, of tk] that by auto have [simp]: "c (replicate N False)[c := True]" "(replicate N False)[c := True] \ replicate N False" apply (auto simp: list_eq_iff_nth_eq nth_list_update) using \c < N\ by blast+ have [simp]: "(replicate N False)[c := True] ! d \ d=c" if "dbs'. blstep t bs bs' \ sim_rel2 bs' cs'" apply simp apply (subst (asm) sim_rel2_def) apply (erule clstep.cases) apply (erule cstep_sng.cases) apply clarsimp_all subgoal apply (intro exI conjI) apply (rule blstep.intros) apply (simp) apply clarsimp apply (rule bstep_sng.enter_wait) apply (auto simp: sim_rel2_def split: if_splits) done subgoal for tk' apply (intro exI conjI) apply (rule blstep.intros) apply (simp) apply clarsimp apply (rule bstep_sng.loop_wait) subgoal apply (clarsimp simp: sim_rel2_def split: if_splits) apply (frule (2) is_REL_state_aux1[OF Rc]) by simp subgoal by (auto simp: sim_rel2_def split: if_splits) done subgoal apply (intro exI conjI) apply (rule blstep.intros) apply (simp) apply (clarsimp split: if_splits) apply (rule bstep_sng.exit_wait) apply (auto simp: sim_rel2_def split: if_splits) done subgoal apply (intro exI conjI) apply (rule blstep.intros) apply (simp) apply clarsimp apply (rule bstep_sng.start_release) apply (auto simp: sim_rel2_def dest: HOLD_AUX split: if_splits) done subgoal apply (intro exI conjI) apply (rule blstep.intros) apply (simp) apply clarsimp apply (rule bstep_sng.release) apply (auto simp: sim_rel2_def dest: is_REL_state_aux2[OF Rc] split: if_splits) by (metis fun_upd_triv is_REL_state_simps(1)) done then show "\bs'. sim_rel2 bs' cs' \ blstep t bs bs'" by blast next fix bs cs l assume "B.reachable bs" "C.reachable cs" "sim_rel2 bs cs" "B.can_step l bs" then show "C.can_step l cs" using c_never_blocked b_never_blocked by simp qed subsection \Transfer Setup\ text \We set up the final simulation relation, and the transfer of the concepts used in the correctness statements. \ definition "sim_rel \ sim_rel1 OO sim_rel2" interpretation sim: simulation as\<^sub>0 alstep cs\<^sub>0 clstep sim_rel unfolding sim_rel_def by (rule sim_trans) unfold_locales lemma xfer_holds: assumes "sim_rel s cs" shows "is_HOLD (tts cs t) \ is_HOLD (tts s t)" using assms unfolding sim_rel_def sim_rel1_def sim_rel2_def by (cases "tts cs t") auto lemma xfer_waits: assumes "sim_rel s cs" shows "is_WAIT (tts cs t) \ is_WAIT (tts s t)" using assms unfolding sim_rel_def sim_rel1_def sim_rel2_def by (cases "tts cs t") auto lemma xfer_init: assumes "sim_rel s cs" shows "tts cs t = INIT \ tts s t = INIT" using assms unfolding sim_rel_def sim_rel1_def sim_rel2_def by auto subsection \Main Theorems\ text_raw \\label{sec:main_theorems}\ subsubsection \Trusted Code Base\ text \ Note that the trusted code base for these theorems is only the formalization of the concrete system as defined in Section~\ref{sec:refine3}. The simulation setup and the abstract systems are only auxiliary constructions for the proof. For completeness, we display the relevant definitions of reachability, runs, and fairness here: @{lemma [display, source] "C.step s s' = (\l. clstep l s s')" by simp} @{thm [display] C.reachable_def[no_vars] C.run_definitions[no_vars]} \ subsubsection \Safety\ text \We show that there is no reachable state in which two different threads hold the lock.\ (* ALWAYS (NOT (HOLD ** HOLD)) *) theorem final_mutual_exclusion: "\C.reachable s; tt'; is_HOLD (tts s t); is_HOLD (tts s t') \ \ False" apply (erule sim.xfer_reachable) apply (simp add: xfer_holds) by (erule (5) mutual_exclusion) subsubsection \Fairness\ text \We show that, whenever a thread \t\ draws a ticket, all other threads \t'\ waiting for the lock will be granted the lock before \t\. \ (* ALWAYS (INIT t\<^sub>1 \ WAIT t\<^sub>2 \ X (WAIT t\<^sub>1) \ HOLD t\<^sub>2 BEFORE HOLD t\<^sub>1 ) *) theorem final_fair: assumes RUN: "C.is_run s" assumes ACQ: "t \Thread \t\ draws ticket in step \i\\ assumes HOLD: "i \Thread \t\ holds lock in step \j\\ assumes WAIT: "t' \Thread \t'\ waits for lock at step \i\\ obtains l where "i \Then, \t'\ gets lock earlier\ using RUN proof (rule sim.xfer_run) fix as assume Ra: "A.is_run as" and SIM[rule_format]: "\i. sim_rel (as i) (s i)" note XFER = xfer_init[OF SIM] xfer_holds[OF SIM] xfer_waits[OF SIM] show ?thesis using assms apply (simp add: XFER) apply (erule (6) fair[OF Ra]) apply (erule (1) that) apply (simp add: XFER) done qed subsubsection \Liveness\ text \We show that, for a fair run, every thread that waits for the lock will eventually hold the lock.\ (* ALWAYS (WAIT \ EVENTUALLY HOLD) *) theorem final_progress: assumes FRUN: "C.is_fair_run s" assumes WAIT: "tj>i. is_HOLD (tts (s j) t)" using FRUN proof (rule sim.xfer_fair_run) fix as assume Ra: "A.is_fair_run as" and SIM[rule_format]: "\i. sim_rel (as i) (s i)" note XFER = xfer_init[OF SIM] xfer_holds[OF SIM] xfer_waits[OF SIM] show ?thesis using assms apply (simp add: XFER) apply (erule (1) progress[OF Ra]) done qed end