(*  Title:      AF_Stream.thy
    Date:       Jan 2007
    Author:     David Trachtenherz
*)

section \<open>\textsc{AutoFocus} message streams\<close>

theory AF_Stream
imports ListSlice
begin

subsection \<open>Basic definitions\<close>

subsubsection \<open>Time-synchronous streams\<close>

datatype 'a message_af = NoMsg | Msg 'a

notation (latex)
  NoMsg  ("\<NoMsg>") and
  Msg  ("\<Msg>")

text \<open>Abbreviation for finite streams\<close>
type_synonym 'a fstream_af = "'a message_af list"

text \<open>Abbreviation for infinite streams\<close>
type_synonym 'a istream_af = "'a message_af ilist"

lemma not_NoMsg_eq: "(m \<noteq> \<NoMsg>) = (\<exists>x. m = \<Msg> x)"
by (case_tac m, simp_all)

lemma not_Msg_eq: "(\<forall>x. m \<noteq> \<Msg> x) = (m = \<NoMsg>) "
by (case_tac m, simp_all)

primrec the_af :: "'a message_af \<Rightarrow> 'a"
  where "the_af (\<Msg> x) = x"

text \<open>
  By this definition one can determine,
  whether data elements of different data structures with messages,
  especially product types of arbitrary sizes and records,
  are pointwise equal to NoMsg, i.e., contain only NoMsg entries.\<close>

consts is_NoMsg :: "'a \<Rightarrow> bool"

overloading is_NoMsg \<equiv> "is_NoMsg :: 'a message_af \<Rightarrow> bool"
begin

primrec is_NoMsg :: "'a message_af \<Rightarrow> bool"
where
  "is_NoMsg \<NoMsg> = True"
| "is_NoMsg (\<Msg> x) = False"

end

overloading is_NoMsg \<equiv> "is_NoMsg :: ('a \<times> 'b) \<Rightarrow> bool"
begin

definition is_NoMsg_tuple_def  :
  "is_NoMsg (p::'a \<times> 'b) \<equiv> (is_NoMsg (fst p) \<and> is_NoMsg (snd p))"

end

overloading is_NoMsg \<equiv> "is_NoMsg :: 'a set \<Rightarrow> bool"
begin

definition is_NoMsg_set_def :
  "is_NoMsg (A::'a set) \<equiv> (\<forall>x\<in>A. is_NoMsg x)"

end

record SomeRecordExample =
  Field1 :: "nat message_af"
  Field2 :: "int message_af"
  Field3 :: "int message_af"
overloading is_NoMsg \<equiv> "is_NoMsg :: 'a SomeRecordExample_scheme \<Rightarrow> bool"
begin

definition is_NoMsg_SomeRecordExample_def :
  "is_NoMsg (r:: 'a SomeRecordExample_scheme) \<equiv>
    Field1 r = \<NoMsg> \<and> Field2 r = \<NoMsg> \<and> Field3 r = \<NoMsg>"

end

definition is_Msg :: "'a \<Rightarrow> bool"
  where "is_Msg x \<equiv> (\<not> is_NoMsg x)"

lemma is_NoMsg_message_af_conv: "is_NoMsg m = (case m of \<NoMsg> \<Rightarrow> True | \<Msg> x \<Rightarrow> False)"
by (case_tac m, simp+)

lemma is_NoMsg_message_af_conv2: "is_NoMsg m = (m = \<NoMsg>)"
by (case_tac m, simp+)

lemma is_Msg_message_af_conv: "is_Msg m = (case m of \<NoMsg> \<Rightarrow> False | \<Msg> x \<Rightarrow> True)"
by (unfold is_Msg_def, case_tac m, simp+)

lemma is_Msg_message_af_conv2: "is_Msg m = (m \<noteq> \<NoMsg>)"
by (unfold is_Msg_def, case_tac m, simp+)

text \<open>Collection for definitions for \<open>is_NoMsg\<close>.\<close>

named_theorems is_NoMsg_defs

declare
  is_NoMsg_tuple_def[is_NoMsg_defs]
  is_NoMsg_set_def [is_NoMsg_defs]
  is_NoMsg_SomeRecordExample_def[is_NoMsg_defs]
  is_Msg_def[is_NoMsg_defs]

lemma not_is_NoMsg: "(\<not> is_NoMsg m) = is_Msg m"
by (simp add: is_NoMsg_defs)

lemma not_is_Msg: "(\<not> is_Msg m) = is_NoMsg m"
by (simp add: is_NoMsg_defs)

lemma "is_NoMsg (\<NoMsg>::(nat message_af))"
by simp

lemma "is_NoMsg (\<NoMsg>::(nat message_af), \<NoMsg>::(nat message_af))"
by (simp add: is_NoMsg_defs)

lemma "is_NoMsg (\<NoMsg>::(nat message_af), \<NoMsg>::(nat message_af), \<NoMsg>::(nat message_af))"
by (simp add: is_NoMsg_defs)

lemma "is_Msg (\<NoMsg>::(nat message_af), \<Msg> (1::nat), \<NoMsg>::(nat message_af))"
by (simp add: is_NoMsg_defs)

lemma "is_NoMsg {\<NoMsg>::(nat message_af), \<NoMsg>}"
by (simp add: is_NoMsg_defs)

lemma "is_Msg {\<NoMsg>::(nat message_af), \<Msg> 1}"
by (simp add: is_NoMsg_defs)

lemma "is_NoMsg \<lparr> Field1 = \<NoMsg>, Field2 = \<NoMsg>, Field3 = \<NoMsg> \<rparr>"
by (simp add: is_NoMsg_defs)

lemma "is_Msg  \<lparr> Field1 = \<NoMsg>, Field2 = Msg 1, Field3 = \<NoMsg> \<rparr>"
by (simp add: is_NoMsg_defs)

subsubsection \<open>Time abstraction\<close>

(* Time abstraction:
   Extracts non-empty messages from a stream =
   time abstraction for time-synchronous streams*)
primrec untime :: "'a fstream_af \<Rightarrow> 'a list" (*("\<registered>_" 100)*)
where
  "untime [] = []"
