Theory Function_Supplementary
section ‹Ticket Lock›
theory Function_Supplementary
imports Main
begin
text ‹This theory contains some function-related definitions and associated
lemmas that are not included in the built-in library. They are grouped into
two sections:
\begin{enumerate}
\item
Predicates that describe functions that are injective or surjective when
restricted to subsets of their domains or images.
\item
A higher-order function that performs a list of updates on a function.
\end{enumerate}
The content of this theory was conceived during a project on formal program
verification of locks (i.e. mutexes). The new definitions and lemmas arose
from the proof of data refinement from an abstract queue-lock to a ticket-lock.
Inspired by the theories \emph{List Index} (Nipkow 2010) and \emph{Fixed-Length
Vectors} (Hupel 2023) on the Archive of Formal Proofs, we hope that these new
definitions and lemmas may also be of help to others.›
subsection ‹Helpers: Inj, Surj and Bij›
text ‹It is sometimes useful to describe a function that is not injective in
itself, but is injective when its image is restricted to a subset.
For example, consider the function $\{ a \mapsto 1, b \mapsto 2, c \mapsto 2 \}$.
This function is not injective, but if its image is restricted to $\{ 1 \}$,
the new function $\{ a \mapsto 1 \}$ becomes injective.
This motivates the following definition.›
definition inj_img :: "('a ⇒ 'b) ⇒ 'b set ⇒ bool" where
"inj_img f B ≡ ∀ x1 x2. f x1 = f x2 ∧ f x1 ∈ B ⟶ x1 = x2"
text ‹Similarly, the next definition describes a function that becomes
surjective when its codomain is restricted to a subset.
In other words, ``surj\_codom $f$ $B$'' means that \emph{every element in $B$
is mapped to by $f$}.
For example, consider the function that maps from the domain $\{ a, b \}$
to the codomain $\{ 1, 2 \}$ with the graph $\{ a \mapsto 1, b \mapsto 1 \}$.
This function is not surjective, but if its codomain is restricted to $\{ 1 \}$,
then the new function becomes surjective.›
definition surj_codom :: "('a ⇒ 'b) ⇒ 'b set ⇒ bool" where
"surj_codom f B ≡ ∀ y ∈ B. (∃ x. f x = y)"
text ‹We can also describe a function that remains surjective on a subset
of its domain.
In other words, ``surj\_on $f$ $A$'' means that \emph{mappings that originate
from $A$ already span the entire codomain}.
Note that this is a notion stronger than plain surjectivity,
which will be shown in the later subsection ``Surj-Related''.›
definition surj_on :: "('a ⇒ 'b) ⇒ 'a set ⇒ bool" where
"surj_on f A ≡ ∀ y. (∃ x ∈ A. f x = y)"
text ‹Note that all three definitions above are most likely not
included in the built-in library, as suggested by the outputs of
the following search-commands.›
find_consts name:"inj"
find_consts name:"surj"
subsubsection ‹Inj-Related›
lemma inj_implies_inj_on: "inj f ⟹ inj_on f A"
using inj_on_subset by blast
lemma inj_implies_inj_img: "inj f ⟹ inj_img f B"
by (simp add: injD inj_img_def)
lemma inj_img_empty: "inj_img f {}"
by (fastforce simp: inj_img_def)
lemma inj_img_singleton: "∀ x. f x ≠ b ⟹ inj_img f {b}"
by (fastforce simp: inj_img_def)
lemma inj_img_subset:
"⟦ inj_img f B ; B' ⊆ B ⟧ ⟹ inj_img f B'"
by (fastforce simp: inj_img_def)
lemma inj_img_superset:
"⟦ inj_img f B ; ∀ x. f x ∉ B' - B ⟧ ⟹ inj_img f B'"
by (fastforce simp: inj_img_def)
lemma inj_img_not_mapped_to: "∀x. f x ∉ B ⟹ inj_img f B"
by (fastforce simp: inj_img_def)
lemma :
"⟦ inj_img f B ; ∀ x. f x ≠ b ⟧ ⟹ inj_img f (B ∪ {b})"
by (fastforce simp: inj_img_def)
lemma inj_img_union_1:
"⟦ inj_img f B1 ; inj_img f B2 ⟧ ⟹ inj_img f (B1 ∪ B2)"
by (fastforce simp: inj_img_def)
lemma inj_img_union_2:
"⟦ inj_img f B1 ; ∀ x. f x ∉ B2 ⟧ ⟹ inj_img f (B1 ∪ B2)"
by (simp add: inj_img_not_mapped_to inj_img_union_1)
lemma inj_img_fun_upd_notin:
"⟦ inj_img f B ; ∀ x. f x ≠ b ⟧ ⟹ inj_img (fun_upd f a b) B"
by (fastforce simp: inj_img_def)
lemma inj_img_fun_upd_singleton:
"∀ x. f x ≠ b ⟹ inj_img (fun_upd f a b) {b}"
by (simp add: inj_img_fun_upd_notin inj_img_singleton)
subsubsection ‹Surj-Related›
text ‹Lemmas related to ``surj codom''.›
lemma surj_implies_surj_codom: "surj f ⟹ surj_codom f B"
by (metis surjD surj_codom_def)
lemma surj_codom_triv: "surj_codom f (f ` A)"
by (fastforce simp: surj_codom_def)
lemma surj_codom_univ: "surj_codom f UNIV = surj f"
by (metis surj_codom_def surj_def UNIV_I)
lemma surj_codom_empty: "surj_codom f {}"
by (fastforce simp: surj_codom_def)
lemma surj_codom_singleton: "b ∈ range f ⟹ surj_codom f {b}"
by (fastforce simp: surj_codom_def)
lemma surj_codom_subset:
"⟦ surj_codom f B ; B' ⊆ B ⟧ ⟹ surj_codom f B'"
by (fastforce simp: surj_codom_def)
lemma surj_codom_union:
"⟦ surj_codom f B1 ; surj_codom f B2 ⟧ ⟹ surj_codom f (B1 ∪ B2)"
by (fastforce simp: surj_codom_def)
text ‹Lemmas related to ``surj on''.›
lemma surj_on_implies_surj: "surj_on f A ⟹ surj f"
by (metis surj_def surj_on_def)
lemma surj_on_univ: "surj_on f UNIV = surj f"
by (metis UNIV_I surjD surj_on_def surj_on_implies_surj)
lemma surj_on_never_emptyset: "¬ surj_on f {}"
by (fastforce simp: surj_on_def)
lemma surj_on_superset:
"⟦ surj_on f A ; A ⊆ A' ⟧ ⟹ surj_on f A'"
by (fastforce simp: surj_on_def)
lemma surj_on_union:
"⟦ surj_on f A1 ; surj_on f A2 ⟧ ⟹ surj_on f (A1 ∪ A2)"
by (fastforce simp: surj_on_superset)
subsubsection ‹Bij and Inv›
text ‹This section relates the new definitions to the existing
``bijective between'' and ``inverse'' definitions.›
lemma bij_betw_implies_inj_img: "bij_betw f UNIV B ⟹ inj_img f B"
by (fastforce simp: bij_betw_def inj_implies_inj_img)
lemma bij_betw_implies_surj_codom: "bij_betw f A B ⟹ surj_codom f B"
by (fastforce intro: f_the_inv_into_f_bij_betw simp: surj_codom_def)
lemma bij_betw_implies_surj_on: "bij_betw f A UNIV ⟹ surj_on f A"
by (meson UNIV_I bij_betw_iff_bijections surj_on_def)
text ‹Other lemmas›
lemma bij_extension:
assumes "a ∉ A"
and "b ∉ B"
and "bij_betw f A B"
shows "bij_betw (fun_upd f a b) (A ∪ {a}) (B ∪ {b})"
by (metis assms bij_betw_combine bij_betw_cong bij_betw_singleton_iff disjoint_insert(1)
fun_upd_other fun_upd_same inf_bot_right)
lemma bij_remove_one:
assumes "a ∈ A"
and "bij_betw f A B"
shows "bij_betw f (A - {a}) (B - {f a})"
using assms by (fastforce simp: bij_betwE bij_betw_DiffI)
lemma set_remove_one_element:
assumes "x ∉ B"
and "B ⊆ A"
and "A - {x} ⊆ B"
shows "A - {x} = B"
using assms by blast
lemma inv_image_restrict_inj:
assumes "bij_betw f A B"
and "inj_img f B"
and "f a ∈ B"
shows "a ∈ inv f ` B"
using assms by (fastforce simp: f_inv_into_f inj_img_def rev_image_eqI)
lemma inv_image_restrict:
assumes "inj_on f A"
and "f a ∈ B"
and "∀x. (f x ∈ B ⟶ x ∈ A)"
shows "a ∈ inv f ` B"
using assms by (fastforce simp: f_inv_into_f inj_onD rev_image_eqI)
lemma inv_image_restrict_neg:
assumes "bij_betw f A B"
and "f a ∉ B"
and "∀x. (f x ∈ B ⟶ x ∈ A)"
shows "a ∉ inv f ` B"
using assms apply clarsimp
by (metis (mono_tags, lifting) f_inv_into_f f_the_inv_into_f_bij_betw range_eqI)
lemma inv_image_restrict_neg':
assumes "surj_codom f B"
and "f a ∉ B"
and "∀x. (f x ∈ B ⟶ x ∈ A)"
shows "a ∉ inv f ` B"
using assms
by (fastforce simp: surj_codom_def f_inv_into_f rangeI)
lemma bij_betw_inv1:
assumes "bij_betw f A B"
and "inj_img f B"
and "f a ∈ B"
shows "inv f (f a) = a"
using assms by (fastforce simp: f_inv_into_f inj_img_def)
lemma bij_betw_inv2:
assumes "bij_betw f A B"
and "b ∈ B"
shows "f (inv f b) = b"
by (metis assms bij_betw_imp_surj_on f_inv_into_f rangeI)
lemma surj_codom_inj_on_vimage_bij_betw:
"⟦ surj_codom f B ; inj_on f (vimage f B) ⟧ ⟹ bij_betw f (vimage f B) B"
apply (rule bij_betwI')
by (fastforce simp: inj_onD surj_codom_def)+
subsection ‹Helpers: Multi-Updates on Functions›
fun fun_upd_list :: "('a ⇒ 'b) ⇒ ('a × 'b) list ⇒ ('a ⇒ 'b)" where
"fun_upd_list f [] = f"
| "fun_upd_list f (xy # xys) = fun_upd (fun_upd_list f xys) (fst xy) (snd xy)"
text ‹This notion can also be defined following the @{text foldl} pattern,
although this alternative form is not used.›
fun fun_upd_list_l :: "('a ⇒ 'b) ⇒ ('a × 'b) list ⇒ ('a ⇒ 'b)" where
"fun_upd_list_l f [] = f"
| "fun_upd_list_l f (xy # xys) = fun_upd_list_l (fun_upd f (fst xy) (snd xy)) xys"
text ‹Examples of the two definitions above.›
value "fun_upd_list (λx.0::nat) [(1::nat,1),(4,3),(6,6),(4,4)] 4"
value "fun_upd_list_l (λx.0::nat) [(1::nat,1),(4,3),(6,6),(4,4)] 4"
text ‹Both definitions above resemble "folds" with some un-currying,
as shown by the following two lemmas.›
lemma fun_upd_list_is_foldr:
"fun_upd_list f0 pairs = foldr (λ pair f. fun_upd f (fst pair) (snd pair)) pairs f0"
by (induct pairs; fastforce)
lemma fun_upd_list_l_is_foldl:
"fun_upd_list_l f0 pairs = foldl (λ f pair. f(fst pair := snd pair)) f0 pairs"
by (induct pairs arbitrary: f0; force)
text ‹These two definitions are equivalent when every domain-value is
updated at most once.›
lemma fun_upd_list_l_distinct_rewrite:
"distinct (map fst (xy # xys))
⟹ fun_upd_list_l (fun_upd f (fst xy) (snd xy)) xys
= fun_upd (fun_upd_list_l f xys) (fst xy) (snd xy)"
proof (induct xys arbitrary: xy f)
case (Cons xy2 xys2)
thus ?case
by (metis (no_types, lifting) distinct_length_2_or_more fun_upd_list_l.simps(2) fun_upd_twist list.simps(9))
qed (fastforce)
lemma fun_upd_list_defs_distinct_equiv:
"distinct (map fst pairs) ⟹ fun_upd_list f pairs = fun_upd_list_l f pairs"
proof (induct pairs)
case (Cons xy xys)
thus ?case
by (fastforce simp: fun_upd_list_l_distinct_rewrite)
qed (fastforce)
text ‹Smaller propositions›
lemma fun_upd_list_distinct_rewrite:
"distinct (map fst (xy # xys))
⟹ fun_upd_list (fun_upd f (fst xy) (snd xy)) xys
= fun_upd (fun_upd_list f xys) (fst xy) (snd xy)"
by (simp add: fun_upd_list_defs_distinct_equiv fun_upd_list_l_distinct_rewrite)
lemma fun_upd_list_hd_1:
"fun_upd_list f (zip (x # xs) (y # ys)) x = y"
by simp
lemma fun_upd_list_hd_2:
"⟦ xs ≠ [] ; ys ≠ [] ⟧ ⟹ fun_upd_list f (zip xs ys) (hd xs) = hd ys"
by (metis fun_upd_list_hd_1 list.collapse)
lemma fun_upd_list_not_hd:
assumes "a ≠ x"
shows "fun_upd_list f (zip (x # xs) (y # ys)) a = fun_upd_list f (zip xs ys) a"
using assms by simp
lemma fun_upd_list_not_updated_map:
assumes "a ∉ set (map fst xys)"
shows "fun_upd_list f xys a = f a"
using assms by (induction xys, simp_all)
lemma fun_upd_list_not_updated_zip:
assumes "a ∉ set xs"
shows "fun_upd_list f (zip xs ys) a = f a"
by (metis assms fun_upd_list_not_updated_map in_set_takeD map_fst_zip_take)
subsubsection ‹Ordering of Updates›
text ‹The next two lemmas shows that the ordering of the updates does not matter,
as long as the updates are distinct.›
lemma fun_upd_list_distinct_reorder:
assumes "distinct (map fst pairs)"
and "ab ∈ set pairs"
shows "fun_upd_list f pairs
= (fun_upd_list f (remove1 ab pairs)) (fst ab := snd ab)"
using assms
proof (induct pairs)
case (Cons xy xys)
thus ?case
proof (cases "ab = xy")
case True
thus ?thesis by simp
next
case False
hence "(fun_upd_list f (remove1 ab (xy # xys))) (fst ab := snd ab)
= fun_upd_list f (xy # xys)"
using Cons.hyps Cons.prems by fastforce
thus ?thesis by fastforce
qed
qed (fastforce)
lemma fun_upd_list_distinct_reorder_general:
assumes "distinct (map fst pairs1)"
and "distinct (map fst pairs2)"
and "set pairs1 = set pairs2"
shows "fun_upd_list f pairs1 = fun_upd_list f pairs2"
using assms
proof (induct pairs1 arbitrary: pairs2)
case (Cons xy xys)
hence "fun_upd_list f pairs2
= (fun_upd_list f (remove1 xy pairs2))(fst xy := snd xy)"
by (metis list.set_intros(1) fun_upd_list_distinct_reorder Cons.prems(2,3))
also have "... = (fun_upd_list f xys)(fst xy := snd xy)"
by (metis (mono_tags, lifting) Cons.hyps Cons.prems distinct_map distinct_remove1 list.simps(9)
remove1.simps(2) set_remove1_eq)
also have "... = fun_upd_list f (xy # xys)"
by simp
ultimately show ?case
by presburger
qed (fastforce)
subsubsection ‹Surjective›
lemma helper_surj_zip_1:
assumes "a ∈ set xs"
and "length xs = length ys"
shows "fun_upd_list f (zip xs ys) a ∈ set ys"
using assms
proof (induction xs arbitrary: ys)
case (Cons x xs)
thus ?case
apply (cases "x = a")
apply (metis Cons.prems(2) fun_upd_list_hd_1 length_0_conv list.distinct(1) list.exhaust_sel
list.set_intros(1))
by (metis Cons.IH Cons.prems(1,2) fun_upd_list_not_hd length_Suc_conv list.set_intros(2) set_ConsD)
qed (fastforce)
lemma fun_upd_list_surj_zip_1:
assumes "length xs = length ys"
shows "fun_upd_list f (zip xs ys) ` set xs ⊆ set ys"
using assms helper_surj_zip_1 by force
lemma fun_upd_list_surj_map_1:
"(fun_upd_list f xys) ` set (map fst xys) ⊆ set (map snd xys)"
by (metis fun_upd_list_surj_zip_1 length_map zip_map_fst_snd)
lemma fun_upd_list_surj_map_2:
assumes "distinct (map fst xys)"
shows "set (map snd xys) ⊆ (fun_upd_list f xys) ` set (map fst xys)"
using assms proof (induct xys)
case (Cons xy tail)
{ fix b assume assms_b: "b ∈ set (map snd (xy # tail))"
hence "∃ a ∈ set (map fst (xy # tail)). (fun_upd_list f (xy # tail)) a = b"
proof (cases "b = snd xy")
case True
show ?thesis using True by simp
next
case False
hence 2: "∃ a ∈ set (map fst tail). (fun_upd_list f tail) a = b"
using Cons assms_b by fastforce
{ fix aa assume 3: "aa ∈ set (map fst tail) ∧ (fun_upd_list f tail) aa = b"
from 3 have 5: "(fun_upd_list f (xy # tail)) aa = b"
using fun_upd_list_not_hd Cons.prems
by (metis (no_types, lifting) distinct.simps(2) list.simps(9) zip_map_fst_snd)
from 5 have ?thesis
by (metis 3 list.set_intros(2) list.simps(9))
}
thus ?thesis using 2 by blast
qed }
thus ?case by blast
qed (fastforce)
subsubsection ‹Injective›
lemma helper_inj_head:
assumes f_def: "f = fun_upd_list f0 (zip xs ys)"
and distinct_ys: "distinct ys"
and length_equal: "length xs = length ys"
and non_empty: "xs ≠ []"
and 0: "a ∈ set xs ∧ b ∈ set xs ∧ a ≠ b"
and 1: "a = hd xs ∧ b ∈ set (tl xs)"
shows "f a ≠ f b"
using assms
by (metis distinct.simps(2) fun_upd_list_hd_1 fun_upd_list_not_hd helper_surj_zip_1 length_0_conv
length_tl list.collapse)
lemma helper_inj_tail:
assumes "distinct xs"
and "distinct ys"
and "length xs = length ys"
and "a ∈ set (tl xs)"
and "b ∈ set (tl xs)"
and "a ≠ b"
shows "fun_upd_list f (zip xs ys) a ≠ fun_upd_list f (zip xs ys) b"
using assms proof (induct xs arbitrary: ys)
case (Cons x xs)
have a_elem: "a ∈ set (tl (x # xs))" using Cons.prems(4) by simp
have b_elem: "b ∈ set (tl (x # xs))" using Cons.prems(5) by simp
have a_not_hd: "a ≠ x" using Cons.prems(1) Cons.prems(4) by force
have b_not_hd: "b ≠ x" using Cons.prems(1) Cons.prems(5) by force
have a: "fun_upd_list f (zip (x # xs) ys) a = fun_upd_list f (zip xs (tl ys)) a"
using a_not_hd fun_upd_list_not_hd
by (metis Cons.prems(3) length_0_conv list.collapse list.distinct(1))
have uneq: "fun_upd_list f (zip xs (tl ys)) a ≠ fun_upd_list f (zip xs (tl ys)) b"
by (metis (no_types, lifting) a_elem b_elem helper_inj_head
Cons.hyps Cons.prems(1) Cons.prems(2) Cons.prems(3) Cons.prems(6)
distinct_tl length_tl list.collapse list.sel(2) list.sel(3) set_ConsD)
have b: "fun_upd_list f (zip xs (tl ys)) b = fun_upd_list f (zip (x # xs) ys) b"
using b_not_hd fun_upd_list_not_hd
by (metis Cons.prems(3) length_0_conv list.collapse list.distinct(1))
from a uneq b show ?case by simp
qed (simp)
theorem fun_upd_list_inj_zip:
assumes "distinct xs"
and "distinct ys"
and "length xs = length ys"
and "xs ≠ []"
shows "inj_on (fun_upd_list f (zip xs ys)) (set xs)"
proof-
{ fix a b assume 0: "a ∈ set xs ∧ b ∈ set xs ∧ a ≠ b"
hence "(a = hd xs ∧ b ∈ set (tl xs)) ∨
(b = hd xs ∧ a ∈ set (tl xs)) ∨
(a ∈ set (tl xs) ∧ b ∈ set (tl xs))"
using assms(4) by (metis list.collapse set_ConsD)
moreover
{ assume "a = hd xs ∧ b ∈ set (tl xs)"
hence "(fun_upd_list f (zip xs ys)) a ≠ (fun_upd_list f (zip xs ys)) b"
using 0 assms helper_inj_head by metis }
moreover
{ assume "b = hd xs ∧ a ∈ set (tl xs)"
hence "(fun_upd_list f (zip xs ys)) a ≠ (fun_upd_list f (zip xs ys)) b"
using 0 assms helper_inj_head by metis }
moreover
{ assume "a ∈ set (tl xs) ∧ b ∈ set (tl xs)"
hence "fun_upd_list f (zip xs ys) a ≠ fun_upd_list f (zip xs ys) b"
using helper_inj_tail by (metis 0 assms(1) assms(2) assms(3)) }
ultimately have "(fun_upd_list f (zip xs ys)) a ≠ (fun_upd_list f (zip xs ys)) b"
by force }
thus ?thesis by (meson inj_onI)
qed
theorem fun_upd_list_surj_zip:
assumes "f = fun_upd_list f0 (zip xs ys)"
and "distinct xs"
and "length xs = length ys"
shows "f ` set xs = set ys"
by (metis assms fun_upd_list_surj_map_2 fun_upd_list_surj_zip_1
inf.absorb_iff2 inf.order_iff zip_eq_conv)
theorem fun_upd_list_bij_betw_zip:
assumes "distinct xs"
and "distinct ys"
and "length xs = length ys"
and "xs ≠ []"
shows "bij_betw (fun_upd_list f (zip xs ys)) (set xs) (set ys)"
using assms
by (fastforce simp add: bij_betw_def fun_upd_list_inj_zip fun_upd_list_surj_zip)
lemma fun_upd_list_distinct:
assumes "distinct (map snd (xy # xys))"
and "f x ∉ set (map snd (xy # xys))"
shows "fun_upd_list f xys x ≠ snd xy"
by (metis assms fun_upd_list_not_updated_map fun_upd_list_surj_map_1
distinct.simps(2) image_eqI list.set_intros(1) list.simps(9) subsetD)
theorem inj_img_fun_upd_list_map:
assumes "distinct (map snd xys)"
and "∀ x. f x ∉ set (map snd xys)"
shows "inj_img (fun_upd_list f xys) (set (map snd xys))"
using assms proof (induct xys)
case (Cons xy xys)
hence"inj_img (fun_upd_list f xys) ({snd xy} ∪ set (map snd xys))"
by (fastforce simp: fun_upd_list_distinct inj_img_def)
thus ?case
unfolding inj_img_def apply clarsimp
by (metis Cons.prems(1,2) fun_upd_list_distinct)
qed (fastforce simp: inj_img_not_mapped_to)
theorem inj_img_fun_upd_list_zip:
assumes "distinct ys"
and "length xs = length ys"
and "∀ x. f x ∉ set ys"
shows "inj_img (fun_upd_list f (zip xs ys)) (set ys)"
by (metis assms inj_img_fun_upd_list_map map_snd_zip)
subsubsection ‹Set- and List-Intervals›
lemma fun_upd_list_new_interval:
assumes "length xs = length ys"
shows "fun_upd_list f (zip xs ys) i ∈ {f i} ∪ set ys"
apply (cases "i ∈ set xs")
apply (fastforce simp: assms intro: helper_surj_zip_1)
by (fastforce intro: fun_upd_list_not_updated_zip)
lemma helper_interval_length:
"length [1 ..< length xs + 1] = length xs"
apply (subst length_upt)
by fastforce
lemma helper_interval_union:
"{0::nat} ∪ {1 ..< n + 1} = {0 ..< n + 1}"
by force
lemma fun_upd_list_interval:
"fun_upd_list (λx.0) (zip xs [1 ..< length xs + 1]) z ∈ {0 ..< length xs + 1}"
apply (cases "z ∈ set xs")
apply (metis Un_iff set_upt helper_interval_union helper_interval_length helper_surj_zip_1)
by (metis fun_upd_list_not_updated_zip add.commute atLeastLessThan_iff less_numeral_extra(1)
trans_less_add1 zero_le)
theorem fun_upd_list_interval_bij:
assumes "f = fun_upd_list (λx.0) (zip xs [1 ..< length xs + 1])"
and "distinct xs"
shows "bij_betw f {i. 1 ≤ f i} {1 ..< length xs + 1}"
proof-
have set_xs : "set [1 ..< length xs + 1] = {1 ..< length xs + 1}"
by force
have "set xs = {i. 1 ≤ f i}"
proof (rule antisym)
show "set xs ⊆ {i. 1 ≤ f i}"
by (metis One_nat_def assms(1) atLeastLessThan_iff helper_interval_length helper_surj_zip_1
set_xs mem_Collect_eq subset_code(1))
show "{i. 1 ≤ f i} ⊆ set xs"
by (metis assms(1) fun_upd_list_not_updated_zip CollectD not_one_le_zero subsetI)
qed
thus ?thesis
by (metis (mono_tags, lifting) assms(1,2) fun_upd_list_bij_betw_zip helper_interval_length
One_nat_def add.right_neutral add_Suc_right bij_betwI' distinct_upt
empty_iff empty_set le_numeral_extra(4) list.size(3)
set_xs upt_eq_Nil_conv)
qed
end