Theory Insert_Spec
section‹Specifying list insertion›
theory Insert_Spec
imports OpSet
begin
text‹In this section we consider only list insertion. We model an insertion
operation as a pair (\isa{ID, ref}), where \isa{ref} is either \isa{None}
(signifying an insertion at the head of the list) or \isa{Some r}
(an insertion immediately after a reference element with ID \isa{r}).
If the reference element does not exist, the operation does nothing.
We provide two different definitions of the interpretation function for list
insertion: \isa{insert-spec} and \isa{insert-alt}. The \isa{insert-alt} definition
matches the paper, while \isa{insert-spec} uses the Isabelle/HOL list datatype,
making it more suitable for formal reasoning. In a later subsection we prove
that the two definitions are in fact equivalent.›
fun insert_spec :: "'oid list ⇒ ('oid × 'oid option) ⇒ 'oid list" where
"insert_spec xs (oid, None) = oid#xs" |
"insert_spec [] (oid, _) = []" |
"insert_spec (x#xs) (oid, Some ref) =
(if x = ref then x # oid # xs
else x # (insert_spec xs (oid, Some ref)))"
fun insert_alt :: "('oid × 'oid option) set ⇒ ('oid × 'oid) ⇒ ('oid × 'oid option) set" where
"insert_alt list_rel (oid, ref) = (
if ∃n. (ref, n) ∈ list_rel
then {(p, n) ∈ list_rel. p ≠ ref} ∪ {(ref, Some oid)} ∪
{(i, n). i = oid ∧ (ref, n) ∈ list_rel}
else list_rel)"
text‹\isa{interp-ins} is the sequential interpretation of a set of insertion
operations. It starts with an empty list as initial state, and then applies
the operations from left to right.›
definition interp_ins :: "('oid × 'oid option) list ⇒ 'oid list" where
"interp_ins ops ≡ foldl insert_spec [] ops"
subsection ‹The \isa{insert-ops} predicate›
text‹We now specialise the definitions from the abstract OpSet section for list
insertion. \isa{insert-opset} is an opset consisting only of insertion operations,
and \isa{insert-ops} is the specialisation of the \isa{spec-ops} predicate for
insertion operations. We prove several useful lemmas about \isa{insert-ops}.›
locale insert_opset = opset opset set_option
for opset :: "('oid::{linorder} × 'oid option) set"
definition insert_ops :: "('oid::{linorder} × 'oid option) list ⇒ bool" where
"insert_ops list ≡ spec_ops list set_option"
lemma insert_ops_NilI [intro!]:
shows "insert_ops []"
by (auto simp add: insert_ops_def spec_ops_def)
lemma insert_ops_rem_last [dest]:
assumes "insert_ops (xs @ [x])"
shows "insert_ops xs"
using assms insert_ops_def spec_ops_rem_last by blast
lemma insert_ops_rem_cons:
assumes "insert_ops (x # xs)"
shows "insert_ops xs"
using assms insert_ops_def spec_ops_rem_cons by blast
lemma insert_ops_appendD:
assumes "insert_ops (xs @ ys)"
shows "insert_ops xs"
using assms by (induction ys rule: List.rev_induct,
auto, metis insert_ops_rem_last append_assoc)
lemma insert_ops_rem_prefix:
assumes "insert_ops (pre @ suf)"
shows "insert_ops suf"
using assms proof(induction pre)
case Nil
then show "insert_ops ([] @ suf) ⟹ insert_ops suf"
by auto
next
case (Cons a pre)
have "sorted (map fst suf)"
using assms by (simp add: insert_ops_def sorted_append spec_ops_def)
moreover have "distinct (map fst suf)"
using assms by (simp add: insert_ops_def spec_ops_def)
ultimately show "insert_ops ((a # pre) @ suf) ⟹ insert_ops suf"
by (simp add: insert_ops_def spec_ops_def)
qed
lemma insert_ops_remove1:
assumes "insert_ops xs"
shows "insert_ops (remove1 x xs)"
using assms insert_ops_def spec_ops_remove1 by blast
lemma last_op_greatest:
assumes "insert_ops (op_list @ [(oid, oper)])"
and "x ∈ set (map fst op_list)"
shows "x < oid"
using assms spec_ops_id_inc insert_ops_def by metis
lemma insert_ops_ref_older:
assumes "insert_ops (pre @ [(oid, Some ref)] @ suf)"
shows "ref < oid"
using assms by (auto simp add: insert_ops_def spec_ops_def)
lemma insert_ops_memb_ref_older:
assumes "insert_ops op_list"
and "(oid, Some ref) ∈ set op_list"
shows "ref < oid"
using assms insert_ops_ref_older split_list_first by fastforce
subsection ‹Properties of the \isa{insert-spec} function›
lemma insert_spec_none [simp]:
shows "set (insert_spec xs (oid, None)) = set xs ∪ {oid}"
by (induction xs, auto simp add: insert_commute sup_commute)
lemma insert_spec_set [simp]:
assumes "ref ∈ set xs"
shows "set (insert_spec xs (oid, Some ref)) = set xs ∪ {oid}"
using assms proof(induction xs)
assume "ref ∈ set []"
thus "set (insert_spec [] (oid, Some ref)) = set [] ∪ {oid}"
by auto
next
fix a xs
assume "ref ∈ set xs ⟹ set (insert_spec xs (oid, Some ref)) = set xs ∪ {oid}"
and "ref ∈ set (a#xs)"
thus "set (insert_spec (a#xs) (oid, Some ref)) = set (a#xs) ∪ {oid}"
by(cases "a = ref", auto simp add: insert_commute sup_commute)
qed
lemma insert_spec_nonex [simp]:
assumes "ref ∉ set xs"
shows "insert_spec xs (oid, Some ref) = xs"
using assms proof(induction xs)
show "insert_spec [] (oid, Some ref) = []"
by simp
next
fix a xs
assume "ref ∉ set xs ⟹ insert_spec xs (oid, Some ref) = xs"
and "ref ∉ set (a#xs)"
thus "insert_spec (a#xs) (oid, Some ref) = a#xs"
by(cases "a = ref", auto simp add: insert_commute sup_commute)
qed
lemma list_greater_non_memb:
fixes oid :: "'oid::{linorder}"
assumes "⋀x. x ∈ set xs ⟹ x < oid"
and "oid ∈ set xs"
shows "False"
using assms by blast
lemma inserted_item_ident:
assumes "a ∈ set (insert_spec xs (e, i))"
and "a ∉ set xs"
shows "a = e"
using assms proof(induction xs)
case Nil
then show "a = e" by (cases i, auto)
next
case (Cons x xs)
then show "a = e"
proof(cases i)
case None
then show "a = e" using assms by auto
next
case (Some ref)
then show "a = e" using Cons by (case_tac "x = ref", auto)
qed
qed
lemma insert_spec_distinct [intro]:
fixes oid :: "'oid::{linorder}"
assumes "distinct xs"
and "⋀x. x ∈ set xs ⟹ x < oid"
and "ref = Some r ⟶ r < oid"
shows "distinct (insert_spec xs (oid, ref))"
using assms(1) assms(2) proof(induction xs)
show "distinct (insert_spec [] (oid, ref))"
by(cases ref, auto)
next
fix a xs
assume IH: "distinct xs ⟹ (⋀x. x ∈ set xs ⟹ x < oid) ⟹ distinct (insert_spec xs (oid, ref))"
and D: "distinct (a#xs)"
and L: "⋀x. x ∈ set (a#xs) ⟹ x < oid"
show "distinct (insert_spec (a#xs) (oid, ref))"
proof(cases "ref")
assume "ref = None"
thus "distinct (insert_spec (a#xs) (oid, ref))"
using D L by auto
next
fix id
assume S: "ref = Some id"
{
assume EQ: "a = id"
hence "id ≠ oid"
using D L by auto
moreover have "id ∉ set xs"
using D EQ by auto
moreover have "oid ∉ set xs"
using L by auto
ultimately have "id ≠ oid ∧ id ∉ set xs ∧ oid ∉ set xs ∧ distinct xs"
using D by auto
}
note T = this
{
assume NEQ: "a ≠ id"
have 0: "a ∉ set (insert_spec xs (oid, Some id))"
using D L by(metis distinct.simps(2) insert_spec.simps(1) insert_spec_none insert_spec_nonex
insert_spec_set insert_iff list.set(2) not_less_iff_gr_or_eq)
have 1: "distinct xs"
using D by auto
have "⋀x. x ∈ set xs ⟹ x < oid"
using L by auto
hence "distinct (insert_spec xs (oid, Some id))"
using S IH[OF 1] by blast
hence "a ∉ set (insert_spec xs (oid, Some id)) ∧ distinct (insert_spec xs (oid, Some id))"
using 0 by auto
}
from this S T show "distinct (insert_spec (a # xs) (oid, ref))"
by clarsimp
qed
qed
lemma insert_after_ref:
assumes "distinct (xs @ ref # ys)"
shows "insert_spec (xs @ ref # ys) (oid, Some ref) = xs @ ref # oid # ys"
using assms by (induction xs, auto)
lemma insert_somewhere:
assumes "ref = None ∨ (ref = Some r ∧ r ∈ set list)"
shows "∃xs ys. list = xs @ ys ∧ insert_spec list (oid, ref) = xs @ oid # ys"
using assms proof(induction list)
assume "ref = None ∨ ref = Some r ∧ r ∈ set []"
thus "∃xs ys. [] = xs @ ys ∧ insert_spec [] (oid, ref) = xs @ oid # ys"
proof
assume "ref = None"
thus "∃xs ys. [] = xs @ ys ∧ insert_spec [] (oid, ref) = xs @ oid # ys"
by auto
next
assume "ref = Some r ∧ r ∈ set []"
thus "∃xs ys. [] = xs @ ys ∧ insert_spec [] (oid, ref) = xs @ oid # ys"
by auto
qed
next
fix a list
assume 1: "ref = None ∨ ref = Some r ∧ r ∈ set (a#list)"
and IH: "ref = None ∨ ref = Some r ∧ r ∈ set list ⟹
∃xs ys. list = xs @ ys ∧ insert_spec list (oid, ref) = xs @ oid # ys"
show "∃xs ys. a # list = xs @ ys ∧ insert_spec (a # list) (oid, ref) = xs @ oid # ys"
proof(rule disjE[OF 1])
assume "ref = None"
thus "∃xs ys. a # list = xs @ ys ∧ insert_spec (a # list) (oid, ref) = xs @ oid # ys"
by force
next
assume "ref = Some r ∧ r ∈ set (a # list)"
hence 2: "r = a ∨ r ∈ set list" and 3: "ref = Some r"
by auto
show "∃xs ys. a # list = xs @ ys ∧ insert_spec (a # list) (oid, ref) = xs @ oid # ys"
proof(rule disjE[OF 2])
assume "r = a"
thus "∃xs ys. a # list = xs @ ys ∧ insert_spec (a # list) (oid, ref) = xs @ oid # ys"
using 3 by(metis append_Cons append_Nil insert_spec.simps(3))
next
assume "r ∈ set list"
from this obtain xs ys
where "list = xs @ ys ∧ insert_spec list (oid, ref) = xs @ oid # ys"
using IH 3 by auto
thus "∃xs ys. a # list = xs @ ys ∧ insert_spec (a # list) (oid, ref) = xs @ oid # ys"
using 3 by clarsimp (metis append_Cons append_Nil)
qed
qed
qed
lemma insert_first_part:
assumes "ref = None ∨ (ref = Some r ∧ r ∈ set xs)"
shows "insert_spec (xs @ ys) (oid, ref) = (insert_spec xs (oid, ref)) @ ys"
using assms proof(induction ys rule: rev_induct)
assume "ref = None ∨ ref = Some r ∧ r ∈ set xs"
thus "insert_spec (xs @ []) (oid, ref) = insert_spec xs (oid, ref) @ []"
by auto
next
fix x xsa
assume IH: "ref = None ∨ ref = Some r ∧ r ∈ set xs ⟹ insert_spec (xs @ xsa) (oid, ref) = insert_spec xs (oid, ref) @ xsa"
and "ref = None ∨ ref = Some r ∧ r ∈ set xs"
thus "insert_spec (xs @ xsa @ [x]) (oid, ref) = insert_spec xs (oid, ref) @ xsa @ [x]"
proof(induction xs)
assume "ref = None ∨ ref = Some r ∧ r ∈ set []"
thus "insert_spec ([] @ xsa @ [x]) (oid, ref) = insert_spec [] (oid, ref) @ xsa @ [x]"
by auto
next
fix a xs
assume 1: "ref = None ∨ ref = Some r ∧ r ∈ set (a # xs)"
and 2: "((ref = None ∨ ref = Some r ∧ r ∈ set xs ⟹ insert_spec (xs @ xsa) (oid, ref) = insert_spec xs (oid, ref) @ xsa) ⟹
ref = None ∨ ref = Some r ∧ r ∈ set xs ⟹ insert_spec (xs @ xsa @ [x]) (oid, ref) = insert_spec xs (oid, ref) @ xsa @ [x])"
and 3: "(ref = None ∨ ref = Some r ∧ r ∈ set (a # xs) ⟹ insert_spec ((a # xs) @ xsa) (oid, ref) = insert_spec (a # xs) (oid, ref) @ xsa)"
show "insert_spec ((a # xs) @ xsa @ [x]) (oid, ref) = insert_spec (a # xs) (oid, ref) @ xsa @ [x]"
proof(rule disjE[OF 1])
assume "ref = None"
thus "insert_spec ((a # xs) @ xsa @ [x]) (oid, ref) = insert_spec (a # xs) (oid, ref) @ xsa @ [x]"
by auto
next
assume "ref = Some r ∧ r ∈ set (a # xs)"
thus "insert_spec ((a # xs) @ xsa @ [x]) (oid, ref) = insert_spec (a # xs) (oid, ref) @ xsa @ [x]"
using 2 3 by auto
qed
qed
qed
lemma insert_second_part:
assumes "ref = Some r"
and "r ∉ set xs"
and "r ∈ set ys"
shows "insert_spec (xs @ ys) (oid, ref) = xs @ (insert_spec ys (oid, ref))"
using assms proof(induction xs)
assume "ref = Some r"
thus "insert_spec ([] @ ys) (oid, ref) = [] @ insert_spec ys (oid, ref)"
by auto
next
fix a xs
assume "ref = Some r" and "r ∉ set (a # xs)" and "r ∈ set ys"
and "ref = Some r ⟹ r ∉ set xs ⟹ r ∈ set ys ⟹ insert_spec (xs @ ys) (oid, ref) = xs @ insert_spec ys (oid, ref)"
thus "insert_spec ((a # xs) @ ys) (oid, ref) = (a # xs) @ insert_spec ys (oid, ref)"
by auto
qed
subsection ‹Properties of the \isa{interp-ins} function›
lemma interp_ins_empty [simp]:
shows "interp_ins [] = []"
by (simp add: interp_ins_def)
lemma interp_ins_tail_unfold:
shows "interp_ins (xs @ [x]) = insert_spec (interp_ins xs) x"
by (clarsimp simp add: interp_ins_def)
lemma interp_ins_subset [simp]:
shows "set (interp_ins op_list) ⊆ set (map fst op_list)"
proof(induction op_list rule: List.rev_induct)
case Nil
then show "set (interp_ins []) ⊆ set (map fst [])"
by (simp add: interp_ins_def)
next
case (snoc x xs)
hence IH: "set (interp_ins xs) ⊆ set (map fst xs)"
using interp_ins_def by blast
obtain oid ref where x_pair: "x = (oid, ref)"
by fastforce
hence spec: "interp_ins (xs @ [x]) = insert_spec (interp_ins xs) (oid, ref)"
by (simp add: interp_ins_def)
then show "set (interp_ins (xs @ [x])) ⊆ set (map fst (xs @ [x]))"
proof(cases ref)
case None
then show "set (interp_ins (xs @ [x])) ⊆ set (map fst (xs @ [x]))"
using IH spec x_pair by auto
next
case (Some a)
then show "set (interp_ins (xs @ [x])) ⊆ set (map fst (xs @ [x]))"
using IH spec x_pair by (cases "a ∈ set (interp_ins xs)", auto)
qed
qed
lemma interp_ins_distinct:
assumes "insert_ops op_list"
shows "distinct (interp_ins op_list)"
using assms proof(induction op_list rule: rev_induct)
case Nil
then show "distinct (interp_ins [])"
by (simp add: interp_ins_def)
next
case (snoc x xs)
hence IH: "distinct (interp_ins xs)" by blast
obtain oid ref where x_pair: "x = (oid, ref)" by force
hence "∀x ∈ set (map fst xs). x < oid"
using last_op_greatest snoc.prems by blast
hence "∀x ∈ set (interp_ins xs). x < oid"
using interp_ins_subset by fastforce
hence "distinct (insert_spec (interp_ins xs) (oid, ref))"
using IH insert_spec_distinct insert_spec_nonex by metis
then show "distinct (interp_ins (xs @ [x]))"
by (simp add: x_pair interp_ins_tail_unfold)
qed
subsection ‹Equivalence of the two definitions of insertion›
text‹At the beginning of this section we gave two different definitions of
interpretation functions for list insertion: \isa{insert-spec} and
\isa{insert-alt}. In this section we prove that the two are equivalent.
We first define how to derive the successor relation from an Isabelle list.
This relation contains (\isa{id}, \isa{None}) if \isa{id} is the last element
of the list, and (\isa{id1}, \isa{id2}) if \isa{id1} is immediately
followed by \isa{id2} in the list.›
fun succ_rel :: "'oid list ⇒ ('oid × 'oid option) set" where
"succ_rel [] = {}" |
"succ_rel [head] = {(head, None)}" |
"succ_rel (head#x#xs) = {(head, Some x)} ∪ succ_rel (x#xs)"
text‹\isa{interp-alt} is the equivalent of \isa{interp-ins}, but using
\isa{insert-alt} instead of \isa{insert-spec}. To match the paper, it uses a
distinct head element to refer to the beginning of the list.›
definition interp_alt :: "'oid ⇒ ('oid × 'oid option) list ⇒ ('oid × 'oid option) set" where
"interp_alt head ops ≡ foldl insert_alt {(head, None)}
(map (λx. case x of
(oid, None) ⇒ (oid, head) |
(oid, Some ref) ⇒ (oid, ref))
ops)"
lemma succ_rel_set_fst:
shows "fst ` (succ_rel xs) = set xs"
by (induction xs rule: succ_rel.induct, auto)
lemma succ_rel_functional:
assumes "(a, b1) ∈ succ_rel xs"
and "(a, b2) ∈ succ_rel xs"
and "distinct xs"
shows "b1 = b2"
using assms proof(induction xs rule: succ_rel.induct)
case 1
then show ?case by simp
next
case (2 head)
then show ?case by simp
next
case (3 head x xs)
then show ?case
proof(cases "a = head")
case True
hence "a ∉ set (x#xs)"
using 3 by auto
hence "a ∉ fst ` (succ_rel (x#xs))"
using succ_rel_set_fst by metis
then show "b1 = b2"
using 3 image_iff by fastforce
next
case False
hence "{(a, b1), (a, b2)} ⊆ succ_rel (x#xs)"
using 3 by auto
moreover have "distinct (x#xs)"
using 3 by auto
ultimately show "b1 = b2"
using "3.IH" by auto
qed
qed
lemma succ_rel_rem_head:
assumes "distinct (x # xs)"
shows "{(p, n) ∈ succ_rel (x # xs). p ≠ x} = succ_rel xs"
proof -
have head_notin: "x ∉ fst ` succ_rel xs"
using assms by (simp add: succ_rel_set_fst)
moreover obtain y where "(x, y) ∈ succ_rel (x # xs)"
by (cases xs, auto)
moreover have "succ_rel (x # xs) = {(x, y)} ∪ succ_rel xs"
using calculation head_notin image_iff by (cases xs, fastforce+)
moreover from this have "⋀n. (x, n) ∈ succ_rel (x # xs) ⟹ n = y"
by (metis Pair_inject fst_conv head_notin image_eqI insertE insert_is_Un)
hence "{(p, n) ∈ succ_rel (x # xs). p ≠ x} = succ_rel (x # xs) - {(x, y)}"
by blast
moreover have "succ_rel (x # xs) - {(x, y)} = succ_rel xs"
using image_iff calculation by fastforce
ultimately show "{(p, n) ∈ succ_rel (x # xs). p ≠ x} = succ_rel xs"
by simp
qed
lemma succ_rel_swap_head:
assumes "distinct (ref # list)"
and "(ref, n) ∈ succ_rel (ref # list)"
shows "succ_rel (oid # list) = {(oid, n)} ∪ succ_rel list"
proof(cases list)
case Nil
then show ?thesis using assms by auto
next
case (Cons a list)
moreover from this have "n = Some a"
by (metis Un_iff assms singletonI succ_rel.simps(3) succ_rel_functional)
ultimately show ?thesis by simp
qed
lemma succ_rel_insert_alt:
assumes "a ≠ ref"
and "distinct (oid # a # b # list)"
shows "insert_alt (succ_rel (a # b # list)) (oid, ref) =
{(a, Some b)} ∪ insert_alt (succ_rel (b # list)) (oid, ref)"
proof(cases "∃n. (ref, n) ∈ succ_rel (a # b # list)")
case True
hence "insert_alt (succ_rel (a # b # list)) (oid, ref) =
{(p, n) ∈ succ_rel (a # b # list). p ≠ ref} ∪ {(ref, Some oid)} ∪
{(i, n). i = oid ∧ (ref, n) ∈ succ_rel (a # b # list)}"
by simp
moreover have "{(p, n) ∈ succ_rel (a # b # list). p ≠ ref} =
{(a, Some b)} ∪ {(p, n) ∈ succ_rel (b # list). p ≠ ref}"
using assms(1) by auto
moreover have "insert_alt (succ_rel (b # list)) (oid, ref) =
{(p, n) ∈ succ_rel (b # list). p ≠ ref} ∪ {(ref, Some oid)} ∪
{(i, n). i = oid ∧ (ref, n) ∈ succ_rel (b # list)}"
proof -
have "∃n. (ref, n) ∈ succ_rel (b # list)"
using assms(1) True by auto
thus ?thesis by simp
qed
moreover have "{(i, n). i = oid ∧ (ref, n) ∈ succ_rel (a # b # list)} =
{(i, n). i = oid ∧ (ref, n) ∈ succ_rel (b # list)}"
using assms(1) by auto
ultimately show ?thesis by simp
next
case False
then show ?thesis by auto
qed
lemma succ_rel_insert_head:
assumes "distinct (ref # list)"
shows "succ_rel (insert_spec (ref # list) (oid, Some ref)) =
insert_alt (succ_rel (ref # list)) (oid, ref)"
proof -
obtain n where ref_in_rel: "(ref, n) ∈ succ_rel (ref # list)"
by (cases list, auto)
moreover from this have "{(p, n) ∈ succ_rel (ref # list). p ≠ ref} = succ_rel list"
using assms succ_rel_rem_head by (metis (mono_tags, lifting))
moreover have "{(i, n). i = oid ∧ (ref, n) ∈ succ_rel (ref # list)} = {(oid, n)}"
proof -
have "⋀nx. (ref, nx) ∈ succ_rel (ref # list) ⟹ nx = n"
using assms by (simp add: succ_rel_functional ref_in_rel)
hence "{(i, n) ∈ succ_rel (ref # list). i = ref} ⊆ {(ref, n)}"
by blast
moreover have "{(ref, n)} ⊆ {(i, n) ∈ succ_rel (ref # list). i = ref}"
by (simp add: ref_in_rel)
ultimately show ?thesis by blast
qed
moreover have "insert_alt (succ_rel (ref # list)) (oid, ref) =
{(p, n) ∈ succ_rel (ref # list). p ≠ ref} ∪ {(ref, Some oid)} ∪
{(i, n). i = oid ∧ (ref, n) ∈ succ_rel (ref # list)}"
proof -
have "∃n. (ref, n) ∈ succ_rel (ref # list)"
using ref_in_rel by blast
thus ?thesis by simp
qed
ultimately have "insert_alt (succ_rel (ref # list)) (oid, ref) =
succ_rel list ∪ {(ref, Some oid)} ∪ {(oid, n)}"
by simp
moreover have "succ_rel (oid # list) = {(oid, n)} ∪ succ_rel list"
using assms ref_in_rel succ_rel_swap_head by metis
hence "succ_rel (ref # oid # list) = {(ref, Some oid), (oid, n)} ∪ succ_rel list"
by auto
ultimately show "succ_rel (insert_spec (ref # list) (oid, Some ref)) =
insert_alt (succ_rel (ref # list)) (oid, ref)"
by auto
qed
lemma succ_rel_insert_later:
assumes "succ_rel (insert_spec (b # list) (oid, Some ref)) =
insert_alt (succ_rel (b # list)) (oid, ref)"
and "a ≠ ref"
and "distinct (a # b # list)"
shows "succ_rel (insert_spec (a # b # list) (oid, Some ref)) =
insert_alt (succ_rel (a # b # list)) (oid, ref)"
proof -
have "succ_rel (a # b # list) = {(a, Some b)} ∪ succ_rel (b # list)"
by simp
moreover have "insert_spec (a # b # list) (oid, Some ref) =
a # (insert_spec (b # list) (oid, Some ref))"
using assms(2) by simp
hence "succ_rel (insert_spec (a # b # list) (oid, Some ref)) =
{(a, Some b)} ∪ succ_rel (insert_spec (b # list) (oid, Some ref))"
by auto
hence "succ_rel (insert_spec (a # b # list) (oid, Some ref)) =
{(a, Some b)} ∪ insert_alt (succ_rel (b # list)) (oid, ref)"
using assms(1) by auto
moreover have "insert_alt (succ_rel (a # b # list)) (oid, ref) =
{(a, Some b)} ∪ insert_alt (succ_rel (b # list)) (oid, ref)"
using succ_rel_insert_alt assms(2) by auto
ultimately show ?thesis by blast
qed
lemma succ_rel_insert_Some:
assumes "distinct list"
shows "succ_rel (insert_spec list (oid, Some ref)) = insert_alt (succ_rel list) (oid, ref)"
using assms proof(induction list)
case Nil
then show "succ_rel (insert_spec [] (oid, Some ref)) = insert_alt (succ_rel []) (oid, ref)"
by simp
next
case (Cons a list)
hence "distinct (a # list)"
by simp
then show "succ_rel (insert_spec (a # list) (oid, Some ref)) =
insert_alt (succ_rel (a # list)) (oid, ref)"
proof(cases "a = ref")
case True
then show ?thesis
using succ_rel_insert_head ‹distinct (a # list)› by metis
next
case False
hence "a ≠ ref" by simp
moreover have "succ_rel (insert_spec list (oid, Some ref)) =
insert_alt (succ_rel list) (oid, ref)"
using Cons.IH Cons.prems by auto
ultimately show "succ_rel (insert_spec (a # list) (oid, Some ref)) =
insert_alt (succ_rel (a # list)) (oid, ref)"
by (cases list, force, metis Cons.prems succ_rel_insert_later)
qed
qed
text‹The main result of this section, that \isa{insert-spec} and \isa{insert-alt}
are equivalent.›
theorem insert_alt_equivalent:
assumes "insert_ops ops"
and "head ∉ fst ` set ops"
and "⋀r. Some r ∈ snd ` set ops ⟹ r ≠ head"
shows "succ_rel (head # interp_ins ops) = interp_alt head ops"
using assms proof(induction ops rule: List.rev_induct)
case Nil
then show "succ_rel (head # interp_ins []) = interp_alt head []"
by (simp add: interp_ins_def interp_alt_def)
next
case (snoc x xs)
have IH: "succ_rel (head # interp_ins xs) = interp_alt head xs"
using snoc by auto
have distinct_list: "distinct (head # interp_ins xs)"
proof -
have "distinct (interp_ins xs)"
using interp_ins_distinct snoc.prems(1) by blast
moreover have "set (interp_ins xs) ⊆ fst ` set xs"
using interp_ins_subset snoc.prems(1) by fastforce
ultimately show "distinct (head # interp_ins xs)"
using snoc.prems(2) by auto
qed
obtain oid r where x_pair: "x = (oid, r)" by force
then show "succ_rel (head # interp_ins (xs @ [x])) = interp_alt head (xs @ [x])"
proof(cases r)
case None
have "interp_alt head (xs @ [x]) = insert_alt (interp_alt head xs) (oid, head)"
by (simp add: interp_alt_def None x_pair)
moreover have "... = insert_alt (succ_rel (head # interp_ins xs)) (oid, head)"
by (simp add: IH)
moreover have "... = succ_rel (insert_spec (head # interp_ins xs) (oid, Some head))"
using distinct_list succ_rel_insert_Some by metis
moreover have "... = succ_rel (head # (insert_spec (interp_ins xs) (oid, None)))"
by auto
moreover have "... = succ_rel (head # (interp_ins (xs @ [x])))"
by (simp add: interp_ins_tail_unfold None x_pair)
ultimately show ?thesis by simp
next
case (Some ref)
have "ref ≠ head"
by (simp add: Some snoc.prems(3) x_pair)
have "interp_alt head (xs @ [x]) = insert_alt (interp_alt head xs) (oid, ref)"
by (simp add: interp_alt_def Some x_pair)
moreover have "... = insert_alt (succ_rel (head # interp_ins xs)) (oid, ref)"
by (simp add: IH)
moreover have "... = succ_rel (insert_spec (head # interp_ins xs) (oid, Some ref))"
using distinct_list succ_rel_insert_Some by metis
moreover have "... = succ_rel (head # (insert_spec (interp_ins xs) (oid, Some ref)))"
using ‹ref ≠ head› by auto
moreover have "... = succ_rel (head # (interp_ins (xs @ [x])))"
by (simp add: interp_ins_tail_unfold Some x_pair)
ultimately show ?thesis by simp
qed
qed
subsection ‹The \isa{list-order} predicate›
text‹\isa{list-order ops x y} holds iff, after interpreting the list of
insertion operations \isa{ops}, the list element with ID \isa{x} appears
before the list element with ID \isa{y} in the resulting list. We prove several
lemmas about this predicate; in particular, that executing additional insertion
operations does not change the relative ordering of existing list elements.›
definition list_order :: "('oid::{linorder} × 'oid option) list ⇒ 'oid ⇒ 'oid ⇒ bool" where
"list_order ops x y ≡ ∃xs ys zs. interp_ins ops = xs @ [x] @ ys @ [y] @ zs"
lemma list_orderI:
assumes "interp_ins ops = xs @ [x] @ ys @ [y] @ zs"
shows "list_order ops x y"
using assms by (auto simp add: list_order_def)
lemma list_orderE:
assumes "list_order ops x y"
shows "∃xs ys zs. interp_ins ops = xs @ [x] @ ys @ [y] @ zs"
using assms by (auto simp add: list_order_def)
lemma list_order_memb1:
assumes "list_order ops x y"
shows "x ∈ set (interp_ins ops)"
using assms by (auto simp add: list_order_def)
lemma list_order_memb2:
assumes "list_order ops x y"
shows "y ∈ set (interp_ins ops)"
using assms by (auto simp add: list_order_def)
lemma list_order_trans:
assumes "insert_ops op_list"
and "list_order op_list x y"
and "list_order op_list y z"
shows "list_order op_list x z"
proof -
obtain xxs xys xzs where 1: "interp_ins op_list = (xxs@[x]@xys)@(y#xzs)"
using assms by (auto simp add: list_order_def interp_ins_def)
obtain yxs yys yzs where 2: "interp_ins op_list = yxs@y#(yys@[z]@yzs)"
using assms by (auto simp add: list_order_def interp_ins_def)
have 3: "distinct (interp_ins op_list)"
using assms interp_ins_distinct by blast
hence "xzs = yys@[z]@yzs"
using distinct_list_split[OF 3, OF 2, OF 1] by auto
hence "interp_ins op_list = xxs@[x]@xys@[y]@yys@[z]@yzs"
using 1 2 3 by clarsimp
thus "list_order op_list x z"
using assms by (metis append.assoc list_orderI)
qed
lemma insert_preserves_order:
assumes "insert_ops ops" and "insert_ops rest"
and "rest = before @ after"
and "ops = before @ (oid, ref) # after"
shows "∃xs ys zs. interp_ins rest = xs @ zs ∧ interp_ins ops = xs @ ys @ zs"
using assms proof(induction after arbitrary: rest ops rule: List.rev_induct)
case Nil
then have 1: "interp_ins ops = insert_spec (interp_ins before) (oid, ref)"
by (simp add: interp_ins_tail_unfold)
then show "∃xs ys zs. interp_ins rest = xs @ zs ∧ interp_ins ops = xs @ ys @ zs"
proof(cases ref)
case None
hence "interp_ins rest = [] @ (interp_ins before) ∧
interp_ins ops = [] @ [oid] @ (interp_ins before)"
using 1 Nil.prems(3) by simp
then show ?thesis by blast
next
case (Some a)
then show ?thesis
proof(cases "a ∈ set (interp_ins before)")
case True
then obtain xs ys where "interp_ins before = xs @ ys ∧
insert_spec (interp_ins before) (oid, ref) = xs @ oid # ys"
using insert_somewhere Some by metis
hence "interp_ins rest = xs @ ys ∧ interp_ins ops = xs @ [oid] @ ys"
using 1 Nil.prems(3) by auto
then show ?thesis by blast
next
case False
hence "interp_ins ops = (interp_ins rest) @ [] @ []"
using insert_spec_nonex 1 Nil.prems(3) Some by simp
then show ?thesis by blast
qed
qed
next
case (snoc oper op_list)
then have "insert_ops ((before @ (oid, ref) # op_list) @ [oper])"
and "insert_ops ((before @ op_list) @ [oper])"
by auto
then have ops1: "insert_ops (before @ op_list)"
and ops2: "insert_ops (before @ (oid, ref) # op_list)"
using insert_ops_appendD by blast+
then obtain xs ys zs where IH1: "interp_ins (before @ op_list) = xs @ zs"
and IH2: "interp_ins (before @ (oid, ref) # op_list) = xs @ ys @ zs"
using snoc.IH by blast
obtain i2 r2 where "oper = (i2, r2)" by force
then show "∃xs ys zs. interp_ins rest = xs @ zs ∧ interp_ins ops = xs @ ys @ zs"
proof(cases r2)
case None
hence "interp_ins (before @ op_list @ [oper]) = (i2 # xs) @ zs"
by (metis IH1 ‹oper = (i2, r2)› append.assoc append_Cons insert_spec.simps(1)
interp_ins_tail_unfold)
moreover have "interp_ins (before @ (oid, ref) # op_list @ [oper]) = (i2 # xs) @ ys @ zs"
by (metis IH2 None ‹oper = (i2, r2)› append.assoc append_Cons insert_spec.simps(1)
interp_ins_tail_unfold)
ultimately show ?thesis
using snoc.prems(3) snoc.prems(4) by blast
next
case (Some r)
then have 1: "interp_ins (before @ (oid, ref) # op_list @ [(i2, r2)]) =
insert_spec (xs @ ys @ zs) (i2, Some r)"
by (metis IH2 append.assoc append_Cons interp_ins_tail_unfold)
have 2: "interp_ins (before @ op_list @ [(i2, r2)]) = insert_spec (xs @ zs) (i2, Some r)"
by (metis IH1 append.assoc interp_ins_tail_unfold Some)
consider (r_xs) "r ∈ set xs" | (r_ys) "r ∈ set ys" | (r_zs) "r ∈ set zs" |
(r_nonex) "r ∉ set (xs @ ys @ zs)"
by auto
then show "∃xs ys zs. interp_ins rest = xs @ zs ∧ interp_ins ops = xs @ ys @ zs"
proof(cases)
case r_xs
from this have "insert_spec (xs @ ys @ zs) (i2, Some r) =
(insert_spec xs (i2, Some r)) @ ys @ zs"
by (meson insert_first_part)
moreover have "insert_spec (xs @ zs) (i2, Some r) = (insert_spec xs (i2, Some r)) @ zs"
by (meson r_xs insert_first_part)
ultimately show ?thesis
using 1 2 ‹oper = (i2, r2)› snoc.prems by auto
next
case r_ys
hence "r ∉ set xs" and "r ∉ set zs"
using IH2 ops2 interp_ins_distinct by force+
moreover from this have "insert_spec (xs @ ys @ zs) (i2, Some r) =
xs @ (insert_spec ys (i2, Some r)) @ zs"
using insert_first_part insert_second_part insert_spec_nonex
by (metis Some UnE r_ys set_append)
moreover have "insert_spec (xs @ zs) (i2, Some r) = xs @ zs"
by (simp add: Some calculation(1) calculation(2))
ultimately show ?thesis
using 1 2 ‹oper = (i2, r2)› snoc.prems by auto
next
case r_zs
hence "r ∉ set xs" and "r ∉ set ys"
using IH2 ops2 interp_ins_distinct by force+
moreover from this have "insert_spec (xs @ ys @ zs) (i2, Some r) =
xs @ ys @ (insert_spec zs (i2, Some r))"
by (metis Some UnE insert_second_part insert_spec_nonex set_append)
moreover have "insert_spec (xs @ zs) (i2, Some r) = xs @ (insert_spec zs (i2, Some r))"
by (simp add: r_zs calculation(1) insert_second_part)
ultimately show ?thesis
using 1 2 ‹oper = (i2, r2)› snoc.prems by auto
next
case r_nonex
then have "insert_spec (xs @ ys @ zs) (i2, Some r) = xs @ ys @ zs"
by simp
moreover have "insert_spec (xs @ zs) (i2, Some r) = xs @ zs"
using r_nonex by simp
ultimately show ?thesis
using 1 2 ‹oper = (i2, r2)› snoc.prems by auto
qed
qed
qed
lemma distinct_fst:
assumes "distinct (map fst A)"
shows "distinct A"
using assms by (induction A) auto
lemma subset_distinct_le:
assumes "set A ⊆ set B" and "distinct A" and "distinct B"
shows "length A ≤ length B"
using assms proof(induction B arbitrary: A)
case Nil
then show "length A ≤ length []" by simp
next
case (Cons a B)
then show "length A ≤ length (a # B)"
proof(cases "a ∈ set A")
case True
have "set (remove1 a A) ⊆ set B"
using Cons.prems by auto
hence "length (remove1 a A) ≤ length B"
using Cons.IH Cons.prems by auto
then show "length A ≤ length (a # B)"
by (simp add: True length_remove1)
next
case False
hence "set A ⊆ set B"
using Cons.prems by auto
hence "length A ≤ length B"
using Cons.IH Cons.prems by auto
then show "length A ≤ length (a # B)"
by simp
qed
qed
lemma set_subset_length_eq:
assumes "set A ⊆ set B" and "length B ≤ length A"
and "distinct A" and "distinct B"
shows "set A = set B"
proof -
have "length A ≤ length B"
using assms by (simp add: subset_distinct_le)
moreover from this have "card (set A) = card (set B)"
using assms by (simp add: distinct_card le_antisym)
ultimately show "set A = set B"
using assms(1) by (simp add: card_subset_eq)
qed
lemma length_diff_Suc_exists:
assumes "length xs - length ys = Suc m"
and "set ys ⊆ set xs"
and "distinct ys" and "distinct xs"
shows "∃e. e ∈ set xs ∧ e ∉ set ys"
using assms proof(induction xs arbitrary: ys)
case Nil
then show "∃e. e ∈ set [] ∧ e ∉ set ys"
by simp
next
case (Cons a xs)
then show "∃e. e ∈ set (a # xs) ∧ e ∉ set ys"
proof(cases "a ∈ set ys")
case True
have IH: "∃e. e ∈ set xs ∧ e ∉ set (remove1 a ys)"
proof -
have "length xs - length (remove1 a ys) = Suc m"
by (metis Cons.prems(1) One_nat_def Suc_pred True diff_Suc_Suc length_Cons
length_pos_if_in_set length_remove1)
moreover have "set (remove1 a ys) ⊆ set xs"
using Cons.prems by auto
ultimately show ?thesis
by (meson Cons.IH Cons.prems distinct.simps(2) distinct_remove1)
qed
moreover have "set ys - {a} ⊆ set xs"
using Cons.prems(2) by auto
ultimately show "∃e. e ∈ set (a # xs) ∧ e ∉ set ys"
by (metis Cons.prems(4) distinct.simps(2) in_set_remove1 set_subset_Cons subsetCE)
next
case False
then show "∃e. e ∈ set (a # xs) ∧ e ∉ set ys"
by auto
qed
qed
lemma app_length_lt_exists:
assumes "xsa @ zsa = xs @ ys"
and "length xsa ≤ length xs"
shows "xsa @ (drop (length xsa) xs) = xs"
using assms by (induction xsa arbitrary: xs zsa ys, simp,
metis append_eq_append_conv_if append_take_drop_id)
lemma list_order_monotonic:
assumes "insert_ops A" and "insert_ops B"
and "set A ⊆ set B"
and "list_order A x y"
shows "list_order B x y"
using assms proof(induction rule: measure_induct_rule[where f="λx. (length x - length A)"])
case (less xa)
have "distinct (map fst A)" and "distinct (map fst xa)" and
"sorted (map fst A)" and "sorted (map fst xa)"
using less.prems by (auto simp add: insert_ops_def spec_ops_def)
hence "distinct A" and "distinct xa"
by (auto simp add: distinct_fst)
then show "list_order xa x y"
proof(cases "length xa - length A")
case 0
hence "set A = set xa"
using set_subset_length_eq less.prems(3) ‹distinct A› ‹distinct xa› diff_is_0_eq by blast
hence "A = xa"
using ‹distinct (map fst A)› ‹distinct (map fst xa)›
‹sorted (map fst A)› ‹sorted (map fst xa)› map_sorted_distinct_set_unique
by (metis distinct_map less.prems(3) subset_Un_eq)
then show "list_order xa x y"
using less.prems(4) by blast
next
case (Suc nat)
then obtain e where "e ∈ set xa" and "e ∉ set A"
using length_diff_Suc_exists ‹distinct A› ‹distinct xa› less.prems(3) by blast
hence IH: "list_order (remove1 e xa) x y"
proof -
have "length (remove1 e xa) - length A < Suc nat"
using diff_Suc_1 diff_commute length_remove1 less_Suc_eq Suc ‹e ∈ set xa› by metis
moreover have "insert_ops (remove1 e xa)"
by (simp add: insert_ops_remove1 less.prems(2))
moreover have "set A ⊆ set (remove1 e xa)"
by (metis (no_types, lifting) ‹e ∉ set A› in_set_remove1 less.prems(3) rev_subsetD subsetI)
ultimately show ?thesis
by (simp add: Suc less.IH less.prems(1) less.prems(4))
qed
then obtain xs ys zs where "interp_ins (remove1 e xa) = xs @ x # ys @ y # zs"
using list_order_def by fastforce
moreover obtain oid ref where e_pair: "e = (oid, ref)"
by fastforce
moreover obtain ps ss where xa_split: "xa = ps @ [e] @ ss" and "e ∉ set ps"
using split_list_first ‹e ∈ set xa› by fastforce
hence "remove1 e (ps @ e # ss) = ps @ ss"
by (simp add: remove1_append)
moreover from this have "insert_ops (ps @ ss)" and "insert_ops (ps @ e # ss)"
using xa_split less.prems(2) by (metis append_Cons append_Nil insert_ops_remove1, auto)
then obtain xsa ysa zsa where "interp_ins (ps @ ss) = xsa @ zsa"
and interp_xa: "interp_ins (ps @ (oid, ref) # ss) = xsa @ ysa @ zsa"
using insert_preserves_order e_pair by metis
moreover have xsa_zsa: "xsa @ zsa = xs @ x # ys @ y # zs"
using interp_ins_def remove1_append calculation xa_split by auto
then show "list_order xa x y"
proof(cases "length xsa ≤ length xs")
case True
then obtain ts where "xsa@ts = xs"
using app_length_lt_exists xsa_zsa by blast
hence "interp_ins xa = (xsa @ ysa @ ts) @ [x] @ ys @ [y] @ zs"
using calculation xa_split by auto
then show "list_order xa x y"
using list_order_def by blast
next
case False
then show "list_order xa x y"
proof(cases "length xsa ≤ length (xs @ x # ys)")
case True
have xsa_zsa1: "xsa @ zsa = (xs @ x # ys) @ (y # zs)"
by (simp add: xsa_zsa)
then obtain us where "xsa @ us = xs @ x # ys"
using app_length_lt_exists True by blast
moreover from this have "xs @ x # (drop (Suc (length xs)) xsa) = xsa"
using append_eq_append_conv_if id_take_nth_drop linorder_not_less
nth_append nth_append_length False by metis
moreover have "us @ y # zs = zsa"
by (metis True xsa_zsa1 append_eq_append_conv_if append_eq_conv_conj calculation(1))
ultimately have "interp_ins xa = xs @ [x] @
((drop (Suc (length xs)) xsa) @ ysa @ us) @ [y] @ zs"
by (simp add: e_pair interp_xa xa_split)
then show "list_order xa x y"
using list_order_def by blast
next
case False
hence "length (xs @ x # ys) < length xsa"
using not_less by blast
hence "length (xs @ x # ys @ [y]) ≤ length xsa"
by simp
moreover have "(xs @ x # ys @ [y]) @ zs = xsa @ zsa"
by (simp add: xsa_zsa)
ultimately obtain vs where "(xs @ x # ys @ [y]) @ vs = xsa"
using app_length_lt_exists by blast
hence "xsa @ ysa @ zsa = xs @ [x] @ ys @ [y] @ vs @ ysa @ zsa"
by simp
hence "interp_ins xa = xs @ [x] @ ys @ [y] @ (vs @ ysa @ zsa)"
using e_pair interp_xa xa_split by auto
then show "list_order xa x y"
using list_order_def by blast
qed
qed
qed
qed
end