# Theory List

theory List
imports Sledgehammer Code_Numeral Lifting_Set
(*  Title:      HOL/List.thy
Author:     Tobias Nipkow
*)

section ‹The datatype of finite lists›

theory List
imports Sledgehammer Code_Numeral Lifting_Set
begin

datatype (set: 'a) list =
Nil  ("[]")
| Cons (hd: 'a) (tl: "'a list")  (infixr "#" 65)
for
map: map
rel: list_all2
pred: list_all
where
"tl [] = []"

datatype_compat list

lemma [case_names Nil Cons, cases type: list]:
― ‹for backward compatibility -- names of variables differ›
"(y = [] ⟹ P) ⟹ (⋀a list. y = a # list ⟹ P) ⟹ P"
by (rule list.exhaust)

lemma [case_names Nil Cons, induct type: list]:
― ‹for backward compatibility -- names of variables differ›
"P [] ⟹ (⋀a list. P list ⟹ P (a # list)) ⟹ P list"
by (rule list.induct)

text ‹Compatibility:›

setup ‹Sign.mandatory_path "list"›

lemmas inducts = list.induct
lemmas recs = list.rec
lemmas cases = list.case

setup ‹Sign.parent_path›

lemmas set_simps = list.set (* legacy *)

syntax
― ‹list Enumeration›
"_list" :: "args => 'a list"    ("[(_)]")

translations
"[x, xs]" == "x#[xs]"
"[x]" == "x#[]"

subsection ‹Basic list processing functions›

primrec (nonexhaustive) last :: "'a list ⇒ 'a" where
"last (x # xs) = (if xs = [] then x else last xs)"

primrec butlast :: "'a list ⇒ 'a list" where
"butlast [] = []" |
"butlast (x # xs) = (if xs = [] then [] else x # butlast xs)"

lemma set_rec: "set xs = rec_list {} (λx _. insert x) xs"
by (induct xs) auto

definition coset :: "'a list ⇒ 'a set" where
[simp]: "coset xs = - set xs"

primrec append :: "'a list ⇒ 'a list ⇒ 'a list" (infixr "@" 65) where
append_Nil: "[] @ ys = ys" |
append_Cons: "(x#xs) @ ys = x # xs @ ys"

primrec rev :: "'a list ⇒ 'a list" where
"rev [] = []" |
"rev (x # xs) = rev xs @ [x]"

primrec filter:: "('a ⇒ bool) ⇒ 'a list ⇒ 'a list" where
"filter P [] = []" |
"filter P (x # xs) = (if P x then x # filter P xs else filter P xs)"

text ‹Special input syntax for filter:›
syntax (ASCII)
"_filter" :: "[pttrn, 'a list, bool] => 'a list"  ("(1[_<-_./ _])")
syntax
"_filter" :: "[pttrn, 'a list, bool] => 'a list"  ("(1[_←_ ./ _])")
translations
"[x<-xs . P]" ⇀ "CONST filter (λx. P) xs"

primrec fold :: "('a ⇒ 'b ⇒ 'b) ⇒ 'a list ⇒ 'b ⇒ 'b" where
fold_Nil:  "fold f [] = id" |
fold_Cons: "fold f (x # xs) = fold f xs ∘ f x"

primrec foldr :: "('a ⇒ 'b ⇒ 'b) ⇒ 'a list ⇒ 'b ⇒ 'b" where
foldr_Nil:  "foldr f [] = id" |
foldr_Cons: "foldr f (x # xs) = f x ∘ foldr f xs"

primrec foldl :: "('b ⇒ 'a ⇒ 'b) ⇒ 'b ⇒ 'a list ⇒ 'b" where
foldl_Nil:  "foldl f a [] = a" |
foldl_Cons: "foldl f a (x # xs) = foldl f (f a x) xs"

primrec concat:: "'a list list ⇒ 'a list" where
"concat [] = []" |
"concat (x # xs) = x @ concat xs"

primrec drop:: "nat ⇒ 'a list ⇒ 'a list" where
drop_Nil: "drop n [] = []" |
drop_Cons: "drop n (x # xs) = (case n of 0 ⇒ x # xs | Suc m ⇒ drop m xs)"
― ‹Warning: simpset does not contain this definition, but separate
theorems for ‹n = 0› and ‹n = Suc k››

primrec take:: "nat ⇒ 'a list ⇒ 'a list" where
take_Nil:"take n [] = []" |
take_Cons: "take n (x # xs) = (case n of 0 ⇒ [] | Suc m ⇒ x # take m xs)"
― ‹Warning: simpset does not contain this definition, but separate
theorems for ‹n = 0› and ‹n = Suc k››

primrec (nonexhaustive) nth :: "'a list => nat => 'a" (infixl "!" 100) where
nth_Cons: "(x # xs) ! n = (case n of 0 ⇒ x | Suc k ⇒ xs ! k)"
― ‹Warning: simpset does not contain this definition, but separate
theorems for ‹n = 0› and ‹n = Suc k››

primrec list_update :: "'a list ⇒ nat ⇒ 'a ⇒ 'a list" where
"list_update [] i v = []" |
"list_update (x # xs) i v =
(case i of 0 ⇒ v # xs | Suc j ⇒ x # list_update xs j v)"

nonterminal lupdbinds and lupdbind

syntax
"_lupdbind":: "['a, 'a] => lupdbind"    ("(2_ :=/ _)")
"" :: "lupdbind => lupdbinds"    ("_")
"_lupdbinds" :: "[lupdbind, lupdbinds] => lupdbinds"    ("_,/ _")
"_LUpdate" :: "['a, lupdbinds] => 'a"    ("_/[(_)]" [900,0] 900)

translations
"_LUpdate xs (_lupdbinds b bs)" == "_LUpdate (_LUpdate xs b) bs"
"xs[i:=x]" == "CONST list_update xs i x"

primrec takeWhile :: "('a ⇒ bool) ⇒ 'a list ⇒ 'a list" where
"takeWhile P [] = []" |
"takeWhile P (x # xs) = (if P x then x # takeWhile P xs else [])"

primrec dropWhile :: "('a ⇒ bool) ⇒ 'a list ⇒ 'a list" where
"dropWhile P [] = []" |
"dropWhile P (x # xs) = (if P x then dropWhile P xs else x # xs)"

primrec zip :: "'a list ⇒ 'b list ⇒ ('a × 'b) list" where
"zip xs [] = []" |
zip_Cons: "zip xs (y # ys) =
(case xs of [] ⇒ [] | z # zs ⇒ (z, y) # zip zs ys)"
― ‹Warning: simpset does not contain this definition, but separate
theorems for ‹xs = []› and ‹xs = z # zs››

abbreviation map2 :: "('a ⇒ 'b ⇒ 'c) ⇒ 'a list ⇒ 'b list ⇒ 'c list" where
"map2 f xs ys ≡ map (λ(x,y). f x y) (zip xs ys)"

primrec product :: "'a list ⇒ 'b list ⇒ ('a × 'b) list" where
"product [] _ = []" |
"product (x#xs) ys = map (Pair x) ys @ product xs ys"

hide_const (open) product

primrec product_lists :: "'a list list ⇒ 'a list list" where
"product_lists [] = [[]]" |
"product_lists (xs # xss) = concat (map (λx. map (Cons x) (product_lists xss)) xs)"

primrec upt :: "nat ⇒ nat ⇒ nat list" ("(1[_..</_'])") where
upt_0: "[i..<0] = []" |
upt_Suc: "[i..<(Suc j)] = (if i <= j then [i..<j] @ [j] else [])"

definition insert :: "'a ⇒ 'a list ⇒ 'a list" where
"insert x xs = (if x ∈ set xs then xs else x # xs)"

definition union :: "'a list ⇒ 'a list ⇒ 'a list" where
"union = fold insert"

hide_const (open) insert union
hide_fact (open) insert_def union_def

primrec find :: "('a ⇒ bool) ⇒ 'a list ⇒ 'a option" where
"find _ [] = None" |
"find P (x#xs) = (if P x then Some x else find P xs)"

text ‹In the context of multisets, ‹count_list› is equivalent to
@{term "count ∘ mset"} and it it advisable to use the latter.›
primrec count_list :: "'a list ⇒ 'a ⇒ nat" where
"count_list [] y = 0" |
"count_list (x#xs) y = (if x=y then count_list xs y + 1 else count_list xs y)"

definition
"extract" :: "('a ⇒ bool) ⇒ 'a list ⇒ ('a list * 'a * 'a list) option"
where "extract P xs =
(case dropWhile (Not ∘ P) xs of
[] ⇒ None |
y#ys ⇒ Some(takeWhile (Not ∘ P) xs, y, ys))"

hide_const (open) "extract"

primrec those :: "'a option list ⇒ 'a list option"
where
"those [] = Some []" |
"those (x # xs) = (case x of
None ⇒ None
| Some y ⇒ map_option (Cons y) (those xs))"

primrec remove1 :: "'a ⇒ 'a list ⇒ 'a list" where
"remove1 x [] = []" |
"remove1 x (y # xs) = (if x = y then xs else y # remove1 x xs)"

primrec removeAll :: "'a ⇒ 'a list ⇒ 'a list" where
"removeAll x [] = []" |
"removeAll x (y # xs) = (if x = y then removeAll x xs else y # removeAll x xs)"

primrec distinct :: "'a list ⇒ bool" where
"distinct [] ⟷ True" |
"distinct (x # xs) ⟷ x ∉ set xs ∧ distinct xs"

primrec remdups :: "'a list ⇒ 'a list" where
"remdups [] = []" |
"remdups (x # xs) = (if x ∈ set xs then remdups xs else x # remdups xs)"

fun remdups_adj :: "'a list ⇒ 'a list" where
"remdups_adj (x # y # xs) = (if x = y then remdups_adj (x # xs) else x # remdups_adj (y # xs))"

primrec replicate :: "nat ⇒ 'a ⇒ 'a list" where
replicate_0: "replicate 0 x = []" |
replicate_Suc: "replicate (Suc n) x = x # replicate n x"

text ‹
Function ‹size› is overloaded for all datatypes. Users may
refer to the list version as ‹length›.›

abbreviation length :: "'a list ⇒ nat" where
"length ≡ size"

definition enumerate :: "nat ⇒ 'a list ⇒ (nat × 'a) list" where
enumerate_eq_zip: "enumerate n xs = zip [n..<n + length xs] xs"

primrec rotate1 :: "'a list ⇒ 'a list" where
"rotate1 [] = []" |
"rotate1 (x # xs) = xs @ [x]"

definition rotate :: "nat ⇒ 'a list ⇒ 'a list" where
"rotate n = rotate1 ^^ n"

definition nths :: "'a list => nat set => 'a list" where
"nths xs A = map fst (filter (λp. snd p ∈ A) (zip xs [0..<size xs]))"

primrec subseqs :: "'a list ⇒ 'a list list" where
"subseqs [] = [[]]" |
"subseqs (x#xs) = (let xss = subseqs xs in map (Cons x) xss @ xss)"

primrec n_lists :: "nat ⇒ 'a list ⇒ 'a list list" where
"n_lists 0 xs = [[]]" |
"n_lists (Suc n) xs = concat (map (λys. map (λy. y # ys) xs) (n_lists n xs))"

hide_const (open) n_lists

fun splice :: "'a list ⇒ 'a list ⇒ 'a list" where
"splice [] ys = ys" |
"splice xs [] = xs" |
"splice (x#xs) (y#ys) = x # y # splice xs ys"

function shuffle where
"shuffle [] ys = {ys}"
| "shuffle xs [] = {xs}"
| "shuffle (x # xs) (y # ys) = (#) x  shuffle xs (y # ys) ∪ (#) y  shuffle (x # xs) ys"
by pat_completeness simp_all
termination by lexicographic_order

text‹Use only if you cannot use @{const Min} instead:›
fun min_list :: "'a::ord list ⇒ 'a" where
"min_list (x # xs) = (case xs of [] ⇒ x | _ ⇒ min x (min_list xs))"

text‹Returns first minimum:›
fun arg_min_list :: "('a ⇒ ('b::linorder)) ⇒ 'a list ⇒ 'a" where
"arg_min_list f [x] = x" |
"arg_min_list f (x#y#zs) = (let m = arg_min_list f (y#zs) in if f x ≤ f m then x else m)"

text‹
\begin{figure}[htbp]
\fbox{
\begin{tabular}{l}
@{lemma "[a,b]@[c,d] = [a,b,c,d]" by simp}\\
@{lemma "length [a,b,c] = 3" by simp}\\
@{lemma "set [a,b,c] = {a,b,c}" by simp}\\
@{lemma "map f [a,b,c] = [f a, f b, f c]" by simp}\\
@{lemma "rev [a,b,c] = [c,b,a]" by simp}\\
@{lemma "hd [a,b,c,d] = a" by simp}\\
@{lemma "tl [a,b,c,d] = [b,c,d]" by simp}\\
@{lemma "last [a,b,c,d] = d" by simp}\\
@{lemma "butlast [a,b,c,d] = [a,b,c]" by simp}\\
@{lemma[source] "filter (λn::nat. n<2) [0,2,1] = [0,1]" by simp}\\
@{lemma "concat [[a,b],[c,d,e],[],[f]] = [a,b,c,d,e,f]" by simp}\\
@{lemma "fold f [a,b,c] x = f c (f b (f a x))" by simp}\\
@{lemma "foldr f [a,b,c] x = f a (f b (f c x))" by simp}\\
@{lemma "foldl f x [a,b,c] = f (f (f x a) b) c" by simp}\\
@{lemma "zip [a,b,c] [x,y,z] = [(a,x),(b,y),(c,z)]" by simp}\\
@{lemma "zip [a,b] [x,y,z] = [(a,x),(b,y)]" by simp}\\
@{lemma "enumerate 3 [a,b,c] = [(3,a),(4,b),(5,c)]" by normalization}\\
@{lemma "List.product [a,b] [c,d] = [(a, c), (a, d), (b, c), (b, d)]" by simp}\\
@{lemma "product_lists [[a,b], [c], [d,e]] = [[a,c,d], [a,c,e], [b,c,d], [b,c,e]]" by simp}\\
@{lemma "splice [a,b,c] [x,y,z] = [a,x,b,y,c,z]" by simp}\\
@{lemma "splice [a,b,c,d] [x,y] = [a,x,b,y,c,d]" by simp}\\
@{lemma "shuffle [a,b] [c,d] =  {[a,b,c,d],[a,c,b,d],[a,c,d,b],[c,a,b,d],[c,a,d,b],[c,d,a,b]}"
@{lemma "take 2 [a,b,c,d] = [a,b]" by simp}\\
@{lemma "take 6 [a,b,c,d] = [a,b,c,d]" by simp}\\
@{lemma "drop 2 [a,b,c,d] = [c,d]" by simp}\\
@{lemma "drop 6 [a,b,c,d] = []" by simp}\\
@{lemma "takeWhile (%n::nat. n<3) [1,2,3,0] = [1,2]" by simp}\\
@{lemma "dropWhile (%n::nat. n<3) [1,2,3,0] = [3,0]" by simp}\\
@{lemma "distinct [2,0,1::nat]" by simp}\\
@{lemma "remdups [2,0,2,1::nat,2] = [0,1,2]" by simp}\\
@{lemma "remdups_adj [2,2,3,1,1::nat,2,1] = [2,3,1,2,1]" by simp}\\
@{lemma "List.insert 2 [0::nat,1,2] = [0,1,2]" by (simp add: List.insert_def)}\\
@{lemma "List.insert 3 [0::nat,1,2] = [3,0,1,2]" by (simp add: List.insert_def)}\\
@{lemma "List.union [2,3,4] [0::int,1,2] = [4,3,0,1,2]" by (simp add: List.insert_def List.union_def)}\\
@{lemma "List.find (%i::int. i>0) [0,0] = None" by simp}\\
@{lemma "List.find (%i::int. i>0) [0,1,0,2] = Some 1" by simp}\\
@{lemma "count_list [0,1,0,2::int] 0 = 2" by (simp)}\\
@{lemma "List.extract (%i::int. i>0) [0,0] = None" by(simp add: extract_def)}\\
@{lemma "List.extract (%i::int. i>0) [0,1,0,2] = Some([0], 1, [0,2])" by(simp add: extract_def)}\\
@{lemma "remove1 2 [2,0,2,1::nat,2] = [0,2,1,2]" by simp}\\
@{lemma "removeAll 2 [2,0,2,1::nat,2] = [0,1]" by simp}\\
@{lemma "nth [a,b,c,d] 2 = c" by simp}\\
@{lemma "[a,b,c,d][2 := x] = [a,b,x,d]" by simp}\\
@{lemma "nths [a,b,c,d,e] {0,2,3} = [a,c,d]" by (simp add:nths_def)}\\
@{lemma "subseqs [a,b] = [[a, b], [a], [b], []]" by simp}\\
@{lemma "List.n_lists 2 [a,b,c] = [[a, a], [b, a], [c, a], [a, b], [b, b], [c, b], [a, c], [b, c], [c, c]]" by (simp add: eval_nat_numeral)}\\
@{lemma "rotate1 [a,b,c,d] = [b,c,d,a]" by simp}\\
@{lemma "rotate 3 [a,b,c,d] = [d,a,b,c]" by (simp add:rotate_def eval_nat_numeral)}\\
@{lemma "replicate 4 a = [a,a,a,a]" by (simp add:eval_nat_numeral)}\\
@{lemma "[2..<5] = [2,3,4]" by (simp add:eval_nat_numeral)}\\
@{lemma "min_list [3,1,-2::int] = -2" by (simp)}\\
@{lemma "arg_min_list (λi. i*i) [3,-1,1,-2::int] = -1" by (simp)}
\end{tabular}}
\caption{Characteristic examples}
\label{fig:Characteristic}
\end{figure}
Figure~\ref{fig:Characteristic} shows characteristic examples
that should give an intuitive understanding of the above functions.
›

text‹The following simple sort functions are intended for proofs,
not for efficient implementations.›

text ‹A sorted predicate w.r.t. a relation:›

fun sorted_wrt :: "('a ⇒ 'a ⇒ bool) ⇒ 'a list ⇒ bool" where
"sorted_wrt P [] = True" |
"sorted_wrt P (x # ys) = ((∀y ∈ set ys. P x y) ∧ sorted_wrt P ys)"

(* FIXME: define sorted in terms of sorted_wrt *)

text ‹A class-based sorted predicate:›

context linorder
begin

fun sorted :: "'a list ⇒ bool" where
"sorted [] = True" |
"sorted (x # ys) = ((∀y ∈ set ys. x ≤ y) ∧ sorted ys)"

lemma sorted_sorted_wrt: "sorted = sorted_wrt (≤)"
proof (rule ext)
fix xs show "sorted xs = sorted_wrt (≤) xs"
by(induction xs rule: sorted.induct) auto
qed

primrec insort_key :: "('b ⇒ 'a) ⇒ 'b ⇒ 'b list ⇒ 'b list" where
"insort_key f x [] = [x]" |
"insort_key f x (y#ys) =
(if f x ≤ f y then (x#y#ys) else y#(insort_key f x ys))"

definition sort_key :: "('b ⇒ 'a) ⇒ 'b list ⇒ 'b list" where
"sort_key f xs = foldr (insort_key f) xs []"

definition insort_insert_key :: "('b ⇒ 'a) ⇒ 'b ⇒ 'b list ⇒ 'b list" where
"insort_insert_key f x xs =
(if f x ∈ f  set xs then xs else insort_key f x xs)"

abbreviation "sort ≡ sort_key (λx. x)"
abbreviation "insort ≡ insort_key (λx. x)"
abbreviation "insort_insert ≡ insort_insert_key (λx. x)"

definition stable_sort_key :: "(('b ⇒ 'a) ⇒ 'b list ⇒ 'b list) ⇒ bool" where
"stable_sort_key sk =
(∀f xs k. filter (λy. f y = k) (sk f xs) = filter (λy. f y = k) xs)"

end

subsubsection ‹List comprehension›

text‹Input syntax for Haskell-like list comprehension notation.
Typical example: ‹[(x,y). x ← xs, y ← ys, x ≠ y]›,
the list of all pairs of distinct elements from ‹xs› and ‹ys›.
The syntax is as in Haskell, except that ‹|› becomes a dot
(like in Isabelle's set comprehension): ‹[e. x ← xs, …]› rather than
\verb![e| x <- xs, ...]!.

The qualifiers after the dot are
\begin{description}
\item[generators] ‹p ← xs›,
where ‹p› is a pattern and ‹xs› an expression of list type, or
\item[guards] ‹b›, where ‹b› is a boolean expression.
%\item[local bindings] @ {text"let x = e"}.
\end{description}

Just like in Haskell, list comprehension is just a shorthand. To avoid
misunderstandings, the translation into desugared form is not reversed
upon output. Note that the translation of ‹[e. x ← xs]› is
optmized to @{term"map (%x. e) xs"}.

It is easy to write short list comprehensions which stand for complex
expressions. During proofs, they may become unreadable (and
mangled). In such cases it can be advisable to introduce separate
definitions for the list comprehensions in question.›

nonterminal lc_qual and lc_quals

syntax
"_listcompr" :: "'a ⇒ lc_qual ⇒ lc_quals ⇒ 'a list"  ("[_ . __")
"_lc_gen" :: "'a ⇒ 'a list ⇒ lc_qual"  ("_ ← _")
"_lc_test" :: "bool ⇒ lc_qual" ("_")
(*"_lc_let" :: "letbinds => lc_qual"  ("let _")*)
"_lc_end" :: "lc_quals" ("]")
"_lc_quals" :: "lc_qual ⇒ lc_quals ⇒ lc_quals"  (", __")

syntax (ASCII)
"_lc_gen" :: "'a ⇒ 'a list ⇒ lc_qual"  ("_ <- _")

parse_translation ‹
let
val NilC = Syntax.const @{const_syntax Nil};
val ConsC = Syntax.const @{const_syntax Cons};
val mapC = Syntax.const @{const_syntax map};
val concatC = Syntax.const @{const_syntax concat};
val IfC = Syntax.const @{const_syntax If};
val dummyC = Syntax.const @{const_syntax Pure.dummy_pattern}

fun single x = ConsC $x$ NilC;

fun pat_tr ctxt p e opti = (* %x. case x of p => e | _ => [] *)
let
(* FIXME proper name context!? *)
val x =
Free (singleton (Name.variant_list (fold Term.add_free_names [p, e] [])) "x", dummyT);
val e = if opti then single e else e;
val case1 = Syntax.const @{syntax_const "_case1"} $p$ e;
val case2 =
Syntax.const @{syntax_const "_case1"} $dummyC$ NilC;
val cs = Syntax.const @{syntax_const "_case2"} $case1$ case2;
in Syntax_Trans.abs_tr [x, Case_Translation.case_tr false ctxt [x, cs]] end;

fun pair_pat_tr (x as Free _) e = Syntax_Trans.abs_tr [x, e]
| pair_pat_tr (_ $p1$ p2) e =
Syntax.const @{const_syntax case_prod} $pair_pat_tr p1 (pair_pat_tr p2 e) | pair_pat_tr dummy e = Syntax_Trans.abs_tr [Syntax.const "_idtdummy", e] fun pair_pat ctxt (Const (@{const_syntax "Pair"},_)$ s $t) = pair_pat ctxt s andalso pair_pat ctxt t | pair_pat ctxt (Free (s,_)) = let val thy = Proof_Context.theory_of ctxt; val s' = Proof_Context.intern_const ctxt s; in not (Sign.declared_const thy s') end | pair_pat _ t = (t = dummyC); fun abs_tr ctxt p e opti = let val p = Term_Position.strip_positions p in if pair_pat ctxt p then (pair_pat_tr p e, true) else (pat_tr ctxt p e opti, false) end fun lc_tr ctxt [e, Const (@{syntax_const "_lc_test"}, _)$ b, qs] =
let
val res =
(case qs of
Const (@{syntax_const "_lc_end"}, _) => single e
| Const (@{syntax_const "_lc_quals"}, _) $q$ qs => lc_tr ctxt [e, q, qs]);
in IfC $b$ res $NilC end | lc_tr ctxt [e, Const (@{syntax_const "_lc_gen"}, _)$ p $es, Const(@{syntax_const "_lc_end"}, _)] = (case abs_tr ctxt p e true of (f, true) => mapC$ f $es | (f, false) => concatC$ (mapC $f$ es))
| lc_tr ctxt
[e, Const (@{syntax_const "_lc_gen"}, _) $p$ es,
Const (@{syntax_const "_lc_quals"}, _) $q$ qs] =
let val e' = lc_tr ctxt [e, q, qs];
in concatC $(mapC$ (fst (abs_tr ctxt p e' false)) $es) end; in [(@{syntax_const "_listcompr"}, lc_tr)] end › ML_val ‹ let val read = Syntax.read_term @{context} o Syntax.implode_input; fun check s1 s2 = read s1 aconv read s2 orelse error ("Check failed: " ^ quote (Input.source_content s1) ^ Position.here_list [Input.pos_of s1, Input.pos_of s2]); in check ‹[(x,y,z). b]› ‹if b then [(x, y, z)] else []›; check ‹[(x,y,z). (x,_,y)←xs]› ‹map (λ(x,_,y). (x, y, z)) xs›; check ‹[e x y. (x,_)←xs, y←ys]› ‹concat (map (λ(x,_). map (λy. e x y) ys) xs)›; check ‹[(x,y,z). x<a, x>b]› ‹if x < a then if b < x then [(x, y, z)] else [] else []›; check ‹[(x,y,z). x←xs, x>b]› ‹concat (map (λx. if b < x then [(x, y, z)] else []) xs)›; check ‹[(x,y,z). x<a, x←xs]› ‹if x < a then map (λx. (x, y, z)) xs else []›; check ‹[(x,y). Cons True x ← xs]› ‹concat (map (λxa. case xa of [] ⇒ [] | True # x ⇒ [(x, y)] | False # x ⇒ []) xs)›; check ‹[(x,y,z). Cons x [] ← xs]› ‹concat (map (λxa. case xa of [] ⇒ [] | [x] ⇒ [(x, y, z)] | x # aa # lista ⇒ []) xs)›; check ‹[(x,y,z). x<a, x>b, x=d]› ‹if x < a then if b < x then if x = d then [(x, y, z)] else [] else [] else []›; check ‹[(x,y,z). x<a, x>b, y←ys]› ‹if x < a then if b < x then map (λy. (x, y, z)) ys else [] else []›; check ‹[(x,y,z). x<a, (_,x)←xs,y>b]› ‹if x < a then concat (map (λ(_,x). if b < y then [(x, y, z)] else []) xs) else []›; check ‹[(x,y,z). x<a, x←xs, y←ys]› ‹if x < a then concat (map (λx. map (λy. (x, y, z)) ys) xs) else []›; check ‹[(x,y,z). x←xs, x>b, y<a]› ‹concat (map (λx. if b < x then if y < a then [(x, y, z)] else [] else []) xs)›; check ‹[(x,y,z). x←xs, x>b, y←ys]› ‹concat (map (λx. if b < x then map (λy. (x, y, z)) ys else []) xs)›; check ‹[(x,y,z). x←xs, (y,_)←ys,y>x]› ‹concat (map (λx. concat (map (λ(y,_). if x < y then [(x, y, z)] else []) ys)) xs)›; check ‹[(x,y,z). x←xs, y←ys,z←zs]› ‹concat (map (λx. concat (map (λy. map (λz. (x, y, z)) zs) ys)) xs)› end; › ML ‹ (* Simproc for rewriting list comprehensions applied to List.set to set comprehension. *) signature LIST_TO_SET_COMPREHENSION = sig val simproc : Proof.context -> cterm -> thm option end structure List_to_Set_Comprehension : LIST_TO_SET_COMPREHENSION = struct (* conversion *) fun all_exists_conv cv ctxt ct = (case Thm.term_of ct of Const (@{const_name Ex}, _)$ Abs _ =>
Conv.arg_conv (Conv.abs_conv (all_exists_conv cv o #2) ctxt) ct
| _ => cv ctxt ct)

fun all_but_last_exists_conv cv ctxt ct =
(case Thm.term_of ct of
Const (@{const_name Ex}, _) $Abs (_, _, Const (@{const_name Ex}, _)$ _) =>
Conv.arg_conv (Conv.abs_conv (all_but_last_exists_conv cv o #2) ctxt) ct
| _ => cv ctxt ct)

fun Collect_conv cv ctxt ct =
(case Thm.term_of ct of
Const (@{const_name Collect}, _) $Abs _ => Conv.arg_conv (Conv.abs_conv cv ctxt) ct | _ => raise CTERM ("Collect_conv", [ct])) fun rewr_conv' th = Conv.rewr_conv (mk_meta_eq th) fun conjunct_assoc_conv ct = Conv.try_conv (rewr_conv' @{thm conj_assoc} then_conv HOLogic.conj_conv Conv.all_conv conjunct_assoc_conv) ct fun right_hand_set_comprehension_conv conv ctxt = HOLogic.Trueprop_conv (HOLogic.eq_conv Conv.all_conv (Collect_conv (all_exists_conv conv o #2) ctxt)) (* term abstraction of list comprehension patterns *) datatype termlets = If | Case of typ * int local val set_Nil_I = @{lemma "set [] = {x. False}" by (simp add: empty_def [symmetric])} val set_singleton = @{lemma "set [a] = {x. x = a}" by simp} val inst_Collect_mem_eq = @{lemma "set A = {x. x ∈ set A}" by simp} val del_refl_eq = @{lemma "(t = t ∧ P) ≡ P" by simp} fun mk_set T = Const (@{const_name set}, HOLogic.listT T --> HOLogic.mk_setT T) fun dest_set (Const (@{const_name set}, _)$ xs) = xs

fun dest_singleton_list (Const (@{const_name Cons}, _) $t$ (Const (@{const_name Nil}, _))) = t
| dest_singleton_list t = raise TERM ("dest_singleton_list", [t])

(*We check that one case returns a singleton list and all other cases
return [], and return the index of the one singleton list case.*)
fun possible_index_of_singleton_case cases =
let
fun check (i, case_t) s =
(case strip_abs_body case_t of
(Const (@{const_name Nil}, _)) => s
| _ => (case s of SOME NONE => SOME (SOME i) | _ => NONE))
in
fold_index check cases (SOME NONE) |> the_default NONE
end

(*returns condition continuing term option*)
fun dest_if (Const (@{const_name If}, _) $cond$ then_t $Const (@{const_name Nil}, _)) = SOME (cond, then_t) | dest_if _ = NONE (*returns (case_expr type index chosen_case constr_name) option*) fun dest_case ctxt case_term = let val (case_const, args) = strip_comb case_term in (case try dest_Const case_const of SOME (c, T) => (case Ctr_Sugar.ctr_sugar_of_case ctxt c of SOME {ctrs, ...} => (case possible_index_of_singleton_case (fst (split_last args)) of SOME i => let val constr_names = map (fst o dest_Const) ctrs val (Ts, _) = strip_type T val T' = List.last Ts in SOME (List.last args, T', i, nth args i, nth constr_names i) end | NONE => NONE) | NONE => NONE) | NONE => NONE) end fun tac ctxt [] = resolve_tac ctxt [set_singleton] 1 ORELSE resolve_tac ctxt [inst_Collect_mem_eq] 1 | tac ctxt (If :: cont) = Splitter.split_tac ctxt @{thms if_split} 1 THEN resolve_tac ctxt @{thms conjI} 1 THEN resolve_tac ctxt @{thms impI} 1 THEN Subgoal.FOCUS (fn {prems, context = ctxt', ...} => CONVERSION (right_hand_set_comprehension_conv (K (HOLogic.conj_conv (Conv.rewr_conv (List.last prems RS @{thm Eq_TrueI})) Conv.all_conv then_conv rewr_conv' @{lemma "(True ∧ P) = P" by simp})) ctxt') 1) ctxt 1 THEN tac ctxt cont THEN resolve_tac ctxt @{thms impI} 1 THEN Subgoal.FOCUS (fn {prems, context = ctxt', ...} => CONVERSION (right_hand_set_comprehension_conv (K (HOLogic.conj_conv (Conv.rewr_conv (List.last prems RS @{thm Eq_FalseI})) Conv.all_conv then_conv rewr_conv' @{lemma "(False ∧ P) = False" by simp})) ctxt') 1) ctxt 1 THEN resolve_tac ctxt [set_Nil_I] 1 | tac ctxt (Case (T, i) :: cont) = let val SOME {injects, distincts, case_thms, split, ...} = Ctr_Sugar.ctr_sugar_of ctxt (fst (dest_Type T)) in (* do case distinction *) Splitter.split_tac ctxt [split] 1 THEN EVERY (map_index (fn (i', _) => (if i' < length case_thms - 1 then resolve_tac ctxt @{thms conjI} 1 else all_tac) THEN REPEAT_DETERM (resolve_tac ctxt @{thms allI} 1) THEN resolve_tac ctxt @{thms impI} 1 THEN (if i' = i then (* continue recursively *) Subgoal.FOCUS (fn {prems, context = ctxt', ...} => CONVERSION (Thm.eta_conversion then_conv right_hand_set_comprehension_conv (K ((HOLogic.conj_conv (HOLogic.eq_conv Conv.all_conv (rewr_conv' (List.last prems)) then_conv (Conv.try_conv (Conv.rewrs_conv (map mk_meta_eq injects)))) Conv.all_conv) then_conv (Conv.try_conv (Conv.rewr_conv del_refl_eq)) then_conv conjunct_assoc_conv)) ctxt' then_conv (HOLogic.Trueprop_conv (HOLogic.eq_conv Conv.all_conv (Collect_conv (fn (_, ctxt'') => Conv.repeat_conv (all_but_last_exists_conv (K (rewr_conv' @{lemma "(∃x. x = t ∧ P x) = P t" by simp})) ctxt'')) ctxt')))) 1) ctxt 1 THEN tac ctxt cont else Subgoal.FOCUS (fn {prems, context = ctxt', ...} => CONVERSION (right_hand_set_comprehension_conv (K (HOLogic.conj_conv ((HOLogic.eq_conv Conv.all_conv (rewr_conv' (List.last prems))) then_conv (Conv.rewrs_conv (map (fn th => th RS @{thm Eq_FalseI}) distincts))) Conv.all_conv then_conv (rewr_conv' @{lemma "(False ∧ P) = False" by simp}))) ctxt' then_conv HOLogic.Trueprop_conv (HOLogic.eq_conv Conv.all_conv (Collect_conv (fn (_, ctxt'') => Conv.repeat_conv (Conv.bottom_conv (K (rewr_conv' @{lemma "(∃x. P) = P" by simp})) ctxt'')) ctxt'))) 1) ctxt 1 THEN resolve_tac ctxt [set_Nil_I] 1)) case_thms) end in fun simproc ctxt redex = let fun make_inner_eqs bound_vs Tis eqs t = (case dest_case ctxt t of SOME (x, T, i, cont, constr_name) => let val (vs, body) = strip_abs (Envir.eta_long (map snd bound_vs) cont) val x' = incr_boundvars (length vs) x val eqs' = map (incr_boundvars (length vs)) eqs val constr_t = list_comb (Const (constr_name, map snd vs ---> T), map Bound (((length vs) - 1) downto 0)) val constr_eq = Const (@{const_name HOL.eq}, T --> T --> @{typ bool})$ constr_t $x' in make_inner_eqs (rev vs @ bound_vs) (Case (T, i) :: Tis) (constr_eq :: eqs') body end | NONE => (case dest_if t of SOME (condition, cont) => make_inner_eqs bound_vs (If :: Tis) (condition :: eqs) cont | NONE => if null eqs then NONE (*no rewriting, nothing to be done*) else let val Type (@{type_name list}, [rT]) = fastype_of1 (map snd bound_vs, t) val pat_eq = (case try dest_singleton_list t of SOME t' => Const (@{const_name HOL.eq}, rT --> rT --> @{typ bool})$
Bound (length bound_vs) $t' | NONE => Const (@{const_name Set.member}, rT --> HOLogic.mk_setT rT --> @{typ bool})$
Bound (length bound_vs) $(mk_set rT$ t))
val reverse_bounds = curry subst_bounds
((map Bound ((length bound_vs - 1) downto 0)) @ [Bound (length bound_vs)])
val eqs' = map reverse_bounds eqs
val pat_eq' = reverse_bounds pat_eq
val inner_t =
fold (fn (_, T) => fn t => HOLogic.exists_const T $absdummy T t) (rev bound_vs) (fold (curry HOLogic.mk_conj) eqs' pat_eq') val lhs = Thm.term_of redex val rhs = HOLogic.mk_Collect ("x", rT, inner_t) val rewrite_rule_t = HOLogic.mk_Trueprop (HOLogic.mk_eq (lhs, rhs)) in SOME ((Goal.prove ctxt [] [] rewrite_rule_t (fn {context = ctxt', ...} => tac ctxt' (rev Tis))) RS @{thm eq_reflection}) end)) in make_inner_eqs [] [] [] (dest_set (Thm.term_of redex)) end end end › simproc_setup list_to_set_comprehension ("set xs") = ‹K List_to_Set_Comprehension.simproc› code_datatype set coset hide_const (open) coset subsubsection ‹@{const Nil} and @{const Cons}› lemma not_Cons_self [simp]: "xs ≠ x # xs" by (induct xs) auto lemma not_Cons_self2 [simp]: "x # xs ≠ xs" by (rule not_Cons_self [symmetric]) lemma neq_Nil_conv: "(xs ≠ []) = (∃y ys. xs = y # ys)" by (induct xs) auto lemma tl_Nil: "tl xs = [] ⟷ xs = [] ∨ (∃x. xs = [x])" by (cases xs) auto lemma Nil_tl: "[] = tl xs ⟷ xs = [] ∨ (∃x. xs = [x])" by (cases xs) auto lemma length_induct: "(⋀xs. ∀ys. length ys < length xs ⟶ P ys ⟹ P xs) ⟹ P xs" by (fact measure_induct) lemma induct_list012: "⟦P []; ⋀x. P [x]; ⋀x y zs. P (y # zs) ⟹ P (x # y # zs)⟧ ⟹ P xs" by induction_schema (pat_completeness, lexicographic_order) lemma list_nonempty_induct [consumes 1, case_names single cons]: "⟦ xs ≠ []; ⋀x. P [x]; ⋀x xs. xs ≠ [] ⟹ P xs ⟹ P (x # xs)⟧ ⟹ P xs" by(induction xs rule: induct_list012) auto lemma inj_split_Cons: "inj_on (λ(xs, n). n#xs) X" by (auto intro!: inj_onI) lemma inj_on_Cons1 [simp]: "inj_on ((#) x) A" by(simp add: inj_on_def) subsubsection ‹@{const length}› text ‹ Needs to come before ‹@› because of theorem ‹append_eq_append_conv›. › lemma length_append [simp]: "length (xs @ ys) = length xs + length ys" by (induct xs) auto lemma length_map [simp]: "length (map f xs) = length xs" by (induct xs) auto lemma length_rev [simp]: "length (rev xs) = length xs" by (induct xs) auto lemma length_tl [simp]: "length (tl xs) = length xs - 1" by (cases xs) auto lemma length_0_conv [iff]: "(length xs = 0) = (xs = [])" by (induct xs) auto lemma length_greater_0_conv [iff]: "(0 < length xs) = (xs ≠ [])" by (induct xs) auto lemma length_pos_if_in_set: "x ∈ set xs ⟹ length xs > 0" by auto lemma length_Suc_conv: "(length xs = Suc n) = (∃y ys. xs = y # ys ∧ length ys = n)" by (induct xs) auto lemma Suc_length_conv: "(Suc n = length xs) = (∃y ys. xs = y # ys ∧ length ys = n)" apply (induct xs, simp, simp) apply blast done lemma impossible_Cons: "length xs <= length ys ==> xs = x # ys = False" by (induct xs) auto lemma list_induct2 [consumes 1, case_names Nil Cons]: "length xs = length ys ⟹ P [] [] ⟹ (⋀x xs y ys. length xs = length ys ⟹ P xs ys ⟹ P (x#xs) (y#ys)) ⟹ P xs ys" proof (induct xs arbitrary: ys) case Nil then show ?case by simp next case (Cons x xs ys) then show ?case by (cases ys) simp_all qed lemma list_induct3 [consumes 2, case_names Nil Cons]: "length xs = length ys ⟹ length ys = length zs ⟹ P [] [] [] ⟹ (⋀x xs y ys z zs. length xs = length ys ⟹ length ys = length zs ⟹ P xs ys zs ⟹ P (x#xs) (y#ys) (z#zs)) ⟹ P xs ys zs" proof (induct xs arbitrary: ys zs) case Nil then show ?case by simp next case (Cons x xs ys zs) then show ?case by (cases ys, simp_all) (cases zs, simp_all) qed lemma list_induct4 [consumes 3, case_names Nil Cons]: "length xs = length ys ⟹ length ys = length zs ⟹ length zs = length ws ⟹ P [] [] [] [] ⟹ (⋀x xs y ys z zs w ws. length xs = length ys ⟹ length ys = length zs ⟹ length zs = length ws ⟹ P xs ys zs ws ⟹ P (x#xs) (y#ys) (z#zs) (w#ws)) ⟹ P xs ys zs ws" proof (induct xs arbitrary: ys zs ws) case Nil then show ?case by simp next case (Cons x xs ys zs ws) then show ?case by ((cases ys, simp_all), (cases zs,simp_all)) (cases ws, simp_all) qed lemma list_induct2': "⟦ P [] []; ⋀x xs. P (x#xs) []; ⋀y ys. P [] (y#ys); ⋀x xs y ys. P xs ys ⟹ P (x#xs) (y#ys) ⟧ ⟹ P xs ys" by (induct xs arbitrary: ys) (case_tac x, auto)+ lemma list_all2_iff: "list_all2 P xs ys ⟷ length xs = length ys ∧ (∀(x, y) ∈ set (zip xs ys). P x y)" by (induct xs ys rule: list_induct2') auto lemma neq_if_length_neq: "length xs ≠ length ys ⟹ (xs = ys) == False" by (rule Eq_FalseI) auto simproc_setup list_neq ("(xs::'a list) = ys") = ‹ (* Reduces xs=ys to False if xs and ys cannot be of the same length. This is the case if the atomic sublists of one are a submultiset of those of the other list and there are fewer Cons's in one than the other. *) let fun len (Const(@{const_name Nil},_)) acc = acc | len (Const(@{const_name Cons},_)$ _ $xs) (ts,n) = len xs (ts,n+1) | len (Const(@{const_name append},_)$ xs $ys) acc = len xs (len ys acc) | len (Const(@{const_name rev},_)$ xs) acc = len xs acc
| len (Const(@{const_name map},_) $_$ xs) acc = len xs acc
| len t (ts,n) = (t::ts,n);

val ss = simpset_of @{context};

fun list_neq ctxt ct =
let
val (Const(_,eqT) $lhs$ rhs) = Thm.term_of ct;
val (ls,m) = len lhs ([],0) and (rs,n) = len rhs ([],0);
fun prove_neq() =
let
val Type(_,listT::_) = eqT;
val size = HOLogic.size_const listT;
val eq_len = HOLogic.mk_eq (size $lhs, size$ rhs);
val neq_len = HOLogic.mk_Trueprop (HOLogic.Not $eq_len); val thm = Goal.prove ctxt [] [] neq_len (K (simp_tac (put_simpset ss ctxt) 1)); in SOME (thm RS @{thm neq_if_length_neq}) end in if m < n andalso submultiset (aconv) (ls,rs) orelse n < m andalso submultiset (aconv) (rs,ls) then prove_neq() else NONE end; in K list_neq end; › subsubsection ‹‹@› -- append› global_interpretation append: monoid append Nil proof fix xs ys zs :: "'a list" show "(xs @ ys) @ zs = xs @ (ys @ zs)" by (induct xs) simp_all show "xs @ [] = xs" by (induct xs) simp_all qed simp lemma append_assoc [simp]: "(xs @ ys) @ zs = xs @ (ys @ zs)" by (fact append.assoc) lemma append_Nil2: "xs @ [] = xs" by (fact append.right_neutral) lemma append_is_Nil_conv [iff]: "(xs @ ys = []) = (xs = [] ∧ ys = [])" by (induct xs) auto lemma Nil_is_append_conv [iff]: "([] = xs @ ys) = (xs = [] ∧ ys = [])" by (induct xs) auto lemma append_self_conv [iff]: "(xs @ ys = xs) = (ys = [])" by (induct xs) auto lemma self_append_conv [iff]: "(xs = xs @ ys) = (ys = [])" by (induct xs) auto lemma append_eq_append_conv [simp]: "length xs = length ys ∨ length us = length vs ==> (xs@us = ys@vs) = (xs=ys ∧ us=vs)" apply (induct xs arbitrary: ys) apply (case_tac ys, simp, force) apply (case_tac ys, force, simp) done lemma append_eq_append_conv2: "(xs @ ys = zs @ ts) = (∃us. xs = zs @ us ∧ us @ ys = ts ∨ xs @ us = zs ∧ ys = us @ ts)" apply (induct xs arbitrary: ys zs ts) apply fastforce apply(case_tac zs) apply simp apply fastforce done lemma same_append_eq [iff, induct_simp]: "(xs @ ys = xs @ zs) = (ys = zs)" by simp lemma append1_eq_conv [iff]: "(xs @ [x] = ys @ [y]) = (xs = ys ∧ x = y)" by simp lemma append_same_eq [iff, induct_simp]: "(ys @ xs = zs @ xs) = (ys = zs)" by simp lemma append_self_conv2 [iff]: "(xs @ ys = ys) = (xs = [])" using append_same_eq [of _ _ "[]"] by auto lemma self_append_conv2 [iff]: "(ys = xs @ ys) = (xs = [])" using append_same_eq [of "[]"] by auto lemma hd_Cons_tl: "xs ≠ [] ==> hd xs # tl xs = xs" by (fact list.collapse) lemma hd_append: "hd (xs @ ys) = (if xs = [] then hd ys else hd xs)" by (induct xs) auto lemma hd_append2 [simp]: "xs ≠ [] ==> hd (xs @ ys) = hd xs" by (simp add: hd_append split: list.split) lemma tl_append: "tl (xs @ ys) = (case xs of [] ⇒ tl ys | z#zs ⇒ zs @ ys)" by (simp split: list.split) lemma tl_append2 [simp]: "xs ≠ [] ==> tl (xs @ ys) = tl xs @ ys" by (simp add: tl_append split: list.split) lemma Cons_eq_append_conv: "x#xs = ys@zs = (ys = [] ∧ x#xs = zs ∨ (∃ys'. x#ys' = ys ∧ xs = ys'@zs))" by(cases ys) auto lemma append_eq_Cons_conv: "(ys@zs = x#xs) = (ys = [] ∧ zs = x#xs ∨ (∃ys'. ys = x#ys' ∧ ys'@zs = xs))" by(cases ys) auto lemma longest_common_prefix: "∃ps xs' ys'. xs = ps @ xs' ∧ ys = ps @ ys' ∧ (xs' = [] ∨ ys' = [] ∨ hd xs' ≠ hd ys')" by (induct xs ys rule: list_induct2') (blast, blast, blast, metis (no_types, hide_lams) append_Cons append_Nil list.sel(1)) text ‹Trivial rules for solving ‹@›-equations automatically.› lemma eq_Nil_appendI: "xs = ys ==> xs = [] @ ys" by simp lemma Cons_eq_appendI: "[| x # xs1 = ys; xs = xs1 @ zs |] ==> x # xs = ys @ zs" by (drule sym) simp lemma append_eq_appendI: "[| xs @ xs1 = zs; ys = xs1 @ us |] ==> xs @ ys = zs @ us" by (drule sym) simp text ‹ Simplification procedure for all list equalities. Currently only tries to rearrange ‹@› to see if - both lists end in a singleton list, - or both lists end in the same list. › simproc_setup list_eq ("(xs::'a list) = ys") = ‹ let fun last (cons as Const (@{const_name Cons}, _)$ _ $xs) = (case xs of Const (@{const_name Nil}, _) => cons | _ => last xs) | last (Const(@{const_name append},_)$ _ $ys) = last ys | last t = t; fun list1 (Const(@{const_name Cons},_)$ _ $Const(@{const_name Nil},_)) = true | list1 _ = false; fun butlast ((cons as Const(@{const_name Cons},_)$ x) $xs) = (case xs of Const (@{const_name Nil}, _) => xs | _ => cons$ butlast xs)
| butlast ((app as Const (@{const_name append}, _) $xs)$ ys) = app $butlast ys | butlast xs = Const(@{const_name Nil}, fastype_of xs); val rearr_ss = simpset_of (put_simpset HOL_basic_ss @{context} addsimps [@{thm append_assoc}, @{thm append_Nil}, @{thm append_Cons}]); fun list_eq ctxt (F as (eq as Const(_,eqT))$ lhs $rhs) = let val lastl = last lhs and lastr = last rhs; fun rearr conv = let val lhs1 = butlast lhs and rhs1 = butlast rhs; val Type(_,listT::_) = eqT val appT = [listT,listT] ---> listT val app = Const(@{const_name append},appT) val F2 = eq$ (app$lhs1$lastl) $(app$rhs1\$lastr)
val eq = HOLogic.mk_Trueprop (HOLogic.mk_eq (F,F2));
val thm = Goal.prove ctxt [] [] eq
(K (simp_tac (put_simpset rearr_ss ctxt) 1));
in SOME ((conv RS (thm RS trans)) RS eq_reflection) end;
in
if list1 lastl andalso list1 lastr then rearr @{thm append1_eq_conv}
else if lastl aconv lastr then rearr @{thm append_same_eq}
else NONE
end;
in fn _ => fn ctxt => fn ct => list_eq ctxt (Thm.term_of ct) end;
›

subsubsection ‹@{const map}›

lemma hd_map: "xs ≠ [] ⟹ hd (map f xs) = f (hd xs)"
by (cases xs) simp_all

lemma map_tl: "map f (tl xs) = tl (map f xs)"
by (cases xs) simp_all

lemma map_ext: "(⋀x. x ∈ set xs ⟶ f x = g x) ==> map f xs = map g xs"
by (induct xs) simp_all

lemma map_ident [simp]: "map (λx. x) = (λxs. xs)"
by (rule ext, induct_tac xs) auto

lemma map_append [simp]: "map f (xs @ ys) = map f xs @ map f ys"
by (induct xs) auto

lemma map_map [simp]: "map f (map g xs) = map (f ∘ g) xs"
by (induct xs) auto

lemma map_comp_map[simp]: "((map f) ∘ (map g)) = map(f ∘ g)"
by (rule ext) simp

lemma rev_map: "rev (map f xs) = map f (rev xs)"
by (induct xs) auto

lemma map_eq_conv[simp]: "(map f xs = map g xs) = (∀x ∈ set xs. f x = g x)"
by (induct xs) auto

lemma map_cong [fundef_cong]:
"xs = ys ⟹ (⋀x. x ∈ set ys ⟹ f x = g x) ⟹ map f xs = map g ys"
by simp

lemma map_is_Nil_conv [iff]: "(map f xs = []) = (xs = [])"
by (cases xs) auto

lemma Nil_is_map_conv [iff]: "([] = map f xs) = (xs = [])"
by (cases xs) auto

lemma map_eq_Cons_conv:
"(map f xs = y#ys) = (∃z zs. xs = z#zs ∧ f z = y ∧ map f zs = ys)"
by (cases xs) auto

lemma Cons_eq_map_conv:
"(x#xs = map f ys) = (∃z zs. ys = z#zs ∧ x = f z ∧ xs = map f zs)"
by (cases ys) auto

lemmas map_eq_Cons_D = map_eq_Cons_conv [THEN iffD1]
lemmas Cons_eq_map_D = Cons_eq_map_conv [THEN iffD1]
declare map_eq_Cons_D [dest!]  Cons_eq_map_D [dest!]

lemma ex_map_conv:
"(∃xs. ys = map f xs) = (∀y ∈ set ys. ∃x. y = f x)"
by(induct ys, auto simp add: Cons_eq_map_conv)

lemma map_eq_imp_length_eq:
assumes "map f xs = map g ys"
shows "length xs = length ys"
using assms
proof (induct ys arbitrary: xs)
case Nil then show ?case by simp
next
case (Cons y ys) then obtain z zs where xs: "xs = z # zs" by auto
from Cons xs have "map f zs = map g ys" by simp
with Cons have "length zs = length ys" by blast
with xs show ?case by simp
qed

lemma map_inj_on:
"[| map f xs = map f ys; inj_on f (set xs Un set ys) |]
==> xs = ys"
apply(frule map_eq_imp_length_eq)
apply(rotate_tac -1)
apply(induct rule:list_induct2)
apply simp
apply(simp)
apply (blast intro:sym)
done

lemma inj_on_map_eq_map:
"inj_on f (set xs Un set ys) ⟹ (map f xs = map f ys) = (xs = ys)"
by(blast dest:map_inj_on)

lemma map_injective:
"map f xs = map f ys ==> inj f ==> xs = ys"
by (induct ys arbitrary: xs) (auto dest!:injD)

lemma inj_map_eq_map[simp]: "inj f ⟹ (map f xs = map f ys) = (xs = ys)"
by(blast dest:map_injective)

lemma inj_mapI: "inj f ==> inj (map f)"
by (iprover dest: map_injective injD intro: inj_onI)

lemma inj_mapD: "inj (map f) ==> inj f"
apply (unfold inj_def)
apply clarify
apply (erule_tac x = "[x]" in allE)
apply (erule_tac x = "[y]" in allE)
apply auto
done

lemma inj_map[iff]: "inj (map f) = inj f"
by (blast dest: inj_mapD intro: inj_mapI)

lemma inj_on_mapI: "inj_on f (⋃(set  A)) ⟹ inj_on (map f) A"
apply(rule inj_onI)
apply(erule map_inj_on)
apply(blast intro:inj_onI dest:inj_onD)
done

lemma map_idI: "(⋀x. x ∈ set xs ⟹ f x = x) ⟹ map f xs = xs"
by (induct xs, auto)

lemma map_fun_upd [simp]: "y ∉ set xs ⟹ map (f(y:=v)) xs = map f xs"
by (induct xs) auto

lemma map_fst_zip[simp]:
"length xs = length ys ⟹ map fst (zip xs ys) = xs"
by (induct rule:list_induct2, simp_all)

lemma map_snd_zip[simp]:
"length xs = length ys ⟹ map snd (zip xs ys) = ys"
by (induct rule:list_induct2, simp_all)

lemma map_fst_zip_take:
"map fst (zip xs ys) = take (min (length xs) (length ys)) xs"
by (induct xs ys rule: list_induct2') simp_all

lemma map_snd_zip_take:
"map snd (zip xs ys) = take (min (length xs) (length ys)) ys"
by (induct xs ys rule: list_induct2') simp_all

lemma map2_map_map: "map2 h (map f xs) (map g xs) = map (λx. h (f x) (g x)) xs"
by (induction xs) (auto)

functor map: map

declare map.id [simp]

subsubsection ‹@{const rev}›

lemma rev_append [simp]: "rev (xs @ ys) = rev ys @ rev xs"
by (induct xs) auto

lemma rev_rev_ident [simp]: "rev (rev xs) = xs"
by (induct xs) auto

lemma rev_swap: "(rev xs = ys) = (xs = rev ys)"
by auto

lemma rev_is_Nil_conv [iff]: "(rev xs = []) = (xs = [])"
by (induct xs) auto

lemma Nil_is_rev_conv [iff]: "([] = rev xs) = (xs = [])"
by (induct xs) auto

lemma rev_singleton_conv [simp]: "(rev xs = [x]) = (xs = [x])"
by (cases xs) auto

lemma singleton_rev_conv [simp]: "([x] = rev xs) = (xs = [x])"
by (cases xs) auto

lemma rev_is_rev_conv [iff]: "(rev xs = rev ys) = (xs = ys)"
apply (induct xs arbitrary: ys, force)
apply (case_tac ys, simp, force)
done

lemma inj_on_rev[iff]: "inj_on rev A"

lemma rev_induct [case_names Nil snoc]:
"[| P []; !!x xs. P xs ==> P (xs @ [x]) |] ==> P xs"
apply(simplesubst rev_rev_ident[symmetric])
apply(rule_tac list = "rev xs" in list.induct, simp_all)
done

lemma rev_exhaust [case_names Nil snoc]:
"(xs = [] ==> P) ==>(!!ys y. xs = ys @ [y] ==> P) ==> P"
by (induct xs rule: rev_induct) auto

lemmas rev_cases = rev_exhaust

lemma rev_nonempty_induct [consumes 1, case_names single snoc]:
assumes "xs ≠ []"
and single: "⋀x. P [x]"
and snoc': "⋀x xs. xs ≠ [] ⟹ P xs ⟹ P (xs@[x])"
shows "P xs"
using ‹xs ≠ []› proof (induct xs rule: rev_induct)
case (snoc x xs) then show ?case
proof (cases xs)
case Nil thus ?thesis by (simp add: single)
next
case Cons with snoc show ?thesis by (fastforce intro!: snoc')
qed
qed simp

lemma rev_eq_Cons_iff[iff]: "(rev xs = y#ys) = (xs = rev ys @ [y])"
by(rule rev_cases[of xs]) auto

subsubsection ‹@{const set}›

declare list.set[code_post]  ― ‹pretty output›

lemma finite_set [iff]: "finite (set xs)"
by (induct xs) auto

lemma set_append [simp]: "set (xs @ ys) = (set xs ∪ set ys)"
by (induct xs) auto

lemma hd_in_set[simp]: "xs ≠ [] ⟹ hd xs ∈ set xs"
by(cases xs) auto

lemma set_subset_Cons: "set xs ⊆ set (x # xs)"
by auto

lemma set_ConsD: "y ∈ set (x # xs) ⟹ y=x ∨ y ∈ set xs"
by auto

lemma set_empty [iff]: "(set xs = {}) = (xs = [])"
by (induct xs) auto

lemma set_empty2[iff]: "({} = set xs) = (xs = [])"
by(induct xs) auto

lemma set_rev [simp]: "set (rev xs) = set xs"
by (induct xs) auto

lemma set_map [simp]: "set (map f xs) = f(set xs)"
by (induct xs) auto

lemma set_filter [simp]: "set (filter P xs) = {x. x ∈ set xs ∧ P x}"
by (induct xs) auto

lemma set_upt [simp]: "set[i..<j] = {i..<j}"
by (induct j) auto

lemma split_list: "x ∈ set xs ⟹ ∃ys zs. xs = ys @ x # zs"
proof (induct xs)
case Nil thus ?case by simp
next
case Cons thus ?case by (auto intro: Cons_eq_appendI)
qed

lemma in_set_conv_decomp: "x ∈ set xs ⟷ (∃ys zs. xs = ys @ x # zs)"
by (auto elim: split_list)

lemma split_list_first: "x ∈ set xs ⟹ ∃ys zs. xs = ys @ x # zs ∧ x ∉ set ys"
proof (induct xs)
case Nil thus ?case by simp
next
case (Cons a xs)
show ?case
proof cases
assume "x = a" thus ?case using Cons by fastforce
next
assume "x ≠ a" thus ?case using Cons by(fastforce intro!: Cons_eq_appendI)
qed
qed

lemma in_set_conv_decomp_first:
"(x ∈ set xs) = (∃ys zs. xs = ys @ x # zs ∧ x ∉ set ys)"
by (auto dest!: split_list_first)

lemma split_list_last: "x ∈ set xs ⟹ ∃ys zs. xs = ys @ x # zs ∧ x ∉ set zs"
proof (induct xs rule: rev_induct)
case Nil thus ?case by simp
next
case (snoc a xs)
show ?case
proof cases
assume "x = a" thus ?case using snoc by (auto intro!: exI)
next
assume "x ≠ a" thus ?case using snoc by fastforce
qed
qed

lemma in_set_conv_decomp_last:
"(x ∈ set xs) = (∃ys zs. xs = ys @ x # zs ∧ x ∉ set zs)"
by (auto dest!: split_list_last)

lemma split_list_prop: "∃x ∈ set xs. P x ⟹ ∃ys x zs. xs = ys @ x # zs ∧ P x"
proof (induct xs)
case Nil thus ?case by simp
next
case Cons thus ?case
qed

lemma split_list_propE:
assumes "∃x ∈ set xs. P x"
obtains ys x zs where "xs = ys @ x # zs" and "P x"
using split_list_prop [OF assms] by blast

lemma split_list_first_prop:
"∃x ∈ set xs. P x ⟹
∃ys x zs. xs = ys@x#zs ∧ P x ∧ (∀y ∈ set ys. ¬ P y)"
proof (induct xs)
case Nil thus ?case by simp
next
case (Cons x xs)
show ?case
proof cases
assume "P x"
hence "x # xs = [] @ x # xs ∧ P x ∧ (∀y∈set []. ¬ P y)" by simp
thus ?thesis by fast
next
assume "¬ P x"
hence "∃x∈set xs. P x" using Cons(2) by simp
thus ?thesis using ‹¬ P x› Cons(1) by (metis append_Cons set_ConsD)
qed
qed

lemma split_list_first_propE:
assumes "∃x ∈ set xs. P x"
obtains ys x zs where "xs = ys @ x # zs" and "P x" and "∀y ∈ set ys. ¬ P y"
using split_list_first_prop [OF assms] by blast

lemma split_list_first_prop_iff:
"(∃x ∈ set xs. P x) ⟷
(∃ys x zs. xs = ys@x#zs ∧ P x ∧ (∀y ∈ set ys. ¬ P y))"
by (rule, erule split_list_first_prop) auto

lemma split_list_last_prop:
"∃x ∈ set xs. P x ⟹
∃ys x zs. xs = ys@x#zs ∧ P x ∧ (∀z ∈ set zs. ¬ P z)"
proof(induct xs rule:rev_induct)
case Nil thus ?case by simp
next
case (snoc x xs)
show ?case
proof cases
assume "P x" thus ?thesis by (auto intro!: exI)
next
assume "¬ P x"
hence "∃x∈set xs. P x" using snoc(2) by simp
thus ?thesis using ‹¬ P x› snoc(1) by fastforce
qed
qed

lemma split_list_last_propE:
assumes "∃x ∈ set xs. P x"
obtains ys x zs where "xs = ys @ x # zs" and "P x" and "∀z ∈ set zs. ¬ P z"
using split_list_last_prop [OF assms] by blast

lemma split_list_last_prop_iff:
"(∃x ∈ set xs. P x) ⟷
(∃ys x zs. xs = ys@x#zs ∧ P x ∧ (∀z ∈ set zs. ¬ P z))"
by rule (erule split_list_last_prop, auto)

lemma finite_list: "finite A ⟹ ∃xs. set xs = A"
by (erule finite_induct) (auto simp add: list.set(2)[symmetric] simp del: list.set(2))

lemma card_length: "card (set xs) ≤ length xs"
by (induct xs) (auto simp add: card_insert_if)

lemma set_minus_filter_out:
"set xs - {y} = set (filter (λx. ¬ (x = y)) xs)"
by (induct xs) auto

lemma append_Cons_eq_iff:
"⟦ x ∉ set xs; x ∉ set ys ⟧ ⟹
xs @ x # ys = xs' @ x # ys' ⟷ (xs = xs' ∧ ys = ys')"
by(auto simp: append_eq_Cons_conv Cons_eq_append_conv append_eq_append_conv2)

subsubsection ‹@{const filter}›

lemma filter_append [simp]: "filter P (xs @ ys) = filter P xs @ filter P ys"
by (induct xs) auto

lemma rev_filter: "rev (filter P xs) = filter P (rev xs)"
by (induct xs) simp_all

lemma filter_filter [simp]: "filter P (filter Q xs) = filter (λx. Q x ∧ P x) xs"
by (induct xs) auto

lemma length_filter_le [simp]: "length (filter P xs) ≤ length xs"
by (induct xs) (auto simp add: le_SucI)

lemma sum_length_filter_compl:
"length(filter P xs) + length(filter (λx. ¬P x) xs) = length xs"
by(induct xs) simp_all

lemma filter_True [simp]: "∀x ∈ set xs. P x ==> filter P xs = xs"
by (induct xs) auto

lemma filter_False [simp]: "∀x ∈ set xs. ¬ P x ==> filter P xs = []"
by (induct xs) auto

lemma filter_empty_conv: "(filter P xs = []) = (∀x∈set xs. ¬ P x)"
by (induct xs) simp_all

lemma filter_id_conv: "(filter P xs = xs) = (∀x∈set xs. P x)"
apply (induct xs)
apply auto
apply(cut_tac P=P and xs=xs in length_filter_le)
apply simp
done

lemma filter_map: "filter P (map f xs) = map f (filter (P ∘ f) xs)"
by (induct xs) simp_all

lemma length_filter_map[simp]:
"length (filter P (map f xs)) = length(filter (P ∘ f) xs)"

lemma filter_is_subset [simp]: "set (filter P xs) ≤ set xs"
by auto

lemma length_filter_less:
"⟦ x ∈ set xs; ¬ P x ⟧ ⟹ length(filter P xs) < length xs"
proof (induct xs)
case Nil thus ?case by simp
next
case (Cons x xs) thus ?case
apply (auto split:if_split_asm)
using length_filter_le[of P xs] apply arith
done
qed

lemma length_filter_conv_card:
"length(filter p xs) = card{i. i < length xs ∧ p(xs!i)}"
proof (induct xs)
case Nil thus ?case by simp
next
case (Cons x xs)
let ?S = "{i. i < length xs ∧ p(xs!i)}"
have fin: "finite ?S" by(fast intro: bounded_nat_set_is_finite)
show ?case (is "?l = card ?S'")
proof (cases)
assume "p x"
hence eq: "?S' = insert 0 (Suc  ?S)"
by(auto simp: image_def split:nat.split dest:gr0_implies_Suc)
have "length (filter p (x # xs)) = Suc(card ?S)"
using Cons ‹p x› by simp
also have "… = Suc(card(Suc  ?S))" using fin
also have "… = card ?S'" using eq fin
finally show ?thesis .
next
assume "¬ p x"
hence eq: "?S' = Suc  ?S"
by(auto simp add: image_def split:nat.split elim:lessE)
have "length (filter p (x # xs)) = card ?S"
using Cons ‹¬ p x› by simp
also have "… = card(Suc  ?S)" using fin
also have "… = card ?S'" using eq fin
finally show ?thesis .
qed
qed

lemma Cons_eq_filterD:
"x#xs = filter P ys ⟹
∃us vs. ys = us @ x # vs ∧ (∀u∈set us. ¬ P u) ∧ P x ∧ xs = filter P vs"
(is "_ ⟹ ∃us vs. ?P ys us vs")
proof(induct ys)
case Nil thus ?case by simp
next
case (Cons y ys)
show ?case (is "∃x. ?Q x")
proof cases
assume Py: "P y"
show ?thesis
proof cases
assume "x = y"
with Py Cons.prems have "?Q []" by simp
then show ?thesis ..
next
assume "x ≠ y"
with Py Cons.prems show ?thesis by simp
qed
next
assume "¬ P y"
with Cons obtain us vs where "?P (y#ys) (y#us) vs" by fastforce
then have "?Q (y#us)" by simp
then show ?thesis ..
qed
qed

lemma filter_eq_ConsD:
"filter P ys = x#xs ⟹
∃us vs. ys = us @ x # vs ∧ (∀u∈set us. ¬ P u) ∧ P x ∧ xs = filter P vs"
by(rule Cons_eq_filterD) simp

lemma filter_eq_Cons_iff:
"(filter P ys = x#xs) =
(∃us vs. ys = us @ x # vs ∧ (∀u∈set us. ¬ P u) ∧ P x ∧ xs = filter P vs)"
by(auto dest:filter_eq_ConsD)

lemma Cons_eq_filter_iff:
"(x#xs = filter P ys) =
(∃us vs. ys = us @ x # vs ∧ (∀u∈set us. ¬ P u) ∧ P x ∧ xs = filter P vs)"
by(auto dest:Cons_eq_filterD)

lemma inj_on_filter_key_eq:
assumes "inj_on f (insert y (set xs))"
shows "filter (λx. f y = f x) xs = filter (HOL.eq y) xs"
using assms by (induct xs) auto

lemma filter_cong[fundef_cong]:
"xs = ys ⟹ (⋀x. x ∈ set ys ⟹ P x = Q x) ⟹ filter P xs = filter Q ys"
apply simp
apply(erule thin_rl)
by (induct ys) simp_all

subsubsection ‹List partitioning›

primrec partition :: "('a ⇒ bool) ⇒'a list ⇒ 'a list × 'a list" where
"partition P [] = ([], [])" |
"partition P (x # xs) =
(let (yes, no) = partition P xs
in if P x then (x # yes, no) else (yes, x # no))"

lemma partition_filter1: "fst (partition P xs) = filter P xs"
by (induct xs) (auto simp add: Let_def split_def)

lemma partition_filter2: "snd (partition P xs) = filter (Not ∘ P) xs"
by (induct xs) (auto simp add: Let_def split_def)

lemma partition_P:
assumes "partition P xs = (yes, no)"
shows "(∀p ∈ set yes.  P p) ∧ (∀p  ∈ set no. ¬ P p)"
proof -
from assms have "yes = fst (partition P xs)" and "no = snd (partition P xs)"
by simp_all
then show ?thesis by (simp_all add: partition_filter1 partition_filter2)
qed

lemma partition_set:
assumes "partition P xs = (yes, no)"
shows "set yes ∪ set no = set xs"
proof -
from assms have "yes = fst (partition P xs)" and "no = snd (partition P xs)"
by simp_all
then show ?thesis by (auto simp add: partition_filter1 partition_filter2)
qed

lemma partition_filter_conv[simp]:
"partition f xs = (filter f xs,filter (Not ∘ f) xs)"
unfolding partition_filter2[symmetric]
unfolding partition_filter1[symmetric] by simp

declare partition.simps[simp del]

subsubsection ‹@{const concat}›

lemma concat_append [simp]: "concat (xs @ ys) = concat xs @ concat ys"
by (induct xs) auto

lemma concat_eq_Nil_conv [simp]: "(concat xss = []) = (∀xs ∈ set xss. xs = [])"
by (induct xss) auto

lemma Nil_eq_concat_conv [simp]: "([] = concat xss) = (∀xs ∈ set xss. xs = [])"
by (induct xss) auto

lemma set_concat [simp]: "set (concat xs) = (UN x:set xs. set x)"
by (induct xs) auto

lemma concat_map_singleton[simp]: "concat(map (%x. [f x]) xs) = map f xs"
by (induct xs) auto

lemma map_concat: "map f (concat xs) = concat (map (map f) xs)"
by (induct xs) auto

lemma filter_concat: "filter p (concat xs) = concat (map (filter p) xs)"
by (induct xs) auto

lemma rev_concat: "rev (concat xs) = concat (map rev (rev xs))"
by (induct xs) auto

lemma concat_eq_concat_iff: "∀(x, y) ∈ set (zip xs ys). length x = length y ==> length xs = length ys ==> (concat xs = concat ys) = (xs = ys)"
proof (induct xs arbitrary: ys)
case (Cons x xs ys)
thus ?case by (cases ys) auto
qed (auto)

lemma concat_injective: "concat xs = concat ys ==> length xs = length ys ==> ∀(x, y) ∈ set (zip xs ys). length x = length y ==> xs = ys"

subsubsection ‹@{const nth}›

lemma nth_Cons_0 [simp, code]: "(x # xs)!0 = x"
by auto

lemma nth_Cons_Suc [simp, code]: "(x # xs)!(Suc n) = xs!n"
by auto

declare nth.simps [simp del]

lemma nth_Cons_pos[simp]: "0 < n ⟹ (x#xs) ! n = xs ! (n - 1)"
by(auto simp: Nat.gr0_conv_Suc)

lemma nth_append:
"(xs @ ys)!n = (if n < length xs then xs!n else ys!(n - length xs))"
apply (induct xs arbitrary: n, simp)
apply (case_tac n, auto)
done

lemma nth_append_length [simp]: "(xs @ x # ys) ! length xs = x"
by (induct xs) auto

lemma nth_append_length_plus[simp]: "(xs @ ys) ! (length xs + n) = ys ! n"
by (induct xs) auto

lemma nth_map [simp]: "n < length xs ==> (map f xs)!n = f(xs!n)"
apply (induct xs arbitrary: n, simp)
apply (case_tac n, auto)
done

lemma nth_tl: "n < length (tl xs) ⟹ tl xs ! n = xs ! Suc n"
by (induction xs) auto

lemma hd_conv_nth: "xs ≠ [] ⟹ hd xs = xs!0"
by(cases xs) simp_all

lemma list_eq_iff_nth_eq:
"(xs = ys) = (length xs = length ys ∧ (∀i<length xs. xs!i = ys!i))"
apply(induct xs arbitrary: ys)
apply force
apply(case_tac ys)
apply simp
done

lemma set_conv_nth: "set xs = {xs!i | i. i < length xs}"
apply (induct xs, simp, simp)
apply safe
apply (metis nat.case(1) nth.simps zero_less_Suc)
apply (metis less_Suc_eq_0_disj nth_Cons_Suc)
apply (case_tac i, simp)
apply (metis diff_Suc_Suc nat.case(2) nth.simps zero_less_diff)
done

lemma in_set_conv_nth: "(x ∈ set xs) = (∃i < length xs. xs!i = x)"
by(auto simp:set_conv_nth)

lemma nth_equal_first_eq:
assumes "x ∉ set xs"
assumes "n ≤ length xs"
shows "(x # xs) ! n = x ⟷ n = 0" (is "?lhs ⟷ ?rhs")
proof
assume ?lhs
show ?rhs
proof (rule ccontr)
assume "n ≠ 0"
then have "n > 0" by simp
with ‹?lhs› have "xs ! (n - 1) = x" by simp
moreover from ‹n > 0› ‹n ≤ length xs› have "n - 1 < length xs" by simp
ultimately have "∃i<length xs. xs ! i = x" by auto
with ‹x ∉ set xs› in_set_conv_nth [of x xs] show False by simp
qed
next
assume ?rhs then show ?lhs by simp
qed

lemma nth_non_equal_first_eq:
assumes "x ≠ y"
shows "(x # xs) ! n = y ⟷ xs ! (n - 1) = y ∧ n > 0" (is "?lhs ⟷ ?rhs")
proof
assume "?lhs" with assms have "n > 0" by (cases n) simp_all
with ‹?lhs› show ?rhs by simp
next
assume "?rhs" then show "?lhs" by simp
qed

lemma list_ball_nth: "⟦n < length xs; ∀x ∈ set xs. P x⟧ ⟹ P(xs!n)"

lemma nth_mem [simp]: "n < length xs ⟹ xs!n ∈ set xs"

lemma all_nth_imp_all_set:
"⟦∀i < length xs. P(xs!i); x ∈ set xs⟧ ⟹ P x"

lemma all_set_conv_all_nth:
"(∀x ∈ set xs. P x) = (∀i. i < length xs ⟶ P (xs ! i))"

lemma rev_nth:
"n < size xs ⟹ rev xs ! n = xs ! (length xs - Suc n)"
proof (induct xs arbitrary: n)
case Nil thus ?case by simp
next
case (Cons x xs)
hence n: "n < Suc (length xs)" by simp
moreover
{ assume "n < length xs"
with n obtain n' where n': "length xs - n = Suc n'"
by (cases "length xs - n", auto)
moreover
from n' have "length xs - Suc n = n'" by simp
ultimately
have "xs ! (length xs - Suc n) = (x # xs) ! (length xs - n)" by simp
}
ultimately
show ?case by (clarsimp simp add: Cons nth_append)
qed

lemma Skolem_list_nth:
"(∀i<k. ∃x. P i x) = (∃xs. size xs = k ∧ (∀i<k. P i (xs!i)))"
(is "_ = (∃xs. ?P k xs)")
proof(induct k)
case 0 show ?case by simp
next
case (Suc k)
show ?case (is "?L = ?R" is "_ = (∃xs. ?P' xs)")
proof
assume "?R" thus "?L" using Suc by auto
next
assume "?L"
with Suc obtain x xs where "?P k xs ∧ P k x" by (metis less_Suc_eq)
thus "?R" ..
qed
qed

subsubsection ‹@{const list_update}›

lemma length_list_update [simp]: "length(xs[i:=x]) = length xs"
by (induct xs arbitrary: i) (auto split: nat.split)

lemma nth_list_update:
"i < length xs==> (xs[i:=x])!j = (if i = j then x else xs!j)"
by (induct xs arbitrary: i j) (auto simp add: nth_Cons split: nat.split)

lemma nth_list_update_eq [simp]: "i < length xs ==> (xs[i:=x])!i = x"

lemma nth_list_update_neq [simp]: "i ≠ j ==> xs[i:=x]!j = xs!j"
by (induct xs arbitrary: i j) (auto simp add: nth_Cons split: nat.split)

lemma list_update_id[simp]: "xs[i := xs!i] = xs"
by (induct xs arbitrary: i) (simp_all split:nat.splits)

lemma list_update_beyond[simp]: "length xs ≤ i ⟹ xs[i:=x] = xs"
apply (induct xs arbitrary: i)
apply simp
apply (case_tac i)
apply simp_all
done

lemma list_update_nonempty[simp]: "xs[k:=x] = [] ⟷ xs=[]"
by (simp only: length_0_conv[symmetric] length_list_update)

lemma list_update_same_conv:
"i < length xs ==> (xs[i := x] = xs) = (xs!i = x)"
by (induct xs arbitrary: i) (auto split: nat.split)

lemma list_update_append1:
"i < size xs ⟹ (xs @ ys)[i:=x] = xs[i:=x] @ ys"
by (induct xs arbitrary: i)(auto split:nat.split)

lemma list_update_append:
"(xs @ ys) [n:= x] =
(if n < length xs then xs[n:= x] @ ys else xs @ (ys [n-length xs:= x]))"
by (induct xs arbitrary: n) (auto split:nat.splits)

lemma list_update_length [simp]:
"(xs @ x # ys)[length xs := y] = (xs @ y # ys)"
by (induct xs, auto)

lemma map_update: "map f (xs[k:= y]) = (map f xs)[k := f y]"
by(induct xs arbitrary: k)(auto split:nat.splits)

lemma rev_update:
"k < length xs ⟹ rev (xs[k:= y]) = (rev xs)[length xs - k - 1 := y]"
by (induct xs arbitrary: k) (auto simp: list_update_append split:nat.splits)

lemma update_zip:
"(zip xs ys)[i:=xy] = zip (xs[i:=fst xy]) (ys[i:=snd xy])"
by (induct ys arbitrary: i xy xs) (auto, case_tac xs, auto split: nat.split)

lemma set_update_subset_insert: "set(xs[i:=x]) <= insert x (set xs)"
by (induct xs arbitrary: i) (auto split: nat.split)

lemma set_update_subsetI: "⟦set xs ⊆ A; x ∈ A⟧ ⟹ set(xs[i := x]) ⊆ A"
by (blast dest!: set_update_subset_insert [THEN subsetD])

lemma set_update_memI: "n < length xs ⟹ x ∈ set (xs[n := x])"
by (induct xs arbitrary: n) (auto split:nat.splits)

lemma list_update_overwrite[simp]:
"xs [i := x, i := y] = xs [i := y]"
apply (induct xs arbitrary: i) apply simp
apply (case_tac i, simp_all)
done

lemma list_update_swap:
"i ≠ i' ⟹ xs [i := x, i' := x'] = xs [i' := x', i := x]"
apply (induct xs arbitrary: i i')
apply simp
apply (case_tac i, case_tac i')
apply auto
apply (case_tac i')
apply auto
done

lemma list_update_code [code]:
"[][i := y] = []"
"(x # xs)[0 := y] = y # xs"
"(x # xs)[Suc i := y] = x # xs[i := y]"
by simp_all

subsubsection ‹@{const last} and @{const butlast}›

lemma last_snoc [simp]: "last (xs @ [x]) = x"
by (induct xs) auto

lemma butlast_snoc [simp]: "butlast (xs @ [x]) = xs"
by (induct xs) auto

lemma last_ConsL: "xs = [] ⟹ last(x#xs) = x"
by simp

lemma last_ConsR: "xs ≠ [] ⟹ last(x#xs) = last xs"
by simp

lemma last_append: "last(xs @ ys) = (if ys = [] then last xs else last ys)"
by (induct xs) (auto)

lemma last_appendL[simp]: "ys = [] ⟹ last(xs @ ys) = last xs"

lemma last_appendR[simp]: "ys ≠ [] ⟹ last(xs @ ys) = last ys"

lemma last_tl: "xs = [] ∨ tl xs ≠ [] ⟹last (tl xs) = last xs"
by (induct xs) simp_all

lemma butlast_tl: "butlast (tl xs) = tl (butlast xs)"
by (induct xs) simp_all

lemma hd_rev: "xs ≠ [] ⟹ hd(rev xs) = last xs"
by(rule rev_exhaust[of xs]) simp_all

lemma last_rev: "xs ≠ [] ⟹ last(rev xs) = hd xs"
by(cases xs) simp_all

lemma last_in_set[simp]: "as ≠ [] ⟹ last as ∈ set as"
by (induct as) auto

lemma length_butlast [simp]: "length (butlast xs) = length xs - 1"
by (induct xs rule: rev_induct) auto

lemma butlast_append:
"butlast (xs @ ys) = (if ys = [] then butlast xs else xs @ butlast ys)"
by (induct xs arbitrary: ys) auto

lemma append_butlast_last_id [simp]:
"xs ≠ [] ⟹ butlast xs @ [last xs] = xs"
by (induct xs) auto

lemma in_set_butlastD: "x ∈ set (butlast xs) ⟹ x ∈ set xs"
by (induct xs) (auto split: if_split_asm)

lemma in_set_butlast_appendI:
"x ∈ set (butlast xs) ∨ x ∈ set (butlast ys) ⟹ x ∈ set (butlast (xs @ ys))"
by (auto dest: in_set_butlastD simp add: butlast_append)

lemma last_drop[simp]: "n < length xs ⟹ last (drop n xs) = last xs"
by (induct xs arbitrary: n)(auto split:nat.split)

lemma nth_butlast:
assumes "n < length (butlast xs)" shows "butlast xs ! n = xs ! n"
proof (cases xs)
case (Cons y ys)
moreover from assms have "butlast xs ! n = (butlast xs @ [last xs]) ! n"
ultimately show ?thesis using append_butlast_last_id by simp
qed simp

lemma last_conv_nth: "xs≠[] ⟹ last xs = xs!(length xs - 1)"
by(induct xs)(auto simp:neq_Nil_conv)

lemma butlast_conv_take: "butlast xs = take (length xs - 1) xs"
by (induction xs rule: induct_list012) simp_all

lemma last_list_update:
"xs ≠ [] ⟹ last(xs[k:=x]) = (if k = size xs - 1 then x else last xs)"
by (auto simp: last_conv_nth)

lemma butlast_list_update:
"butlast(xs[k:=x]) =
(if k = size xs - 1 then butlast xs else (butlast xs)[k:=x])"
by(cases xs rule:rev_cases)(auto simp: list_update_append split: nat.splits)

lemma last_map: "xs ≠ [] ⟹ last (map f xs) = f (last xs)"
by (cases xs rule: rev_cases) simp_all

lemma map_butlast: "map f (butlast xs) = butlast (map f xs)"
by (induct xs) simp_all

lemma snoc_eq_iff_butlast:
"xs @ [x] = ys ⟷ (ys ≠ [] ∧ butlast ys = xs ∧ last ys = x)"
by fastforce

corollary longest_common_suffix:
"∃ss xs' ys'. xs = xs' @ ss ∧ ys = ys' @ ss
∧ (xs' = [] ∨ ys' = [] ∨ last xs' ≠ last ys')"
using longest_common_prefix[of "rev xs" "rev ys"]
unfolding rev_swap rev_append by (metis last_rev rev_is_Nil_conv)

subsubsection ‹@{const take} and @{const drop}›

lemma take_0: "take 0 xs = []"
by (induct xs) auto

lemma drop_0: "drop 0 xs = xs"
by (induct xs) auto

lemma take0[simp]: "take 0 = (λxs. [])"
by(rule ext) (rule take_0)

lemma drop0[simp]: "drop 0 = (λx. x)"
by(rule ext) (rule drop_0)

lemma take_Suc_Cons [simp]: "take (Suc n) (x # xs) = x # take n xs"
by simp

lemma drop_Suc_Cons [simp]: "drop (Suc n) (x # xs) = drop n xs"
by simp

declare take_Cons [simp del] and drop_Cons [simp del]

lemma take_Suc: "xs ≠ [] ⟹ take (Suc n) xs = hd xs # take n (tl xs)"

lemma drop_Suc: "drop (Suc n) xs = drop n (tl xs)"
by(cases xs, simp_all)

lemma hd_take[simp]: "j > 0 ⟹ hd (take j xs) = hd xs"
by (metis gr0_conv_Suc list.sel(1) take.simps(1) take_Suc)

lemma take_tl: "take n (tl xs) = tl (take (Suc n) xs)"
by (induct xs arbitrary: n) simp_all

lemma drop_tl: "drop n (tl xs) = tl(drop n xs)"
by(induct xs arbitrary: n, simp_all add:drop_Cons drop_Suc split:nat.split)

lemma tl_take: "tl (take n xs) = take (n - 1) (tl xs)"
by (cases n, simp, cases xs, auto)

lemma tl_drop: "tl (drop n xs) = drop n (tl xs)"
by (simp only: drop_tl)

lemma nth_via_drop: "drop n xs = y#ys ⟹ xs!n = y"
by (induct xs arbitrary: n, simp)(auto simp: drop_Cons nth_Cons split: nat.splits)

lemma take_Suc_conv_app_nth:
"i < length xs ⟹ take (Suc i) xs = take i xs @ [xs!i]"
apply (induct xs arbitrary: i, simp)
apply (case_tac i, auto)
done

lemma Cons_nth_drop_Suc:
"i < length xs ⟹ (xs!i) # (drop (Suc i) xs) = drop i xs"
apply (induct xs arbitrary: i, simp)
apply (case_tac i, auto)
done

lemma length_take [simp]: "length (take n xs) = min (length xs) n"
by (induct n arbitrary: xs) (auto, case_tac xs, auto)

lemma length_drop [simp]: "length (drop n xs) = (length xs - n)"
by (induct n arbitrary: xs) (auto, case_tac xs, auto)

lemma take_all [simp]: "length xs <= n ==> take n xs = xs"
by (induct n arbitrary: xs) (auto, case_tac xs, auto)

lemma drop_all [simp]: "length xs <= n ==> drop n xs = []"
by (induct n arbitrary: xs) (auto, case_tac xs, auto)

lemma take_append [simp]:
"take n (xs @ ys) = (take n xs @ take (n - length xs) ys)"
by (induct n arbitrary: xs) (auto, case_tac xs, auto)

lemma drop_append [simp]:
"drop n (xs @ ys) = drop n xs @ drop (n - length xs) ys"
by (induct n arbitrary: xs) (auto, case_tac xs, auto)

lemma take_take [simp]: "take n (take m xs) = take (min n m) xs"
apply (induct m arbitrary: xs n, auto)
apply (case_tac xs, auto)
apply (case_tac n, auto)
done

lemma drop_drop [simp]: "drop n (drop m xs) = drop (n + m) xs"
apply (induct m arbitrary: xs, auto)
apply (case_tac xs, auto)
done

lemma take_drop: "take n (drop m xs) = drop m (take (n + m) xs)"
apply (induct m arbitrary: xs n, auto)
apply (case_tac xs, auto)
done

lemma drop_take: "drop n (take m xs) = take (m-n) (drop n xs)"
by(induct xs arbitrary: m n)(auto simp: take_Cons drop_Cons split: nat.split)

lemma append_take_drop_id [simp]: "take n xs @ drop n xs = xs"
apply (induct n arbitrary: xs, auto)
apply (case_tac xs, auto)
done

lemma take_eq_Nil[simp]: "(take n xs = []) = (n = 0 ∨ xs = [])"
by(induct xs arbitrary: n)(auto simp: take_Cons split:nat.split)

lemma drop_eq_Nil[simp]: "(drop n xs = []) = (length xs <= n)"
by (induct xs arbitrary: n) (auto simp: drop_Cons split:nat.split)

lemma take_map: "take n (map f xs) = map f (take n xs)"
apply (induct n arbitrary: xs, auto)
apply (case_tac xs, auto)
done

lemma drop_map: "drop n (map f xs) = map f (drop n xs)"
apply (induct n arbitrary: xs, auto)
apply (case_tac xs, auto)
done

lemma rev_take: "rev (take i xs) = drop (length xs - i) (rev xs)"
apply (induct xs arbitrary: i, auto)
apply (case_tac i, auto)
done

lemma rev_drop: "rev (drop i xs) = take (length xs - i) (rev xs)"
apply (induct xs arbitrary: i, auto)
apply (case_tac i, auto)
done

lemma drop_rev: "drop n (rev xs) = rev (take (length xs - n) xs)"
by (cases "length xs < n") (auto simp: rev_take)

lemma take_rev: "take n (rev xs) = rev (drop (length xs - n) xs)"
by (cases "length xs < n") (auto simp: rev_drop)

lemma nth_take [simp]: "i < n ==> (take n xs)!i = xs!i"
apply (induct xs arbitrary: i n, auto)
apply (case_tac n, blast)
apply (case_tac i, auto)
done

lemma nth_drop [simp]:
"n <= length xs ==> (drop n xs)!i = xs!(n + i)"
apply (induct n arbitrary: xs i, auto)
apply (case_tac xs, auto)
done

lemma butlast_take:
"n <= length xs ==> butlast (take n xs) = take (n - 1) xs"
by (simp add: butlast_conv_take min.absorb1 min.absorb2)

lemma butlast_drop: "butlast (drop n xs) = drop n (butlast xs)"
by (simp add: butlast_conv_take drop_take ac_simps)

lemma take_butlast: "n < length xs ==> take n (butlast xs) = take n xs"

lemma drop_butlast: "drop n (butlast xs) = butlast (drop n xs)"
by (simp add: butlast_conv_take drop_take ac_simps)

lemma hd_drop_conv_nth: "n < length xs ⟹ hd(drop n xs) = xs!n"

lemma set_take_subset_set_take:
"m <= n ⟹ set(take m xs) <= set(take n xs)"
apply (induct xs arbitrary: m n)
apply simp
apply (case_tac n)
apply (auto simp: take_Cons)
done

lemma set_take_subset: "set(take n xs) ⊆ set xs"
by(induct xs arbitrary: n)(auto simp:take_Cons split:nat.split)

lemma set_drop_subset: "set(drop n xs) ⊆ set xs"
by(induct xs arbitrary: n)(auto simp:drop_Cons split:nat.split)

lemma set_drop_subset_set_drop:
"m >= n ⟹ set(drop m xs) <= set(drop n xs)"
apply(induct xs arbitrary: m n)
apply(auto simp:drop_Cons split:nat.split)
by (metis set_drop_subset subset_iff)

lemma in_set_takeD: "x ∈ set(take n xs) ⟹ x ∈ set xs"
using set_take_subset by fast

lemma in_set_dropD: "x ∈ set(drop n xs) ⟹ x ∈ set xs"
using set_drop_subset by fast

lemma append_eq_conv_conj:
"(xs @ ys = zs) = (xs = take (length xs) zs ∧ ys = drop (length xs) zs)"
apply (induct xs arbitrary: zs, simp, clarsimp)
apply (case_tac zs, auto)
done

lemma take_add:  "take (i+j) xs = take i xs @ take j (drop i xs)"
apply (induct xs arbitrary: i, auto)
apply (case_tac i, simp_all)
done

lemma append_eq_append_conv_if:
"(xs⇩1 @ xs⇩2 = ys⇩1 @ ys⇩2) =
(if size xs⇩1 ≤ size ys⇩1
then xs⇩1 = take (size xs⇩1) ys⇩1 ∧ xs⇩2 = drop (size xs⇩1) ys⇩1 @ ys⇩2
else take (size ys⇩1) xs⇩1 = ys⇩1 ∧ drop (size ys⇩1) xs⇩1 @ xs⇩2 = ys⇩2)"
apply(induct xs⇩1 arbitrary: ys⇩1)
apply simp
apply(case_tac ys⇩1)
apply simp_all
done

lemma take_hd_drop:
"n < length xs ⟹ take n xs @ [hd (drop n xs)] = take (Suc n) xs"
apply(induct xs arbitrary: n)
apply simp
done

lemma id_take_nth_drop:
"i < length xs ⟹ xs = take i xs @ xs!i # drop (Suc i) xs"
proof -
assume si: "i < length xs"
hence "xs = take (Suc i) xs @ drop (Suc i) xs" by auto
moreover
from si have "take (Suc i) xs = take i xs @ [xs!i]"
apply (rule_tac take_Suc_conv_app_nth) by arith
ultimately show ?thesis by auto
qed

lemma take_update_cancel[simp]: "n ≤ m ⟹ take n (xs[m := y]) = take n xs"

lemma drop_update_cancel[simp]: "n < m ⟹ drop m (xs[n := x]) = drop m xs"

lemma upd_conv_take_nth_drop:
"i < length xs ⟹ xs[i:=a] = take i xs @ a # drop (Suc i) xs"
proof -
assume i: "i < length xs"
have "xs[i:=a] = (take i xs @ xs!i # drop (Suc i) xs)[i:=a]"
by(rule arg_cong[OF id_take_nth_drop[OF i]])
also have "… = take i xs @ a # drop (Suc i) xs"
using i by (simp add: list_update_append)
finally show ?thesis .
qed

lemma take_update_swap: "take m (xs[n := x]) = (take m xs)[n := x]"
apply(cases "n ≥ length xs")
apply simp
apply(simp add: upd_conv_take_nth_drop take_Cons drop_take min_def diff_Suc
split: nat.split)
done

lemma drop_update_swap: "m ≤ n ⟹ drop m (xs[n := x]) = (drop m xs)[n-m := x]"
apply(cases "n ≥ length xs")
apply simp
done

lemma nth_image: "l ≤ size xs ⟹ nth xs  {0..<l} = set(take l xs)"
by(auto simp: set_conv_nth image_def) (metis Suc_le_eq nth_take order_trans)

subsubsection ‹@{const takeWhile} and @{const dropWhile}›

lemma length_takeWhile_le: "length (takeWhile P xs) ≤ length xs"
by (induct xs) auto

lemma takeWhile_dropWhile_id [simp]: "takeWhile P xs @ dropWhile P xs = xs"
by (induct xs) auto

lemma takeWhile_append1 [simp]:
"⟦x ∈ set xs; ¬P(x)⟧ ⟹ takeWhile P (xs @ ys) = takeWhile P xs"
by (induct xs) auto

lemma takeWhile_append2 [simp]:
"(⋀x. x ∈ set xs ⟹ P x) ⟹ takeWhile P (xs @ ys) = xs @ takeWhile P ys"
by (induct xs) auto

lemma takeWhile_tail: "¬ P x ⟹ takeWhile P (xs @ (x#l)) = takeWhile P xs"
by (induct xs) auto

lemma takeWhile_nth: "j < length (takeWhile P xs) ⟹ takeWhile P xs ! j = xs ! j"
apply (subst (3) takeWhile_dropWhile_id[symmetric]) unfolding nth_append by auto

lemma dropWhile_nth: "j < length (dropWhile P xs) ⟹
dropWhile P xs ! j = xs ! (j + length (takeWhile P xs))"
apply (subst (3) takeWhile_dropWhile_id[symmetric]) unfolding nth_append by auto

lemma length_dropWhile_le: "length (dropWhile P xs) ≤ length xs"
by (induct xs) auto

lemma dropWhile_append1 [simp]:
"⟦x ∈ set xs; ¬P(x)⟧ ⟹ dropWhile P (xs @ ys) = (dropWhile P xs)@ys"
by (induct xs) auto

lemma dropWhile_append2 [simp]:
"(⋀x. x ∈ set xs ⟹ P(x)) ==> dropWhile P (xs @ ys) = dropWhile P ys"
by (induct xs) auto

lemma dropWhile_append3:
"¬ P y ⟹dropWhile P (xs @ y # ys) = dropWhile P xs @ y # ys"
by (induct xs) auto

lemma dropWhile_last:
"x ∈ set xs ⟹ ¬ P x ⟹ last (dropWhile P xs) = last xs"
by (auto simp add: dropWhile_append3 in_set_conv_decomp)

lemma set_dropWhileD: "x ∈ set (dropWhile P xs) ⟹ x ∈ set xs"
by (induct xs) (auto split: if_split_asm)

lemma set_takeWhileD: "x ∈ set (takeWhile P xs) ⟹ x ∈ set xs ∧ P x"
by (induct xs) (auto split: if_split_asm)

lemma takeWhile_eq_all_conv[simp]:
"(takeWhile P xs = xs) = (∀x ∈ set xs. P x)"
by(induct xs, auto)

lemma dropWhile_eq_Nil_conv[simp]:
"(dropWhile P xs = []) = (∀x ∈ set xs. P x)"
by(induct xs, auto)

lemma dropWhile_eq_Cons_conv:
"(dropWhile P xs = y#ys) = (xs = takeWhile P xs @ y # ys ∧ ¬ P y)"
by(induct xs, auto)

lemma distinct_takeWhile[simp]: "distinct xs ==> distinct (takeWhile P xs)"
by (induct xs) (auto dest: set_takeWhileD)

lemma distinct_dropWhile[simp]: "distinct xs ==> distinct (dropWhile P xs)"
by (induct xs) auto

lemma takeWhile_map: "takeWhile P (map f xs) = map f (takeWhile (P ∘ f) xs)"
by (induct xs) auto

lemma dropWhile_map: "dropWhile P (map f xs) = map f (dropWhile (P ∘ f) xs)"
by (induct xs) auto

lemma takeWhile_eq_take: "takeWhile P xs = take (length (takeWhile P xs)) xs"
by (induct xs) auto

lemma dropWhile_eq_drop: "dropWhile P xs = drop (length (takeWhile P xs)) xs"
by (induct xs) auto

lemma hd_dropWhile: "dropWhile P xs ≠ [] ⟹ ¬ P (hd (dropWhile P xs))"
by (induct xs) auto

lemma takeWhile_eq_filter:
assumes "⋀ x. x ∈ set (dropWhile P xs) ⟹ ¬ P x"
shows "takeWhile P xs = filter P xs"
proof -
have A: "filter P xs = filter P (takeWhile P xs @ dropWhile P xs)"
by simp
have B: "filter P (dropWhile P xs) = []"
unfolding filter_empty_conv using assms by blast
have "filter P xs = takeWhile P xs"
unfolding A filter_append B
by (auto simp add: filter_id_conv dest: set_takeWhileD)
thus ?thesis ..
qed

lemma takeWhile_eq_take_P_nth:
"⟦ ⋀ i. ⟦ i < n ; i < length xs ⟧ ⟹ P (xs ! i) ; n < length xs ⟹ ¬ P (xs ! n) ⟧ ⟹
takeWhile P xs = take n xs"
proof (induct xs arbitrary: n)
case Nil
thus ?case by simp
next
case (Cons x xs)
show ?case
proof (cases n)
case 0
with Cons show ?thesis by simp
next
case [simp]: (Suc n')
have "P x" using Cons.prems(1)[of 0] by simp
moreover have "takeWhile P xs = take n' xs"
proof (rule Cons.hyps)
fix i
assume "i < n'" "i < length xs"
thus "P (xs ! i)" using Cons.prems(1)[of "Suc i"] by simp
next
assume "n' < length xs"
thus "¬ P (xs ! n')" using Cons by auto
qed
ultimately show ?thesis by simp
qed
qed

lemma nth_length_takeWhile:
"length (takeWhile P xs) < length xs ⟹ ¬ P (xs ! length (takeWhile P xs))"
by (induct xs) auto

lemma length_takeWhile_less_P_nth:
assumes all: "⋀ i. i < j ⟹ P (xs ! i)" and "j ≤ length xs"
shows "j ≤ length (takeWhile P xs)"
proof (rule classical)
assume "¬ ?thesis"
hence "length (takeWhile P xs) < length xs" using assms by simp
thus ?thesis using all ‹¬ ?thesis› nth_length_takeWhile[of P xs] by auto
qed

lemma takeWhile_neq_rev: "⟦distinct xs; x ∈ set xs⟧ ⟹
takeWhile (λy. y ≠ x) (rev xs) = rev (tl (dropWhile (λy. y ≠ x) xs))"
by(induct xs) (auto simp: takeWhile_tail[where l="[]"])

lemma dropWhile_neq_rev: "⟦distinct xs; x ∈ set xs⟧ ⟹
dropWhile (λy. y ≠ x) (rev xs) = x # rev (takeWhile (λy. y ≠ x) xs)"
apply(induct xs)
apply simp
apply auto
apply(subst dropWhile_append2)
apply auto
done

lemma takeWhile_not_last:
"distinct xs ⟹ takeWhile (λy. y ≠ last xs) xs = butlast xs"
by(induction xs rule: induct_list012) auto

lemma takeWhile_cong [fundef_cong]:
"⟦l = k; ⋀x. x ∈ set l ⟹ P x = Q x⟧
⟹ takeWhile P l = takeWhile Q k"
by (induct k arbitrary: l) (simp_all)

lemma dropWhile_cong [fundef_cong]:
"⟦l = k; ⋀x. x ∈ set l ⟹ P x = Q x⟧
⟹ dropWhile P l = dropWhile Q k"
by (induct k arbitrary: l, simp_all)

lemma takeWhile_idem [simp]:
"takeWhile P (takeWhile P xs) = takeWhile P xs"
by (induct xs) auto

lemma dropWhile_idem [simp]:
"dropWhile P (dropWhile P xs) = dropWhile P xs"
by (induct xs) auto

subsubsection ‹@{const zip}›

lemma zip_Nil [simp]: "zip [] ys = []"
by (induct ys) auto

lemma zip_Cons_Cons [simp]: "zip (x # xs) (y # ys) = (x, y) # zip xs ys"
by simp

declare zip_Cons [simp del]

lemma [code]:
"zip [] ys = []"
"zip xs [] = []"
"zip (x # xs) (y # ys) = (x, y) # zip xs ys"
by (fact zip_Nil zip.simps(1) zip_Cons_Cons)+

lemma zip_Cons1:
"zip (x#xs) ys = (case ys of [] ⇒ [] | y#ys ⇒ (x,y)#zip xs ys)"
by(auto split:list.split)

lemma length_zip [simp]:
"length (zip xs ys) = min (length xs) (length ys)"
by (induct xs ys rule:list_induct2') auto

lemma zip_obtain_same_length:
assumes "⋀zs ws n. length zs = length ws ⟹ n = min (length xs) (length ys)
⟹ zs = take n xs ⟹ ws = take n ys ⟹ P (zip zs ws)"
shows "P (zip xs ys)"
proof -
let ?n = "min (length xs) (length ys)"
have "P (zip (take ?n xs) (take ?n ys))"
by (rule assms) simp_all
moreover have "zip xs ys = zip (take ?n xs) (take ?n ys)"
proof (induct xs arbitrary: ys)
case Nil then show ?case by simp
next
case (Cons x xs) then show ?case by (cases ys) simp_all
qed
ultimately show ?thesis by simp
qed

lemma zip_append1:
"zip (xs @ ys) zs =
zip xs (take (length xs) zs) @ zip ys (drop (length xs) zs)"
by (induct xs zs rule:list_induct2') auto

lemma zip_append2:
"zip xs (ys @ zs) =
zip (take (length ys) xs) ys @ zip (drop (length ys) xs) zs"
by (induct xs ys rule:list_induct2') auto

lemma zip_append [simp]:
"[| length xs = length us |] ==>
zip (xs@ys) (us@vs) = zip xs us @ zip ys vs"

lemma zip_rev:
"length xs = length ys ==> zip (rev xs) (rev ys) = rev (zip xs ys)"
by (induct rule:list_induct2, simp_all)

lemma zip_map_map:
"zip (map f xs) (map g ys) = map (λ (x, y). (f x, g y)) (zip xs ys)"
proof (induct xs arbitrary: ys)
case (Cons x xs) note Cons_x_xs = Cons.hyps
show ?case
proof (cases ys)
case (Cons y ys')
show ?thesis unfolding Cons using Cons_x_xs by simp
qed simp
qed simp

lemma zip_map1:
"zip (map f xs) ys = map (λ(x, y). (f x, y)) (zip xs ys)"
using zip_map_map[of f xs "λx. x" ys] by simp

lemma zip_map2:
"zip xs (map f ys) = map (λ(x, y). (x, f y)) (zip xs ys)"
using zip_map_map[of "λx. x" xs f ys] by simp

lemma map_zip_map:
"map f (zip (map g xs) ys) = map (%(x,y). f(g x, y)) (zip xs ys)"
by (auto simp: zip_map1)

lemma map_zip_map2:
"map f (zip xs (map g ys)) = map (%(x,y). f(x, g y)) (zip xs ys)"
by (auto simp: zip_map2)

text‹Courtesy of Andreas Lochbihler:›
lemma zip_same_conv_map: "zip xs xs = map (λx. (x, x)) xs"
by(induct xs) auto

lemma nth_zip [simp]:
"[| i < length xs; i < length ys|] ==> (zip xs ys)!i = (xs!i, ys!i)"
apply (induct ys arbitrary: i xs, simp)
apply (case_tac xs)
apply (simp_all add: nth.simps split: nat.split)
done

lemma set_zip:
"set (zip xs ys) = {(xs!i, ys!i) | i. i < min (length xs) (length ys)}"

lemma zip_same: "((a,b) ∈ set (zip xs xs)) = (a ∈ set xs ∧ a = b)"
by(induct xs) auto

lemma zip_update:
"zip (xs[i:=x]) (ys[i:=y]) = (zip xs ys)[i:=(x,y)]"

lemma zip_replicate [simp]:
"zip (replicate i x) (replicate j y) = replicate (min i j) (x,y)"
apply (induct i arbitrary: j, auto)
apply (case_tac j, auto)
done

lemma zip_replicate1: "zip (replicate n x) ys = map (Pair x) (take n ys)"
by(induction ys arbitrary: n)(case_tac [2] n, simp_all)

lemma take_zip:
"take n (zip xs ys) = zip (take n xs) (take n ys)"
apply (induct n arbitrary: xs ys)
apply simp
apply (case_tac xs, simp)
apply (case_tac ys, simp_all)
done

lemma drop_zip:
"drop n (zip xs ys) = zip (drop n xs) (drop n ys)"
apply (induct n arbitrary: xs ys)
apply simp
apply (case_tac xs, simp)
apply (case_tac ys, simp_all)
done

lemma zip_takeWhile_fst: "zip (takeWhile P xs) ys = takeWhile (P ∘ fst) (zip xs ys)"
proof (induct xs arbitrary: ys)
case (Cons x xs) thus ?case by (cases ys) auto
qed simp

lemma zip_takeWhile_snd: "zip xs (takeWhile P ys) = takeWhile (P ∘ snd) (zip xs ys)"
proof (induct xs arbitrary: ys)
case (Cons x xs) thus ?case by (cases ys) auto
qed simp

lemma set_zip_leftD: "(x,y)∈ set (zip xs ys) ⟹ x ∈ set xs"
by (induct xs ys rule:list_induct2') auto

lemma set_zip_rightD: "(x,y)∈ set (zip xs ys) ⟹ y ∈ set ys"
by (induct xs ys rule:list_induct2') auto

lemma in_set_zipE:
"(x,y) ∈ set(zip xs ys) ⟹ (⟦ x ∈ set xs; y ∈ set ys ⟧ ⟹ R) ⟹ R"
by(blast dest: set_zip_leftD set_zip_rightD)

lemma zip_map_fst_snd: "zip (map fst zs) (map snd zs) = zs"
by (induct zs) simp_all

lemma zip_eq_conv:
"length xs = length ys ⟹ zip xs ys = zs ⟷ map fst zs = xs ∧ map snd zs = ys"

lemma in_set_zip:
"p ∈ set (zip xs ys) ⟷ (∃n. xs ! n = fst p ∧ ys ! n = snd p
∧ n < length xs ∧ n < length ys)"
by (cases p) (auto simp add: set_zip)

lemma in_set_impl_in_set_zip1:
assumes "length xs = length ys"
assumes "x ∈ set xs"
obtains y where "(x, y) ∈ set (zip xs ys)"
proof -
from assms have "x ∈ set (map fst (zip xs ys))" by simp
from this that show ?thesis by fastforce
qed

lemma in_set_impl_in_set_zip2:
assumes "length xs = length ys"
assumes "y ∈ set ys"
obtains x where "(x, y) ∈ set (zip xs ys)"
proof -
from assms have "y ∈ set (map snd (zip xs ys))" by simp
from this that show ?thesis by fastforce
qed

lemma pair_list_eqI:
assumes "map fst xs = map fst ys" and "map snd xs = map snd ys"
shows "xs = ys"
proof -
from assms(1) have "length xs = length ys" by (rule map_eq_imp_length_eq)
from this assms show ?thesis
by (induct xs ys rule: list_induct2) (simp_all add: prod_eqI)
qed

subsubsection ‹@{const list_all2}›

lemma list_all2_lengthD [intro?]:
"list_all2 P xs ys ==> length xs = length ys"

lemma list_all2_Nil [iff, code]: "list_all2 P [] ys = (ys = [])"

lemma list_all2_Nil2 [iff, code]: "list_all2 P xs [] = (xs = [])"

lemma list_all2_Cons [iff, code]:
"list_all2 P (x # xs) (y # ys) = (P x y ∧ list_all2 P xs ys)"

lemma list_all2_Cons1:
"list_all2 P (x # xs) ys = (∃z zs. ys = z # zs ∧ P x z ∧ list_all2 P xs zs)"
by (cases ys) auto

lemma list_all2_Cons2:
"list_all2 P xs (y # ys) = (∃z zs. xs = z # zs ∧ P z y ∧ list_all2 P zs ys)"
by (cases xs) auto

lemma list_all2_induct
[consumes 1, case_names Nil Cons, induct set: list_all2]:
assumes P: "list_all2 P xs ys"
assumes Nil: "R [] []"
assumes Cons: "⋀x xs y ys.
⟦P x y; list_all2 P xs ys; R xs ys⟧ ⟹ R (x # xs) (y # ys)"
shows "R xs ys"
using P
by (induct xs arbitrary: ys) (auto simp add: list_all2_Cons1 Nil Cons)

lemma list_all2_rev [iff]:
"list_all2 P (rev xs) (rev ys) = list_all2 P xs ys"
by (simp add: list_all2_iff zip_rev cong: conj_cong)

lemma list_all2_rev1:
"list_all2 P (rev xs) ys = list_all2 P xs (rev ys)"
by (subst list_all2_rev [symmetric]) simp

lemma list_all2_append1:
"list_all2 P (xs @ ys) zs =
(∃us vs. zs = us @ vs ∧ length us = length xs ∧ length vs = length ys ∧
list_all2 P xs us ∧ list_all2 P ys vs)"
apply (rule iffI)
apply (rule_tac x = "take (length xs) zs" in exI)
apply (rule_tac x = "drop (length xs) zs" in exI)
apply (force split: nat_diff_split simp add: min_def, clarify)
done

lemma list_all2_append2:
"list_all2 P xs (ys @ zs) =
(∃us vs. xs = us @ vs ∧ length us = length ys ∧ length vs = length zs ∧
list_all2 P us ys ∧ list_all2 P vs zs)"
apply (rule iffI)
apply (rule_tac x = "take (length ys) xs" in exI)
apply (rule_tac x = "drop (length ys) xs" in exI)
apply (force split: nat_diff_split simp add: min_def, clarify)
done

lemma list_all2_append:
"length xs = length ys ⟹
list_all2 P (xs@us) (ys@vs) = (list_all2 P xs ys ∧ list_all2 P us vs)"
by (induct rule:list_induct2, simp_all)

lemma list_all2_appendI [intro?, trans]:
"⟦ list_all2 P a b; list_all2 P c d ⟧ ⟹ list_all2 P (a@c) (b@d)"

lemma list_all2_conv_all_nth:
"list_all2 P xs ys =
(length xs = length ys ∧ (∀i < length xs. P (xs!i) (ys!i)))"
by (force simp add: list_all2_iff set_zip)

lemma list_all2_trans:
assumes tr: "!!a b c. P1 a b ==> P2 b c ==> P3 a c"
shows "!!bs cs. list_all2 P1 as bs ==> list_all2 P2 bs cs ==> list_all2 P3 as cs"
(is "!!bs cs. PROP ?Q as bs cs")
proof (induct as)
fix x xs bs assume I1: "!!bs cs. PROP ?Q xs bs cs"
show "!!cs. PROP ?Q (x # xs) bs cs"
proof (induct bs)
fix y ys cs assume I2: "!!cs. PROP ?Q (x # xs) ys cs"
show "PROP ?Q (x # xs) (y # ys) cs"
by (induct cs) (auto intro: tr I1 I2)
qed simp
qed simp

lemma list_all2_all_nthI [intro?]:
"length a = length b ⟹ (⋀n. n < length a ⟹ P (a!n) (b!n)) ⟹ list_all2 P a b"

lemma list_all2I:
"∀x ∈ set (zip a b). case_prod P x ⟹ length a = length b ⟹ list_all2 P a b"

lemma list_all2_nthD:
"⟦ list_all2 P xs ys; p < size xs ⟧ ⟹ P (xs!p) (ys!p)"

lemma list_all2_nthD2:
"⟦list_all2 P xs ys; p < size ys⟧ ⟹ P (xs!p) (ys!p)"
by (frule list_all2_lengthD) (auto intro: list_all2_nthD)

lemma list_all2_map1:
"list_all2 P (map f as) bs = list_all2 (λx y. P (f x) y) as bs"

lemma list_all2_map2:
"list_all2 P as (map f bs) = list_all2 (λx y. P x (f y)) as bs"

lemma list_all2_refl [intro?]:
"(⋀x. P x x) ⟹ list_all2 P xs xs"

lemma list_all2_update_cong:
"⟦ list_all2 P xs ys; P x y ⟧ ⟹ list_all2 P (xs[i:=x]) (ys[i:=y])"
by (cases "i < length ys") (auto simp add: list_all2_conv_all_nth nth_list_update)

lemma list_all2_takeI [simp,intro?]:
"list_all2 P xs ys ⟹ list_all2 P (take n xs) (take n ys)"
apply (induct xs arbitrary: n ys)
apply simp
apply (case_tac n)
apply auto
done

lemma list_all2_dropI [simp,intro?]:
"list_all2 P as bs ⟹ list_all2 P (drop n as) (drop n bs)"
apply (induct as arbitrary: n bs, simp)
apply (case_tac n, simp, simp)
done

lemma list_all2_mono [intro?]:
"list_all2 P xs ys ⟹ (⋀xs ys. P xs ys ⟹ Q xs ys) ⟹ list_all2 Q xs ys"
apply (induct xs arbitrary: ys, simp)
apply (case_tac ys, auto)
done

lemma list_all2_eq:
"xs = ys ⟷ list_all2 (=) xs ys"
by (induct xs ys rule: list_induct2') auto

lemma list_eq_iff_zip_eq:
"xs = ys ⟷ length xs = length ys ∧ (∀(x,y) ∈ set (zip xs ys). x = y)"
by(auto simp add: set_zip list_all2_eq list_all2_conv_all_nth cong: conj_cong)

lemma list_all2_same: "list_all2 P xs xs ⟷ (∀x∈set xs. P x x)"

lemma zip_assoc:
"zip xs (zip ys zs) = map (λ((x, y), z). (x, y, z)) (zip (zip xs ys) zs)"
by(rule list_all2_all_nthI[where P="(=)", unfolded list.rel_eq]) simp_all

lemma zip_commute: "zip xs ys = map (λ(x, y). (y, x)) (zip ys xs)"
by(rule list_all2_all_nthI[where P="(=)", unfolded list.rel_eq]) simp_all

lemma zip_left_commute:
"zip xs (zip ys zs) = map (λ(y, (x, z)). (x, y, z)) (zip ys (zip xs zs))"
by(rule list_all2_all_nthI[where P="(=)", unfolded list.rel_eq]) simp_all

lemma zip_replicate2: "zip xs (replicate n y) = map (λx. (x, y)) (take n xs)"

subsubsection ‹@{const List.product} and @{const product_lists}›

lemma product_concat_map:
"List.product xs ys = concat (map (λx. map (λy. (x,y)) ys) xs)"
by(induction xs) (simp)+

lemma set_product[simp]: "set (List.product xs ys) = set xs × set ys"
by (induct xs) auto

lemma length_product [simp]:
"length (List.product xs ys) = length xs * length ys"
by (induct xs) simp_all

lemma product_nth:
assumes "n < length xs * length ys"
shows "List.product xs ys ! n = (xs ! (n div length ys), ys ! (n mod length ys))"
using assms proof (induct xs arbitrary: n)
case Nil then show ?case by simp
next
case (Cons x xs n)
then have "length ys > 0" by auto
with Cons show ?case
by (auto simp add: nth_append not_less le_mod_geq le_div_geq)
qed

lemma in_set_product_lists_length:
"xs ∈ set (product_lists xss) ⟹ length xs = length xss"
by (induct xss arbitrary: xs) auto

lemma product_lists_set:
"set (product_lists xss) = {xs. list_all2 (λx ys. x ∈ set ys) xs xss}" (is "?L = Collect ?R")
proof (intro equalityI subsetI, unfold mem_Collect_eq)
fix xs assume "xs ∈ ?L"
then have "length xs = length xss" by (rule in_set_product_lists_length)
from this ‹xs ∈ ?L› show "?R xs" by (induct xs xss rule: list_induct2) auto
next
fix xs assume "?R xs"
then show "xs ∈ ?L" by induct auto
qed

subsubsection ‹@{const fold} with natural argument order›

lemma fold_simps [code]: ― ‹eta-expanded variant for generated code -- enables tail-recursion optimisation in Scala›
"fold f [] s = s"
"fold f (x # xs) s = fold f xs (f x s)"
by simp_all

lemma fold_remove1_split:
"⟦ ⋀x y. x ∈ set xs ⟹ y ∈ set xs ⟹ f x ∘ f y = f y ∘ f x;
x ∈ set xs ⟧
⟹ fold f xs = fold f (remove1 x xs) ∘ f x"
by (induct xs) (auto simp add: comp_assoc)

lemma fold_cong [fundef_cong]:
"a = b ⟹ xs = ys ⟹ (⋀x. x ∈ set xs ⟹ f x = g x)
⟹ fold f xs a = fold g ys b"
by (induct ys arbitrary: a b xs) simp_all

lemma fold_id: "(⋀x. x ∈ set xs ⟹ f x = id) ⟹ fold f xs = id"
by (induct xs) simp_all

lemma fold_commute:
"(⋀x. x ∈ set xs ⟹ h ∘ g x = f x ∘ h) ⟹ h ∘ fold g xs = fold f xs ∘ h"
by (induct xs) (simp_all add: fun_eq_iff)

lemma fold_commute_apply:
assumes "⋀x. x ∈ set xs ⟹ h ∘ g x = f x ∘ h"
shows "h (fold g xs s) = fold f xs (h s)"
proof -
from assms have "h ∘ fold g xs = fold f xs ∘ h" by (rule fold_commute)
then show ?thesis by (simp add: fun_eq_iff)
qed

lemma fold_invariant:
"⟦ ⋀x. x ∈ set xs ⟹ Q x;  P s;  ⋀x s. Q x ⟹ P s ⟹ P (f x s) ⟧
⟹ P (fold f xs s)"
by (induct xs arbitrary: s) simp_all

lemma fold_append [simp]: "fold f (xs @ ys) = fold f ys ∘ fold f xs"
by (induct xs) simp_all

lemma fold_map [code_unfold]: "fold g (map f xs) = fold (g ∘ f) xs"
by (induct xs) simp_all

lemma fold_filter:
"fold f (filter P xs) = fold (λx. if P x then f x else id) xs"
by (induct xs) simp_all

lemma fold_rev:
"(⋀x y. x ∈ set xs ⟹ y ∈ set xs ⟹ f y ∘ f x = f x ∘ f y)
⟹ fold f (rev xs) = fold f xs"
by (induct xs) (simp_all add: fold_commute_apply fun_eq_iff)

lemma fold_Cons_rev: "fold Cons xs = append (rev xs)"
by (induct xs) simp_all

lemma rev_conv_fold [code]: "rev xs = fold Cons xs []"

lemma fold_append_concat_rev: "fold append xss = append (concat (rev xss))"
by (induct xss) simp_all

text ‹@{const Finite_Set.fold} and @{const fold}›

lemma (in comp_fun_commute) fold_set_fold_remdups:
"Finite_Set.fold f y (set xs) = fold f (remdups xs) y"
by (rule sym, induct xs arbitrary: y) (simp_all add: fold_fun_left_comm insert_absorb)

lemma (in comp_fun_idem) fold_set_fold:
"Finite_Set.fold f y (set xs) = fold f xs y"
by (rule sym, induct xs arbitrary: y) (simp_all add: fold_fun_left_comm)

lemma union_set_fold [code]: "set xs ∪ A = fold Set.insert xs A"
proof -
interpret comp_fun_idem Set.insert
by (fact comp_fun_idem_insert)
show ?thesis by (simp add: union_fold_insert fold_set_fold)
qed

lemma union_coset_filter [code]:
"List.coset xs ∪ A = List.coset (List.filter (λx. x ∉ A) xs)"
by auto

lemma minus_set_fold [code]: "A - set xs = fold Set.remove xs A"
proof -
interpret comp_fun_idem Set.remove
by (fact comp_fun_idem_remove)
show ?thesis
by (simp add: minus_fold_remove [of _ A] fold_set_fold)
qed

lemma minus_coset_filter [code]:
"A - List.coset xs = set (List.filter (λx. x ∈ A) xs)"
by auto

lemma inter_set_filter [code]:
"A ∩ set xs = set (List.filter (λx. x ∈ A) xs)"
by auto

lemma inter_coset_fold [code]:
"A ∩ List.coset xs = fold Set.remove xs A"
by (simp add: Diff_eq [symmetric] minus_set_fold)

lemma (in semilattice_set) set_eq_fold [code]:
"F (set (x # xs)) = fold f xs x"
proof -
interpret comp_fun_idem f
by standard (simp_all add: fun_eq_iff left_commute)
show ?thesis by (simp add: eq_fold fold_set_fold)
qed

lemma (in complete_lattice) Inf_set_fold:
"Inf (set xs) = fold inf xs top"
proof -
interpret comp_fun_idem "inf :: 'a ⇒ 'a ⇒ 'a"
by (fact comp_fun_idem_inf)
show ?thesis by (simp add: Inf_fold_inf fold_set_fold inf_commute)
qed

declare Inf_set_fold [where 'a = "'a set", code]

lemma (in complete_lattice) Sup_set_fold:
"Sup (set xs) = fold sup xs bot"
proof -
interpret comp_fun_idem "sup :: 'a ⇒ 'a ⇒ 'a"
by (fact comp_fun_idem_sup)
show ?thesis by (simp add: Sup_fold_sup fold_set_fold sup_commute)
qed

declare Sup_set_fold [where 'a = "'a set", code]

lemma (in complete_lattice) INF_set_fold:
"INFIMUM (set xs) f = fold (inf ∘ f) xs top"
using Inf_set_fold [of "map f xs "] by (simp add: fold_map)

declare INF_set_fold [code]

lemma (in complete_lattice) SUP_set_fold:
"SUPREMUM (set xs) f = fold (sup ∘ f) xs bot"
using Sup_set_fold [of "map f xs "] by (simp add: fold_map)

declare SUP_set_fold [code]

subsubsection ‹Fold variants: @{const foldr} and @{const foldl}›

text ‹Correspondence›

lemma foldr_conv_fold [code_abbrev]: "foldr f xs = fold f (rev xs)"
by (induct xs) simp_all

lemma foldl_conv_fold: "foldl f s xs = fold (λx s. f s x) xs s"
by (induct xs arbitrary: s) simp_all

lemma foldr_conv_foldl: ― ‹The Third Duality Theorem'' in Bird \& Wadler:›
"foldr f xs a = foldl (λx y. f y x) a (rev xs)"

lemma foldl_conv_foldr:
"foldl f a xs = foldr (λx y. f y x) (rev xs) a"

lemma foldr_fold:
"(⋀x y. x ∈ set xs ⟹ y ∈ set xs ⟹ f y ∘ f x = f x ∘ f y)
⟹ foldr f xs = fold f xs"
unfolding foldr_conv_fold by (rule fold_rev)

lemma foldr_cong [fundef_cong]:
"a = b ⟹ l = k ⟹ (⋀a x. x ∈ set l ⟹ f x a = g x a) ⟹ foldr f l a = foldr g k b"
by (auto simp add: foldr_conv_fold intro!: fold_cong)

lemma foldl_cong [fundef_cong]:
"a = b ⟹ l = k ⟹ (⋀a x. x ∈ set l ⟹ f a x = g a x) ⟹ foldl f a l = foldl g b k"
by (auto simp add: foldl_conv_fold intro!: fold_cong)

lemma foldr_append [simp]: "foldr f (xs @ ys) a = foldr f xs (foldr f ys a)"

lemma foldl_append [simp]: "foldl f a (xs @ ys) = foldl f (foldl f a xs) ys"

lemma foldr_map [code_unfold]: "foldr g (map f xs) a = foldr (g ∘ f) xs a"
by (simp add: foldr_conv_fold fold_map rev_map)

lemma foldr_filter:
"foldr f (filter P xs) = foldr (λx. if P x then f x else id) xs"
by (simp add: foldr_conv_fold rev_filter fold_filter)

lemma foldl_map [code_unfold]:
"foldl g a (map f xs) = foldl (λa x. g a (f x)) a xs"
by (simp add: foldl_conv_fold fold_map comp_def)

lemma concat_conv_foldr [code]:
"concat xss = foldr append xss []"

subsubsection ‹@{const upt}›

lemma upt_rec[code]: "[i..<j] = (if i<j then i#[Suc i..<j] else [])"
― ‹simp does not terminate!›
by (induct j) auto

lemmas upt_rec_numeral[simp] = upt_rec[of "numeral m" "numeral n"] for m n

lemma upt_conv_Nil [simp]: "j <= i ==> [i..<j] = []"
by (subst upt_rec) simp

lemma upt_eq_Nil_conv[simp]: "([i..<j] = []) = (j = 0 ∨ j <= i)"
by(induct j)simp_all

lemma upt_eq_Cons_conv:
"([i..<j] = x#xs) = (i < j ∧ i = x ∧ [i+1..<j] = xs)"
apply(induct j arbitrary: x xs)
apply simp
apply arith
done

lemma upt_Suc_append: "i <= j ==> [i..<(Suc j)] = [i..<j]@[j]"
― ‹Only needed if ‹upt_Suc› is deleted from the simpset.›
by simp

lemma upt_conv_Cons: "i < j ==> [i..<j] = i # [Suc i..<j]"

lemma upt_conv_Cons_Cons: ― ‹no precondition›
"m # n # ns = [m..<q] ⟷ n # ns = [Suc m..<q]"
proof (cases "m < q")
case False then show ?thesis by simp
next
case True then show ?thesis by (simp add: upt_conv_Cons)
qed

lemma upt_add_eq_append: "i<=j ==> [i..<j+k] = [i..<j]@[j..<j+k]"
― ‹LOOPS as a simprule, since ‹j <= j›.›
by (induct k) auto

lemma length_upt [simp]: "length [i..<j] = j - i"
by (induct j) (auto simp add: Suc_diff_le)

lemma nth_upt [simp]: "i + k < j ==> [i..<j] ! k = i + k"
by (induct j) (auto simp add: less_Suc_eq nth_append split: nat_diff_split)

lemma hd_upt[simp]: "i < j ⟹ hd[i..<j] = i"

lemma tl_upt: "tl [m..<n] = [Suc m..<n]"

lemma last_upt[simp]: "i < j ⟹ last[i..<j] = j - 1"
by(cases j)(auto simp: upt_Suc_append)

lemma take_upt [simp]: "i+m <= n ==> take m [i..<n] = [i..<i+m]"
apply (induct m arbitrary: i, simp)
apply (subst upt_rec)
apply (rule sym)
apply (subst upt_rec)
apply (simp del: upt.simps)
done

lemma drop_upt[simp]: "drop m [i..<j] = [i+m..<j]"
by(induct j) auto

lemma map_Suc_upt: "map Suc [m..<n] = [Suc m..<Suc n]"
by (induct n) auto

lemma map_add_upt: "map (λi. i + n) [0..<m] = [n..<m + n]"
by (induct m) simp_all

lemma nth_map_upt: "i < n-m ==> (map f [m..<n]) ! i = f(m+i)"
apply (induct n m  arbitrary: i rule: diff_induct)
prefer 3 apply (subst map_Suc_upt[symmetric])
done

lemma map_decr_upt: "map (λn. n - Suc 0) [Suc m..<Suc n] = [m..<n]"
by (induct n) simp_all

lemma map_upt_Suc: "map f [0 ..< Suc n] = f 0 # map (λi. f (Suc i)) [0 ..< n]"
by (induct n arbitrary: f) auto

lemma nth_take_lemma:
"k ≤ length xs ⟹ k ≤ length ys ⟹
(⋀i. i < k ⟶ xs!i = ys!i) ⟹ take k xs = take k ys"
apply (atomize, induct k arbitrary: xs ys)
apply (simp_all add: less_Suc_eq_0_disj all_conj_distrib, clarify)
txt ‹Both lists must be non-empty›
apply (case_tac xs, simp)
apply (case_tac ys, clarify)
apply (simp (no_asm_use))
apply clarify
txt ‹prenexing's needed, not miniscoping›
apply (simp (no_asm_use) add: all_simps [symmetric] del: all_simps)
apply blast
done

lemma nth_equalityI:
"[| length xs = length ys; ∀i < length xs. xs!i = ys!i |] ==> xs = ys"
by (frule nth_take_lemma [OF le_refl eq_imp_le]) simp_all

lemma map_nth:
"map (λi. xs ! i) [0..<length xs] = xs"
by (rule nth_equalityI, auto)

lemma list_all2_antisym:
"⟦ (⋀x y. ⟦P x y; Q y x⟧ ⟹ x = y); list_all2 P xs ys; list_all2 Q ys xs ⟧
⟹ xs = ys"
apply (rule nth_equalityI, blast, simp)
done

lemma take_equalityI: "(∀i. take i xs = take i ys) ==> xs = ys"
― ‹The famous take-lemma.›
apply (drule_tac x = "max (length xs) (length ys)" in spec)
done

lemma take_Cons':
"take n (x # xs) = (if n = 0 then [] else x # take (n - 1) xs)"
by (cases n) simp_all

lemma drop_Cons':
"drop n (x # xs) = (if n = 0 then x # xs else drop (n - 1) xs)"
by (cases n) simp_all

lemma nth_Cons': "(x # xs)!n = (if n = 0 then x else xs!(n - 1))"
by (cases n) simp_all

lemma take_Cons_numeral [simp]:
"take (numeral v) (x # xs) = x # take (numeral v - 1) xs"

lemma drop_Cons_numeral [simp]:
"drop (numeral v) (x # xs) = drop (numeral v - 1) xs"

lemma nth_Cons_numeral [simp]:
"(x # xs) ! numeral v = xs ! (numeral v - 1)"

subsubsection ‹‹upto›: interval-list on @{typ int}›

function upto :: "int ⇒ int ⇒ int list" ("(1[_../_])") where
"upto i j = (if i ≤ j then i # [i+1..j] else [])"
by auto
termination
by(relation "measure(%(i::int,j). nat(j - i + 1))") auto

declare upto.simps[simp del]

lemmas upto_rec_numeral [simp] =
upto.simps[of "numeral m" "numeral n"]
upto.simps[of "numeral m" "- numeral n"]
upto.simps[of "- numeral m" "numeral n"]
upto.simps[of "- numeral m" "- numeral n"] for m n

lemma upto_empty[simp]: "j < i ⟹ [i..j] = []"

lemma upto_single[simp]: "[i..i] = [i]"

lemma upto_Nil[simp]: "[i..j] = [] ⟷ j < i"

lemma upto_Nil2[simp]: "[] = [i..j] ⟷ j < i"

lemma upto_rec1: "i ≤ j ⟹ [i..j] = i#[i+1..j]"

lemma upto_rec2: "i ≤ j ⟹ [i..j] = [i..j - 1]@[j]"
proof(induct "nat(j-i)" arbitrary: i j)
case 0 thus ?case by(simp add: upto.simps)
next
case (Suc n)
hence "n = nat (j - (i + 1))" "i < j" by linarith+
from this(2) Suc.hyps(1)[OF this(1)] Suc(2,3) upto_rec1 show ?case by simp
qed

lemma length_upto[simp]: "length [i..j] = nat(j - i + 1)"
by(induction i j rule: upto.induct) (auto simp: upto.simps)

lemma set_upto[simp]: "set[i..j] = {i..j}"
proof(induct i j rule:upto.induct)
case (1 i j)
from this show ?case
unfolding upto.simps[of i j] by auto
qed

lemma nth_upto: "i + int k ≤ j ⟹ [i..j] ! k = i + int k"
apply(induction i j arbitrary: k rule: upto.induct)
apply(subst upto_rec1)
done

lemma upto_split1:
"i ≤ j ⟹ j ≤ k ⟹ [i..k] = [i..j-1] @ [j..k]"
proof (induction j rule: int_ge_induct)
case base thus ?case by (simp add: upto_rec1)
next
case step thus ?case using upto_rec1 upto_rec2 by simp
qed

lemma upto_split2:
"i ≤ j ⟹ j ≤ k ⟹ [i..k] = [i..j] @ [j+1..k]"
using upto_rec1 upto_rec2 upto_split1 by auto

lemma upto_split3: "⟦ i ≤ j; j ≤ k ⟧ ⟹ [i..k] = [i..j-1] @ j # [j+1..k]"
using upto_rec1 upto_split1 by auto

text‹Tail recursive version for code generation:›

definition upto_aux :: "int ⇒ int ⇒ int list ⇒ int list" where
"upto_aux i j js = [i..j] @ js"

lemma upto_aux_rec [code]:
"upto_aux i j js = (if j<i then js else upto_aux i (j - 1) (j#js))"

lemma upto_code[code]: "[i..j] = upto_aux i j []"

subsubsection ‹@{const distinct} and @{const remdups} and @{const remdups_adj}›

lemma distinct_tl: "distinct xs ⟹ distinct (tl xs)"
by (cases xs) simp_all

lemma distinct_append [simp]:
"distinct (xs @ ys) = (distinct xs ∧ distinct ys ∧ set xs ∩ set ys = {})"
by (induct xs) auto

lemma distinct_rev[simp]: "distinct(rev xs) = distinct xs"
by(induct xs) auto

lemma set_remdups [simp]: "set (remdups xs) = set xs"
by (induct xs) (auto simp add: insert_absorb)

lemma distinct_remdups [iff]: "distinct (remdups xs)"
by (induct xs) auto

lemma distinct_remdups_id: "distinct xs ==> remdups xs = xs"
by (induct xs, auto)

lemma remdups_id_iff_distinct [simp]: "remdups xs = xs ⟷ distinct xs"
by (metis distinct_remdups distinct_remdups_id)

lemma finite_distinct_list: "finite A ⟹ ∃xs. set xs = A ∧ distinct xs"
by (metis distinct_remdups finite_list set_remdups)

lemma remdups_eq_nil_iff [simp]: "(remdups x = []) = (x = [])"
by (induct x, auto)

lemma remdups_eq_nil_right_iff [simp]: "([] = remdups x) = (x = [])"
by (induct x, auto)

lemma length_remdups_leq[iff]: "length(remdups xs) <= length xs"
by (induct xs) auto

lemma length_remdups_eq[iff]:
"(length (remdups xs) = length xs) = (remdups xs = xs)"
apply(induct xs)
apply auto
apply(subgoal_tac "length (remdups xs) <= length xs")
apply arith
apply(rule length_remdups_leq)
done

lemma remdups_filter: "remdups(filter P xs) = filter P (remdups xs)"
by (induct xs) auto

lemma distinct_map:
"distinct(map f xs) = (distinct xs ∧ inj_on f (set xs))"
by (induct xs) auto

lemma distinct_map_filter:
"distinct (map f xs) ⟹ distinct (map f (filter P xs))"
by (induct xs) auto

lemma distinct_filter [simp]: "distinct xs ==> distinct (filter P xs)"
by (induct xs) auto

lemma distinct_upt[simp]: "distinct[i..<j]"
by (induct j) auto

lemma distinct_upto[simp]: "distinct[i..j]"
apply(induct i j rule:upto.induct)
apply(subst upto.simps)
apply(simp)
done

lemma distinct_take[simp]: "distinct xs ⟹ distinct (take i xs)"
apply(induct xs arbitrary: i)
apply simp
apply (case_tac i)
apply simp_all
apply(blast dest:in_set_takeD)
done

lemma distinct_drop[simp]: "distinct xs ⟹ distinct (drop i xs)"
apply(induct xs arbitrary: i)
apply simp
apply (case_tac i)
apply simp_all
done

lemma distinct_list_update:
assumes d: "distinct xs" and a: "a ∉ set xs - {xs!i}"
shows "distinct (xs[i:=a])"
proof (cases "i < length xs")
case True
with a have "a ∉ set (take i xs @ xs ! i # drop (Suc i) xs) - {xs!i}"
apply (drule_tac id_take_nth_drop) by simp
with d True show ?thesis
apply (drule subst [OF id_take_nth_drop]) apply assumption
apply simp apply (cases "a = xs!i") apply simp by blast
next
case False with d show ?thesis by auto
qed

lemma distinct_concat:
"⟦ distinct xs;
⋀ ys. ys ∈ set xs ⟹ distinct ys;
⋀ ys zs. ⟦ ys ∈ set xs ; zs ∈ set xs ; ys ≠ zs ⟧ ⟹ set ys ∩ set zs = {}
⟧ ⟹ distinct (concat xs)"
by (induct xs) auto

text ‹It is best to avoid this indexed version of distinct, but
sometimes it is useful.›

lemma distinct_conv_nth:
"distinct xs = (∀i < size xs. ∀j < size xs. i ≠ j ⟶ xs!i ≠ xs!j)"
apply (induct xs, simp, simp)
apply (rule iffI, clarsimp)
apply (case_tac i)
apply (case_tac j, simp)
apply (case_tac j)
apply (clarsimp simp add: set_conv_nth, simp)
apply (rule conjI)
apply (erule_tac x = 0 in allE, simp)
apply (erule_tac x = "Suc i" in allE, simp, clarsimp)
apply (erule_tac x = "Suc i" in allE, simp)
apply (erule_tac x = "Suc j" in allE, simp)
done

lemma nth_eq_iff_index_eq:
"⟦ distinct xs; i < length xs; j < length xs ⟧ ⟹ (xs!i = xs!j) = (i = j)"
by(auto simp: distinct_conv_nth)

lemma distinct_Ex1:
"distinct xs ⟹ x ∈ set xs ⟹ (∃!i. i < length xs ∧ xs ! i = x)"
by (auto simp: in_set_conv_nth nth_eq_iff_index_eq)

lemma inj_on_nth: "distinct xs ⟹ ∀i ∈ I. i < length xs ⟹ inj_on (nth xs) I"
by (rule inj_onI) (simp add: nth_eq_iff_index_eq)

lemma bij_betw_nth:
assumes "distinct xs" "A = {..<length xs}" "B = set xs"
shows   "bij_betw ((!) xs) A B"
using assms unfolding bij_betw_def
by (auto intro!: inj_on_nth simp: set_conv_nth)

lemma set_update_distinct: "⟦ distinct xs;  n < length xs ⟧ ⟹
set(xs[n := x]) = insert x (set xs - {xs!n})"
by(auto simp: set_eq_iff in_set_conv_nth nth_list_update nth_eq_iff_index_eq)

lemma distinct_swap[simp]: "⟦ i < size xs; j < size xs ⟧ ⟹
distinct(xs[i := xs!j, j := xs!i]) = distinct xs"
apply safe
apply metis+
done

lemma set_swap[simp]:
"⟦ i < size xs; j < size xs ⟧ ⟹ set(xs[i := xs!j, j := xs!i]) = set xs"

lemma distinct_card: "distinct xs ==> card (set xs) = size xs"
by (induct xs) auto

lemma card_distinct: "card (set xs) = size xs ==> distinct xs"
proof (induct xs)
case Nil thus ?case by simp
next
case (Cons x xs)
show ?case
proof (cases "x ∈ set xs")
case False with Cons show ?thesis by simp
next
case True with Cons.prems
have "card (set xs) = Suc (length xs)"
by (simp add: card_insert_if split: if_split_asm)
moreover have "card (set xs) ≤ length xs" by (rule card_length)
ultimately have False by simp
thus ?thesis ..
qed
qed

lemma distinct_length_filter: "distinct xs ⟹ length (filter P xs) = card ({x. P x} Int set xs)"
by (induct xs) (auto)

lemma not_distinct_decomp: "¬ distinct ws ⟹ ∃xs ys zs y. ws = xs@[y]@ys@[y]@zs"
apply (induct n == "length ws" arbitrary:ws) apply simp
apply(case_tac ws) apply simp
apply (simp split:if_split_asm)
apply (metis Cons_eq_appendI eq_Nil_appendI split_list)
done

lemma not_distinct_conv_prefix:
defines "dec as xs y ys ≡ y ∈ set xs ∧ distinct xs ∧ as = xs @ y # ys"
shows "¬distinct as ⟷ (∃xs y ys. dec as xs y ys)" (is "?L = ?R")
proof
assume "?L" then show "?R"
proof (induct "length as" arbitrary: as rule: less_induct)
case less
obtain xs ys zs y where decomp: "as = (xs @ y # ys) @ y # zs"
using not_distinct_decomp[OF less.prems] by auto
show ?case
proof (cases "distinct (xs @ y # ys)")
case True
with decomp have "dec as (xs @ y # ys) y zs" by (simp add: dec_def)
then show ?thesis by blast
next
case False
with less decomp obtain xs' y' ys' where "dec (xs @ y # ys) xs' y' ys'"
by atomize_elim auto
with decomp have "dec as xs' y' (ys' @ y # zs)" by (simp add: dec_def)
then show ?thesis by blast
qed
qed
qed (auto simp: dec_def)

lemma distinct_product:
"distinct xs ⟹ distinct ys ⟹ distinct (List.product xs ys)"
by (induct xs) (auto intro: inj_onI simp add: distinct_map)

lemma distinct_product_lists:
assumes "∀xs ∈ set xss. distinct xs"
shows "distinct (product_lists xss)"
using assms proof (induction xss)
case (Cons xs xss) note * = this
then show ?case
proof (cases "product_lists xss")
case Nil then show ?thesis by (induct xs) simp_all
next
case (Cons ps pss) with * show ?thesis
by (auto intro!: inj_onI distinct_concat simp add: distinct_map)
qed
qed simp

lemma length_remdups_concat:
"length (remdups (concat xss)) = card (⋃xs∈set xss. set xs)"

lemma length_remdups_card_conv: "length(remdups xs) = card(set xs)"
proof -
have xs: "concat[xs] = xs" by simp
from length_remdups_concat[of "[xs]"] show ?thesis unfolding xs by simp
qed

lemma remdups_remdups: "remdups (remdups xs) = remdups xs"
by (induct xs) simp_all

lemma distinct_butlast:
assumes "distinct xs"
shows "distinct (butlast xs)"
proof (cases "xs = []")
case False
from ‹xs ≠ []› obtain ys y where "xs = ys @ [y]" by (cases xs rule: rev_cases) auto
with ‹distinct xs› show ?thesis by simp
qed (auto)

lemma remdups_map_remdups:
"remdups (map f (remdups xs)) = remdups (map f xs)"
by (induct xs) simp_all

lemma distinct_zipI1:
assumes "distinct xs"
shows "distinct (zip xs ys)"
proof (rule zip_obtain_same_length)
fix xs' :: "'a list" and ys' :: "'b list" and n
assume "length xs' = length ys'"
assume "xs' = take n xs"
with assms have "distinct xs'" by simp
with ‹length xs' = length ys'› show "distinct (zip xs' ys')"
by (induct xs' ys' rule: list_induct2) (auto elim: in_set_zipE)
qed

lemma distinct_zipI2:
assumes "distinct ys"
shows "distinct (zip xs ys)"
proof (rule zip_obtain_same_length)
fix xs' :: "'b list" and ys' :: "'a list" and n
assume "length xs' = length ys'"
assume "ys' = take n ys"
with assms have "distinct ys'" by simp
with ‹length xs' = length ys'› show "distinct (zip xs' ys')"
by (induct xs' ys' rule: list_induct2) (auto elim: in_set_zipE)
qed

lemma set_take_disj_set_drop_if_distinct:
"distinct vs ⟹ i ≤ j ⟹ set (take i vs) ∩ set (drop j vs) = {}"
by (auto simp: in_set_conv_nth distinct_conv_nth)

(* The next two lemmas help Sledgehammer. *)

lemma distinct_singleton: "distinct [x]" by simp

lemma distinct_length_2_or_more:
"distinct (a # b # xs) ⟷ (a ≠ b ∧ distinct (a # xs) ∧ distinct (b # xs))"
by force

(∃f::nat => nat. mono f ∧ f  {0 ..< size xs} = {0 ..< size ys}
∧ (∀i < size xs. xs!i = ys!(f i))
∧ (∀i. i + 1 < size xs ⟶ (xs!i = xs!(i+1) ⟷ f i = f(i+1))))" (is "?L ⟷ (∃f. ?p f xs ys)")
proof
assume ?L
then show "∃f. ?p f xs ys"
proof (induct xs arbitrary: ys rule: remdups_adj.induct)
case (1 ys)
thus ?case by (intro exI[of _ id]) (auto simp: mono_def)
next
case (2 x ys)
thus ?case by (intro exI[of _ id]) (auto simp: mono_def)
next
case (3 x1 x2 xs ys)
let ?xs = "x1 # x2 # xs"
let ?cond = "x1 = x2"
define zs where "zs = remdups_adj (x2 # xs)"
from 3(1-2)[of zs]
obtain f where p: "?p f (x2 # xs) zs" unfolding zs_def by (cases ?cond) auto
then have f0: "f 0 = 0"
by (intro mono_image_least[where f=f]) blast+
from p have mono: "mono f" and f_xs_zs: "f  {0..<length (x2 # xs)} = {0..<length zs}" by auto
have ys: "ys = (if x1 = x2 then zs else x1 # zs)"
unfolding 3(3)[symmetric] zs_def by auto
have zs0: "zs ! 0 = x2" unfolding zs_def by (induct xs) auto
have zsne: "zs ≠ []" unfolding zs_def by (induct xs) auto
let ?Succ = "if ?cond then id else Suc"
let ?x1 = "if ?cond then id else Cons x1"
let ?f = "λ i. if i = 0 then 0 else ?Succ (f (i - 1))"
have ys: "ys = ?x1 zs" unfolding ys by (cases ?cond, auto)
have mono: "mono ?f" using ‹mono f› unfolding mono_def by auto
show ?case unfolding ys
proof (intro exI[of _ ?f] conjI allI impI)
show "mono ?f" by fact
next
fix i assume i: "i < length ?xs"
with p show "?xs ! i = ?x1 zs ! (?f i)" using zs0 by auto
next
fix i assume i: "i + 1 < length ?xs"
with p show "(?xs ! i = ?xs ! (i + 1)) = (?f i = ?f (i + 1))"
by (cases i) (auto simp: f0)
next
have id: "{0 ..< length (?x1 zs)} = insert 0 (?Succ  {0 ..< length zs})"
using zsne by (cases ?cond, auto)
{ fix i  assume "i < Suc (length xs)"
hence "Suc i ∈ {0..<Suc (Suc (length xs))} ∩ Collect ((<) 0)" by auto
from imageI[OF this, of "λi. ?Succ (f (i - Suc 0))"]
have "?Succ (f i) ∈ (λi. ?Succ (f (i - Suc 0)))  ({0..<Suc (Suc (length xs))} ∩ Collect ((<) 0))" by auto
}
then show "?f  {0 ..< length ?xs} = {0 ..< length (?x1  zs)}"
unfolding id f_xs_zs[symmetric] by auto
qed
qed
next
assume "∃ f. ?p f xs ys"
then show ?L
proof (induct xs arbitrary: ys rule: remdups_adj.induct)
case 1 then show ?case by auto
next
case (2 x) then obtain f where f_img: "f  {0 ..< size [x]} = {0 ..< size ys}"
and f_nth: "⋀i. i < size [x] ⟹ [x]!i = ys!(f i)"
by blast

have "length ys = card (f  {0 ..< size [x]})"
using f_img by auto
then have *: "length ys = 1" by auto
then have "f 0 = 0" using f_img by auto
with * show ?case using f_nth by (cases ys) auto
next
case (3 x1 x2 xs)
from "3.prems" obtain f where f_mono: "mono f"
and f_img: "f  {0..<length (x1 # x2 # xs)} = {0..<length ys}"
and f_nth:
"⋀i. i < length (x1 # x2 # xs) ⟹ (x1 # x2 # xs) ! i = ys ! f i"
"⋀i. i + 1 < length (x1 # x2 #xs) ⟹
((x1 # x2 # xs) ! i = (x1 # x2 # xs) ! (i + 1)) = (f i = f (i + 1))"
by blast

show ?case
proof cases
assume "x1 = x2"

let ?f' = "f ∘ Suc"

have "remdups_adj (x1 # xs) = ys"
proof (intro "3.hyps" exI conjI impI allI)
show "mono ?f'"
using f_mono by (simp add: mono_iff_le_Suc)
next
have "?f'  {0 ..< length (x1 # xs)} = f  {Suc 0 ..< length (x1 # x2 # xs)}"
by safe (fastforce, rename_tac x, case_tac x, auto)
also have "… = f  {0 ..< length (x1 # x2 # xs)}"
proof -
have "f 0 = f (Suc 0)" using ‹x1 = x2› f_nth[of 0] by simp
then show ?thesis by safe (fastforce, rename_tac x, case_tac x, auto)
qed
also have "… = {0 ..< length ys}" by fact
finally show  "?f'  {0 ..< length (x1 # xs)} = {0 ..< length ys}" .
qed (insert f_nth[of "Suc i" for i], auto simp: ‹x1 = x2›)
then show ?thesis using ‹x1 = x2› by simp
next
assume "x1 ≠ x2"

have "2 ≤ length ys"
proof -
have "2 = card {f 0, f 1}" using ‹x1 ≠ x2› f_nth[of 0] by auto
also have "… ≤ card (f  {0..< length (x1 # x2 # xs)})"
by (rule card_mono) auto
finally show ?thesis using f_img by simp
qed

have "f 0 = 0" using f_mono f_img by (rule mono_image_least) simp

have "f (Suc 0) = Suc 0"
proof (rule ccontr)
assume "f (Suc 0) ≠ Suc 0"
then have "Suc 0 < f (Suc 0)" using f_nth[of 0] ‹x1 ≠ x2› ‹f 0 = 0› by auto
then have "⋀i. Suc 0 < f (Suc i)"
using f_mono
by (meson Suc_le_mono le0 less_le_trans monoD)
then have "⋀i. Suc 0 ≠ f i" using ‹f 0 = 0›
by (case_tac i) fastforce+
then have "Suc 0 ∉ f  {0 ..< length (x1 # x2 # xs)}" by auto
then show False using f_img ‹2 ≤ length ys› by auto
qed

obtain ys' where "ys = x1 # x2 # ys'"
using ‹2 ≤ length ys› f_nth[of 0] f_nth[of 1]
apply (cases ys)
apply (rename_tac [2] ys', case_tac [2] ys')
by (auto simp: ‹f 0 = 0› ‹f (Suc 0) = Suc 0›)

define f' where "f' x = f (Suc x) - 1" for x

{ fix i
have "Suc 0 ≤ f (Suc 0)" using f_nth[of 0] ‹x1 ≠ x2› ‹f 0 = 0›  by auto
also have "… ≤ f (Suc i)" using f_mono by (rule monoD) arith
finally have "Suc 0 ≤ f (Suc i)" .
} note Suc0_le_f_Suc = this

{ fix i have "f (Suc i) = Suc (f' i)"
using Suc0_le_f_Suc[of i] by (auto simp: f'_def)
} note f_Suc = this

have "remdups_adj (x2 # xs) = (x2 # ys')"
proof (intro "3.hyps" exI conjI impI allI)
show "mono f'"
using Suc0_le_f_Suc f_mono by (auto simp: f'_def mono_iff_le_Suc le_diff_iff)
next
have "f'  {0 ..< length (x2 # xs)} = (λx. f x - 1)  {0 ..< length (x1 # x2 #xs)}"
apply safe
apply (rename_tac [!] n,  case_tac [!] n)
apply (auto simp: f'_def ‹f 0 = 0› ‹f (Suc 0) = Suc 0› intro: rev_image_eqI)
done
also have "… = (λx. x - 1)  f  {0 ..< length (x1 # x2 #xs)}"
by (auto simp: image_comp)
also have "… = (λx. x - 1)  {0 ..< length ys}"
by (simp only: f_img)
also have "… = {0 ..< length (x2 # ys')}"
using ‹ys = _› by (fastforce intro: rev_image_eqI)
finally show  "f'  {0 ..< length (x2 # xs)} = {0 ..< length (x2 # ys')}" .
qed (insert f_nth[of "Suc i" for i] ‹x1 ≠ x2›, auto simp add: f_Suc ‹ys = _›)
then show ?case using ‹ys = _› ‹x1 ≠ x2› by simp
qed
qed
qed

by (induction xs rule: remdups_adj.induct) simp_all

(case remdups_adj xs of [] ⇒ [x] | y # xs ⇒ if x = y then y # xs else x # y # xs)"
by (induct xs arbitrary: x) (auto split: list.splits)

"remdups_adj (xs @ [x,y]) = remdups_adj (xs @ [x]) @ (if x = y then [] else [y])"
by (induct xs rule: remdups_adj.induct, simp_all)

"Suc i < length (remdups_adj xs) ⟹ remdups_adj xs ! i ≠ remdups_adj xs ! Suc i"
proof (induction xs arbitrary: i rule: remdups_adj.induct)
case (3 x y xs i)
thus ?case by (cases i, cases "x = y") (simp, auto simp: hd_conv_nth[symmetric])
qed simp_all

by (induct xs rule: remdups_adj.induct, auto)

lemma remdups_adj_length_ge1[simp]: "xs ≠ [] ⟹ length (remdups_adj xs) ≥ Suc 0"
by (induct xs rule: remdups_adj.induct, simp_all)

by (induct xs rule: remdups_adj.induct, simp_all)

by (induct xs rule: remdups_adj.induct, simp_all)

by (induct xs rule: remdups_adj.induct, auto)

by (induct xs rule: remdups_adj.induct, simp_all)

"remdups_adj (xs⇩1 @ x # xs⇩2) = remdups_adj (xs⇩1 @ [x]) @ tl (remdups_adj (x # xs⇩2))"
by (induct xs⇩1 rule: remdups_adj.induct, simp_all)

"remdups_adj xs = [x] ⟹ xs = replicate (length xs) x"
by (induct xs rule: remdups_adj.induct, auto split: if_split_asm)

assumes "inj f"

"remdups_adj (replicate n x) = (if n = 0 then [] else [x])"
by (induction n) (auto simp: remdups_adj_Cons)

lemma remdups_upt [simp]: "remdups [m..<n] = [m..<n]"
proof (cases "m ≤ n")
case False then show ?thesis by simp
next
case True then obtain q where "n = m + q"
moreover have "remdups [m..<m + q] = [m..<m + q]"
by (induct q) simp_all
ultimately show ?thesis by simp
qed

subsubsection ‹@{const insert}›

lemma in_set_insert [simp]:
"x ∈ set xs ⟹ List.insert x xs = xs"

lemma not_in_set_insert [simp]:
"x ∉ set xs ⟹ List.insert x xs = x # xs"

lemma insert_Nil [simp]: "List.insert x [] = [x]"
by simp

lemma set_insert [simp]: "set (List.insert x xs) = insert x (set xs)"

lemma distinct_insert [simp]: "distinct (List.insert x xs) = distinct xs"

lemma insert_remdups:
"List.insert x (remdups xs) = remdups (List.insert x xs)"

subsubsection ‹@{const List.union}›

text‹This is all one should need to know about union:›
lemma set_union[simp]: "set (List.union xs ys) = set xs ∪ set ys"
unfolding List.union_def
by(induct xs arbitrary: ys) simp_all

lemma distinct_union[simp]: "distinct(List.union xs ys) = distinct ys"
unfolding List.union_def
by(induct xs arbitrary: ys) simp_all

subsubsection ‹@{const List.find}›

lemma find_None_iff: "List.find P xs = None ⟷ ¬ (∃x. x ∈ set xs ∧ P x)"
proof (induction xs)
case Nil thus ?case by simp
next
case (Cons x xs) thus ?case by (fastforce split: if_splits)
qed

lemma find_Some_iff:
"List.find P xs = Some x ⟷
(∃i<length xs. P (xs!i) ∧ x = xs!i ∧ (∀j<i. ¬ P (xs!j)))"
proof (induction xs)
case Nil thus ?case by simp
next
case (Cons x xs) thus ?case
apply(auto simp: nth_Cons' split: if_splits)
using diff_Suc_1[unfolded One_nat_def] less_Suc_eq_0_disj by fastforce
qed

lemma find_cong[fundef_cong]:
assumes "xs = ys" and "⋀x. x ∈ set ys ⟹ P x = Q x"
shows "List.find P xs = List.find Q ys"
proof (cases "List.find P xs")
case None thus ?thesis by (metis find_None_iff assms)
next
case (Some x)
hence "List.find Q ys = Some x" using assms
thus ?thesis using Some by auto
qed

lemma find_dropWhile:
"List.find P xs = (case dropWhile (Not ∘ P) xs
of [] ⇒ None
| x # _ ⇒ Some x)"
by (induct xs) simp_all

subsubsection ‹@{const count_list}›

lemma count_notin[simp]: "x ∉ set xs ⟹ count_list xs x = 0"
by (induction xs) auto

lemma count_le_length: "count_list xs x ≤ length xs"
by (induction xs) auto

lemma sum_count_set:
"set xs ⊆ X ⟹ finite X ⟹ sum (count_list xs) X = length xs"
apply(induction xs arbitrary: X)
apply simp
by (metis Diff_eq sum.remove)

subsubsection ‹@{const List.extract}›

lemma extract_None_iff: "List.extract P xs = None ⟷ ¬ (∃ x∈set xs. P x)"
by(auto simp: extract_def dropWhile_eq_Cons_conv split: list.splits)
(metis in_set_conv_decomp)

lemma extract_SomeE:
"List.extract P xs = Some (ys, y, zs) ⟹
xs = ys @ y # zs ∧ P y ∧ ¬ (∃ y ∈ set ys. P y)"
by(auto simp: extract_def dropWhile_eq_Cons_conv split: list.splits)

lemma extract_Some_iff:
"List.extract P xs = Some (ys, y, zs) ⟷
xs = ys @ y # zs ∧ P y ∧ ¬ (∃ y ∈ set ys. P y)"
by(auto simp: extract_def dropWhile_eq_Cons_conv dest: set_takeWhileD split: list.splits)

lemma extract_Nil_code[code]: "List.extract P [] = None"

lemma extract_Cons_code[code]:
"List.extract P (x # xs) = (if P x then Some ([], x, xs) else
(case List.extract P xs of
None ⇒ None |
Some (ys, y, zs) ⇒ Some (x#ys, y, zs)))"
by(auto simp add: extract_def comp_def split: list.splits)
(metis dropWhile_eq_Nil_conv list.distinct(1))

subsubsection ‹@{const remove1}›

lemma remove1_append:
"remove1 x (xs @ ys) =
(if x ∈ set xs then remove1 x xs @ ys else xs @ remove1 x ys)"
by (induct xs) auto

lemma remove1_commute: "remove1 x (remove1 y zs) = remove1 y (remove1 x zs)"
by (induct zs) auto

lemma in_set_remove1[simp]:
"a ≠ b ⟹ a ∈ set(remove1 b xs) = (a ∈ set xs)"
apply (induct xs)
apply auto
done

lemma set_remove1_subset: "set(remove1 x xs) ⊆ set xs"
apply(induct xs)
apply simp
apply simp
apply blast
done

lemma set_remove1_eq [simp]: "distinct xs ⟹ set(remove1 x xs) = set xs - {x}"
apply(induct xs)
apply simp
apply simp
apply blast
done

lemma length_remove1:
"length(remove1 x xs) = (if x ∈ set xs then length xs - 1 else length xs)"
by (induct xs) (auto dest!:length_pos_if_in_set)

lemma remove1_filter_not[simp]:
"¬ P x ⟹ remove1 x (filter P xs) = filter P xs"
by(induct xs) auto

lemma filter_remove1:
"filter Q (remove1 x xs) = remove1 x (filter Q xs)"
by (induct xs) auto

lemma notin_set_remove1[simp]: "x ∉ set xs ⟹ x ∉ set(remove1 y xs)"
by(insert set_remove1_subset) fast

lemma distinct_remove1[simp]: "distinct xs ⟹ distinct(remove1 x xs)"
by (induct xs) simp_all

lemma remove1_remdups:
"distinct xs ⟹ remove1 x (remdups xs) = remdups (remove1 x xs)"
by (induct xs) simp_all

lemma remove1_idem: "x ∉ set xs ⟹ remove1 x xs = xs"
by (induct xs) simp_all

subsubsection ‹@{const removeAll}›

lemma removeAll_filter_not_eq:
"removeAll x = filter (λy. x ≠ y)"
proof
fix xs
show "removeAll x xs = filter (λy. x ≠ y) xs"
by (induct xs) auto
qed

lemma removeAll_append[simp]:
"removeAll x (xs @ ys) = removeAll x xs @ removeAll x ys"
by (induct xs) auto

lemma set_removeAll[simp]: "set(removeAll x xs) = set xs - {x}"
by (induct xs) auto

lemma removeAll_id[simp]: "x ∉ set xs ⟹ removeAll x xs = xs"
by (induct xs) auto

(* Needs count:: 'a ⇒ 'a list ⇒ nat
lemma length_removeAll:
"length(removeAll x xs) = length xs - count x xs"
*)

lemma removeAll_filter_not[simp]:
"¬ P x ⟹ removeAll x (filter P xs) = filter P xs"
by(induct xs) auto

lemma distinct_removeAll:
"distinct xs ⟹ distinct (removeAll x xs)"

lemma distinct_remove1_removeAll:
"distinct xs ⟹ remove1 x xs = removeAll x xs"
by (induct xs) simp_all

lemma map_removeAll_inj_on: "inj_on f (insert x (set xs)) ⟹
map f (removeAll x xs) = removeAll (f x) (map f xs)"

lemma map_removeAll_inj: "inj f ⟹
map f (removeAll x xs) = removeAll (f x) (map f xs)"
by (rule map_removeAll_inj_on, erule subset_inj_on, rule subset_UNIV)

lemma length_removeAll_less_eq [simp]:
"length (removeAll x xs) ≤ length xs"

lemma length_removeAll_less [termination_simp]:
"x ∈ set xs ⟹ length (removeAll x xs) < length xs"
by (auto dest: length_filter_less simp add: removeAll_filter_not_eq)

subsubsection ‹@{const replicate}›

lemma length_replicate [simp]: "length (replicate n x) = n"
by (induct n) auto

lemma replicate_eqI:
assumes "length xs = n" and "⋀y. y ∈ set xs ⟹ y = x"
shows "xs = replicate n x"
using assms
proof (induct xs arbitrary: n)
case Nil then show ?case by simp
next
case (Cons x xs) then show ?case by (cases n) simp_all
qed

lemma Ex_list_of_length: "∃xs. length xs = n"
by (rule exI[of _ "replicate n undefined"]) simp

lemma map_replicate [simp]: "map f (replicate n x) = replicate n (f x)"
by (induct n) auto

lemma map_replicate_const:
"map (λ x. k) lst = replicate (length lst) k"
by (induct lst) auto

lemma replicate_app_Cons_same:
"(replicate n x) @ (x # xs) = x # replicate n x @ xs"
by (induct n) auto

lemma rev_replicate [simp]: "rev (replicate n x) = replicate n x"
by (induct n) (auto simp: replicate_app_Cons_same)

lemma replicate_add: "replicate (n + m) x = replicate n x @ replicate m x"
by (induct n) auto

text‹Courtesy of Matthias Daum:›
lemma append_replicate_commute:
"replicate n x @ replicate k x = replicate k x @ replicate n x"
done

text‹Courtesy of Andreas Lochbihler:›
lemma filter_replicate:
"filter P (replicate n x) = (if P x then replicate n x else [])"
by(induct n) auto

lemma hd_replicate [simp]: "n ≠ 0 ==> hd (replicate n x) = x"
by (induct n) auto

lemma tl_replicate [simp]: "tl (replicate n x) = replicate (n - 1) x"
by (induct n) auto

lemma last_replicate [simp]: "n ≠ 0 ==> last (replicate n x) = x"
by (atomize (full), induct n) auto

lemma nth_replicate[simp]: "i < n ==> (replicate n x)!i = x"
by (induct n arbitrary: i)(auto simp: nth_Cons split: nat.split)

text‹Courtesy of Matthias Daum (2 lemmas):›
lemma take_replicate[simp]: "take i (replicate k x) = replicate (min i k) x"
apply (case_tac "k ≤ i")
apply (drule not_le_imp_less)
apply (subgoal_tac "replicate k x = replicate i x @ replicate (k - i) x")
apply  simp
done

lemma drop_replicate[simp]: "drop i (replicate k x) = replicate (k-i) x"
apply (induct k arbitrary: i)
apply simp
apply clarsimp
apply (case_tac i)
apply simp
apply clarsimp
done

lemma set_replicate_Suc: "set (replicate (Suc n) x) = {x}"
by (induct n) auto

lemma set_replicate [simp]: "n ≠ 0 ==> set (replicate n x) = {x}"
by (fast dest!: not0_implies_Suc intro!: set_replicate_Suc)

lemma set_replicate_conv_if: "set (replicate n x) = (if n = 0 then {} else {x})"
by auto

lemma in_set_replicate[simp]: "(x ∈ set (replicate n y)) = (x = y ∧ n ≠ 0)"

lemma Ball_set_replicate[simp]:
"(∀x ∈ set(replicate n a). P x) = (P a ∨ n=0)"

lemma Bex_set_replicate[simp]:
"(∃x ∈ set(replicate n a). P x) = (P a ∧ n≠0)"

lemma replicate_append_same:
"replicate i x @ [x] = x # replicate i x"
by (induct i) simp_all

lemma map_replicate_trivial:
"map (λi. x) [0..<i] = replicate i x"
by (induct i) (simp_all add: replicate_append_same)

lemma concat_replicate_trivial[simp]:
"concat (replicate i []) = []"
by (induct i) (auto simp add: map_replicate_const)

lemma replicate_empty[simp]: "(replicate n x = []) ⟷ n=0"
by (induct n) auto

lemma empty_replicate[simp]: "([] = replicate n x) ⟷ n=0"
by (induct n) auto

lemma replicate_eq_replicate[simp]:
"(replicate m x = replicate n y) ⟷ (m=n ∧ (m≠0 ⟶ x=y))"
apply(induct m arbitrary: n)
apply simp
apply(induct_tac n)
apply auto
done

lemma replicate_length_filter:
"replicate (length (filter (λy. x = y) xs)) x = filter (λy. x = y) xs"
by (induct xs) auto

lemma comm_append_are_replicate:
"⟦ xs ≠ []; ys ≠ []; xs @ ys = ys @ xs ⟧
⟹ ∃m n zs. concat (replicate m zs) = xs ∧ concat (replicate n zs) = ys"
proof (induction "length (xs @ ys)" arbitrary: xs ys rule: less_induct)
case less

define xs' ys' where "xs' = (if (length xs ≤ length ys) then xs else ys)"
and "ys' = (if (length xs ≤ length ys) then ys else xs)"
then have
prems': "length xs' ≤ length ys'"
"xs' @ ys' = ys' @ xs'"
and "xs' ≠ []"
and len: "length (xs @ ys) = length (xs' @ ys')"
using less by (auto intro: less.hyps)

from prems'
obtain ws where "ys' = xs' @ ws"
by (auto simp: append_eq_append_conv2)

have "∃m n zs. concat (replicate m zs) = xs' ∧ concat (replicate n zs) = ys'"
proof (cases "ws = []")
case True
then have "concat (replicate 1 xs') = xs'"
and "concat (replicate 1 xs') = ys'"
using ‹ys' = xs' @ ws› by auto
then show ?thesis by blast
next
case False
from ‹ys' = xs' @ ws› and ‹xs' @ ys' = ys' @ xs'›
have "xs' @ ws = ws @ xs'" by simp
then have "∃m n zs. concat (replicate m zs) = xs' ∧ concat (replicate n zs) = ws"
using False and ‹xs' ≠ []› and ‹ys' = xs' @ ws› and len
by (intro less.hyps) auto
then obtain m n zs where *: "concat (replicate m zs) = xs'"
and "concat (replicate n zs) = ws" by blast
then have "concat (replicate (m + n) zs) = ys'"
using ‹ys' = xs' @ ws›
with * show ?thesis by blast
qed
then show ?case
using xs'_def ys'_def by meson
qed

lemma comm_append_is_replicate:
fixes xs ys :: "'a list"
assumes "xs ≠ []" "ys ≠ []"
assumes "xs @ ys = ys @ xs"
shows "∃n zs. n > 1 ∧ concat (replicate n zs) = xs @ ys"
proof -
obtain m n zs where "concat (replicate m zs) = xs"
and "concat (replicate n zs) = ys"
using comm_append_are_replicate[of xs ys, OF assms] by blast
then have "m + n > 1" and "concat (replicate (m+n) zs) = xs @ ys"
using ‹xs ≠ []› and ‹ys ≠ []›
then show ?thesis by blast
qed

lemma Cons_replicate_eq:
"x # xs = replicate n y ⟷ x = y ∧ n > 0 ∧ xs = replicate (n - 1) x"
by (induct n) auto

lemma replicate_length_same:
"(∀y∈set xs. y = x) ⟹ replicate (length xs) x = xs"
by (induct xs) simp_all

lemma foldr_replicate [simp]:
"foldr f (replicate n x) = f x ^^ n"
by (induct n) (simp_all)

lemma fold_replicate [simp]:
"fold f (replicate n x) = f x ^^ n"
by (subst foldr_fold [symmetric]) simp_all

subsubsection ‹@{const enumerate}›

lemma enumerate_simps [simp, code]:
"enumerate n [] = []"
"enumerate n (x # xs) = (n, x) # enumerate (Suc n) xs"
apply (auto simp add: enumerate_eq_zip not_le)
apply (cases "n < n + length xs")
done

lemma length_enumerate [simp]:
"length (enumerate n xs) = length xs"

lemma map_fst_enumerate [simp]:
"map fst (enumerate n xs) = [n..<n + length xs]"

lemma map_snd_enumerate [simp]:
"map snd (enumerate n xs) = xs"

lemma in_set_enumerate_eq:
"p ∈ set (enumerate n xs) ⟷ n ≤ fst p ∧ fst p < length xs + n ∧ nth xs (fst p - n) = snd p"
proof -
{ fix m
assume "n ≤ m"
moreover assume "m < length xs + n"
ultimately have "[n..<n + length xs] ! (m - n) = m ∧
xs ! (m - n) = xs ! (m - n) ∧ m - n < length xs" by auto
then have "∃q. [n..<n + length xs] ! q = m ∧
xs ! q = xs ! (m - n) ∧ q < length xs" ..
} then show ?thesis by (cases p) (auto simp add: enumerate_eq_zip in_set_zip)
qed

lemma nth_enumerate_eq: "m < length xs ⟹ enumerate n xs ! m = (n + m, xs ! m)"

lemma enumerate_replicate_eq:
"enumerate n (replicate m a) = map (λq. (q, a)) [n..<n + m]"
by (rule pair_list_eqI)

lemma enumerate_Suc_eq:
"enumerate (Suc n) xs = map (apfst Suc) (enumerate n xs)"
by (rule pair_list_eqI)

lemma distinct_enumerate [simp]:
"distinct (enumerate n xs)"

lemma enumerate_append_eq:
"enumerate n (xs @ ys) = enumerate n xs @ enumerate (n + length xs) ys"
unfolding enumerate_eq_zip
apply auto
apply (subst zip_append [symmetric]) apply simp
done

lemma enumerate_map_upt:
"enumerate n (map f [n..<m]) = map (λk. (k, f k)) [n..<m]"
by (cases "n ≤ m") (simp_all add: zip_map2 zip_same_conv_map enumerate_eq_zip)

subsubsection ‹@{const rotate1} and @{const rotate}›

lemma rotate0[simp]: "rotate 0 = id"

lemma rotate_Suc[simp]: "rotate (Suc n) xs = rotate1(rotate n xs)"

"rotate (m+n) = rotate m ∘ rotate n"

lemma rotate_rotate: "rotate m (rotate n xs) = rotate (m+n) xs"

lemma rotate1_rotate_swap: "rotate1 (rotate n xs) = rotate n (rotate1 xs)"

lemma rotate1_length01[simp]: "length xs <= 1 ⟹ rotate1 xs = xs"
by(cases xs) simp_all

lemma rotate_length01[simp]: "length xs <= 1 ⟹ rotate n xs = xs"
apply(induct n)
apply simp
done

lemma rotate1_hd_tl: "xs ≠ [] ⟹ rotate1 xs = tl xs @ [hd xs]"
by (cases xs) simp_all

lemma rotate_drop_take:
"rotate n xs = drop (n mod length xs) xs @ take (n mod length xs) xs"
apply(induct n)
apply simp
apply(cases "xs = []")
apply (simp)
apply(case_tac "n mod length xs = 0")
take_hd_drop linorder_not_le)
done

lemma rotate_conv_mod: "rotate n xs = rotate (n mod length xs) xs"

lemma rotate_id[simp]: "n mod length xs = 0 ⟹ rotate n xs = xs"

lemma length_rotate1[simp]: "length(rotate1 xs) = length xs"
by (cases xs) simp_all

lemma length_rotate[simp]: "length(rotate n xs) = length xs"
by (induct n arbitrary: xs) (simp_all add:rotate_def)

lemma distinct1_rotate[simp]: "distinct(rotate1 xs) = distinct xs"
by (cases xs) auto

lemma distinct_rotate[simp]: "distinct(rotate n xs) = distinct xs"

lemma rotate_map: "rotate n (map f xs) = map f (rotate n xs)"

lemma set_rotate1[simp]: "set(rotate1 xs) = set xs"
by (cases xs) auto

lemma set_rotate[simp]: "set(rotate n xs) = set xs"

lemma rotate1_is_Nil_conv[simp]: "(rotate1 xs = []) = (xs = [])"
by (cases xs) auto

lemma rotate_is_Nil_conv[simp]: "(rotate n xs = []) = (xs = [])"

lemma rotate_rev:
"rotate n (rev xs) = rev(rotate (length xs - (n mod length xs)) xs)"
proof (cases "length xs = 0 ∨ n mod length xs = 0")
case False
then show ?thesis
qed force

lemma hd_rotate_conv_nth: "xs ≠ [] ⟹ hd(rotate n xs) = xs!(n mod length xs)"
apply(subgoal_tac "length xs ≠ 0")
prefer 2 apply simp
using mod_less_divisor[of "length xs" n] by arith

lemma rotate_append: "rotate (length l) (l @ q) = q @ l"
by (induct l arbitrary: q) (auto simp add: rotate1_rotate_swap)

subsubsection ‹@{const nths} --- a generalization of @{const nth} to sets›

lemma nths_empty [simp]: "nths xs {} = []"

lemma nths_nil [simp]: "nths [] A = []"

lemma length_nths:
"length (nths xs I) = card{i. i < length xs ∧ i ∈ I}"

lemma nths_shift_lemma_Suc:
"map fst (filter (%p. P(Suc(snd p))) (zip xs is)) =
map fst (filter (%p. P(snd p)) (zip xs (map Suc is)))"
apply(induct xs arbitrary: "is")
apply simp
apply (case_tac "is")
apply simp
apply simp
done

lemma nths_shift_lemma:
"map fst (filter (λp. snd p ∈ A) (zip xs [i..<i + length xs])) =
map fst (filter (λp. snd p + i ∈ A) (zip xs [0..<length xs]))"

lemma nths_append:
"nths (l @ l') A = nths l A @ nths l' {j. j + length l ∈ A}"
apply (unfold nths_def)
apply (induct l' rule: rev_induct, simp)
done

lemma nths_Cons:
"nths (x # l) A = (if 0 ∈ A then [x] else []) @ nths l {j. Suc j ∈ A}"
apply (induct l rule: rev_induct)
apply (simp del: append_Cons add: append_Cons[symmetric] nths_append)
done

lemma nths_map: "nths (map f xs) I = map f (nths xs I)"
by(induction xs arbitrary: I) (simp_all add: nths_Cons)

lemma set_nths: "set(nths xs I) = {xs!i|i. i<size xs ∧ i ∈ I}"
apply(induct xs arbitrary: I)
apply(auto simp: nths_Cons nth_Cons split:nat.split dest!: gr0_implies_Suc)
done

lemma set_nths_subset: "set(nths xs I) ⊆ set xs"

lemma notin_set_nthsI[simp]: "x ∉ set xs ⟹ x ∉ set(nths xs I)"

lemma in_set_nthsD: "x ∈ set(nths xs I) ⟹ x ∈ set xs"

lemma nths_singleton [simp]: "nths [x] A = (if 0 ∈ A then [x] else [])"

lemma distinct_nthsI[simp]: "distinct xs ⟹ distinct (nths xs I)"
by (induct xs arbitrary: I) (auto simp: nths_Cons)

lemma nths_upt_eq_take [simp]: "nths l {..<n} = take n l"
by (induct l rule: rev_induct)

lemma filter_eq_nths: "filter P xs = nths xs {i. i<length xs ∧ P(xs!i)}"
by(induction xs) (auto simp: nths_Cons)

lemma filter_in_nths:
"distinct xs ⟹ filter (%x. x ∈ set (nths xs s)) xs = nths xs s"
proof (induct xs arbitrary: s)
case Nil thus ?case by simp
next
case (Cons a xs)
then have "∀x. x ∈ set xs ⟶ x ≠ a" by auto
with Cons show ?case by(simp add: nths_Cons cong:filter_cong)
qed

subsubsection ‹@{const subseqs} and @{const List.n_lists}›

lemma length_subseqs: "length (subseqs xs) = 2 ^ length xs"
by (induct xs) (simp_all add: Let_def)

lemma subseqs_powset: "set  set (subseqs xs) = Pow (set xs)"
proof -
have aux: "⋀x A. set  Cons x  A = insert x  set  A"
have "set (map set (subseqs xs)) = Pow (set xs)"
by (induct xs) (simp_all add: aux Let_def Pow_insert Un_commute comp_def del: map_map)
then show ?thesis by simp
qed

lemma distinct_set_subseqs:
assumes "distinct xs"
shows "distinct (map set (subseqs xs))"
proof (rule card_distinct)
have "finite (set xs)" ..
then have "card (Pow (set xs)) = 2 ^ card (set xs)"
by (rule card_Pow)
with assms distinct_card [of xs] have "card (Pow (set xs)) = 2 ^ length xs"
by simp
then show "card (set (map set (subseqs xs))) = length (map set (subseqs xs))"
qed

lemma n_lists_Nil [simp]: "List.n_lists n [] = (if n = 0 then [[]] else [])"
by (induct n) simp_all

lemma length_n_lists_elem: "ys ∈ set (List.n_lists n xs) ⟹ length ys = n"
by (induct n arbitrary: ys) auto

lemma set_n_lists: "set (List.n_lists n xs) = {ys. length ys = n ∧ set ys ⊆ set xs}"
proof (rule set_eqI)
fix ys :: "'a list"
show "ys ∈ set (List.n_lists n xs) ⟷ ys ∈ {ys. length ys = n ∧ set ys ⊆ set xs}"
proof -
have "ys ∈ set (List.n_lists n xs) ⟹ length ys = n"
by (induct n arbitrary: ys) auto
moreover have "⋀x. ys ∈ set (List.n_lists n xs) ⟹ x ∈ set ys ⟹ x ∈ set xs"
by (induct n arbitrary: ys) auto
moreover have "set ys ⊆ set xs ⟹ ys ∈ set (List.n_lists (length ys) xs)"
by (induct ys) auto
ultimately show ?thesis by auto
qed
qed

lemma subseqs_refl: "xs ∈ set (subseqs xs)"
by (induct xs) (simp_all add: Let_def)

lemma subset_subseqs: "X ⊆ set xs ⟹ X ∈ set  set (subseqs xs)"
unfolding subseqs_powset by simp

lemma Cons_in_subseqsD: "y # ys ∈ set (subseqs xs) ⟹ ys ∈ set (subseqs xs)"
by (induct xs) (auto simp: Let_def)

lemma subseqs_distinctD: "⟦ ys ∈ set (subseqs xs); distinct xs ⟧ ⟹ distinct ys"
proof (induct xs arbitrary: ys)
case (Cons x xs ys)
then show ?case
by (auto simp: Let_def) (metis Pow_iff contra_subsetD image_eqI subseqs_powset)
qed simp

subsubsection ‹@{const splice}›

lemma splice_Nil2 [simp, code]: "splice xs [] = xs"
by (cases xs) simp_all

declare splice.simps(1,3)[code]
declare splice.simps(2)[simp del]

lemma length_splice[simp]: "length(splice xs ys) = length xs + length ys"
by (induct xs ys rule: splice.induct) auto

subsubsection ‹@{const shuffle}›

lemma Nil_in_shuffle[simp]: "[] ∈ shuffle xs ys ⟷ xs = [] ∧ ys = []"
by (induct xs ys rule: shuffle.induct) auto

lemma shuffleE:
"zs ∈ shuffle xs ys ⟹
(zs = xs ⟹ ys = [] ⟹ P) ⟹
(zs = ys ⟹ xs = [] ⟹ P) ⟹
(⋀x xs' z zs'. xs = x # xs' ⟹ zs = z # zs' ⟹ x = z ⟹ zs' ∈ shuffle xs' ys ⟹ P) ⟹
(⋀y ys' z zs'. ys = y # ys' ⟹ zs = z # zs' ⟹ y = z ⟹ zs' ∈ shuffle xs ys' ⟹ P) ⟹ P"
by (induct xs ys rule: shuffle.induct) auto

lemma Cons_in_shuffle_iff:
"z # zs ∈ shuffle xs ys ⟷
(xs ≠ [] ∧ hd xs = z ∧ zs ∈ shuffle (tl xs) ys ∨
ys ≠ [] ∧ hd ys = z ∧ zs ∈ shuffle xs (tl ys))"
by (induct xs ys rule: shuffle.induct) auto

lemma splice_in_shuffle [simp, intro]: "splice xs ys ∈ shuffle xs ys"
by (induction xs ys rule: splice.induct) (simp_all add: Cons_in_shuffle_iff)

lemma Nil_in_shuffleI: "xs = [] ⟹ ys = [] ⟹ [] ∈ shuffle xs ys"
by simp

lemma Cons_in_shuffle_leftI: "zs ∈ shuffle xs ys ⟹ z # zs ∈ shuffle (z # xs) ys"
by (cases ys) auto

lemma Cons_in_shuffle_rightI: "zs ∈ shuffle xs ys ⟹ z # zs ∈ shuffle xs (z # ys)"
by (cases xs) auto

lemma finite_shuffle [simp, intro]: "finite (shuffle xs ys)"
by (induction xs ys rule: shuffle.induct) simp_all

lemma length_shuffle: "zs ∈ shuffle xs ys ⟹ length zs = length xs + length ys"
by (induction xs ys arbitrary: zs rule: shuffle.induct) auto

lemma set_shuffle: "zs ∈ shuffle xs ys ⟹ set zs = set xs ∪ set ys"
by (induction xs ys arbitrary: zs rule: shuffle.induct) auto

lemma distinct_disjoint_shuffle:
assumes "distinct xs" "distinct ys" "set xs ∩ set ys = {}" "zs ∈ shuffle xs ys"
shows   "distinct zs"
using assms
proof (induction xs ys arbitrary: zs rule: shuffle.induct)
case (3 x xs y ys)
show ?case
proof (cases zs)
case (Cons z zs')
with "3.prems" and "3.IH"[of zs'] show ?thesis by (force dest: set_shuffle)
qed simp_all
qed simp_all

lemma shuffle_commutes: "shuffle xs ys = shuffle ys xs"
by (induction xs ys rule: shuffle.induct) (simp_all add: Un_commute)

lemma Cons_shuffle_subset1: "(#) x  shuffle xs ys ⊆ shuffle (x # xs) ys"
by (cases ys) auto

lemma Cons_shuffle_subset2: "(#) y  shuffle xs ys ⊆ shuffle xs (y # ys)"
by (cases xs) auto

lemma filter_shuffle:
"filter P  shuffle xs ys = shuffle (filter P xs) (filter P ys)"
proof -
have *: "filter P  (#) x  A = (if P x then (#) x  filter P  A else filter P  A)" for x A
by (auto simp: image_image)
show ?thesis
by (induction xs ys rule: shuffle.induct)
(simp_all split: if_splits add: image_Un * Un_absorb1 Un_absorb2
Cons_shuffle_subset1 Cons_shuffle_subset2)
qed

lemma filter_shuffle_disjoint1:
assumes "set xs ∩ set ys = {}" "zs ∈ shuffle xs ys"
shows   "filter (λx. x ∈ set xs) zs = xs" (is "filter ?P _ = _")
and   "filter (λx. x ∉ set xs) zs = ys" (is "filter ?Q _ = _")
using assms
proof -
from assms have "filter ?P zs ∈ filter ?P  shuffle xs ys" by blast
also have "filter ?P  shuffle xs ys = shuffle (filter ?P xs) (filter ?P ys)"
by (rule filter_shuffle)
also have "filter ?P xs = xs" by (rule filter_True) simp_all
also have "filter ?P ys = []" by (rule filter_False) (insert assms(1), auto)
also have "shuffle xs [] = {xs}" by simp
finally show "filter ?P zs = xs" by simp
next
from assms have "filter ?Q zs ∈ filter ?Q  shuffle xs ys" by blast
also have "filter ?Q  shuffle xs ys = shuffle (filter ?Q xs) (filter ?Q ys)"
by (rule filter_shuffle)
also have "filter ?Q ys = ys" by (rule filter_True) (insert assms(1), auto)
also have "filter ?Q xs = []" by (rule filter_False) (insert assms(1), auto)
also have "shuffle [] ys = {ys}" by simp
finally show "filter ?Q zs = ys" by simp
qed

lemma filter_shuffle_disjoint2:
assumes "set xs ∩ set ys = {}" "zs ∈ shuffle xs ys"
shows   "filter (λx. x ∈ set ys) zs = ys" "filter (λx. x ∉ set ys) zs = xs"
using filter_shuffle_disjoint1[of ys xs zs] assms

lemma partition_in_shuffle:
"xs ∈ shuffle (filter P xs) (filter (λx. ¬P x) xs)"
proof (induction xs)
case (Cons x xs)
show ?case
proof (cases "P x")
case True
hence "x # xs ∈ (#) x  shuffle (filter P xs) (filter (λx. ¬P x) xs)"
by (intro imageI Cons.IH)
also have "… ⊆ shuffle (filter P (x # xs)) (filter (λx. ¬P x) (x # xs))"
finally show ?thesis .
next
case False
hence "x # xs ∈ (#) x  shuffle (filter P xs) (filter (λx. ¬P x) xs)"
by (intro imageI Cons.IH)
also have "… ⊆ shuffle (filter P (x # xs)) (filter (λx. ¬P x) (x # xs))"
finally show ?thesis .
qed
qed auto

lemma inv_image_partition:
assumes "⋀x. x ∈ set xs ⟹ P x" "⋀y. y ∈ set ys ⟹ ¬P y"
shows   "partition P - {(xs, ys)} = shuffle xs ys"
proof (intro equalityI subsetI)
fix zs assume zs: "zs ∈ shuffle xs ys"
hence [simp]: "set zs = set xs ∪ set ys" by (rule set_shuffle)
from assms have "filter P zs = filter (λx. x ∈ set xs) zs"
"filter (λx. ¬P x) zs = filter (λx. x ∈ set ys) zs"
by (intro filter_cong refl; force)+
moreover from assms have "set xs ∩ set ys = {}" by auto
ultimately show "zs ∈ partition P - {(xs, ys)}" using zs
by (simp add: o_def filter_shuffle_disjoint1 filter_shuffle_disjoint2)
next
fix zs assume "zs ∈ partition P - {(xs, ys)}"
thus "zs ∈ shuffle xs ys" using partition_in_shuffle[of zs] by (auto simp: o_def)
qed

subsubsection ‹Transpose›

function transpose where
"transpose []             = []" |
"transpose ([]     # xss) = transpose xss" |
"transpose ((x#xs) # xss) =
(x # [h. (h#t) ← xss]) # transpose (xs # [t. (h#t) ← xss])"
by pat_completeness auto

"concat (map (case_list [] (λh t. [h])) xss) =
map (λxs. hd xs) (filter (λys. ys ≠ []) xss)"
by (induct xss) (auto split: list.split)

lemma transpose_aux_filter_tail:
"concat (map (case_list [] (λh t. [t])) xss) =
map (λxs. tl xs) (filter (λys. ys ≠ []) xss)"
by (induct xss) (auto split: list.split)

lemma transpose_aux_max:
"max (Suc (length xs)) (foldr (λxs. max (length xs)) xss 0) =
Suc (max (length xs) (foldr (λx. max (length x - Suc 0)) (filter (λys. ys ≠ []) xss) 0))"
(is "max _ ?foldB = Suc (max _ ?foldA)")
proof (cases "(filter (λys. ys ≠ []) xss) = []")
case True
hence "foldr (λxs. max (length xs)) xss 0 = 0"
proof (induct xss)
case (Cons x xs)
then have "x = []" by (cases x) auto
with Cons show ?case by auto
qed simp
thus ?thesis using True by simp
next
case False

have foldA: "?foldA = foldr (λx. max (length x)) (filter (λys. ys ≠ []) xss) 0 - 1"
by (induct xss) auto
have foldB: "?foldB = foldr (λx. max (length x)) (filter (λys. ys ≠ []) xss) 0"
by (induct xss) auto

have "0 < ?foldB"
proof -
from False
obtain z zs where zs: "(filter (λys. ys ≠ []) xss) = z#zs" by (auto simp: neq_Nil_conv)
hence "z ∈ set (filter (λys. ys ≠ []) xss)" by auto
hence "z ≠ []" by auto
thus ?thesis
unfolding foldB zs
by (auto simp: max_def intro: less_le_trans)
qed
thus ?thesis
unfolding foldA foldB max_Suc_Suc[symmetric]
by simp
qed

termination transpose
by (relation "measure (λxs. foldr (λxs. max (length xs)) xs 0 + length xs)")
(auto simp: transpose_aux_filter_tail foldr_map comp_def transpose_aux_max less_Suc_eq_le)

lemma transpose_empty: "(transpose xs = []) ⟷ (∀x ∈ set xs. x = [])"
by (induct rule: transpose.induct) simp_all

lemma length_transpose:
fixes xs :: "'a list list"
shows "length (transpose xs) = foldr (λxs. max (length xs)) xs 0"
by (induct rule: transpose.induct)
(auto simp: transpose_aux_filter_tail foldr_map comp_def transpose_aux_max
max_Suc_Suc[symmetric] simp del: max_Suc_Suc)

lemma nth_transpose:
fixes xs :: "'a list list"
assumes "i < length (transpose xs)"
shows "transpose xs ! i = map (λxs. xs ! i) (filter (λys. i < length ys) xs)"
using assms proof (induct arbitrary: i rule: transpose.induct)
case (3 x xs xss)
define XS where "XS = (x # xs) # xss"
hence [simp]: "XS ≠ []" by auto
thus ?case
proof (cases i)
case 0
next
case (Suc j)
have *: "⋀xss. xs # map tl xss = map tl ((x#xs)#xss)" by simp
have **: "⋀xss. (x#xs) # filter (λys. ys ≠ []) xss = filter (λys. ys ≠ []) ((x#xs)#xss)" by simp
{ fix x have "Suc j < length x ⟷ x ≠ [] ∧ j < length x - Suc 0"
by (cases x) simp_all
} note *** = this

have j_less: "j < length (transpose (xs # concat (map (case_list [] (λh t. [t])) xss)))"
using "3.prems" by (simp add: transpose_aux_filter_tail length_transpose Suc)

show ?thesis
unfolding transpose.simps ‹i = Suc j› nth_Cons_Suc "3.hyps"[OF j_less]
apply (auto simp: transpose_aux_filter_tail filter_map comp_def length_transpose * ** *** XS_def[symmetric])
apply (rule list.exhaust)
by auto
qed
qed simp_all

lemma transpose_map_map:
"transpose (map (map f) xs) = map (map f) (transpose xs)"
proof (rule nth_equalityI, safe)
have [simp]: "length (transpose (map (map f) xs)) = length (transpose xs)"
by (simp add: length_transpose foldr_map comp_def)
show "length (transpose (map (map f) xs)) = length (map (map f) (transpose xs))" by simp

fix i assume "i < length (transpose (map (map f) xs))"
thus "transpose (map (map f) xs) ! i = map (map f) (transpose xs) ! i"
by (simp add: nth_transpose filter_map comp_def)
qed

subsubsection ‹@{const min} and @{const arg_min}›

lemma min_list_Min: "xs ≠ [] ⟹ min_list xs = Min (set xs)"
by (induction xs rule: induct_list012)(auto)

lemma f_arg_min_list_f: "xs ≠ [] ⟹ f (arg_min_list f xs) = Min (f  (set xs))"
by(induction f xs rule: arg_min_list.induct) (auto simp: min_def intro!: antisym)

lemma arg_min_list_in: "xs ≠ [] ⟹ arg_min_list f xs ∈ set xs"
by(induction xs rule: induct_list012) (auto simp: Let_def)

subsubsection ‹(In)finiteness›

lemma finite_maxlen:
"finite (M::'a list set) ⟹ ∃n. ∀s∈M. size s < n"
proof (induct rule: finite.induct)
case emptyI show ?case by simp
next
case (insertI M xs)
then obtain n where "∀s∈M. length s < n" by blast
hence "∀s∈insert xs M. size s < max n (size xs) + 1" by auto
thus ?case ..
qed

lemma lists_length_Suc_eq:
"{xs. set xs ⊆ A ∧ length xs = Suc n} =
(λ(xs, n). n#xs)  ({xs. set xs ⊆ A ∧ length xs = n} × A)"
by (auto simp: length_Suc_conv)

lemma
assumes "finite A"
shows finite_lists_length_eq: "finite {xs. set xs ⊆ A ∧ length xs = n}"
and card_lists_length_eq: "card {xs. set xs ⊆ A ∧ length xs = n} = (card A)^n"
using ‹finite A›
by (induct n)
(auto simp: card_image inj_split_Cons lists_length_Suc_eq cong: conj_cong)

lemma finite_lists_length_le:
assumes "finite A" shows "finite {xs. set xs ⊆ A ∧ length xs ≤ n}"
(is "finite ?S")
proof-
have "?S = (⋃n∈{0..n}. {xs. set xs ⊆ A ∧ length xs = n})" by auto
thus ?thesis by (auto intro!: finite_lists_length_eq[OF ‹finite A›] simp only:)
qed

lemma card_lists_length_le:
assumes "finite A" shows "card {xs. set xs ⊆ A ∧ length xs ≤ n} = (∑i≤n. card A^i)"
proof -
have "(∑i≤n. card A^i) = card (⋃i≤n. {xs. set xs ⊆ A ∧ length xs = i})"
using ‹finite A›
by (subst card_UN_disjoint)
also have "(⋃i≤n. {xs. set xs ⊆ A ∧ length xs = i}) = {xs. set xs ⊆ A ∧ length xs ≤ n}"
by auto
finally show ?thesis by simp
qed

lemma finite_lists_distinct_length_eq [intro]:
assumes "finite A"
shows "finite {xs. length xs = n ∧ distinct xs ∧ set xs ⊆ A}" (is "finite ?S")
proof -
have "finite {xs. set xs ⊆ A ∧ length xs = n}"
using ‹finite A› by (rule finite_lists_length_eq)
moreover have "?S ⊆ {xs. set xs ⊆ A ∧ length xs = n}" by auto
ultimately show ?thesis using finite_subset by auto
qed

lemma card_lists_distinct_length_eq:
assumes "finite A" "k ≤ card A"
shows "card {xs. length xs = k ∧ distinct xs ∧ set xs ⊆ A} = ∏{card A - k + 1 .. card A}"
using assms
proof (induct k)
case 0
then have "{xs. length xs = 0 ∧ distinct xs ∧ set xs ⊆ A} = {[]}" by auto
then show ?case by simp
next
case (Suc k)
let "?k_list" = "λk xs. length xs = k ∧ distinct xs ∧ set xs ⊆ A"
have inj_Cons: "⋀A. inj_on (λ(xs, n). n # xs) A"  by (rule inj_onI) auto

from Suc have "k ≤ card A" by simp
moreover note ‹finite A›
moreover have "finite {xs. ?k_list k xs}"
by (rule finite_subset) (use finite_lists_length_eq[OF ‹finite A›, of k] in auto)
moreover have "⋀i j. i ≠ j ⟶ {i} × (A - set i) ∩ {j} × (A - set j) = {}"
by auto
moreover have "⋀i. i ∈ {xs. ?k_list k xs} ⟹ card (A - set i) = card A - k"
moreover have "{xs. ?k_list (Suc k) xs} =
(λ(xs, n). n#xs)  ⋃((λxs. {xs} × (A - set xs))  {xs. ?k_list k xs})"
by (auto simp: length_Suc_conv)
moreover have "Suc (card A - Suc k) = card A - k" using Suc.prems by simp
then have "(card A - k) * ∏{Suc (card A - k)..card A} = ∏{Suc (card A - Suc k)..card A}"
by (subst prod.insert[symmetric]) (simp add: atLeastAtMost_insertL)+
ultimately show ?case
by (simp add: card_image inj_Cons card_UN_disjoint Suc.hyps algebra_simps)
qed

lemma card_lists_distinct_length_eq':
assumes "k < card A"
shows "card {xs. length xs = k ∧ distinct xs ∧ set xs ⊆ A} = ∏{card A - k + 1 .. card A}"
proof -
from ‹k < card A› have "finite A" and "k ≤ card A" using card_infinite by force+
from this show ?thesis by (rule card_lists_distinct_length_eq)
qed

lemma infinite_UNIV_listI: "¬ finite(UNIV::'a list set)"
apply (rule notI)
apply (drule finite_maxlen)
apply clarsimp
apply (erule_tac x = "replicate n undefined" in allE)
by simp

subsection ‹Sorting›

subsubsection ‹@{const sorted_wrt}›

text ‹Sometimes the second equation in the definition of @{const sorted_wrt} is too aggressive
because it relates each list element to \emph{all} its successors. Then this equation

lemma sorted_wrt1: "sorted_wrt P [x] = True"
by(simp)

lemma sorted_wrt2: "transp P ⟹ sorted_wrt P (x # y # zs) = (P x y ∧ sorted_wrt P (y # zs))"
apply(induction zs arbitrary: x y)
apply(auto dest: transpD)
apply (meson transpD)
done

lemmas sorted_wrt2_simps = sorted_wrt1 sorted_wrt2

lemma sorted_wrt_true [simp]:
"sorted_wrt (λ_ _. True) xs"
by (induction xs) simp_all

lemma sorted_wrt_append:
"sorted_wrt P (xs @ ys) ⟷
sorted_wrt P xs ∧ sorted_wrt P ys ∧ (∀x∈set xs. ∀y∈set ys. P x y)"
by (induction xs) auto

lemma sorted_wrt_map:
"sorted_wrt R (map f xs) = sorted_wrt (λx y. R (f x) (f y)) xs"
by (induction xs) simp_all

lemma sorted_wrt_rev:
"sorted_wrt P (rev xs) = sorted_wrt (λx y. P y x) xs"
by (induction xs) (auto simp add: sorted_wrt_append)

lemma sorted_wrt_mono_rel:
"(⋀x y. ⟦ x ∈ set xs; y ∈ set xs; P x y ⟧ ⟹ Q x y) ⟹ sorted_wrt P xs ⟹ sorted_wrt Q xs"
by(induction xs)(auto)

lemma sorted_wrt01: "length xs ≤ 1 ⟹ sorted_wrt P xs"
by(auto simp: le_Suc_eq length_Suc_conv)

lemma sorted_wrt_iff_nth_less:
"sorted_wrt P xs = (∀i j. i < j ⟶ j < length xs ⟶ P (xs ! i) (xs ! j))"
apply(induction xs)
apply(auto simp add: in_set_conv_nth Ball_def nth_Cons split: nat.split)
done

lemma sorted_wrt_nth_less:
"⟦ sorted_wrt P xs; i < j; j < length xs ⟧ ⟹ P (xs ! i) (xs ! j)"
by(auto simp: sorted_wrt_iff_nth_less)

lemma sorted_wrt_upt[simp]: "sorted_wrt (<) [m..<n]"
by(induction n) (auto simp: sorted_wrt_append)

lemma sorted_wrt_upto[simp]: "sorted_wrt (<) [i..j]"
apply(induction i j rule: upto.induct)
apply(subst upto.simps)
apply(simp)
done

text ‹Each element is greater or equal to its index:›

lemma sorted_wrt_less_idx:
"sorted_wrt (<) ns ⟹ i < length ns ⟹ i ≤ ns!i"
proof (induction ns arbitrary: i rule: rev_induct)
case Nil thus ?case by simp
next
case snoc
thus ?case
by (auto simp: nth_append sorted_wrt_append)
(metis less_antisym not_less nth_mem)
qed

subsubsection ‹@{const sorted}›

context linorder
begin

text ‹Sometimes the second equation in the definition of @{const sorted} is too aggressive
because it relates each list element to \emph{all} its successors. Then this equation
Executable code is one such use case.›

lemma sorted1: "sorted [x] = True"
by simp

lemma sorted2: "sorted (x # y # zs) = (x ≤ y ∧ sorted (y # zs))"
by(induction zs) auto

lemmas sorted2_simps = sorted1 sorted2

lemmas [code] = sorted.simps(1) sorted2_simps

lemma sorted_append:
"sorted (xs@ys) = (sorted xs ∧ sorted ys ∧ (∀x ∈ set xs. ∀y ∈ set ys. x≤y))"

lemma sorted_map:
"sorted (map f xs) = sorted_wrt (λx y. f x ≤ f y) xs"

lemma sorted01: "length xs ≤ 1 ⟹ sorted xs"

lemma sorted_tl:
"sorted xs ⟹ sorted (tl xs)"
by (cases xs) (simp_all)

lemma sorted_iff_nth_mono_less:
"sorted xs = (∀i j. i < j ⟶ j < length xs ⟶ xs ! i ≤ xs ! j)"

lemma sorted_iff_nth_mono:
"sorted xs = (∀i j. i ≤ j ⟶ j < length xs ⟶ xs ! i ≤ xs ! j)"
by (auto simp: sorted_iff_nth_mono_less nat_less_le)

lemma sorted_nth_mono:
"sorted xs ⟹ i ≤ j ⟹ j < length xs ⟹ xs!i ≤ xs!j"
by (auto simp: sorted_iff_nth_mono)

lemma sorted_rev_nth_mono:
"sorted (rev xs) ⟹ i ≤ j ⟹ j < length xs ⟹ xs!j ≤ xs!i"
using sorted_nth_mono[ of "rev xs" "length xs - j - 1" "length xs - i - 1"]
rev_nth[of "length xs - i - 1" "xs"] rev_nth[of "length xs - j - 1" "xs"]
by auto

lemma sorted_map_remove1:
"sorted (map f xs) ⟹ sorted (map f (remove1 x xs))"
by (induct xs) (auto)

lemma sorted_remove1: "sorted xs ⟹ sorted (remove1 a xs)"
using sorted_map_remove1 [of "λx. x"] by simp

lemma sorted_butlast:
assumes "xs ≠ []" and "sorted xs"
shows "sorted (butlast xs)"
proof -
from ‹xs ≠ []› obtain ys y where "xs = ys @ [y]"
by (cases xs rule: rev_cases) auto
with ‹sorted xs› show ?thesis by (simp add: sorted_append)
qed

lemma sorted_replicate [simp]: "sorted(replicate n x)"
by(induction n) (auto)

lemma sorted_remdups[simp]:
"sorted xs ⟹ sorted (remdups xs)"
by (induct xs) (auto)

"sorted xs ⟹ sorted (remdups_adj xs)"
by (induct xs rule: remdups_adj.induct, simp_all split: if_split_asm)

lemma sorted_nths: "sorted xs ⟹ sorted (nths xs I)"
by(induction xs arbitrary: I)(auto simp: nths_Cons)

lemma sorted_distinct_set_unique:
assumes "sorted xs" "distinct xs" "sorted ys" "distinct ys" "set xs = set ys"
shows "xs = ys"
proof -
from assms have 1: "length xs = length ys" by (auto dest!: distinct_card)
from assms show ?thesis
proof(induct rule:list_induct2[OF 1])
case 1 show ?case by simp
next
case 2 thus ?case by simp (metis Diff_insert_absorb antisym insertE insert_iff)
qed
qed

lemma map_sorted_distinct_set_unique:
assumes "inj_on f (set xs ∪ set ys)"
assumes "sorted (map f xs)" "distinct (map f xs)"
"sorted (map f ys)" "distinct (map f ys)"
assumes "set xs = set ys"
shows "xs = ys"
proof -
from assms have "map f xs = map f ys"
with ‹inj_on f (set xs ∪ set ys)› show "xs = ys"
by (blast intro: map_inj_on)
qed

lemma
assumes "sorted xs"
shows sorted_take: "sorted (take n xs)"
and sorted_drop: "sorted (drop n xs)"
proof -
from assms have "sorted (take n xs @ drop n xs)" by simp
then show "sorted (take n xs)" and "sorted (drop n xs)"
unfolding sorted_append by simp_all
qed

lemma sorted_dropWhile: "sorted xs ⟹ sorted (dropWhile P xs)"
by (auto dest: sorted_drop simp add: dropWhile_eq_drop)

lemma sorted_takeWhile: "sorted xs ⟹ sorted (takeWhile P xs)"
by (subst takeWhile_eq_take) (auto dest: sorted_take)

lemma sorted_filter:
"sorted (map f xs) ⟹ sorted (map f (filter P xs))"
by (induct xs) simp_all

lemma foldr_max_sorted:
assumes "sorted (rev xs)"
shows "foldr max xs y = (if xs = [] then y else max (xs ! 0) y)"
using assms
proof (induct xs)
case (Cons x xs)
then have "sorted (rev xs)" using sorted_append by auto
with Cons show ?case
by (cases xs) (auto simp add: sorted_append max_def)
qed simp

lemma filter_equals_takeWhile_sorted_rev:
assumes sorted: "sorted (rev (map f xs))"
shows "filter (λx. t < f x) xs = takeWhile (λ x. t < f x) xs"
(is "filter ?P xs = ?tW")
proof (rule takeWhile_eq_filter[symmetric])
let "?dW" = "dropWhile ?P xs"
fix x assume "x ∈ set ?dW"
then obtain i where i: "i < length ?dW" and nth_i: "x = ?dW ! i"
unfolding in_set_conv_nth by auto
hence "length ?tW + i < length (?tW @ ?dW)"
unfolding length_append by simp
hence i': "length (map f ?tW) + i < length (map f xs)" by simp
have "(map f ?tW @ map f ?dW) ! (length (map f ?tW) + i) ≤
(map f ?tW @ map f ?dW) ! (length (map f ?tW) + 0)"
using sorted_rev_nth_mono[OF sorted _ i', of "length ?tW"]
unfolding map_append[symmetric] by simp
hence "f x ≤ f (?dW ! 0)"
unfolding nth_append_length_plus nth_i
using i preorder_class.le_less_trans[OF le0 i] by simp
also have "... ≤ t"
using hd_dropWhile[of "?P" xs] le0[THEN preorder_class.le_less_trans, OF i]
using hd_conv_nth[of "?dW"] by simp
finally show "¬ t < f x" by simp
qed

lemma sorted_map_same:
"sorted (map f (filter (λx. f x = g xs) xs))"
proof (induct xs arbitrary: g)
case Nil then show ?case by simp
next
case (Cons x xs)
then have "sorted (map f (filter (λy. f y = (λxs. f x) xs) xs))" .
moreover from Cons have "sorted (map f (filter (λy. f y = (g ∘ Cons x) xs) xs))" .
ultimately show ?case by simp_all
qed

lemma sorted_same:
"sorted (filter (λx. x = g xs) xs)"
using sorted_map_same [of "λx. x"] by simp

end

lemma sorted_upt[simp]: "sorted [m..<n]"
by(simp add: sorted_sorted_wrt sorted_wrt_mono_rel[OF _ sorted_wrt_upt])

lemma sorted_upto[simp]: "sorted [m..n]"
by(simp add: sorted_sorted_wrt sorted_wrt_mono_rel[OF</`