| "untime (x#xs) =
    (if x = \<NoMsg>
     then (untime xs)
     else (the_af x) # (untime xs))"

lemma untime_eq_filter[rule_format]: "
  map (\<lambda>x. \<Msg> x) (untime s) = filter (\<lambda>x. x \<noteq> \<NoMsg>) s"
apply (induct s, simp)
apply (case_tac a, simp_all)
done

text \<open>The following lemma involves @{term the_af} function
  and thus is some more limited than the previous lemma\<close>
corollary untime_eq_filter2[rule_format]: "
  untime s = map (\<lambda>x. the_af x) (filter (\<lambda>x. x \<noteq> \<NoMsg>) s)"
by (induct s, simp_all)

definition untime_length :: "'a fstream_af \<Rightarrow> nat"
  where "untime_length s \<equiv> length (untime s)"

primrec untime_length_cnt :: "'a fstream_af \<Rightarrow> nat"
where
  "untime_length_cnt [] = 0"
| "untime_length_cnt (x # xs) =
    (if x = \<NoMsg> then 0 else Suc 0) + untime_length_cnt xs"

lemma untime_length_eq_untime_length_cnt: "
  untime_length s = untime_length_cnt s"
by (induct s, simp_all add: untime_length_def)

definition untime_length_filter :: "'a fstream_af \<Rightarrow> nat"
  where "untime_length_filter s \<equiv> length (filter (\<lambda>x. x \<noteq> \<NoMsg>) s)"

lemma untime_empty_conv: "(untime s = []) = (\<forall>n<length s. s ! n = \<NoMsg>)"
apply (induct s)
 apply simp
apply (force simp add: nth.simps split: nat.split)
done

lemma untime_not_empty_conv: "(untime s \<noteq> []) = (\<exists>n<length s. s ! n \<noteq> \<NoMsg>)"
by (simp add: untime_empty_conv)

corollary untime_empty_imp_NoMsg[rule_format]: "
  \<lbrakk> untime s = []; n < length s \<rbrakk> \<Longrightarrow> s ! n = \<NoMsg>"
by (rule untime_empty_conv[THEN iffD1, rule_format])

lemma untime_nth_eq_filter: "
  n < untime_length s \<Longrightarrow>
  \<Msg> (untime s ! n) = (filter (\<lambda>x. x \<noteq> \<NoMsg>) s) ! n"
by (simp add: untime_eq_filter[symmetric] untime_length_def)

corollary untime_nth_eq_filter2: "
  n < untime_length s \<Longrightarrow>
  untime s ! n = the_af ((filter (\<lambda>x. x \<noteq> \<NoMsg>) s) ! n)"
by (simp add: untime_length_def untime_nth_eq_filter[symmetric])

lemma untime_hd_eq_filter_hd: "
  untime s \<noteq> [] \<Longrightarrow>
  \<Msg> (hd (untime s)) = hd (filter (\<lambda>x. x \<noteq> \<NoMsg>) s)"
by (simp add: untime_eq_filter[symmetric] hd_eq_first[symmetric])

corollary untime_hd_eq_filter_hd2: "
  untime s \<noteq> [] \<Longrightarrow>
  hd (untime s) = the_af (hd (filter (\<lambda>x. x \<noteq> \<NoMsg>) s))"
by (simp add: untime_hd_eq_filter_hd[symmetric])

lemma untime_last_eq_filter_last: "
  untime s \<noteq> [] \<Longrightarrow>
  \<Msg> (last (untime s)) = last (filter (\<lambda>x. x \<noteq> \<NoMsg>) s)"
by (simp add: untime_eq_filter[symmetric] last_nth)

corollary untime_last_eq_filter_last2: "
  untime s \<noteq> [] \<Longrightarrow>
  last (untime s) = the_af (last (filter (\<lambda>x. x \<noteq> \<NoMsg>) s))"
by (simp add: untime_last_eq_filter_last[symmetric])

subsection \<open>Expanding and compressing lists and streams\<close>

subsubsection \<open>Expanding message streams\<close>

primrec f_expand :: "'a fstream_af \<Rightarrow> nat \<Rightarrow> 'a fstream_af" (infixl "\<odot>\<^sub>f" 100)
where
  f_expand_Nil: "[] \<odot>\<^sub>f k = []"
| f_expand_Cons: "(x # xs) \<odot>\<^sub>f k =
    (if 0 < k then x # \<NoMsg>\<^bsup>k - Suc 0\<^esup> @ (xs \<odot>\<^sub>f k) else [])"

definition i_expand :: "'a istream_af \<Rightarrow> nat \<Rightarrow> 'a istream_af" (infixl "\<odot>\<^sub>i" 100)
where
  "i_expand \<equiv> \<lambda>f k n.
   (if k = 0 then \<NoMsg> else
    if n mod k = 0 then f (n div k) else \<NoMsg>)"

primrec f_expand_Suc :: "'a fstream_af \<Rightarrow> nat \<Rightarrow> 'a fstream_af" (infixl "\<odot>\<^bsub>fSuc\<^esub>" 100)
where
  "f_expand_Suc [] k = []"
| "f_expand_Suc (x # xs) k = x # \<NoMsg>\<^bsup>k\<^esup> @ (f_expand_Suc xs k)"

definition i_expand_Suc :: "'a istream_af \<Rightarrow> nat \<Rightarrow> 'a istream_af" (infixl "\<odot>\<^bsub>iSuc\<^esub>" 100)
  where "i_expand_Suc \<equiv> \<lambda>f k n. if n mod (Suc k) = 0 then f (n div (Suc k)) else \<NoMsg>"

notation
  f_expand  (infixl "\<odot>" 100) and
  i_expand  (infixl "\<odot>" 100)

lemma length_f_expand_Suc[simp]: "length (f_expand_Suc xs k) = length xs * Suc k"
by (induct xs, simp+)

lemma i_expand_if: "
  f \<odot>\<^sub>i k = (if k = 0 then (\<lambda>n. \<NoMsg>) else
    (\<lambda>n. if n mod k = 0 then f (n div k) else \<NoMsg>))"
by (simp add: i_expand_def ilist_eq_iff)

lemma f_expand_one: "0 < k \<Longrightarrow> [a] \<odot>\<^sub>f k = a # \<NoMsg>\<^bsup>k - Suc 0\<^esup>"
by simp

lemma f_expand_0[simp]: "xs \<odot>\<^sub>f 0 = []"
by (induct xs, simp+)
corollary f_expand_0_is_zero_element: "xs \<odot>\<^sub>f 0 = ys \<odot>\<^sub>f 0"
by simp
lemma i_expand_0[simp]: "f \<odot>\<^sub>i 0 = (\<lambda>n. \<NoMsg>)"
by (simp add: i_expand_def)
corollary i_expand_0_is_zero_element: "f \<odot>\<^sub>i 0 = g \<odot>\<^sub>i 0"
by simp

lemma f_expand_gr0_f_expand_Suc: "0 < k \<Longrightarrow> xs \<odot>\<^sub>f k = f_expand_Suc xs (k - Suc 0)"
by (induct xs, simp+)
lemma i_expand_gr0_i_expand_Suc: "0 < k \<Longrightarrow> f \<odot>\<^sub>i k = i_expand_Suc f (k - Suc 0)"
by (simp add: i_expand_def i_expand_Suc_def ilist_eq_iff)
lemma i_expand_gr0: "
  0 < k \<Longrightarrow> f \<odot>\<^sub>i k = (\<lambda>n. if n mod k = 0 then f (n div k) else \<NoMsg>)"
by (simp add: i_expand_if)
lemma f_expand_1[simp]: "xs \<odot>\<^sub>f Suc 0 = xs"
by (induct xs, simp+)
lemma i_expand_1[simp]: "f \<odot>\<^sub>i Suc 0 = f"
by (simp add: i_expand_gr0)

lemma f_expand_length[simp]: "length (xs \<odot>\<^sub>f k) = length xs * k"
apply (case_tac k, simp)
apply (simp add: f_expand_gr0_f_expand_Suc)
done

lemma f_expand_empty_conv: "(xs \<odot>\<^sub>f k = []) = (xs = [] \<or> k = 0)"
by (simp add: length_0_conv[symmetric] del: length_0_conv)
lemma f_expand_not_empty_conv: "(xs \<odot>\<^sub>f k \<noteq> []) = (xs \<noteq> [] \<and> 0 < k)"
by (simp add: f_expand_empty_conv)

lemma f_expand_Cons: "
  0 < k \<Longrightarrow> (x # xs) \<odot>\<^sub>f k = x # \<NoMsg>\<^bsup>k - Suc 0\<^esup> @ (xs \<odot>\<^sub>f k)"
by simp
lemma f_expand_append[simp]: "\<And>ys. (xs @ ys) \<odot>\<^sub>f k = (xs \<odot>\<^sub>f k) @ (ys \<odot>\<^sub>f k)"
apply (case_tac "k = 0", simp)
apply (induct xs, simp+)
done

lemma f_expand_snoc: "
  0 < k \<Longrightarrow> (xs @ [x]) \<odot>\<^sub>f k = xs \<odot>\<^sub>f k @ x # replicate (k - Suc 0) \<NoMsg>"
by simp

lemma f_expand_nth_mult: "\<And>n.
  \<lbrakk> n < length xs; 0 < k \<rbrakk> \<Longrightarrow> (xs \<odot>\<^sub>f k) ! (n * k) = xs ! n"
apply (induct xs)
 apply simp
apply (case_tac n, simp)
apply (simp add: nth_append append_Cons[symmetric] del: append_Cons)
done

lemma i_expand_nth_mult: "0 < k \<Longrightarrow> (f \<odot>\<^sub>i k) (n * k) = f n"
by (simp add: i_expand_gr0)

lemma f_expand_nth_if: "\<And>n.
  n < length xs * k \<Longrightarrow>
  (xs \<odot>\<^sub>f k) ! n = (if n mod k = 0 then xs ! (n div k) else \<NoMsg>)"
apply (case_tac "k = 0", simp)
apply (simp, intro conjI impI)
 apply (clarsimp simp: f_expand_nth_mult mult.commute[of k] elim!: dvdE)
apply (induct xs, simp)
apply (simp add: nth_append append_Cons[symmetric] del: append_Cons)
apply (intro conjI impI)
 apply (simp add: nth_Cons')
apply (case_tac "length xs = 0", simp)
apply (simp add: add.commute[of k] diff_less_conv[symmetric] mod_diff_self2)
done

corollary f_expand_nth_mod_eq_0: "
  \<lbrakk> n < length xs * k; n mod k = 0 \<rbrakk> \<Longrightarrow> (xs \<odot>\<^sub>f k) ! n = xs ! (n div k)"
by (simp add: f_expand_nth_if)

corollary f_expand_nth_mod_neq_0: "
  \<lbrakk> n < length xs * k; 0 < n mod k \<rbrakk> \<Longrightarrow> (xs \<odot>\<^sub>f k) ! n = \<NoMsg>"
by (simp add: f_expand_nth_if)

lemma f_expand_nth_0_upto_k_minus_1_if: "
  \<lbrakk> t < length xs; n = t * k + i; i < k \<rbrakk> \<Longrightarrow>
  (xs \<odot>\<^sub>f k) ! n = (if i = 0 then xs ! t else \<NoMsg>)"
apply (subst f_expand_nth_if)
 apply (drule Suc_leI[of t])
 apply (drule mult_le_mono1[of _ _ k])
 apply simp+
done

lemma f_expand_take_mult: "xs \<odot>\<^sub>f k \<down> (n * k) = (xs \<down> n) \<odot>\<^sub>f k"
apply (clarsimp simp add: list_eq_iff min_def)
apply (rename_tac i)
apply (case_tac "\<not> i < n * k", simp)
apply (subgoal_tac "i < length xs * k")
 prefer 2
 apply (rule_tac y="n * k" in order_le_less_trans, simp+)
apply (clarsimp simp: f_expand_nth_if elim!: dvdE)
done

lemma f_expand_take_mod: "
  n mod k = 0 \<Longrightarrow> xs \<odot>\<^sub>f k \<down> n = xs \<down> (n div k) \<odot>\<^sub>f k"
  by (clarsimp simp: mult.commute[of k] f_expand_take_mult elim!: dvdE)

lemma f_expand_drop_mult: "xs \<odot>\<^sub>f k \<up> (n * k) = (xs \<up> n) \<odot>\<^sub>f k"
apply (insert arg_cong[OF append_take_drop_id, of "\<lambda>x. x \<odot>\<^sub>f k" n xs])
apply (drule ssubst[OF append_take_drop_id, of _ "xs \<odot>\<^sub>f k" "n * k"])
apply (simp only: f_expand_append)
apply (simp only: f_expand_take_mult)
apply simp
done

lemma f_expand_drop_mod: "
  n mod k = 0 \<Longrightarrow> xs \<odot>\<^sub>f k \<up> n = xs \<up> (n div k) \<odot>\<^sub>f k"
  by (clarsimp simp: mult.commute[of k] f_expand_drop_mult elim!: dvdE)

lemma f_expand_take_mult_Suc: "
  \<lbrakk> n < length xs; i < k \<rbrakk> \<Longrightarrow>
  xs \<odot>\<^sub>f k \<down> (n * k + Suc i) = (xs \<down> n) \<odot>\<^sub>f k @ (xs ! n # \<NoMsg>\<^bsup>i\<^esup>)"
apply (subgoal_tac "n * k + Suc i \<le> length xs * k")
 prefer 2
 apply (drule Suc_leI[of n])
 apply (drule mult_le_mono1[of "Suc n" _ k])
 apply simp
apply (clarsimp simp: list_eq_iff min_eqR nth_append f_expand_nth_if min_def nth_Cons' elim!: dvdE)
apply (simp add: mult.commute[of k] linorder_not_less)
apply (drule_tac n=ka in le_neq_implies_less, simp+)
apply (drule_tac n=ka in Suc_leI)
apply (drule_tac j=ka in mult_le_mono1[of _ _ k])
apply simp
done

lemma f_expand_take_Suc: "
  n < length xs * k \<Longrightarrow>
  xs \<odot>\<^sub>f k \<down> Suc n = (xs \<down> (n div k)) \<odot>\<^sub>f k @ (xs ! (n div k) # \<NoMsg>\<^bsup>n mod k\<^esup>)"
apply (case_tac "k = 0", simp)
apply (insert f_expand_take_mult_Suc[of "n div k" xs "n mod k" k])
apply (simp add: div_less_conv)
done

lemma i_expand_nth_if: "
  0 < k \<Longrightarrow> (f \<odot>\<^sub>i k) n = (if n mod k = 0 then f (n div k) else \<NoMsg>)"
by (simp add: i_expand_gr0)
corollary i_expand_nth_mod_eq_0: "
  \<lbrakk> 0 < k; n mod k = 0 \<rbrakk> \<Longrightarrow> (f \<odot>\<^sub>i k) n = f (n div k)"
by (simp add: i_expand_gr0)
corollary i_expand_nth_mod_neq_0: "
  0 < n mod k \<Longrightarrow> (f \<odot>\<^sub>i k) n = \<NoMsg>"
apply (case_tac "k = 0", simp)
apply (simp add: i_expand_gr0)
done

lemma i_expand_nth_0_upto_k_minus_1_if: "
  \<lbrakk> n = t * k + i; i < k \<rbrakk> \<Longrightarrow>
  (f \<odot>\<^sub>i k) n = (if i = 0 then f t else \<NoMsg>)"
by (simp add: i_expand_nth_if)

lemma i_expand_i_take_mult: "f \<odot>\<^sub>i k \<Down> (n * k) = (f \<Down> n) \<odot>\<^sub>f k"
apply (case_tac "k = 0", simp)
apply (clarsimp simp: list_eq_iff i_expand_nth_if f_expand_nth_if elim!: dvdE)
done

lemma i_expand_i_take_mod: "
  n mod k = 0 \<Longrightarrow> f \<odot>\<^sub>i k \<Down> n = f \<Down> (n div k) \<odot>\<^sub>f k"
  by (clarsimp simp: mult.commute[of k] i_expand_i_take_mult elim!: dvdE)

lemma i_expand_i_drop_mult: "(f \<odot>\<^sub>i k) \<Up> (n * k) = (f \<Up> n) \<odot>\<^sub>i k"
apply (case_tac "k = 0", simp)
apply (clarsimp simp: ilist_eq_iff i_expand_nth_if)
done

lemma i_expand_i_drop_mod: "
  n mod k = 0 \<Longrightarrow> f \<odot>\<^sub>i k \<Up> n = f \<Up> (n div k) \<odot>\<^sub>i k"
  by (clarsimp simp: mult.commute[of k] i_expand_i_drop_mult elim!: dvdE)

lemma i_expand_i_take_mult_Suc: "
  i < k \<Longrightarrow> f \<odot>\<^sub>i k \<Down> (n * k + Suc i) = (f \<Down> n) \<odot>\<^sub>f k @ (f n # \<NoMsg>\<^bsup>i\<^esup>)"
apply (clarsimp simp: list_eq_iff, rename_tac i')
apply (clarsimp simp: i_expand_nth_if f_expand_nth_if nth_append nth_Cons' elim!: dvdE)
apply (simp add: linorder_not_less mult.commute[of k])
apply (drule_tac n=ka in le_neq_implies_less, simp+)
apply (drule_tac n=ka in Suc_leI)
apply (drule_tac j=ka in mult_le_mono1[of _ _ k])
apply simp
done

lemma i_expand_i_take_Suc: "
  0 < k \<Longrightarrow> f \<odot>\<^sub>i k \<Down> Suc n = (f \<Down> (n div k)) \<odot>\<^sub>f k @ (f (n div k) # \<NoMsg>\<^bsup>n mod k\<^esup>)"
apply (insert i_expand_i_take_mult_Suc[of "n mod k" k "n div k" f])
apply simp
done

lemma f_expand_nth_interval_eq_nth_append_replicate_NoMsg[rule_format]: "
  \<lbrakk> 0 < k; t < length xs; t * k \<le> t1; t1 \<le> t * k + k - Suc 0 \<rbrakk> \<Longrightarrow>
  xs \<odot>\<^sub>f k \<down> Suc t1 \<up> (t * k) = xs ! t # \<NoMsg>\<^bsup>t1 - t * k\<^esup>"
apply (rule_tac t="Suc t1" and s="t * k + Suc (t1 - t * k)" in subst, simp)
apply (subst f_expand_take_mult_Suc)
apply simp+
done

lemma f_expand_nth_interval_eq_replicate_NoMsg: "
  \<lbrakk> 0 < k; t * k < t1; t1 \<le> t2; t2 \<le> t * k + k; t2 \<le> length xs * k\<rbrakk> \<Longrightarrow>
  xs \<odot>\<^sub>f k \<down> t2 \<up> t1 = \<NoMsg>\<^bsup>t2 - t1\<^esup>"
apply (clarsimp simp: list_eq_iff min_eqR f_expand_nth_if elim!: dvdE, rename_tac i q)
apply (drule_tac i=i and k=t1 in add_less_mono2, simp)
apply (drule_tac i="t * k" and j=t1 and m=i in trans_less_add1)
apply (drule_tac x="t * k" and y="t1 + i" and m=k in less_mod_eq_imp_add_divisor_le, simp)
apply simp
done

lemma i_expand_nth_interval_eq_nth_append_replicate_NoMsg[rule_format]: "
  \<lbrakk> 0 < k; t * k \<le> t1; t1 \<le> t * k + k - Suc 0 \<rbrakk> \<Longrightarrow>
  f \<odot>\<^sub>i k \<Down> Suc t1 \<up> (t * k) = f t # \<NoMsg>\<^bsup>t1 - t * k\<^esup>"
by (simp add: list_eq_iff Suc_diff_le i_expand_nth_if nth_Cons')

lemma i_expand_nth_interval_eq_replicate_NoMsg: "
  \<lbrakk> 0 < k; t * k < t1; t1 \<le> t2; t2 \<le> t * k + k \<rbrakk> \<Longrightarrow>
  f \<odot>\<^sub>i k \<Down> t2 \<up> t1 = \<NoMsg>\<^bsup>t2 - t1\<^esup>"
apply (clarsimp simp: list_eq_iff i_expand_nth_if add.commute[of k])
apply (drule_tac i=i and k=t1 in add_less_mono2, simp)
apply (drule_tac i="t * k" and j=t1 and m=i in trans_less_add1)
apply (drule_tac x="t * k" and y="t1 + i" and m=k in less_mod_eq_imp_add_divisor_le, simp)
apply simp
done

lemma f_expand_replicate_NoMsg[simp]: "(\<NoMsg>\<^bsup>n\<^esup>) \<odot>\<^sub>f k =  \<NoMsg>\<^bsup>n * k\<^esup>"
  by (clarsimp simp: list_eq_iff f_expand_nth_if elim!: dvdE)

lemma i_expand_const_NoMsg[simp]: "(\<lambda>n. \<NoMsg>) \<odot>\<^sub>i k = (\<lambda>n. \<NoMsg>)"
by (simp add: i_expand_def ilist_eq_iff)

lemma f_expand_assoc: "xs \<odot>\<^sub>f a \<odot>\<^sub>f b = xs \<odot>\<^sub>f (a * b)"
apply (induct xs)
 apply simp
apply (simp add: replicate_add[symmetric] diff_mult_distrib)
done

lemma i_expand_assoc: "f \<odot>\<^sub>i a \<odot>\<^sub>i b = f \<odot>\<^sub>i (a * b)"
by (fastforce simp: i_expand_def ilist_eq_iff)

lemma f_expand_commute: "xs \<odot>\<^sub>f a \<odot>\<^sub>f b = xs \<odot>\<^sub>f b \<odot>\<^sub>f a"
by (simp add: f_expand_assoc mult.commute[of b])

lemma i_expand_commute: "f \<odot>\<^sub>i a \<odot>\<^sub>i b = f \<odot>\<^sub>i b \<odot>\<^sub>i a"
by (simp add: i_expand_assoc mult.commute[of b])

lemma i_expand_i_append: "(xs \<frown> f) \<odot>\<^sub>i k = xs \<odot>\<^sub>f k \<frown> (f \<odot>\<^sub>i k)"
apply (case_tac "k = 0", simp)
apply (clarsimp simp add: ilist_eq_iff i_expand_gr0 i_append_nth)
apply (case_tac "x < length xs * k")
 apply (frule_tac n=x and k="length xs" in div_less_conv[THEN iffD2, of k, rule_format], simp)
 apply (simp add: f_expand_nth_if)
apply (simp add: linorder_not_less)
apply (frule div_le_mono[of _ _ k])
apply (simp add: mod_diff_mult_self1 div_diff_mult_self1)
done

lemma f_expand_eq_conv: "
  0 < k \<Longrightarrow> (xs \<odot>\<^sub>f k = ys \<odot>\<^sub>f k) = (xs = ys)"
apply (rule iffI)
 apply (clarsimp simp: list_eq_iff, rename_tac i)
 apply (drule_tac x="i * k" in spec)
 apply (simp add: f_expand_nth_mult)
apply simp
done

lemma i_expand_eq_conv: "
  0 < k \<Longrightarrow> (f \<odot>\<^sub>i k = g \<odot>\<^sub>i k) = (f = g)"
apply (rule iffI)
 apply (clarsimp simp: ilist_eq_iff, rename_tac i)
 apply (drule_tac x="i * k" in spec)
 apply (simp add: i_expand_nth_mult)
apply simp
done

lemma f_expand_eq_conv': "
  (xs' \<odot>\<^sub>f k = xs) =
  (length xs' * k = length xs \<and>
  (\<forall>i<length xs. xs ! i = (if i mod k = 0 then xs' ! (i div k) else \<NoMsg>)))"
by (fastforce simp: list_eq_iff f_expand_nth_if)

lemma i_expand_eq_conv': "
  0 < k \<Longrightarrow> (f' \<odot>\<^sub>i k = f) =
  (\<forall>i. f i = (if i mod k = 0 then f' (i div k) else \<NoMsg>))"
by (fastforce simp: ilist_eq_iff i_expand_nth_if)

subsubsection \<open>Aggregating lists\<close>

definition f_aggregate :: "'a list \<Rightarrow> nat \<Rightarrow> ('a list \<Rightarrow> 'a) \<Rightarrow> 'a list"
  where "f_aggregate s k ag \<equiv> map ag (list_slice s k)"

definition i_aggregate :: "'a ilist \<Rightarrow> nat \<Rightarrow> ('a list \<Rightarrow> 'a) \<Rightarrow> 'a ilist"
  where "i_aggregate s k ag \<equiv> \<lambda>n. ag (s \<Up> (n * k) \<Down> k)"

lemma f_aggregate_0[simp]: "f_aggregate xs 0 ag = []"
by (simp add: f_aggregate_def list_slice_0)

lemma f_aggregate_1: "
  (\<And>x. ag [x] = x) \<Longrightarrow>
  f_aggregate xs (Suc 0) ag = xs"
by (simp add: list_eq_iff f_aggregate_def list_slice_1)

lemma f_aggregate_Nil[simp]: "f_aggregate [] k ag = []"
by (simp add: f_aggregate_def list_slice_Nil)

lemma f_aggregate_length[simp]: "length (f_aggregate xs k ag) = length xs div k"
by (simp add: f_aggregate_def list_slice_length)

lemma f_aggregate_empty_conv: "
  0 < k \<Longrightarrow> (f_aggregate xs k ag = []) = (length xs < k)"
by (simp add: length_0_conv[symmetric] div_eq_0_conv' del: length_0_conv )

lemma f_aggregate_one: "
  \<lbrakk> 0 < k; length xs = k \<rbrakk> \<Longrightarrow> f_aggregate xs k ag = [ag xs]"
by (simp add: f_aggregate_def list_slice_def)

lemma f_aggregate_Cons: "
  \<lbrakk> 0 < k; length xs = k \<rbrakk> \<Longrightarrow>
  f_aggregate (xs @ ys) k ag = ag xs # (f_aggregate ys k ag)"
by (simp add: f_aggregate_def list_slice_def)

lemma f_aggregate_eq_f_aggregate_take: "
  f_aggregate (xs \<down> (length xs div k * k)) k ag = f_aggregate xs k ag"
by (simp add: f_aggregate_def list_slice_eq_list_slice_take)

lemma f_aggregate_nth: "
  n < length xs div k \<Longrightarrow>
  (f_aggregate xs k ag) ! n = ag (xs \<up> (n * k) \<down> k)"
by (simp add: f_aggregate_def list_slice_length list_slice_nth)

lemma f_aggregate_nth_eq_sublist_list: "
  n < length xs div k \<Longrightarrow>
  (f_aggregate xs k ag) ! n = ag (sublist_list xs [n * k..<n * k + k])"
apply (frule less_div_imp_mult_add_divisor_le)
apply (simp add: f_aggregate_nth take_drop_eq_sublist_list)
done

lemma f_aggregate_take_nth: "
  \<And>xs m. \<lbrakk> n < length xs div k; n < m div k \<rbrakk> \<Longrightarrow>
  f_aggregate (xs \<down> m) k ag ! n = f_aggregate xs k ag ! n"
apply (simp add: f_aggregate_nth drop_take)
apply (drule_tac n=m in less_div_imp_mult_add_divisor_le)
apply (simp add: min_eqL)
done

lemma f_aggregate_hd: "
  \<lbrakk> 0 < k; k \<le> length xs \<rbrakk> \<Longrightarrow>
  hd (f_aggregate xs k ag) = ag (xs \<down> k)"
apply (drule div_le_mono[of _ _ k])
apply (simp add: Suc_le_eq)
apply (subst hd_eq_first[symmetric])
 apply (simp add: length_greater_0_conv[symmetric])
apply (simp add: f_aggregate_nth)
done

lemma f_aggregate_append_mod: "
  length xs mod k = 0 \<Longrightarrow>
  f_aggregate (xs @ ys) k ag =
  f_aggregate xs k ag @ f_aggregate ys k ag"
by (simp add: f_aggregate_def list_slice_append_mod)
lemma f_aggregate_append_mult: "
  length xs = m * k \<Longrightarrow>
  f_aggregate (xs @ ys) k ag =
  f_aggregate xs k ag @ f_aggregate ys k ag"
by (simp add: f_aggregate_append_mod)

lemma f_aggregate_snoc: "
  \<lbrakk> 0 < k; length ys = k; length xs mod k = 0 \<rbrakk> \<Longrightarrow>
  f_aggregate (xs @ ys) k ag = f_aggregate xs k ag @ [ag ys]"
by (simp add: f_aggregate_append_mod f_aggregate_one)

lemma f_aggregate_take: "
  f_aggregate (xs \<down> n) k ag = f_aggregate xs k ag \<down> (n div k)"
apply (case_tac "k = 0", simp)
apply (simp add: list_eq_iff)
apply (case_tac "length xs \<le> n")
 apply (simp add: min_eqL div_le_mono f_aggregate_nth)
apply (clarsimp simp: linorder_not_le min_eqR div_le_mono f_aggregate_nth drop_take)
apply (frule less_div_imp_mult_add_divisor_le)
apply (simp add: min_eqL)
apply (subgoal_tac "i < length xs div k")
 prefer 2
 apply (drule_tac y=n in order_le_less_trans, assumption)
 apply (drule_tac m="i * k + k" and k=k in div_le_mono[OF less_imp_le])
 apply simp
apply (simp add: f_aggregate_nth)
done

lemma f_aggregate_take_mult: "
  f_aggregate (xs \<down> (n * k)) k ag = f_aggregate xs k ag \<down> n"
by (simp add: f_aggregate_take)

lemma f_aggregate_drop_mult: "
  f_aggregate (xs \<up> (n * k)) k ag = f_aggregate xs k ag \<up> n"
by (simp add: list_eq_iff div_diff_mult_self1 f_aggregate_nth add_mult_distrib add.commute[of "n * k"])
lemma f_aggregate_drop_mod: "
  n mod k = 0 \<Longrightarrow> f_aggregate (xs \<up> n) k ag = f_aggregate xs k ag \<up> (n div k)"
  by (clarsimp simp: mult.commute[of k] f_aggregate_drop_mult elim!: dvdE)

lemma f_aggregate_assoc: "
  (\<And>xs. length xs mod a = 0 \<Longrightarrow> ag (f_aggregate xs a ag) = ag xs) \<Longrightarrow>
  f_aggregate (f_aggregate xs a ag) b ag = f_aggregate xs (a * b) ag"
apply (clarsimp simp add: list_eq_iff div_mult2_eq f_aggregate_nth, rename_tac i)
apply (simp add: take_drop f_aggregate_take_mult[symmetric])
apply (simp add: add_mult_distrib2 mult.commute[of _ a] f_aggregate_drop_mult[symmetric] mult.assoc[symmetric])
apply (drule_tac x="(xs \<down> (a * b + a * i * b) \<up> (a * i * b))" in meta_spec)
apply (subgoal_tac "a * b + a * i * b \<le> length xs")
 prefer 2
 apply (simp add: div_mult2_eq[symmetric])
 apply (drule_tac x=i in less_div_imp_mult_add_divisor_le)
 apply (simp add: mult.assoc[symmetric] mult.commute[of _ a] add.commute[of _ "a * b"])
apply (simp add: min_eqR)
done

lemma f_aggregate_commute: "
  \<lbrakk> \<And>xs. length xs mod a = 0 \<Longrightarrow> ag (f_aggregate xs a ag) = ag xs;
    \<And>xs. length xs mod b = 0 \<Longrightarrow> ag (f_aggregate xs b ag) = ag xs \<rbrakk> \<Longrightarrow>
  f_aggregate (f_aggregate xs a ag) b ag = f_aggregate (f_aggregate xs b ag) a ag"
by (simp add: f_aggregate_assoc mult.commute[of _ b])

lemma i_aggregate_0[simp]: "i_aggregate f 0 ag = (\<lambda>x. ag [])"
by (simp add: i_aggregate_def)
lemma i_aggregate_1: "(\<And>x. ag [x] = x) \<Longrightarrow> i_aggregate f (Suc 0) ag = f"
by (simp add: i_aggregate_def i_take_first)
lemma i_aggregate_nth: "i_aggregate f k ag n = ag (f \<Up> (n * k) \<Down> k)"
by (simp add: i_aggregate_def)
lemma i_aggregate_hd: "i_aggregate f k ag 0 = ag (f \<Down> k)"
by (simp add: i_aggregate_nth)

lemma i_aggregate_nth_eq_map: "i_aggregate f k ag n = ag (map f [n * k..<n * k + k])"
by (simp add: i_aggregate_nth i_take_drop_eq_map)

lemma i_aggregate_i_append_mod: "
  length xs mod k = 0 \<Longrightarrow>
  i_aggregate (xs \<frown> f) k ag = f_aggregate xs k ag \<frown> i_aggregate f k ag"
apply (clarsimp simp: ilist_eq_iff i_aggregate_nth i_append_nth f_aggregate_nth mult.commute[of k] diff_mult_distrib elim!: dvdE, rename_tac i n)
apply (drule_tac n=i in Suc_leI)
apply (drule mult_le_mono1[of _ _ k])
apply simp
done

lemma i_aggregate_i_append_mult: "
  length xs = m * k \<Longrightarrow>
  i_aggregate (xs \<frown> f) k ag = f_aggregate xs k ag \<frown> i_aggregate f k ag"
by (rule i_aggregate_i_append_mod, simp)

lemma i_aggregate_Cons: "
  \<lbrakk> 0 < k; length xs = k \<rbrakk> \<Longrightarrow>
  i_aggregate (xs \<frown> f) k ag = [ag xs] \<frown> (i_aggregate f k ag)"
apply (insert i_aggregate_i_append_mod[of xs k f ag], simp)
apply (simp add: f_aggregate_def list_slice_div_eq_1)
done

lemma i_aggregate_take_nth: "
  n < m div k \<Longrightarrow> f_aggregate (f \<Down> m) k ag ! n = i_aggregate f k ag n"
apply (simp add: f_aggregate_nth i_aggregate_nth)
apply (drule less_div_imp_mult_add_divisor_le)
apply (simp add: i_take_drop_map i_take_drop_eq_map take_map)
done

lemma i_aggregate_i_take: "
  f_aggregate (f \<Down> n) k ag = i_aggregate f k ag \<Down> (n div k)"
by (simp add: list_eq_iff i_aggregate_take_nth)

lemma i_aggregate_i_take_mult: "
  0 < k \<Longrightarrow> f_aggregate (f \<Down> (n * k)) k ag = i_aggregate f k ag \<Down> n"
by (simp add: i_aggregate_i_take)

lemma i_aggregate_i_drop_mult: "
  i_aggregate (f \<Up> (n * k)) k ag = i_aggregate f k ag \<Up> n"
by (simp add: ilist_eq_iff i_aggregate_nth add_mult_distrib)

lemma i_aggregate_i_drop_mod: "
  n mod k = 0 \<Longrightarrow>
  i_aggregate (f \<Up> n) k ag = i_aggregate f k ag \<Up> (n div k)"
  by (clarsimp simp: mult.commute[of k] i_aggregate_i_drop_mult ilist_eq_iff elim!: dvdE)

lemma i_aggregate_assoc: "
  \<lbrakk> 0 < a; 0 < b;
    \<And>xs. length xs mod a = 0 \<Longrightarrow> ag (f_aggregate xs a ag) = ag xs \<rbrakk> \<Longrightarrow>
  i_aggregate (i_aggregate f a ag) b ag = i_aggregate f (a * b) ag"
apply (simp add: ilist_eq_iff i_aggregate_nth)
apply (simp add: i_aggregate_i_drop_mult[symmetric] i_aggregate_i_take_mult[symmetric] mult.commute[of a] mult.assoc)
done

lemma i_aggregate_commute: "
  \<lbrakk> 0 < a; 0 < b;
    \<And>xs. length xs mod a = 0 \<Longrightarrow> ag (f_aggregate xs a ag) = ag xs;
    \<And>xs. length xs mod b = 0 \<Longrightarrow> ag (f_aggregate xs b ag) = ag xs \<rbrakk> \<Longrightarrow>
  i_aggregate (i_aggregate xs a ag) b ag = i_aggregate (i_aggregate xs b ag) a ag"
by (simp add: i_aggregate_assoc mult.commute[of _ b])

subsubsection \<open>Compressing message streams\<close>

text \<open>Determines the last non-empty message.\<close>
primrec last_message :: "'a fstream_af \<Rightarrow> 'a message_af"
where
  "last_message [] = \<NoMsg>"
| "last_message (x # xs) = (if last_message xs = \<NoMsg> then x else last_message xs)"

definition f_shrink :: "'a fstream_af \<Rightarrow> nat \<Rightarrow> 'a fstream_af" (infixl "\<div>\<^sub>f" 100)
  where "f_shrink xs k \<equiv> f_aggregate xs k last_message"

definition i_shrink :: "'a istream_af \<Rightarrow> nat \<Rightarrow> 'a istream_af" (infixl "\<div>\<^sub>i" 100)
  where "i_shrink f k \<equiv> i_aggregate f k last_message"

notation
  f_shrink  (infixl "\<div>" 100) and
  i_shrink  (infixl "\<div>" 100)

lemmas f_shrink_defs = f_shrink_def f_aggregate_def
lemmas i_shrink_defs = i_shrink_def i_aggregate_def

lemma last_message_Nil: "last_message [] = \<NoMsg>"
by simp

lemma last_message_one: "last_message [m] = m"
by simp

lemma last_message_replicate: "0 < n \<Longrightarrow> last_message (m\<^bsup>n\<^esup>) = m"
apply (induct n, simp)
apply (case_tac n, simp+)
done

lemma last_message_replicate_NoMsg: "last_message (\<NoMsg>\<^bsup>n\<^esup>) = \<NoMsg>"
apply (case_tac "n = 0", simp)
apply (simp add: last_message_replicate)
done

lemma last_message_Cons_NoMsg: "last_message (\<NoMsg> # xs) = last_message xs"
by simp

lemma last_message_append_one: "
  last_message (xs @ [m]) = (if m = \<NoMsg> then last_message xs else m)"
apply (induct xs, simp)
apply (case_tac "m = \<NoMsg>", simp+)
done

lemma last_message_append: "\<And>xs.
  last_message (xs @ ys) = (
  if last_message ys = \<NoMsg> then last_message xs else last_message ys)"
apply (induct ys, simp)
apply (drule_tac x="xs @ [a]" in meta_spec)
apply (simp add: last_message_append_one)
done

corollary last_message_append_replicate_NoMsg: "
  last_message (xs @ \<NoMsg>\<^bsup>n\<^esup>) = last_message xs"
by (simp add: last_message_append last_message_replicate_NoMsg)

lemma last_message_replicate_NoMsg_append: "
  last_message (\<NoMsg>\<^bsup>n\<^esup> @ xs) = last_message xs"
by (simp add: last_message_append last_message_replicate_NoMsg)

lemma last_message_NoMsg_conv: "
  (last_message xs = \<NoMsg>) = (\<forall>i<length xs. xs ! i = \<NoMsg>)"
apply (induct xs, simp)
apply (simp add: nth_Cons')
apply (safe, simp_all)
apply (rename_tac i)
apply (drule_tac x="Suc i" in spec)
apply simp
done

lemma last_message_not_NoMsg_conv: "
  (last_message xs \<noteq> \<NoMsg>) = (\<exists>i<length xs. xs ! i \<noteq> \<NoMsg>)"
by (simp add: last_message_NoMsg_conv)

lemma not_NoMsg_imp_last_message: "
  \<lbrakk> i < length xs; xs ! i \<noteq> \<NoMsg> \<rbrakk> \<Longrightarrow> last_message xs \<noteq> \<NoMsg>"
by (rule last_message_not_NoMsg_conv[THEN iffD2, OF exI, OF conjI])

lemma last_message_exists_nth: "
  last_message xs \<noteq> \<NoMsg> \<Longrightarrow>
  \<exists>i<length xs. last_message xs = xs ! i \<and> (\<forall>j<length xs. i < j \<longrightarrow> xs ! j = \<NoMsg>)"
apply (induct xs, simp)
apply (rename_tac a xs)
apply (case_tac "last_message xs = \<NoMsg>")
 apply (rule_tac x=0 in exI)
 apply clarsimp
 apply (rename_tac j, case_tac j, simp)
 apply (simp add: last_message_NoMsg_conv)
apply (rule ccontr)
apply (clarsimp, rename_tac i)
apply (drule_tac x="Suc i" in spec)
apply (clarsimp simp: nth_Cons')
done

lemma last_message_exists_nth': "
  last_message xs \<noteq> \<NoMsg> \<Longrightarrow> \<exists>i<length xs. last_message xs = xs ! i"
by (blast dest: last_message_exists_nth)

lemma last_messageI2_aux: "\<And>i.
  \<lbrakk> i < length xs; xs ! i \<noteq> \<NoMsg>;
    \<forall>j. i < j \<and> j < length xs \<longrightarrow> xs ! j = \<NoMsg> \<rbrakk> \<Longrightarrow>
  last_message xs = xs ! i"
apply (induct xs, simp)
apply (simp add: nth_Cons')
apply (case_tac i)
 apply simp
 apply (subgoal_tac "\<forall>j. j < length xs \<longrightarrow> xs ! j = \<NoMsg>")
  prefer 2
  apply (clarify, drule_tac x="Suc j" in spec)
  apply simp
 apply (simp add: last_message_NoMsg_conv)
apply clarsimp
apply (rename_tac i)
apply (intro conjI impI)
 apply (simp add: not_NoMsg_imp_last_message)
apply (subgoal_tac "\<forall>j. i < j \<and> j < length xs \<longrightarrow> xs ! j = \<NoMsg>")
 prefer 2
 apply (clarify, drule_tac x="Suc j" in spec)
 apply simp
apply simp
done

lemma last_messageI2: "
  \<lbrakk> i < length xs; xs ! i \<noteq> \<NoMsg>;
    \<And>j. \<lbrakk> i < j; j < length xs \<rbrakk> \<Longrightarrow> xs ! j = \<NoMsg> \<rbrakk> \<Longrightarrow>
  last_message xs = xs ! i"
by (blast intro: last_messageI2_aux)

lemma last_messageI: "
  \<lbrakk> m \<noteq> \<NoMsg>; i < length xs; xs ! i = m;
    \<And>j. \<lbrakk> i < j; j < length xs \<rbrakk> \<Longrightarrow> xs ! j = \<NoMsg> \<rbrakk> \<Longrightarrow>
  last_message xs = m"
by (blast intro: last_messageI2)

lemma last_message_Msg_eq_last: "
  \<lbrakk> xs \<noteq> []; last xs \<noteq> \<NoMsg> \<rbrakk> \<Longrightarrow> last_message xs = last xs"
by (simp add: last_nth last_messageI2)

lemma last_message_conv: "
  m \<noteq> \<NoMsg> \<Longrightarrow>
  (last_message xs = m) =
  (\<exists>i<length xs. xs ! i = m \<and> (\<forall>j<length xs. i < j \<longrightarrow> xs ! j = \<NoMsg>))"
apply (rule iffI)
 apply (cut_tac xs=xs in last_message_exists_nth, simp)
 apply clarify
 apply (rule_tac x=i in exI)
 apply simp
apply (clarsimp simp: last_messageI)
done

lemma last_message_conv_if: "
  (last_message xs = m) =
  (if m = \<NoMsg> then \<forall>i<length xs. xs ! i = \<NoMsg>
   else \<exists>i<length xs. xs ! i = m \<and> (\<forall>j<length xs. i < j \<longrightarrow> xs ! j = \<NoMsg>))"
by (simp add: last_message_NoMsg_conv last_message_conv)

lemma last_message_not_NoMsg_eq_conv: "
  \<lbrakk> last_message xs \<noteq> \<NoMsg>; last_message ys \<noteq> \<NoMsg> \<rbrakk> \<Longrightarrow>
  (last_message xs = last_message ys) =
  (\<exists>i j. i < length xs \<and> j < length ys \<and> xs ! i \<noteq> \<NoMsg> \<and>
         xs ! i = ys ! j \<and>
         (\<forall>n<length xs. i < n \<longrightarrow> xs ! n = \<NoMsg>) \<and>
         (\<forall>n<length ys. j < n \<longrightarrow> ys ! n = \<NoMsg>))"
apply (simp add: last_message_conv[where m="last_message ys"])
apply (frule last_message_exists_nth[of xs])
apply (frule last_message_exists_nth[of ys])
apply (rule iffI)
 apply (clarsimp, rename_tac i j)
 apply (rule_tac x=j in exI, simp)
 apply (rule_tac x=i in exI, simp)
apply (clarsimp, rename_tac i1 j1 i j)
apply (rule_tac x=i in exI, simp)
apply (subgoal_tac "last_message ys = ys ! j", simp)
apply (rule last_messageI2)
apply simp+
done

lemma f_shrink_0[simp]: "xs \<div>\<^sub>f 0 = []"
by (simp add: f_shrink_defs list_slice_0)

lemma f_shrink_1[simp]: "xs \<div>\<^sub>f Suc 0 = xs"
by (simp add: f_shrink_def f_aggregate_1)

lemma f_shrink_Nil[simp]: "[] \<div>\<^sub>f k = []"
by (simp add: f_shrink_def list_slice_Nil)

lemma f_shrink_length: "length (xs \<div>\<^sub>f k) = length xs div k"
by (simp add: f_shrink_def)

lemma f_shrink_empty_conv: "0 < k \<Longrightarrow> (xs \<div>\<^sub>f k = []) = (length xs < k)"
by (simp add: f_shrink_def f_aggregate_empty_conv)

lemma f_shrink_Cons: "
  \<lbrakk> 0 < k; length xs = k \<rbrakk> \<Longrightarrow> (xs @ ys) \<div>\<^sub>f k = last_message xs # (ys \<div>\<^sub>f k)"
by (simp add: f_shrink_def f_aggregate_Cons)

lemma f_shrink_one: "
  \<lbrakk> 0 < k; length xs = k \<rbrakk> \<Longrightarrow> xs \<div>\<^sub>f k = [last_message xs]"
by (simp add: f_shrink_def f_aggregate_one)

lemma f_shrink_eq_f_shrink_take: "
  xs \<down> (length xs div k * k) \<div>\<^sub>f k = xs \<div>\<^sub>f k"
by (simp add: f_shrink_defs list_slice_eq_list_slice_take)

lemma f_shrink_nth: "
  n < length xs div k \<Longrightarrow>
  (xs \<div>\<^sub>f k) ! n = last_message (xs \<up> (n * k) \<down> k)"
by (simp add: f_shrink_def f_aggregate_nth)

lemma f_shrink_nth_eq_sublist_list: "
  n < length xs div k \<Longrightarrow>
  (xs \<div>\<^sub>f k) ! n = last_message (sublist_list xs [n * k..<n * k + k])"
by (simp add: f_shrink_def f_aggregate_nth_eq_sublist_list)

lemma f_shrink_take_nth: "
  \<lbrakk> n < length xs div k; n < m div k \<rbrakk> \<Longrightarrow> (xs \<down> m) \<div>\<^sub>f k ! n = xs \<div>\<^sub>f k ! n"
by (simp add: f_shrink_def f_aggregate_take_nth)

lemma f_shrink_hd: "
  \<lbrakk> 0 < k; k \<le> length xs \<rbrakk> \<Longrightarrow> hd (xs \<div>\<^sub>f k) = last_message (xs \<down> k)"
by (simp add: f_shrink_def f_aggregate_hd)

lemma f_shrink_append_mod: "
  length xs mod k = 0 \<Longrightarrow> (xs @ ys) \<div>\<^sub>f k = xs \<div>\<^sub>f k @ (ys \<div>\<^sub>f k)"
by (simp add: f_shrink_defs list_slice_append_mod)

lemma f_shrink_append_mult: "
  length xs = m * k \<Longrightarrow> (xs @ ys) \<div>\<^sub>f k = xs \<div>\<^sub>f k @ (ys \<div>\<^sub>f k)"
by (simp add: f_shrink_append_mod)

lemma f_shrink_snoc: "
  \<lbrakk> 0 < k; length ys = k; length xs mod k = 0 \<rbrakk> \<Longrightarrow>
  (xs @ ys) \<div>\<^sub>f k = xs \<div>\<^sub>f k @ [last_message ys]"
by (simp add: f_shrink_append_mod f_shrink_one)

lemma f_shrink_last_message[rule_format]: "
  length xs mod k = 0 \<longrightarrow> last_message (xs \<div>\<^sub>f k) = last_message xs"
apply (case_tac "k = 0", simp)
apply (rule append_constant_length_induct[of k])
 apply simp
apply (simp add: f_shrink_Cons last_message_append)
done

lemma f_shrink_replicate: "m\<^bsup>n\<^esup> \<div>\<^sub>f k = m\<^bsup>n div k\<^esup>"
apply (case_tac "k = 0", simp)
apply (case_tac "n < k")
 apply (simp add: f_shrink_empty_conv)
apply (clarsimp simp: list_eq_iff f_shrink_length f_shrink_nth)
apply (rule last_message_replicate)
apply (clarsimp simp: min_def)
apply (drule mult_less_mono1[of _ "n div k" k], simp)
apply (simp add: div_mult_cancel)
done

lemma f_shrink_f_expand_id: "0 < k \<Longrightarrow> xs \<odot>\<^sub>f k \<div>\<^sub>f k = xs"
apply (simp add: list_eq_iff)
apply (simp add: f_shrink_length f_shrink_nth f_expand_drop_mult f_expand_take_mod drop_take_1 last_message_replicate_NoMsg)
done

lemma f_expand_f_shrink_id_take[rule_format]: "
  \<lbrakk> \<forall>i<length xs. 0 < i mod k \<longrightarrow> xs ! i = \<NoMsg> \<rbrakk> \<Longrightarrow>
  xs \<div>\<^sub>f k \<odot>\<^sub>f k = xs \<down> (length xs div k * k)"
apply (case_tac "k = 0", simp)
apply (induct xs rule: append_constant_length_induct[of k])
 apply (simp add: f_shrink_empty_conv[symmetric])
apply (drule meta_mp)
 apply clarify
 apply (drule_tac x="length xs + i" in spec, simp)
apply (simp add: f_shrink_append_mod)
apply (rule_tac t=xs and s="(xs ! 0) # replicate (k - Suc 0) \<NoMsg>" in subst)
 apply (simp (no_asm_simp) add: list_eq_iff nth_Cons')
 apply (clarify, rename_tac i)
 apply (drule_tac x=i in spec)
 apply (simp add: nth_append)
apply (simp (no_asm_simp) add: list_eq_iff)
apply (clarsimp simp: f_shrink_length f_expand_nth_if f_shrink_nth last_message_replicate_NoMsg nth_Cons')
done

corollary f_expand_f_shrink_id_mod_0: "
  \<lbrakk> length xs mod k = 0;
    \<And>i. \<lbrakk> i < length xs; 0 < i mod k \<rbrakk> \<Longrightarrow> xs ! i = \<NoMsg> \<rbrakk> \<Longrightarrow>
  xs \<div>\<^sub>f k \<odot>\<^sub>f k = xs"
by (clarsimp simp: f_expand_f_shrink_id_take)

lemma f_shrink_take: "
  xs \<down> n \<div>\<^sub>f k = xs \<div>\<^sub>f k \<down> (n div k)"
by (simp add: f_shrink_def f_aggregate_take)

lemma f_shrink_take_mult: "xs \<down> (n * k) \<div>\<^sub>f k = xs \<div>\<^sub>f k \<down> n"
by (simp add: f_shrink_def f_aggregate_take_mult)

lemma f_shrink_drop_mult: "xs \<up> (n * k) \<div>\<^sub>f k = xs \<div>\<^sub>f k \<up> n"
by (simp add: f_shrink_def f_aggregate_drop_mult)

lemma f_shrink_drop_mod: "
  n mod k = 0 \<Longrightarrow> xs \<up> n \<div>\<^sub>f k = xs \<div>\<^sub>f k \<up> (n div k)"
by (simp add: f_shrink_def f_aggregate_drop_mod)

lemma f_shrink_eq_conv: "
  (xs \<div>\<^sub>f k1 = ys \<div>\<^sub>f k2) =
  (length xs div k1 = length ys div k2 \<and>
  (\<forall>i<length xs div k1.
      last_message (xs \<up> (i * k1) \<down> k1) = last_message (ys \<up> (i * k2) \<down> k2)))"
apply (case_tac "k1 = 0")
 apply (simp add: eq_commute[of "[]"] length_0_conv[symmetric] f_shrink_length del: length_0_conv)
apply (case_tac "k2 = 0")
 apply (fastforce simp: f_shrink_empty_conv div_eq_0_conv')
apply (simp add: list_eq_iff f_shrink_length)
apply (rule conj_cong, simp)
apply (rule all_imp_eqI, simp)
apply (simp add: f_shrink_nth)
done

lemma f_shrink_eq_conv': "
  (xs' \<div>\<^sub>f k = xs) =
  (length xs' div k = length xs \<and>
  (\<forall>i<length xs.
      if xs ! i = \<NoMsg> then (\<forall>j<k. xs' ! (i * k + j) = \<NoMsg>)
      else (\<exists>n<k. xs' ! (i * k + n) = xs ! i \<and>
                  (\<forall>j<k. n < j \<longrightarrow> xs' ! (i * k + j) = \<NoMsg>))))"
apply (case_tac "k = 0", fastforce)
apply (simp add: list_eq_iff f_shrink_length split del: if_split)
apply (rule conj_cong, simp)
apply (rule all_imp_eqI, simp)
apply (cut_tac x=i in less_div_imp_mult_add_divisor_le[of _ "length xs'" k], simp)
apply (clarsimp simp: f_shrink_nth last_message_conv_if min_eqR)
apply (rule ex_imp_eqI, simp)
apply simp
done

lemma f_shrink_assoc: "xs \<div>\<^sub>f a \<div>\<^sub>f b = xs \<div>\<^sub>f (a * b)"
by (unfold f_shrink_def, rule f_aggregate_assoc, fold f_shrink_def, rule f_shrink_last_message)

lemma f_shrink_commute: "xs \<div>\<^sub>f a \<div>\<^sub>f b = xs \<div>\<^sub>f b \<div>\<^sub>f a"
by (simp add: f_shrink_assoc mult.commute[of a])

lemma i_shrink_0[simp]: "f \<div>\<^sub>i 0 = (\<lambda>n. \<NoMsg>)"
by (simp add: i_shrink_defs)
lemma i_shrink_1[simp]: "f \<div>\<^sub>i Suc 0 = f"
by (simp add: i_shrink_def i_aggregate_1)
lemma i_shrink_nth: "(f \<div>\<^sub>i k) n = last_message (f \<Up> (n * k) \<Down> k)"
by (simp add: i_shrink_defs)
lemma i_shrink_nth_eq_map: "(f \<div>\<^sub>i k) n = last_message (map f [n * k..<n * k + k])"
by (simp add: i_shrink_def i_aggregate_nth_eq_map)
lemma i_shrink_hd: "(f \<div>\<^sub>i k) 0 = last_message (f \<Down> k)"
by (simp add: i_shrink_nth)

lemma i_shrink_i_append_mod: "
  length xs mod k = 0 \<Longrightarrow> (xs \<frown> f) \<div>\<^sub>i k = xs \<div>\<^sub>f k \<frown> (f \<div>\<^sub>i k)"
by (simp add: f_shrink_def i_shrink_def i_aggregate_i_append_mod)

lemma i_shrink_i_append_mult: "
  length xs = m * k \<Longrightarrow> (xs \<frown> f) \<div>\<^sub>i k = xs \<div>\<^sub>f k \<frown> (f \<div>\<^sub>i k)"
by (simp add: i_shrink_i_append_mod)

lemma i_shrink_Cons: "
  \<lbrakk> 0 < k; length xs = k \<rbrakk> \<Longrightarrow> (xs \<frown> f) \<div>\<^sub>i k = [last_message xs] \<frown> (f \<div>\<^sub>i k)"
by (simp add: f_shrink_def i_shrink_def i_aggregate_Cons)

lemma i_shrink_take_nth: "
  n < m div k \<Longrightarrow> (f \<Down> m) \<div>\<^sub>f k ! n = (f \<div>\<^sub>i k) n"
by (simp add: f_shrink_def i_shrink_def i_aggregate_take_nth)

lemma i_shrink_const[simp]: "0 < k \<Longrightarrow> (\<lambda>x. m) \<div>\<^sub>i k = (\<lambda>x. m)"
by (simp add: ilist_eq_iff i_shrink_nth last_message_replicate)

lemma i_shrink_const_NoMsg[simp]: "(\<lambda>x. \<NoMsg>) \<div>\<^sub>i k = (\<lambda>x. \<NoMsg>)"
by (case_tac "k = 0", simp+)

lemma i_shrink_i_expand_id: "0 < k \<Longrightarrow> f \<odot>\<^sub>i k \<div>\<^sub>i k = f"
by (simp add: ilist_eq_iff i_shrink_nth i_expand_i_drop_mult i_expand_i_take_mod i_drop_i_take_1 last_message_replicate_NoMsg)

lemma i_shrink_i_take_mult: "0 < k \<Longrightarrow> f \<Down> (n * k) \<div>\<^sub>f k = f \<div>\<^sub>i k \<Down> n"
by (simp add: f_shrink_def i_shrink_def i_aggregate_i_take_mult)

lemma i_shrink_i_drop_mult: "f \<Up> (n * k) \<div>\<^sub>i k = f \<div>\<^sub>i k \<Up> n"
by (simp add: f_shrink_def i_shrink_def i_aggregate_i_drop_mult)
lemma i_shrink_i_drop_mod: "
  n mod k = 0 \<Longrightarrow> f \<Up> n \<div>\<^sub>i k = f \<div>\<^sub>i k \<Up> (n div k)"
by (simp add: f_shrink_def i_shrink_def i_aggregate_i_drop_mod)

lemma i_shrink_eq_conv: "
  (f \<div>\<^sub>i k1 = g \<div>\<^sub>i k2) =
  (\<forall>i. last_message (f \<Up> (i * k1) \<Down> k1) =
       last_message (g \<Up> (i * k2) \<Down> k2))"
by (simp add: ilist_eq_iff i_shrink_nth)

lemma i_shrink_eq_conv': "
  (f' \<div>\<^sub>i k = f) =
  (\<forall>i. if f i = \<NoMsg> then \<forall>j<k. f' (i * k + j) = \<NoMsg>
       else \<exists>n<k. f' (i * k + n) = f i \<and>
                    (\<forall>j<k. n < j \<longrightarrow> f' (i * k + j) = \<NoMsg>))"
apply (simp add: ilist_eq_iff)
apply (case_tac "k = 0", fastforce)
apply (rule all_eqI, rename_tac i)
apply (simp add: i_shrink_nth)
apply (case_tac "f i = NoMsg")
 apply (simp add: last_message_NoMsg_conv)
apply (force simp add: last_message_conv)
done

lemma i_shrink_commute: "f \<div>\<^sub>i a \<div>\<^sub>i b = f \<div>\<^sub>i b \<div>\<^sub>i a"
by (simp add: i_shrink_assoc mult.commute[of a])

subsubsection \<open>Holding last messages in everly cycle of a stream\<close>

primrec last_message_hold_init :: "'a fstream_af \<Rightarrow> 'a message_af \<Rightarrow> 'a fstream_af"
where
  "last_message_hold_init [] m = []"
| "last_message_hold_init (x # xs) m =
    (if x = \<NoMsg> then m else x) #
    (last_message_hold_init xs (if x = \<NoMsg> then m else x))"

definition last_message_hold :: "'a fstream_af \<Rightarrow> 'a fstream_af"
  where "last_message_hold xs \<equiv> last_message_hold_init xs \<NoMsg>"

lemma last_message_hold_init_length[simp]: "
  \<And>m. length (last_message_hold_init xs m) = length xs"
by (induct xs, simp+)

lemma last_message_hold_init_nth: "
  \<And>i m. i < length xs \<Longrightarrow>
  (last_message_hold_init xs m) ! i = last_message (m # xs \<down> Suc i)"
apply (induct xs, simp)
apply (simp add: nth_Cons')
done

lemma last_message_hold_init_snoc: "
  last_message_hold_init (xs @ [x]) m =
  last_message_hold_init xs m @
    [if x = \<NoMsg> then last_message (m # xs) else x]"
by (simp add: list_eq_iff nth_append last_message_hold_init_nth last_message_append)

lemma last_message_hold_init_append[rule_format]: "
  \<And>xs m. last_message_hold_init (xs @ ys) m =
  last_message_hold_init xs m @ last_message_hold_init ys (last_message (m # xs))"
apply (induct ys, simp)
apply (rule_tac x=a in subst[OF append_eq_Cons, rule_format])
apply (simp only: append_Cons[symmetric] append_assoc[symmetric])
apply (simp add: last_message_hold_init_snoc last_message_append)
done

lemma last_message_hold_length[simp]: "length (last_message_hold xs) = length xs"
by (simp add: last_message_hold_def)

lemma last_message_hold_Nil[simp]: "last_message_hold [] = []"
by (simp add: last_message_hold_def)

lemma last_message_hold_one[simp]: "last_message_hold [x] = [x]"
by (simp add: last_message_hold_def)

lemma last_message_hold_nth: "
  i < length xs \<Longrightarrow> last_message_hold xs ! i = last_message (xs \<down> Suc i)"
by (simp add: last_message_hold_def last_message_hold_init_nth)

lemma last_message_hold_last: "
  xs \<noteq> [] \<Longrightarrow> last (last_message_hold xs) = last_message xs"
apply (subgoal_tac "last_message_hold xs \<noteq> []")
 prefer 2
 apply (simp add: length_greater_0_conv[symmetric] del: length_greater_0_conv)
apply (simp add: last_nth last_message_hold_nth length_greater_0_conv[symmetric] del: length_greater_0_conv)
done

lemma last_message_hold_take: "
  last_message_hold xs \<down> n = last_message_hold (xs \<down> n)"
apply (case_tac "length xs \<le> n", simp)
apply (simp add: list_eq_iff last_message_hold_nth min_eqL)
done

lemma last_message_hold_snoc: "
  last_message_hold (xs @ [x]) =
  last_message_hold xs @ [if x = \<NoMsg> then last_message xs else x]"
by (simp add: last_message_hold_def last_message_hold_init_snoc)

lemma last_message_hold_append: "
  last_message_hold (xs @ ys) =
  last_message_hold xs @ last_message_hold_init ys (last_message xs)"
by (simp add: last_message_hold_def last_message_hold_init_append)

lemma last_message_hold_append': "
  last_message_hold (xs @ ys) =
  last_message_hold xs @ tl (last_message_hold (last_message xs # ys))"
apply (simp add: last_message_hold_append)
apply (simp add: last_message_hold_def)
done

lemma last_message_hold_idem[simp]: "
  last_message_hold (last_message_hold xs) = last_message_hold xs"
by (simp add: list_eq_iff last_message_hold_nth last_message_hold_take)

text \<open>
  Returns for each point in time the currently last non-empty message
  of the current stream cycle of length \<open>k\<close>.\<close>

definition f_last_message_hold :: "'a fstream_af \<Rightarrow> nat \<Rightarrow> 'a fstream_af" (infixl "\<longmapsto>\<^sub>f" 100)
  where "f_last_message_hold xs k \<equiv> concat (map last_message_hold (list_slice2 xs k))"

definition i_last_message_hold :: "'a istream_af \<Rightarrow> nat \<Rightarrow> 'a istream_af" (infixl "\<longmapsto>\<^sub>i" 100)
  where "i_last_message_hold f k \<equiv> \<lambda>n. last_message (f \<Up> (n - n mod k) \<Down> Suc (n mod k))"

notation
  f_last_message_hold  (infixl "\<longmapsto>" 100) and
  i_last_message_hold  (infixl "\<longmapsto>" 100)

lemma f_last_message_hold_0[simp]: "xs \<longmapsto>\<^sub>f 0 = last_message_hold xs"
by (simp add: f_last_message_hold_def list_slice2_0)

lemma f_last_message_hold_1[simp]: "xs \<longmapsto>\<^sub>f (Suc 0) = xs"
apply (simp add: f_last_message_hold_def list_slice2_1)
apply (induct xs, simp+)
done

lemma f_last_message_hold_Nil[simp]: "[] \<longmapsto>\<^sub>f k = []"
by (simp add: f_last_message_hold_def list_slice2_Nil)

lemma f_last_message_hold_length[simp]: "length (xs \<longmapsto>\<^sub>f k) = length xs"
apply (case_tac "k = 0", simp)
apply (simp add: f_last_message_hold_def)
apply (induct xs rule: append_constant_length_induct[of k])
 apply (simp add: list_slice2_le)
apply (simp add: list_slice2_append_mod list_slice2_mod_0 list_slice_div_eq_1)
done

lemma f_last_message_hold_le: "length xs \<le> k \<Longrightarrow> xs \<longmapsto>\<^sub>f k = last_message_hold xs"
by (simp add: f_last_message_hold_def list_slice2_le)

lemma f_last_message_hold_append_mult: "
  length xs = m * k \<Longrightarrow> (xs @ ys) \<longmapsto>\<^sub>f k = xs \<longmapsto>\<^sub>f k @ (ys \<longmapsto>\<^sub>f k)"
by (simp add: f_last_message_hold_def list_slice2_append_mod)

lemma f_last_message_hold_append_mod: "
  length xs mod k = 0 \<Longrightarrow> (xs @ ys) \<longmapsto>\<^sub>f k = xs \<longmapsto>\<^sub>f k @ (ys \<longmapsto>\<^sub>f k)"
by (simp add: f_last_message_hold_def list_slice2_append_mod)

lemma f_last_message_hold_nth[rule_format]: "
  \<forall>n. n < length xs \<longrightarrow> xs \<longmapsto>\<^sub>f k ! n = last_message (xs \<up> (n div k * k) \<down> Suc (n mod k))"
apply (case_tac "k = 0")
 apply (simp add: last_message_hold_nth)
apply (induct xs rule: append_constant_length_induct[of k])
 apply (simp add: f_last_message_hold_def list_slice2_le last_message_hold_nth)
apply (simp add: f_last_message_hold_append_mod nth_append)
apply (intro allI conjI impI)
 apply (simp add: f_last_message_hold_def list_slice2_mod_0 list_slice_div_eq_1 last_message_hold_nth)
apply (case_tac "n < k", simp)
apply (simp add: linorder_not_less last_message_append div_mult_cancel)
apply (subgoal_tac "k + n mod k \<le> n")
 prefer 2
 apply (drule div_le_mono[of _ _ k], drule mult_le_mono1[of _ _ k])
 apply (simp add: div_mult_cancel)
apply (simp add: mod_diff_self2 add.commute[of k])
done

lemma f_last_message_hold_take: "xs \<down> n \<longmapsto>\<^sub>f k = xs \<longmapsto>\<^sub>f k \<down> n"
by (clarsimp simp: list_eq_iff f_last_message_hold_nth drop_take div_mult_cancel min_eqL)

lemma f_last_message_hold_drop_mult: "
  xs \<up> (n * k) \<longmapsto>\<^sub>f k = xs \<longmapsto>\<^sub>f k \<up> (n * k)"
apply (rule subst[OF append_take_drop_id, of _ "n * k" xs])
apply (case_tac "length xs \<le> n * k", simp)
apply (simp add: f_last_message_hold_append_mod min_eqR del: append_take_drop_id)
done

lemma f_last_message_hold_drop_mod: "
  n mod k = 0 \<Longrightarrow> xs \<up> n \<longmapsto>\<^sub>f k = xs \<longmapsto>\<^sub>f k \<up> n"
  by (clarsimp simp: mult.commute[of k] f_last_message_hold_drop_mult elim!: dvdE)

lemma f_last_message_hold_idem: "xs \<longmapsto>\<^sub>f k \<longmapsto>\<^sub>f k = xs \<longmapsto>\<^sub>f k"
apply (case_tac "k = 0", simp)
apply (simp add: list_eq_iff f_last_message_hold_nth f_last_message_hold_drop_mod[symmetric] f_last_message_hold_take[symmetric])
apply (simp add: f_last_message_hold_le min.coboundedI2 Suc_mod_le_divisor)
done

lemma f_shrink_nth_eq_f_last_message_hold_last: "
  n < length xs div k \<Longrightarrow> xs \<div>\<^sub>f k ! n = last (xs \<longmapsto>\<^sub>f k \<up> (n * k) \<down> k)"
apply (case_tac "k = 0", simp)
apply (case_tac "xs = []", simp)
apply (simp add: f_shrink_nth f_last_message_hold_drop_mult[symmetric] f_last_message_hold_take[symmetric])
apply (drule less_div_imp_mult_add_divisor_le)
apply (simp add: f_last_message_hold_le last_message_hold_last)
done

lemma f_shrink_nth_eq_f_last_message_hold_nth: "
  n < length xs div k \<Longrightarrow> xs \<div>\<^sub>f k ! n = xs \<longmapsto>\<^sub>f k ! (n * k + k - Suc 0)"
apply (case_tac "k = 0", simp)
apply (simp add: f_shrink_nth_eq_f_last_message_hold_last)
apply (frule less_div_imp_mult_add_divisor_le)
apply (simp add: last_nth min_eqR)
done

lemma last_message_f_last_message_hold: "
  last_message (xs \<longmapsto>\<^sub>f k) = last_message xs"
apply (case_tac "k = 0", simp)
apply (induct xs rule: append_constant_length_induct[of k])
 apply (simp add: f_last_message_hold_le)
apply (simp add: f_last_message_hold_append_mult last_message_append f_last_message_hold_le)
done

lemma i_last_message_hold_0[simp]: "f \<longmapsto>\<^sub>i 0 = (\<lambda>n. last_message (f \<Down> Suc n))"
by (simp add: i_last_message_hold_def)
lemma i_last_message_hold_1[simp]: "f \<longmapsto>\<^sub>i Suc 0 = f"
by (simp add: i_last_message_hold_def i_drop_i_take_1)

lemma i_last_message_hold_nth: "
  (f \<longmapsto>\<^sub>i k) n = last_message (f \<Up> (n - n mod k) \<Down> Suc (n mod k))"
by (simp add: i_last_message_hold_def)

lemma i_last_message_hold_i_append_mult: "
  length xs = m * k \<Longrightarrow> (xs \<frown> f) \<longmapsto>\<^sub>i k = (xs \<longmapsto>\<^sub>f k) \<frown> (f \<longmapsto>\<^sub>i k)"
apply (case_tac "k = 0", simp)
apply (clarsimp simp: ilist_eq_iff i_last_message_hold_nth i_append_nth f_last_message_hold_nth div_mult_cancel linorder_not_less)
apply (subgoal_tac "length xs \<le> x - x mod k")
 prefer 2
 apply (drule div_le_mono[of _ _ k])
 apply (simp add: div_mult_cancel[symmetric])
apply (simp add: mod_diff_mult_self1)
apply (drule_tac j="x - x mod k" and k="x mod k" in add_le_mono1)
apply (simp add: add.commute[of "m * k"])
done

lemma i_last_message_hold_i_append_mod: "
  length xs mod k = 0 \<Longrightarrow> (xs \<frown> f) \<longmapsto>\<^sub>i k = (xs \<longmapsto>\<^sub>f k) \<frown> (f \<longmapsto>\<^sub>i k)"
  by (clarsimp simp: mult.commute[of k] elim!: dvdE, rule i_last_message_hold_i_append_mult)

lemma i_last_message_hold_i_take: "f \<Down> n \<longmapsto>\<^sub>f k = (f \<longmapsto>\<^sub>i k) \<Down> n"
by (simp add: list_eq_iff f_last_message_hold_nth i_last_message_hold_nth div_mult_cancel i_take_drop min_eqR)

lemma i_last_message_hold_i_drop_mult: "
  f \<Up> (n * k) \<longmapsto>\<^sub>i k = f \<longmapsto>\<^sub>i k \<Up> (n * k)"
by (simp add: ilist_eq_iff i_last_message_hold_nth)

lemma i_last_message_hold_i_drop_mod: "
  n mod k = 0 \<Longrightarrow> f \<Up> n \<longmapsto>\<^sub>i k = f \<longmapsto>\<^sub>i k \<Up> n"
  by (clarsimp simp: mult.commute[of k] elim!: dvdE, rule i_last_message_hold_i_drop_mult)

lemma i_last_message_hold_idem: "f \<longmapsto>\<^sub>i k \<longmapsto>\<^sub>i k = f \<longmapsto>\<^sub>i k"
by (simp add: ilist_eq_iff i_last_message_hold_nth minus_mod_eq_mult_div i_last_message_hold_i_drop_mod[symmetric] i_last_message_hold_i_take[symmetric] last_message_f_last_message_hold)

lemma i_shrink_nth_eq_i_last_message_hold_nth: "
  0 < k \<Longrightarrow> (f \<div>\<^sub>i k) n = (f \<longmapsto>\<^sub>i k) (n * k + k - Suc 0)"
apply (simp add: i_shrink_nth i_last_message_hold_nth)
apply (simp add: diff_add_assoc del: add_diff_assoc)
done

lemma i_shrink_nth_eq_i_last_message_hold_last: "
  0 < k \<Longrightarrow> (f \<div>\<^sub>i k) n = last (f \<longmapsto>\<^sub>i k \<Up> (n * k) \<Down> k)"
by (simp add: last_nth i_shrink_nth_eq_i_last_message_hold_nth)

subsubsection \<open>Compressing lists\<close>

text \<open>
  Lists/Non-message streams
  do not have to permit the empty message \<open>\<NoMsg>\<close>
  to be element.
  Thus, they are compressed by factor @{term k}
  by just aggregating every sequence of length k
  to its last element.\<close>

definition f_shrink_last :: "'a list \<Rightarrow> nat \<Rightarrow> 'a list"   (infixl "\<div>\<^bsub>fl\<^esub>" 100)
  where "f_shrink_last xs k \<equiv> f_aggregate xs k last"

definition i_shrink_last :: "'a ilist \<Rightarrow> nat \<Rightarrow> 'a ilist" (infixl "\<div>\<^bsub>il\<^esub>" 100)
  where "i_shrink_last f k \<equiv> i_aggregate f k last"

notation
  f_shrink_last  (infixl "\<div>\<^sub>l" 100) and
  i_shrink_last  (infixl "\<div>\<^sub>l" 100)

lemma f_shrink_last_0[simp]: "xs \<div>\<^bsub>fl\<^esub> 0 = []"
by (simp add: f_shrink_last_def f_aggregate_def list_slice_0)

lemma f_shrink_last_1[simp]: "xs \<div>\<^bsub>fl\<^esub> Suc 0 = xs"
by (simp add: f_shrink_last_def f_aggregate_1)

lemma f_shrink_last_Nil[simp]: "[] \<div>\<^bsub>fl\<^esub> k = []"
by (simp add: f_shrink_last_def f_aggregate_def list_slice_Nil)

lemma f_shrink_last_length: "length (xs \<div>\<^bsub>fl\<^esub> k) = length xs div k"
by (simp add: f_shrink_last_def)

lemma f_shrink_last_empty_conv: "
  0 < k \<Longrightarrow> (xs \<div>\<^bsub>fl\<^esub> k = []) = (length xs < k)"
by (simp add: f_shrink_last_def f_aggregate_empty_conv)

lemma f_shrink_last_Cons: "
  \<lbrakk> 0 < k;
 length xs = k \<rbrakk> \<Longrightarrow> (xs @ ys) \<div>\<^bsub>fl\<^esub> k = last xs # (ys \<div>\<^bsub>fl\<^esub> k)"
by (simp add: f_shrink_last_def f_aggregate_Cons)

lemma f_shrink_last_one: "
  \<lbrakk> 0 < k; length xs = k \<rbrakk> \<Longrightarrow> xs \<div>\<^bsub>fl\<^esub> k = [last xs]"
by (simp add: f_shrink_last_def f_aggregate_one)

lemma f_shrink_last_eq_f_shrink_last_take: "
  xs \<down> (length xs div k * k) \<div>\<^bsub>fl\<^esub> k = xs \<div>\<^bsub>fl\<^esub> k"
by (simp add: f_shrink_last_def f_aggregate_eq_f_aggregate_take)

lemma f_shrink_last_nth: "
  n < length xs div k \<Longrightarrow> (xs \<div>\<^bsub>fl\<^esub> k) ! n = xs ! (n * k + k - Suc 0)"
apply (case_tac "k = 0", simp)
apply (frule less_div_imp_mult_add_divisor_le)
apply (simp add: f_shrink_last_def f_aggregate_nth last_take2)
done

corollary f_shrink_last_nth': "
  n < length xs div k \<Longrightarrow> (xs \<div>\<^bsub>fl\<^esub> k) ! n = xs ! (Suc n * k - Suc 0)"
by (simp add: f_shrink_last_nth add.commute[of k])

lemma f_shrink_last_hd: "
  \<lbrakk> 0 < k; k \<le> length xs \<rbrakk> \<Longrightarrow> hd (xs \<div>\<^bsub>fl\<^esub> k) = xs ! (k - Suc 0)"
by (simp add: f_shrink_last_def f_aggregate_hd last_take2)

lemma f_shrink_last_map: "(map f xs) \<div>\<^bsub>fl\<^esub> k = map f (xs \<div>\<^bsub>fl\<^esub> k)"
apply (case_tac "k = 0", simp)
apply (clarsimp simp: list_eq_iff f_shrink_last_length)
apply (frule less_div_imp_mult_add_divisor_le)
apply (simp add: f_shrink_last_nth)
done

lemma f_shrink_last_append_mod: "
  length xs mod k = 0 \<Longrightarrow> (xs @ ys) \<div>\<^bsub>fl\<^esub> k = xs \<div>\<^bsub>fl\<^esub> k @ (ys \<div>\<^bsub>fl\<^esub> k)"
by (simp add: f_shrink_last_def f_aggregate_append_mod)

lemma f_shrink_last_append_mult: "
  length xs = m * k \<Longrightarrow> (xs @ ys) \<div>\<^bsub>fl\<^esub> k = xs \<div>\<^bsub>fl\<^esub> k @ (ys \<div>\<^bsub>fl\<^esub> k)"
by (unfold f_shrink_last_def, rule f_aggregate_append_mult)

lemma f_shrink_last_snoc: "
  \<lbrakk> 0 < k; length ys = k; length xs mod k = 0 \<rbrakk> \<Longrightarrow>
  (xs @ ys) \<div>\<^bsub>fl\<^esub> k = xs \<div>\<^bsub>fl\<^esub> k @ [last ys]"
by (simp add: f_shrink_last_append_mod f_shrink_last_one)

lemma f_shrink_last_last: "
  length xs mod k = 0 \<Longrightarrow> last (xs \<div>\<^bsub>fl\<^esub> k) = last xs"
apply (case_tac "k = 0", simp)
apply (case_tac "xs = []", simp)
apply (subgoal_tac "k \<le> length xs")
 prefer 2
 apply (rule ccontr, simp)
apply (rule subst[OF append_take_drop_id[of "length xs - k" xs]])
apply (subst f_shrink_last_snoc)
apply (simp add: min_eqR mod_diff_self2)+
done

lemma f_shrink_last_replicate: "m\<^bsup>n\<^esup> \<div>\<^bsub>fl\<^esub> k = m\<^bsup>n div k\<^esup>"
apply (case_tac "k = 0", simp)
apply (clarsimp simp: list_eq_iff f_shrink_last_length)
apply (frule less_div_imp_mult_add_divisor_le)
apply (simp add: f_shrink_last_nth)
done

lemma f_shrink_last_take: "
  xs \<down> n \<div>\<^bsub>fl\<^esub> k = xs \<div>\<^bsub>fl\<^esub> k \<down> (n div k)"
by (unfold f_shrink_last_def, rule f_aggregate_take)

lemma f_shrink_last_take_mult: "xs \<down> (n * k) \<div>\<^bsub>fl\<^esub> k = xs \<div>\<^bsub>fl\<^esub> k \<down> n"
by (unfold f_shrink_last_def, rule f_aggregate_take_mult)

lemma f_shrink_last_drop_mult: "xs \<up> (n * k) \<div>\<^bsub>fl\<^esub> k = xs \<div>\<^bsub>fl\<^esub> k \<up> n"
by (unfold f_shrink_last_def, rule f_aggregate_drop_mult)

lemma f_shrink_last_drop_mod: "
  n mod k = 0 \<Longrightarrow> xs \<up> n \<div>\<^bsub>fl\<^esub> k = xs \<div>\<^bsub>fl\<^esub> k \<up> (n div k)"
by (unfold f_shrink_last_def, rule f_aggregate_drop_mod)

lemma f_shrink_last_assoc: "xs \<div>\<^bsub>fl\<^esub> a \<div>\<^bsub>fl\<^esub> b = xs \<div>\<^bsub>fl\<^esub> (a * b)"
by (unfold f_shrink_last_def, rule f_aggregate_assoc, fold f_shrink_last_def, rule f_shrink_last_last)

lemma f_shrink_last_commute: "xs \<div>\<^bsub>fl\<^esub> a \<div>\<^bsub>fl\<^esub> b = xs \<div>\<^bsub>fl\<^esub> b \<div>\<^bsub>fl\<^esub> a"
by (simp add: f_shrink_last_assoc mult.commute[of a])

lemma i_shrink_last_1[simp]: "f \<div>\<^bsub>il\<^esub> Suc 0 = f"
by (simp add: i_shrink_last_def i_aggregate_1)

lemma i_shrink_last_nth: "0 < k \<Longrightarrow> (f \<div>\<^bsub>il\<^esub> k) n =  f (n * k + k - Suc 0)"
by (simp add: i_shrink_last_def i_aggregate_nth last_i_take2)

lemma i_shrink_last_nth': "0 < k \<Longrightarrow> (f \<div>\<^bsub>il\<^esub> k) n =  f (Suc n * k - Suc 0)"
by (simp add: i_shrink_last_nth add.commute[of k])

lemma i_shrink_last_hd: "(f \<div>\<^bsub>il\<^esub> k) 0 = last (f \<Down> k)"
apply (case_tac "k = 0")
 apply (simp add: i_shrink_last_def)
apply (simp add: i_shrink_last_nth last_i_take2)
done

lemma i_shrink_last_o: "0 < k \<Longrightarrow> (f \<circ> g) \<div>\<^bsub>il\<^esub> k = f \<circ> (g \<div>\<^bsub>il\<^esub> k)"
by (simp add: ilist_eq_iff i_shrink_last_nth)

lemma i_shrink_last_i_append_mod: "
  length xs mod k = 0 \<Longrightarrow> (xs \<frown> f) \<div>\<^bsub>il\<^esub> k = xs \<div>\<^bsub>fl\<^esub> k \<frown> (f \<div>\<^bsub>il\<^esub> k)"
by (simp add: f_shrink_last_def i_shrink_last_def i_aggregate_i_append_mod)

lemma i_shrink_last_i_append_mult: "
  length xs = m * k \<Longrightarrow> (xs \<frown> f) \<div>\<^bsub>il\<^esub> k = xs \<div>\<^bsub>fl\<^esub> k \<frown> (f \<div>\<^bsub>il\<^esub> k)"
by (simp add: i_shrink_last_i_append_mod)

lemma i_shrink_last_Cons: "
  \<lbrakk> 0 < k; length xs = k \<rbrakk> \<Longrightarrow> (xs \<frown> f) \<div>\<^bsub>il\<^esub> k = [last xs] \<frown> (f \<div>\<^bsub>il\<^esub> k)"
by (simp add: f_shrink_last_def i_shrink_last_def i_aggregate_Cons)

lemma i_shrink_last_const: "0 < k \<Longrightarrow> (\<lambda>x. m) \<div>\<^bsub>il\<^esub> k = (\<lambda>x. m)"
by (simp add: ilist_eq_iff i_shrink_last_nth)

lemma i_shrink_last_i_take_mult: "
  0 < k \<Longrightarrow> f \<Down> (n * k) \<div>\<^bsub>fl\<^esub> k = f \<div>\<^bsub>il\<^esub> k \<Down> n"
by (simp add: f_shrink_last_def i_shrink_last_def i_aggregate_i_take_mult)

lemma i_shrink_last_i_take: "
  f \<Down> n \<div>\<^bsub>fl\<^esub> k = f \<div>\<^bsub>il\<^esub> k \<Down> (n div k)"
by (simp add: f_shrink_last_def i_shrink_last_def i_aggregate_i_take)

lemma i_shrink_last_i_drop_mult: "f \<Up> (n * k) \<div>\<^bsub>il\<^esub> k = f \<div>\<^bsub>il\<^esub> k \<Up> n"
by (simp add: f_shrink_last_def i_shrink_last_def i_aggregate_i_drop_mult)
lemma i_shrink_last_i_drop_mod: "
  n mod k = 0 \<Longrightarrow> f \<Up> n \<div>\<^bsub>il\<^esub> k = f \<div>\<^bsub>il\<^esub> k \<Up> (n div k)"
by (simp add: f_shrink_last_def i_shrink_last_def i_aggregate_i_drop_mod)

lemma i_shrink_last_assoc: "f \<div>\<^bsub>il\<^esub> a \<div>\<^bsub>il\<^esub> b = f \<div>\<^bsub>il\<^esub> (a * b)"
apply (unfold i_shrink_last_def)
apply (case_tac "b = 0", simp)
apply (case_tac "a = 0", simp add: i_aggregate_def)
apply (simp add: i_aggregate_assoc f_shrink_last_last[unfolded f_shrink_last_def])
done

lemma i_shrink_last_commute: "f \<div>\<^bsub>il\<^esub> a \<div>\<^bsub>il\<^esub> b = f \<div>\<^bsub>il\<^esub> b \<div>\<^bsub>il\<^esub> a"
by (simp add: i_shrink_last_assoc mult.commute[of a])

text \<open>
  Shrinking a message stream with \<open>last_message\<close> as aggregation function
  corresponds to shrinking the stream holding last message in each cycle
  with \<open>last\<close> as aggregation function.\<close>

lemma f_shrink_eq_f_last_message_hold_shrink_last: "
  xs \<div>\<^sub>f k = xs \<longmapsto>\<^sub>f k \<div>\<^bsub>fl\<^esub> k"
by (simp add: list_eq_iff f_shrink_length f_shrink_last_length f_shrink_nth_eq_f_last_message_hold_nth f_shrink_last_nth)

lemma i_shrink_eq_i_last_message_hold_shrink_last: "
  0 < k \<Longrightarrow> f \<div>\<^sub>i k = f \<longmapsto>\<^sub>i k \<div>\<^bsub>il\<^esub> k"
by (simp add: ilist_eq_iff i_shrink_last_nth i_shrink_nth_eq_i_last_message_hold_nth)

end


theory ArityEtaExpansion
imports EtaExpansion "Arity-Nominal" TransformTools
begin

lift_definition Aeta_expand :: "Arity \<Rightarrow> exp \<Rightarrow> exp" is "eta_expand".

lemma Aeta_expand_eqvt[eqvt]: "\<pi> \<bullet> Aeta_expand a e = Aeta_expand (\<pi> \<bullet> a) (\<pi> \<bullet> e)"
  apply (cases a)
  apply simp
  apply transfer
  apply simp
  done

lemma Aeta_expand_0[simp]: "Aeta_expand 0 e = e"
  by transfer simp

lemma Aeta_expand_inc[simp]: "Aeta_expand (inc\<cdot>n) e = (Lam [fresh_var e]. Aeta_expand n (App e (fresh_var e)))"
  apply (simp add: inc_def)
  by transfer simp

lemma isLam_Aeta_expand: "isLam e \<Longrightarrow> isLam (Aeta_expand a e)"
  by transfer (rule isLam_eta_expand)

lemma isVal_Aeta_expand: "isVal e \<Longrightarrow> isVal (Aeta_expand a e)"
  by transfer (rule isVal_eta_expand)

lemma Aeta_expand_fresh[simp]: "a \<sharp> Aeta_expand n e = a \<sharp> e" by transfer simp
lemma Aeta_expand_fresh_star[simp]: "a \<sharp>* Aeta_expand n e = a \<sharp>* e" by (auto simp add: fresh_star_def)

interpretation supp_bounded_transform Aeta_expand
  apply standard
  using Aeta_expand_fresh
  apply (auto simp add: fresh_def)
  done

end


(*<*)
theory Sugar
imports
  "HOL-Library.LaTeXsugar"
  "HOL-Library.OptionalSugar"
begin
no_translations
  ("prop") "P \<and> Q \<Longrightarrow> R" <= ("prop") "P \<Longrightarrow> Q \<Longrightarrow> R"
(*>*)
text\<open>
\section{Introduction}

This document is for those Isabelle users who have mastered
the art of mixing \LaTeX\ text and Isabelle theories and never want to
typeset a theorem by hand anymore because they have experienced the
bliss of writing \verb!@!\verb!{thm[display,mode=latex_sum] sum_Suc_diff [no_vars]}!
and seeing Isabelle typeset it for them:
@{thm[display,mode=latex_sum] sum_Suc_diff[no_vars]}
No typos, no omissions, no sweat.
If you have not experienced that joy, read Chapter 4, \emph{Presenting
Theories}, \<^cite>\<open>LNCS2283\<close> first.

If you have mastered the art of Isabelle's \emph{antiquotations},
i.e.\ things like the above \verb!@!\verb!{thm...}!, beware: in your vanity
you may be tempted to think that all readers of the stunning
documents you can now produce at the drop of a hat will be struck with
awe at the beauty unfolding in front of their eyes. Until one day you
come across that very critical of readers known as the ``common referee''.
He has the nasty habit of refusing to understand unfamiliar notation
like Isabelle's infamous \<open>\<lbrakk> \<rbrakk> \<Longrightarrow>\<close> no matter how many times you
explain it in your paper. Even worse, he thinks that using \<open>\<lbrakk>
\<rbrakk>\<close> for anything other than denotational semantics is a cardinal sin
that must be punished by instant rejection.

This document shows you how to make Isabelle and \LaTeX\ cooperate to
produce ordinary looking mathematics that hides the fact that it was
typeset by a machine. You merely need to load the right files:
\begin{itemize}
\item Import theory \texttt{LaTeXsugar} in the header of your own
theory.  You may also want bits of \texttt{OptionalSugar}, which you can
copy selectively into your own theory or import as a whole.  Both
theories live in \texttt{HOL/Library}.

\item Should you need additional \LaTeX\ packages (the text will tell
you so), you include them at the beginning of your \LaTeX\ document,
typically in \texttt{root.tex}. For a start, you should
\verb!\usepackage{amssymb}! --- otherwise typesetting
@{prop[source]"\<not>(\<exists>x. P x)"} will fail because the AMS symbol
\<open>\<nexists>\<close> is missing.
\end{itemize}

\section{HOL syntax}

\subsection{Logic}

The formula @{prop[source]"\<not>(\<exists>x. P x)"} is typeset as \<^prop>\<open>\<not>(\<exists>x. P x)\<close>.

The predefined constructs \<open>if\<close>, \<open>let\<close> and
\<open>case\<close> are set in sans serif font to distinguish them from
other functions. This improves readability:
\begin{itemize}
\item \<^term>\<open>if b then e\<^sub>1 else e\<^sub>2\<close> instead of \<open>if b then e\<^sub>1 else e\<^sub>2\<close>.
\item \<^term>\<open>let x = e\<^sub>1 in e\<^sub>2\<close> instead of \<open>let x = e\<^sub>1 in e\<^sub>2\<close>.
\item \<^term>\<open>case x of True \<Rightarrow> e\<^sub>1 | False \<Rightarrow> e\<^sub>2\<close> instead of\\
      \<open>case x of True \<Rightarrow> e\<^sub>1 | False \<Rightarrow> e\<^sub>2\<close>.
\end{itemize}

\subsection{Sets}

Although set syntax in HOL is already close to
standard, we provide a few further improvements:
\begin{itemize}
\item \<^term>\<open>{x. P}\<close> instead of \<open>{x. P}\<close>.
\item \<^term>\<open>{}\<close> instead of \<open>{}\<close>, where
 \<^term>\<open>{}\<close> is also input syntax.
\item \<^term>\<open>insert a (insert b (insert c M))\<close> instead of \<open>insert a (insert b (insert c M))\<close>.
\item \<^term>\<open>card A\<close> instead of \<open>card A\<close>.
\end{itemize}

\subsection{Lists}

If lists are used heavily, the following notations increase readability:
\begin{itemize}
\item \<^term>\<open>x # xs\<close> instead of \<open>x # xs\<close>,
      where \<^term>\<open>x # xs\<close> is also input syntax.
\item \<^term>\<open>length xs\<close> instead of \<open>length xs\<close>.
\item \<^term>\<open>nth xs n\<close> instead of \<open>nth xs n\<close>,
      the $n$th element of \<open>xs\<close>.

\item Human readers are good at converting automatically from lists to
sets. Hence \texttt{OptionalSugar} contains syntax for suppressing the
conversion function \<^const>\<open>set\<close>: for example, @{prop[source]"x \<in> set xs"}
becomes \<^prop>\<open>x \<in> set xs\<close>.

\item The \<open>@\<close> operation associates implicitly to the right,
which leads to unpleasant line breaks if the term is too long for one
line. To avoid this, \texttt{OptionalSugar} contains syntax to group
\<open>@\<close>-terms to the left before printing, which leads to better
line breaking behaviour:
@{term[display]"term\<^sub>0 @ term\<^sub>1 @ term\<^sub>2 @ term\<^sub>3 @ term\<^sub>4 @ term\<^sub>5 @ term\<^sub>6 @ term\<^sub>7 @ term\<^sub>8 @ term\<^sub>9 @ term\<^sub>1\<^sub>0"}

\end{itemize}

\subsection{Numbers}

Coercions between numeric types are alien to mathematicians who
consider, for example, \<^typ>\<open>nat\<close> as a subset of \<^typ>\<open>int\<close>.
\texttt{OptionalSugar} contains syntax for suppressing numeric coercions such
as \<^const>\<open>int\<close> \<open>::\<close> \<^typ>\<open>nat \<Rightarrow> int\<close>. For example,
@{term[source]"int 5"} is printed as \<^term>\<open>int 5\<close>. Embeddings of types
\<^typ>\<open>nat\<close>, \<^typ>\<open>int\<close>, \<^typ>\<open>real\<close> are covered; non-injective coercions such
as \<^const>\<open>nat\<close> \<open>::\<close> \<^typ>\<open>int \<Rightarrow> nat\<close> are not and should not be
hidden.

\section{Printing constants and their type}

Instead of
\verb!@!\verb!{const myconst}! \verb!@!\verb!{text "::"}! \verb!@!\verb!{typeof myconst}!,
you can write \verb!@!\verb!{const_typ myconst}! using the new antiquotation
\texttt{const\_typ} defined in \texttt{LaTeXsugar}. For example,
\verb!@!\verb!{const_typ length}! produces \<^const_typ>\<open>length\<close> (see below for how to suppress
the question mark).
This works both for genuine constants and for variables fixed in some context,
especially in a locale.

\section{Printing theorems}

The \<^prop>\<open>P \<Longrightarrow> Q \<Longrightarrow> R\<close> syntax is a bit idiosyncratic. If you would like
to avoid it, you can easily print the premises as a conjunction:
\<^prop>\<open>P \<and> Q \<Longrightarrow> R\<close>. See \texttt{OptionalSugar} for the required ``code''.

\subsection{Question marks}

If you print anything, especially theorems, containing
schematic variables they are prefixed with a question mark:
\verb!@!\verb!{thm conjI}! results in @{thm conjI}. Most of the time
you would rather not see the question marks. There is an attribute
\verb!no_vars! that you can attach to the theorem that turns its
schematic into ordinary free variables:
\begin{quote}
\verb!@!\verb!{thm conjI[no_vars]}!\\
\showout @{thm conjI[no_vars]}
\end{quote}
This \verb!no_vars! business can become a bit tedious.
If you would rather never see question marks, simply put
\begin{quote}
\verb!options [show_question_marks = false]!
\end{quote}
into the relevant \texttt{ROOT} file, just before the \texttt{theories} for that session.
The rest of this document is produced with this flag set to \texttt{false}.
\<close>

(*<*)declare [[show_question_marks = false]](*>*)

subsection \<open>Qualified names\<close>

text\<open>If there are multiple declarations of the same name, Isabelle prints
the qualified name, for example \<open>T.length\<close>, where \<open>T\<close> is the
theory it is defined in, to distinguish it from the predefined @{const[source]
"List.length"}. In case there is no danger of confusion, you can insist on
short names (no qualifiers) by setting the \verb!names_short!
configuration option in the context.

\subsection {Variable names\label{sec:varnames}}

It sometimes happens that you want to change the name of a
variable in a theorem before printing it. This can easily be achieved
with the help of Isabelle's instantiation attribute \texttt{where}:
\begin{quote}
\verb!@!\verb!{thm conjI[where P = \<phi> and Q = \<psi>]}!\\
\showout @{thm conjI[where P = \<phi> and Q = \<psi>]}
\end{quote}
To support the ``\_''-notation for irrelevant variables
the constant \texttt{DUMMY} has been introduced:
\begin{quote}
\verb!@!\verb!{thm fst_conv[of _ DUMMY]}!\\
\showout @{thm fst_conv[of _ DUMMY]}
\end{quote}
As expected, the second argument has been replaced by ``\_'',
but the first argument is the ugly \<open>x1.0\<close>, a schematic variable
with suppressed question mark. Schematic variables that end in digits,
e.g. \<open>x1\<close>, are still printed with a trailing \<open>.0\<close>,
e.g. \<open>x1.0\<close>, their internal index. This can be avoided by
turning the last digit into a subscript: write \<^verbatim>\<open>x\<^sub>1\<close> and
obtain the much nicer \<open>x\<^sub>1\<close>. Alternatively, you can display trailing digits of
schematic and free variables as subscripts with the \texttt{sub} style:
\begin{quote}
\verb!@!\verb!{thm (sub) fst_conv[of _ DUMMY]}!\\
\showout @{thm (sub) fst_conv[of _ DUMMY]}
\end{quote}
The insertion of underscores can be automated with the \verb!dummy_pats! style:
\begin{quote}
\verb!@!\verb!{thm (dummy_pats,sub) fst_conv}!\\
\showout @{thm (dummy_pats,sub) fst_conv}
\end{quote}
The theorem must be an equation. Then every schematic variable that occurs
on the left-hand but not the right-hand side is replaced by \texttt{DUMMY}.
This is convenient for displaying functional programs.

Variables that are bound by quantifiers or lambdas can be renamed
with the help of the attribute \verb!rename_abs!.
It expects a list of names or underscores, similar to the \texttt{of} attribute:
\begin{quote}
\verb!@!\verb!{thm split_paired_All[rename_abs _ l r]}!\\
\showout @{thm split_paired_All[rename_abs _ l r]}
\end{quote}

Sometimes Isabelle $\eta$-contracts terms, for example in the following definition:
\<close>
fun eta where
"eta (x \<cdot> xs) = (\<forall>y \<in> set xs. x < y)"
text\<open>
\noindent
If you now print the defining equation, the result is not what you hoped for:
\begin{quote}
\verb!@!\verb!{thm eta.simps}!\\
\showout @{thm eta.simps}
\end{quote}
In such situations you can put the abstractions back by explicitly $\eta$-expanding upon output:
\begin{quote}
\verb!@!\verb!{thm (eta_expand z) eta.simps}!\\
\showout @{thm (eta_expand z) eta.simps}
\end{quote}
Instead of a single variable \verb!z! you can give a whole list \verb!x y z!
to perform multiple $\eta$-expansions.

\subsection{Inference rules}

To print theorems as inference rules you need to include Didier
R\'emy's \texttt{mathpartir} package~\<^cite>\<open>mathpartir\<close>
for typesetting inference rules in your \LaTeX\ file.

Writing \verb!@!\verb!{thm[mode=Rule] conjI}! produces
@{thm[mode=Rule] conjI}, even in the middle of a sentence.
If you prefer your inference rule on a separate line, maybe with a name,
\begin{center}
@{thm[mode=Rule] conjI} {\sc conjI}
\end{center}
is produced by
\begin{quote}
\verb!\begin{center}!\\
\verb!@!\verb!{thm[mode=Rule] conjI} {\sc conjI}!\\
\verb!\end{center}!
\end{quote}
It is not recommended to use the standard \texttt{display} option
together with \texttt{Rule} because centering does not work and because
the line breaking mechanisms of \texttt{display} and \texttt{mathpartir} can
clash.

Of course you can display multiple rules in this fashion:
\begin{quote}
\verb!\begin{center}!\\
\verb!@!\verb!{thm[mode=Rule] conjI} {\sc conjI} \\[1ex]!\\
\verb!@!\verb!{thm[mode=Rule] conjE} {\sc disjI$_1$} \qquad!\\
\verb!@!\verb!{thm[mode=Rule] disjE} {\sc disjI$_2$}!\\
\verb!\end{center}!
\end{quote}
yields
\begin{center}\small
@{thm[mode=Rule] conjI} {\sc conjI} \\[1ex]
@{thm[mode=Rule] disjI1} {\sc disjI$_1$} \qquad
@{thm[mode=Rule] disjI2} {\sc disjI$_2$}
\end{center}

The \texttt{mathpartir} package copes well if there are too many
premises for one line:
\begin{center}
@{prop[mode=Rule] "\<lbrakk> A \<longrightarrow> B; B \<longrightarrow> C; C \<longrightarrow> D; D \<longrightarrow> E; E \<longrightarrow> F; F \<longrightarrow> G;
 G \<longrightarrow> H; H \<longrightarrow> I; I \<longrightarrow> J; J \<longrightarrow> K \<rbrakk> \<Longrightarrow> A \<longrightarrow> K"}
\end{center}

Limitations: 1. Premises and conclusion must each not be longer than
the line.  2. Premises that are \<open>\<Longrightarrow>\<close>-implications are again
displayed with a horizontal line, which looks at least unusual.

In case you print theorems without premises no rule will be printed by the
\texttt{Rule} print mode. However, you can use \texttt{Axiom} instead:
\begin{quote}
\verb!\begin{center}!\\
\verb!@!\verb!{thm[mode=Axiom] refl} {\sc refl}! \\
\verb!\end{center}!
\end{quote}
yields
\begin{center}
@{thm[mode=Axiom] refl} {\sc refl}
\end{center}

\subsection{Displays and font sizes}

When displaying theorems with the \texttt{display} option, for example as in
\verb!@!\verb!{thm[display] refl}! @{thm[display] refl} the theorem is
set in small font. It uses the \LaTeX-macro \verb!\isastyle!,
which is also the style that regular theory text is set in, e.g.\<close>

lemma "t = t"
(*<*)oops(*>*)

text\<open>\noindent Otherwise \verb!\isastyleminor! is used,
which does not modify the font size (assuming you stick to the default
\verb!\isabellestyle{it}! in \texttt{root.tex}). If you prefer
normal font size throughout your text, include
\begin{quote}
\verb!\renewcommand{\isastyle}{\isastyleminor}!
\end{quote}
in \texttt{root.tex}. On the other hand, if you like the small font,
just put \verb!\isastyle! in front of the text in question,
e.g.\ at the start of one of the center-environments above.

The advantage of the display option is that you can display a whole
list of theorems in one go. For example,
\verb!@!\verb!{thm[display] append.simps}!
generates @{thm[display] append.simps}

\subsection{If-then}

If you prefer a fake ``natural language'' style you can produce
the body of
\newtheorem{theorem}{Theorem}
\begin{theorem}
@{thm[mode=IfThen] le_trans}
\end{theorem}
by typing
\begin{quote}
\verb!@!\verb!{thm[mode=IfThen] le_trans}!
\end{quote}

In order to prevent odd line breaks, the premises are put into boxes.
At times this is too drastic:
\begin{theorem}
@{prop[mode=IfThen] "longpremise \<Longrightarrow> longerpremise \<Longrightarrow> P(f(f(f(f(f(f(f(f(f(x)))))))))) \<Longrightarrow> longestpremise \<Longrightarrow> conclusion"}
\end{theorem}
In which case you should use \texttt{IfThenNoBox} instead of
\texttt{IfThen}:
\begin{theorem}
@{prop[mode=IfThenNoBox] "longpremise \<Longrightarrow> longerpremise \<Longrightarrow> P(f(f(f(f(f(f(f(f(f(x)))))))))) \<Longrightarrow> longestpremise \<Longrightarrow> conclusion"}
\end{theorem}

\subsection{Doing it yourself\label{sec:yourself}}

If for some reason you want or need to present theorems your
own way, you can extract the premises and the conclusion explicitly
and combine them as you like:
\begin{itemize}
\item \verb!@!\verb!{thm (prem 1)! $thm$\verb!}!
prints premise 1 of $thm$.
\item \verb!@!\verb!{thm (concl)! $thm$\verb!}!
prints the conclusion of $thm$.
\end{itemize}
For example, ``from @{thm (prem 2) conjI} and
@{thm (prem 1) conjI} we conclude @{thm (concl) conjI}''
is produced by
\begin{quote}
\verb!from !\verb!@!\verb!{thm (prem 2) conjI}! \verb!and !\verb!@!\verb!{thm (prem 1) conjI}!\\
\verb!we conclude !\verb!@!\verb!{thm (concl) conjI}!
\end{quote}
Thus you can rearrange or hide premises and typeset the theorem as you like.
Styles like \verb!(prem 1)! are a general mechanism explained
in \S\ref{sec:styles}.

\subsection{Patterns}

In \S\ref{sec:varnames} we shows how to create patterns containing ``\<^term>\<open>DUMMY\<close>''.
You can drive this game even further and extend the syntax of let
bindings such that certain functions like \<^term>\<open>fst\<close>, \<^term>\<open>hd\<close>,
etc.\ are printed as patterns. \texttt{OptionalSugar} provides the following:

\begin{center}
\begin{tabular}{l@ {~~produced by~~}l}
\<^term>\<open>let x = fst p in t\<close> & \verb!@!\verb!{term "let x = fst p in t"}!\\
\<^term>\<open>let x = snd p in t\<close> & \verb!@!\verb!{term "let x = snd p in t"}!\\
\<^term>\<open>let x = hd xs in t\<close> & \verb!@!\verb!{term "let x = hd xs in t"}!\\
\<^term>\<open>let x = tl xs in t\<close> & \verb!@!\verb!{term "let x = tl xs in t"}!\\
\<^term>\<open>let x = the y in t\<close> & \verb!@!\verb!{term "let x = the y in t"}!\\
\end{tabular}
\end{center}

\section {Styles\label{sec:styles}}

The \verb!thm! antiquotation works nicely for single theorems, but
sets of equations as used in definitions are more difficult to
typeset nicely: people tend to prefer aligned \<open>=\<close> signs.

To deal with such cases where it is desirable to dive into the structure
of terms and theorems, Isabelle offers antiquotations featuring ``styles'':

\begin{quote}
\verb!@!\verb!{thm (style) thm}!\\
\verb!@!\verb!{prop (style) thm}!\\
\verb!@!\verb!{term (style) term}!\\
\verb!@!\verb!{term_type (style) term}!\\
\verb!@!\verb!{typeof (style) term}!\\
\end{quote}

 A ``style'' is a transformation of a term. There are predefined
 styles, namely \verb!lhs! and \verb!rhs!, \verb!prem! with one argument, and \verb!concl!.
For example, the output
\begin{center}
\begin{tabular}{l@ {~~\<open>=\<close>~~}l}
@{thm (lhs) append_Nil} & @{thm (rhs) append_Nil}\\
@{thm (lhs) append_Cons} & @{thm (rhs) append_Cons}
\end{tabular}
\end{center}
is produced by the following code:
\begin{quote}
  \verb!\begin{center}!\\
  \verb!\begin{tabular}{l@ {~~!\verb!@!\verb!{text "="}~~}l}!\\
  \verb!@!\verb!{thm (lhs) append_Nil} & @!\verb!{thm (rhs) append_Nil}\\!\\
  \verb!@!\verb!{thm (lhs) append_Cons} & @!\verb!{thm (rhs) append_Cons}!\\
  \verb!\end{tabular}!\\
  \verb!\end{center}!
\end{quote}
Note the space between \verb!@! and \verb!{! in the tabular argument.
It prevents Isabelle from interpreting \verb!@ {~~...~~}!
as an antiquotation. The styles \verb!lhs! and \verb!rhs!
extract the left hand side (or right hand side respectively) from the
conclusion of propositions consisting of a binary operator
(e.~g.~\<open>=\<close>, \<open>\<equiv>\<close>, \<open><\<close>).

Likewise, \verb!concl! may be used as a style to show just the
conclusion of a proposition. For example, take \verb!hd_Cons_tl!:
\begin{center}
  @{thm hd_Cons_tl}
\end{center}
To print just the conclusion,
\begin{center}
  @{thm (concl) hd_Cons_tl}
\end{center}
type
\begin{quote}
  \verb!\begin{center}!\\
  \verb!@!\verb!{thm (concl) hd_Cons_tl}!\\
  \verb!\end{center}!
\end{quote}
Beware that any options must be placed \emph{before} the style, as in this example.

Further use cases can be found in \S\ref{sec:yourself}.
If you are not afraid of ML, you may also define your own styles.
Have a look at module \<^ML_structure>\<open>Term_Style\<close>.

\section {Proofs}

Full proofs, even if written in beautiful Isar style, are
likely to be too long and detailed to be included in conference
papers, but some key lemmas might be of interest.
It is usually easiest to put them in figures like the one in Fig.\
\ref{fig:proof}. This was achieved with the \isakeyword{text\_raw} command:
\<close>
text_raw \<open>
  \begin{figure}
  \begin{center}\begin{minipage}{0.6\textwidth}
  \isastyleminor\isamarkuptrue
\<close>
lemma True
proof -
  \<comment> \<open>pretty trivial\<close>
  show True by force
qed
text_raw \<open>
  \end{minipage}\end{center}
  \caption{Example proof in a figure.}\label{fig:proof}
  \end{figure}
\<close>
text \<open>

\begin{quote}
\small
\verb!text_raw {!\verb!*!\\
\verb!  \begin{figure}!\\
\verb!  \begin{center}\begin{minipage}{0.6\textwidth}!\\
\verb!  \isastyleminor\isamarkuptrue!\\
\verb!*!\verb!}!\\
\verb!lemma True!\\
\verb!proof -!\\
\verb!  -- "pretty trivial"!\\
\verb!  show True by force!\\
\verb!qed!\\
\verb!text_raw {!\verb!*!\\
\verb!  \end{minipage}\end{center}!\\
\verb!  \caption{Example proof in a figure.}\label{fig:proof}!\\
\verb!  \end{figure}!\\
\verb!*!\verb!}!
\end{quote}

Other theory text, e.g.\ definitions, can be put in figures, too.

\section{Theory snippets}

This section describes how to include snippets of a theory text in some other \LaTeX\ document.
The typical scenario is that the description of your theory is not part of the theory text but
a separate document that antiquotes bits of the theory. This works well for terms and theorems
but there are no antiquotations, for example, for function definitions or proofs. Even if there are antiquotations,
the output is usually a reformatted (by Isabelle) version of the input and may not look like
you wanted it to look. Here is how to include a snippet of theory text (in \LaTeX\ form) in some
other \LaTeX\ document, in 4 easy steps. Beware that these snippets are not processed by
any antiquotation mechanism: the resulting \LaTeX\ text is more or less exactly what you wrote
in the theory, without any added sugar.

\subsection{Theory markup}

Include some markers at the beginning and the end of the theory snippet you want to cut out.
You have to place the following lines before and after the snippet, where snippets must always be
consecutive lines of theory text:
\begin{quote}
\verb!\text_raw{!\verb!*\snip{!\emph{snippetname}\verb!}{1}{2}{%*!\verb!}!\\
\emph{theory text}\\
\verb!\text_raw{!\verb!*!\verb!}%endsnip*!\verb!}!
\end{quote}
where \emph{snippetname} should be a unique name for the snippet. The numbers \texttt{1}
and \texttt{2} are explained in a moment.

\subsection{Generate the \texttt{.tex} file}

Run your theory \texttt{T} with the \texttt{isabelle} \texttt{build} tool
to generate the \LaTeX-file \texttt{T.tex} which is needed for the next step,
extraction of marked snippets.
You may also want to process \texttt{T.tex} to generate a pdf document.
This requires a definition of \texttt{\char`\\snippet}:
\begin{verbatim}
\newcommand{\repeatisanl}[1]
  {\ifnum#1=0\else\isanewline\repeatisanl{\numexpr#1-1}\fi}
\newcommand{\snip}[4]{\repeatisanl#2#4\repeatisanl#3}
\end{verbatim}
Parameter 2 and 3 of \texttt{\char`\\snippet} are numbers (the \texttt{1}
and \texttt{2} above) and determine how many newlines are inserted before and after the snippet.
Unfortunately \texttt{text\_raw} eats up all preceding and following newlines
and they have to be inserted again in this manner. Otherwise the document generated from \texttt{T.tex}
will look ugly around the snippets. It can take some iterations to get the number of required
newlines exactly right.

\subsection{Extract marked snippets}
\label{subsec:extract}

Extract the marked bits of text with a shell-level script, e.g.
\begin{quote}
\verb!sed -n '/\\snip{/,/endsnip/p' T.tex > !\emph{snippets}\verb!.tex!
\end{quote}
File \emph{snippets}\texttt{.tex} (the name is arbitrary) now contains a sequence of blocks like this
\begin{quote}
\verb!\snip{!\emph{snippetname}\verb!}{1}{2}{%!\\
\emph{theory text}\\
\verb!}%endsnip!
\end{quote}

\subsection{Including snippets}

In the preamble of the document where the snippets are to be used you define \texttt{\char`\\snip}
and input \emph{snippets}\texttt{.tex}:
\begin{verbatim}
\newcommand{\snip}[4]
  {\expandafter\newcommand\csname #1\endcsname{#4}}
\input{snippets}
\end{verbatim}
This definition of \texttt{\char`\\snip} simply has the effect of defining for each snippet
\emph{snippetname} a \LaTeX\ command \texttt{\char`\\}\emph{snippetname}
that produces the corresponding snippet text. In the body of your document you can display that text
like this:
\begin{quote}
\verb!\begin{isabelle}!\\
\texttt{\char`\\}\emph{snippetname}\\
\verb!\end{isabelle}!
\end{quote}
The \texttt{isabelle} environment is the one defined in the standard file
\texttt{isabelle.sty} which most likely you are loading anyway.
\<close>

(*<*)
end
(*>*)