Theory Multitape_Alphabet_Enlargement.AlphabetEnlargement_Codec
theory AlphabetEnlargement_Codec
imports AlphabetEnlargement_Window
begin
subsection ‹Encoder and decoder structural lemmas›
subsubsection ‹Decoder structural lemmas and ceiling-div arithmetic›
text ‹Decoder structural lemmas. ‹ae_decode_block_pure›
resolves to the full enum image (length ‹c›); the analogous
fact for padded blocks (length = the padded witness)
splits the enum list at the witness index and uses the
takeWhile / append decomposition.›
lemma ae_decode_block_pure:
fixes f :: "'c :: enum ⇒ 'a"
assumes "is_pure_block bl f"
shows "ae_decode_block bl f
= map f (enum_class.enum :: 'c list)"
proof -
have all_neq: "∀a ∈ set (map f (enum_class.enum :: 'c list)). a ≠ bl"
using assms unfolding is_pure_block_def by auto
show ?thesis
unfolding ae_decode_block_def
using all_neq by (simp add: takeWhile_eq_all_conv)
qed
lemma length_ae_decode_block_pure:
fixes f :: "'c :: enum ⇒ 'a"
assumes "is_pure_block bl f"
shows "length (ae_decode_block bl f) = card (UNIV :: 'c set)"
using ae_decode_block_pure[OF assms] card_eq_length_enum[symmetric]
by simp
lemma ae_decode_block_padded:
fixes f :: "'c :: enum ⇒ 'a"
assumes k_lt: "k < length (enum_class.enum :: 'c list)"
and prefix: "∀x. c_idx x < k ⟶ f x ≠ bl"
and suffix: "∀x. k ≤ c_idx x ⟶ f x = bl"
shows "ae_decode_block bl f
= map f (take k (enum_class.enum :: 'c list))"
proof -
let ?xs = "enum_class.enum :: 'c list"
let ?P = "λa. a ≠ bl"
have at_k: "f (?xs ! k) = bl"
proof -
have "c_idx (?xs ! k) = k" using k_lt by (rule c_idx_enum_nth)
thus ?thesis using suffix by simp
qed
have prefix_neq: "∀a ∈ set (map f (take k ?xs)). ?P a"
proof
fix a assume a_in: "a ∈ set (map f (take k ?xs))"
then obtain i where i_lt: "i < length (take k ?xs)"
and a_eq: "a = map f (take k ?xs) ! i"
by (auto simp: in_set_conv_nth)
from i_lt have i_lt_k: "i < k" using k_lt by simp
hence i_lt_n: "i < length ?xs" using k_lt by linarith
have a_alt: "a = f (?xs ! i)"
using a_eq i_lt_k k_lt by (simp add: nth_map nth_take)
have c_idx_at: "c_idx (?xs ! i) = i" using i_lt_n by (rule c_idx_enum_nth)
show "?P a" using prefix c_idx_at i_lt_k a_alt by simp
qed
have xs_split: "?xs = take k ?xs @ ?xs ! k # drop (Suc k) ?xs"
using k_lt by (rule id_take_nth_drop)
have map_split: "map f ?xs = map f (take k ?xs) @ f (?xs ! k) # map f (drop (Suc k) ?xs)"
proof -
have "map f ?xs = map f (take k ?xs @ ?xs ! k # drop (Suc k) ?xs)"
using xs_split by simp
thus ?thesis by simp
qed
have step1: "takeWhile ?P (map f ?xs)
= map f (take k ?xs)
@ takeWhile ?P (f (?xs ! k) # map f (drop (Suc k) ?xs))"
proof -
have "takeWhile ?P (map f ?xs)
= takeWhile ?P (map f (take k ?xs)
@ f (?xs ! k) # map f (drop (Suc k) ?xs))"
using map_split by simp
also have "… = map f (take k ?xs)
@ takeWhile ?P (f (?xs ! k) # map f (drop (Suc k) ?xs))"
using prefix_neq by (subst takeWhile_append) auto
finally show ?thesis .
qed
have step2: "takeWhile ?P (f (?xs ! k) # map f (drop (Suc k) ?xs)) = []"
using at_k by simp
show ?thesis
unfolding ae_decode_block_def
using step1 step2 by simp
qed
text ‹Decoder structural lemmas (continued). ‹ae_decode_input›
is compositional under list append; the length-of-decode for
an all-pure prefix is exactly ‹length ⋅ c›.›
lemma ae_decode_input_append:
"ae_decode_input bl (xs @ ys)
= ae_decode_input bl xs @ ae_decode_input bl ys"
by (simp add: ae_decode_input_def)
lemma length_ae_decode_input_pure:
fixes xs :: "('c :: enum ⇒ 'a) list"
assumes "∀s < length xs. is_pure_block bl (xs ! s)"
shows "length (ae_decode_input bl xs) = length xs * card (UNIV :: 'c set)"
using assms
proof (induction xs)
case Nil
show ?case by (simp add: ae_decode_input_def)
next
case (Cons x xs)
have x_pure: "is_pure_block bl x"
using Cons.prems[rule_format, of 0] by simp
have rest_pure: "∀s < length xs. is_pure_block bl (xs ! s)"
proof (intro allI impI)
fix s assume "s < length xs"
hence "Suc s < length (x # xs)" by simp
thus "is_pure_block bl (xs ! s)"
using Cons.prems[rule_format, of "Suc s"] by simp
qed
have "length (ae_decode_input bl (x # xs))
= length (ae_decode_block bl x) + length (ae_decode_input bl xs)"
by (simp add: ae_decode_input_def)
also have "… = card (UNIV :: 'c set) + length xs * card (UNIV :: 'c set)"
using length_ae_decode_block_pure[OF x_pure] Cons.IH[OF rest_pure]
by simp
also have "… = length (x # xs) * card (UNIV :: 'c set)"
by simp
finally show ?case .
qed
text ‹Ceiling-division arithmetic. Used by the encoder/decoder
round-trip lemmas below.›
lemma ceil_div_mult_c:
fixes m c :: nat
assumes "0 < c"
shows "(m * c + c - 1) div c = m"
using assms
proof (induction m)
case 0
show ?case using `0 < c` by simp
next
case (Suc m)
have c_ne: "c ≠ 0" using `0 < c` by simp
have eq: "Suc m * c + c - 1 = (m * c + c - 1) + c"
using `0 < c` by simp
have step: "((m * c + c - 1) + c) div c = (m * c + c - 1) div c + 1"
by (rule div_add_self2[OF c_ne])
have ih: "(m * c + c - 1) div c = m" using Suc.IH[OF `0 < c`] .
have "(Suc m * c + c - 1) div c = ((m * c + c - 1) + c) div c"
using eq by (rule arg_cong[where f = "λx. x div c"])
also have "… = (m * c + c - 1) div c + 1" using step .
also have "… = m + 1" using ih by simp
finally show ?case by simp
qed
lemma ceil_div_2c_minus_1:
fixes c :: nat
assumes "0 < c"
shows "(c + c - 1) div c = 1"
proof -
have "c + c - 1 = 1 * c + c - 1" by simp
thus ?thesis using ceil_div_mult_c[OF assms, of 1] by simp
qed
lemma ceil_div_k_plus_c_minus_1:
fixes k c :: nat
assumes "0 < c" "0 < k" "k ≤ c"
shows "(k + c - 1) div c = 1"
proof -
have lo: "k + c - 1 ≥ c" using assms by simp
have hi: "k + c - 1 < c + c"
using assms by linarith
have "(k + c - 1) div c = ((k + c - 1) - c) div c + 1"
using lo assms by (auto simp: le_div_geq)
also have "(k + c - 1) - c = k - 1" by simp
also have "(k - 1) div c = 0" using assms by simp
finally show ?thesis by simp
qed
subsubsection ‹Encoder list operations and image properties›
text ‹Encoding an input that is the image of a block function
under the canonical enumeration yields the singleton list
containing that block. Used by the round-trip lemma in the
forward direction of ‹ae_validation_canonical_iff_encoder_image›:
a pure last-block decodes to its full enum image, and that
image re-encodes back to the original block.›
lemma encode_input_map_enum:
fixes f :: "'c :: enum ⇒ 'a"
shows "encode_input bl (map f (enum_class.enum :: 'c list))
= ([f] :: ('c ⇒ 'a) list)"
proof -
let ?c = "card (UNIV :: 'c set)"
let ?xs = "enum_class.enum :: 'c list"
let ?w = "map f ?xs"
let ?lhs = "encode_input bl ?w :: ('c ⇒ 'a) list"
have len_xs: "length ?xs = ?c"
using card_eq_length_enum[symmetric] .
have len_w: "length ?w = ?c" using len_xs by simp
have c_pos: "0 < ?c"
proof -
have "(c_first :: 'c) ∈ UNIV" by simp
thus ?thesis by (simp add: card_gt_0_iff)
qed
have len_lhs: "length ?lhs = 1"
proof -
have "length ?lhs = (length ?w + ?c - 1) div ?c"
using length_encode_input[where bl = bl and u = "?w"] .
also have "… = (?c + ?c - 1) div ?c" using len_w by simp
also have "… = 1" using ceil_div_2c_minus_1[OF c_pos] .
finally show ?thesis .
qed
have zero_lt_lhs: "0 < length ?lhs" using len_lhs by simp
have nth0_at: "⋀x :: 'c. (?lhs ! 0) x = f x"
proof -
fix x :: 'c
have idx_lt_xs: "c_idx x < length ?xs"
using c_idx_in_range(1) .
have idx_lt_w: "c_idx x < length ?w" using idx_lt_xs by simp
have nth_step: "(?lhs ! 0) x =
(let j = 0 * ?c + c_idx x
in if j < length ?w then ?w ! j else bl)"
by (rule encode_input_nth[OF zero_lt_lhs])
have w_at: "?w ! c_idx x = f x"
using idx_lt_xs c_idx_in_range(2)[of x] by (simp add: nth_map)
show "(?lhs ! 0) x = f x"
using nth_step idx_lt_w w_at by (simp add: Let_def)
qed
have nth0: "?lhs ! 0 = f"
using nth0_at by (rule ext)
show ?thesis
proof (rule nth_equalityI)
show "length ?lhs = length ([f] :: ('c ⇒ 'a) list)"
using len_lhs by simp
next
fix i assume i_lt: "i < length ?lhs"
hence "i = 0" using len_lhs by simp
thus "?lhs ! i = ([f] :: ('c ⇒ 'a) list) ! i"
using nth0 by simp
qed
qed
text ‹Generalisation of ‹encode_input_map_enum›: encoding the
image of ‹f› over a length-‹m› prefix of the canonical
enumeration (where ‹0 < m ≤ c›, and ‹f x = bl› on the
out-of-prefix indices) yields the singleton list ‹[f]›.
Used by the round-trip in the forward direction of
‹ae_validation_canonical_iff_encoder_image›: a padded last
block decodes to its enum-prefix, and that prefix
re-encodes back to the original block.›
lemma encode_input_map_take_enum:
fixes f :: "'c :: enum ⇒ 'a"
assumes m_pos: "0 < m"
and m_le: "m ≤ length (enum_class.enum :: 'c list)"
and pad: "∀x. c_idx x ≥ m ⟶ f x = bl"
shows "encode_input bl (map f (take m (enum_class.enum :: 'c list)))
= ([f] :: ('c ⇒ 'a) list)"
proof -
let ?c = "card (UNIV :: 'c set)"
let ?xs = "enum_class.enum :: 'c list"
let ?w = "map f (take m ?xs)"
let ?lhs = "encode_input bl ?w :: ('c ⇒ 'a) list"
have len_xs: "length ?xs = ?c" using card_eq_length_enum[symmetric] .
have c_pos: "0 < ?c"
proof -
have "(c_first :: 'c) ∈ UNIV" by simp
thus ?thesis by (simp add: card_gt_0_iff)
qed
have m_le_c: "m ≤ ?c" using m_le len_xs by simp
have len_take: "length (take m ?xs) = m"
using m_le by simp
have len_w: "length ?w = m" using len_take by simp
have len_lhs: "length ?lhs = 1"
proof -
have "length ?lhs = (length ?w + ?c - 1) div ?c"
using length_encode_input[where bl = bl and u = "?w"] .
also have "… = (m + ?c - 1) div ?c" using len_w by simp
also have "… = 1"
using ceil_div_k_plus_c_minus_1[OF c_pos m_pos m_le_c] .
finally show ?thesis .
qed
have zero_lt_lhs: "0 < length ?lhs" using len_lhs by simp
have nth0_at: "⋀x :: 'c. (?lhs ! 0) x = f x"
proof -
fix x :: 'c
have idx_lt_xs: "c_idx x < length ?xs" using c_idx_in_range(1) .
have nth_step: "(?lhs ! 0) x =
(let j = 0 * ?c + c_idx x
in if j < length ?w then ?w ! j else bl)"
by (rule encode_input_nth[OF zero_lt_lhs])
show "(?lhs ! 0) x = f x"
proof (cases "c_idx x < m")
case True
hence j_lt_w: "c_idx x < length ?w" using len_w by simp
have w_at: "?w ! c_idx x = f x"
proof -
have take_at: "(take m ?xs) ! c_idx x = ?xs ! c_idx x"
using True by simp
have map_at: "?w ! c_idx x = f ((take m ?xs) ! c_idx x)"
using True len_take by (simp add: nth_map)
have "?w ! c_idx x = f (?xs ! c_idx x)"
using map_at take_at by simp
also have "?xs ! c_idx x = x" using c_idx_in_range(2)[of x] .
finally show ?thesis .
qed
show ?thesis using nth_step j_lt_w w_at by (simp add: Let_def)
next
case False
hence m_le_idx: "m ≤ c_idx x" by simp
have f_eq_bl: "f x = bl" using pad m_le_idx by simp
have body_bl: "(let j = 0 * ?c + c_idx x
in if j < length ?w then ?w ! j else bl) = bl"
using m_le_idx m_le len_w by (auto simp: Let_def)
have lhs_bl: "(?lhs ! 0) x = bl"
using nth_step body_bl by simp
show ?thesis using lhs_bl f_eq_bl by simp
qed
qed
have nth0: "?lhs ! 0 = f" using nth0_at by (rule ext)
show ?thesis
proof (rule nth_equalityI)
show "length ?lhs = length ([f] :: ('c ⇒ 'a) list)"
using len_lhs by simp
next
fix i assume i_lt: "i < length ?lhs"
hence "i = 0" using len_lhs by simp
thus "?lhs ! i = ([f] :: ('c ⇒ 'a) list) ! i"
using nth0 by simp
qed
qed
text ‹The encoder splits over an append whenever the prefix's
length is a multiple of ‹c›: each block of ‹encode_input
bl (u1 @ u2)› falls entirely within ‹u1› or entirely within
‹u2›, so the encoded list is the concatenation of the per-half
encodings. Used by the round-trip's ‹rev_induct›: when peeling
off the last block of a well-formed ‹w›, the prefix's
decode is all-pure (length divisible by ‹c›), allowing the
encoder to split.›
lemma encode_input_append_div:
fixes u1 u2 :: "'a list"
assumes c_div: "card (UNIV :: 'c :: enum set) dvd length u1"
shows "(encode_input bl (u1 @ u2) :: ('c ⇒ 'a) list)
= encode_input bl u1 @ encode_input bl u2"
proof -
let ?c = "card (UNIV :: 'c set)"
let ?lhs = "encode_input bl (u1 @ u2) :: ('c ⇒ 'a) list"
let ?rhs1 = "encode_input bl u1 :: ('c ⇒ 'a) list"
let ?rhs2 = "encode_input bl u2 :: ('c ⇒ 'a) list"
have c_pos: "0 < ?c"
proof -
have "(c_first :: 'c) ∈ UNIV" by simp
thus ?thesis by (simp add: card_gt_0_iff)
qed
obtain n where n_eq: "length u1 = n * ?c"
using c_div by (auto simp: dvd_def mult.commute)
have len_rhs1: "length ?rhs1 = n"
proof -
have "length ?rhs1 = (length u1 + ?c - 1) div ?c"
using length_encode_input[where bl = bl and u = u1] .
also have "… = (n * ?c + ?c - 1) div ?c" using n_eq by simp
also have "… = n" using ceil_div_mult_c[OF c_pos] .
finally show ?thesis .
qed
have len_rhs2: "length ?rhs2 = (length u2 + ?c - 1) div ?c"
using length_encode_input[where bl = bl and u = u2] .
have len_lhs: "length ?lhs = n + length ?rhs2"
proof -
have rearr: "n * ?c + length u2 + ?c - 1
= (length u2 + ?c - 1) + n * ?c"
using c_pos by arith
have c_ne: "?c ≠ 0" using c_pos by simp
have "length ?lhs = (length u1 + length u2 + ?c - 1) div ?c"
using length_encode_input[where bl = bl and u = "u1 @ u2"] by simp
also have "… = (n * ?c + length u2 + ?c - 1) div ?c"
using n_eq by simp
also have "… = ((length u2 + ?c - 1) + n * ?c) div ?c"
using rearr by simp
also have "… = n + (length u2 + ?c - 1) div ?c"
using c_ne by (simp add: div_mult_self1)
also have "… = n + length ?rhs2" using len_rhs2 by simp
finally show ?thesis .
qed
have len_eq: "length ?lhs = length (?rhs1 @ ?rhs2)"
using len_rhs1 len_lhs by simp
show ?thesis
proof (rule nth_equalityI)
show "length ?lhs = length (?rhs1 @ ?rhs2)" using len_eq .
next
fix i assume i_lt: "i < length ?lhs"
have lhs_step: "⋀x :: 'c. (?lhs ! i) x =
(let j = i * ?c + c_idx x
in if j < length (u1 @ u2)
then (u1 @ u2) ! j else bl)"
using encode_input_nth[OF i_lt] by simp
show "?lhs ! i = (?rhs1 @ ?rhs2) ! i"
proof (cases "i < n")
case True
have i_lt_rhs1: "i < length ?rhs1" using True len_rhs1 by simp
have rhs_split: "(?rhs1 @ ?rhs2) ! i = ?rhs1 ! i"
using i_lt_rhs1 by (simp add: nth_append)
have rhs1_step: "⋀x :: 'c. (?rhs1 ! i) x =
(let j = i * ?c + c_idx x
in if j < length u1 then u1 ! j else bl)"
using encode_input_nth[OF i_lt_rhs1] by simp
show ?thesis
proof (rule ext)
fix x :: 'c
have idx_lt_c: "c_idx x < ?c" using c_idx_lt_card .
have j_lt_u1: "i * ?c + c_idx x < length u1"
proof -
have step1: "i + 1 ≤ n" using True by simp
hence step2: "(i + 1) * ?c ≤ n * ?c" by (rule mult_le_mono1)
hence step3: "i * ?c + ?c ≤ n * ?c"
by (simp add: distrib_right)
have "i * ?c + c_idx x < i * ?c + ?c" using idx_lt_c by simp
also have "… ≤ n * ?c" using step3 .
also have "… = length u1" using n_eq by simp
finally show ?thesis .
qed
have j_lt_lhs: "i * ?c + c_idx x < length (u1 @ u2)"
using j_lt_u1 by simp
have lhs_at: "(?lhs ! i) x = u1 ! (i * ?c + c_idx x)"
proof -
have "(?lhs ! i) x = (u1 @ u2) ! (i * ?c + c_idx x)"
using lhs_step[of x] j_lt_lhs by (simp add: Let_def)
also have "… = u1 ! (i * ?c + c_idx x)"
using j_lt_u1 by (simp add: nth_append)
finally show ?thesis .
qed
have rhs_at: "(?rhs1 ! i) x = u1 ! (i * ?c + c_idx x)"
using rhs1_step[of x] j_lt_u1 by (simp add: Let_def)
show "(?lhs ! i) x = ((?rhs1 @ ?rhs2) ! i) x"
using lhs_at rhs_at rhs_split by simp
qed
next
case False
hence n_le_i: "n ≤ i" by simp
define i' where "i' = i - n"
have i_eq: "i = i' + n" using n_le_i unfolding i'_def by simp
have i_mul: "i * ?c = i' * ?c + n * ?c"
using i_eq by (simp add: distrib_right)
have len_rhs1_le: "length ?rhs1 ≤ i"
using n_le_i len_rhs1 by simp
have rhs2_idx: "i - length ?rhs1 = i'"
using i'_def len_rhs1 by simp
have i'_lt_rhs2: "i' < length ?rhs2"
using i_lt len_lhs n_le_i unfolding i'_def by linarith
have rhs_split: "(?rhs1 @ ?rhs2) ! i = ?rhs2 ! i'"
using len_rhs1_le rhs2_idx by (simp add: nth_append)
have rhs2_step: "⋀x :: 'c. (?rhs2 ! i') x =
(let j' = i' * ?c + c_idx x
in if j' < length u2 then u2 ! j' else bl)"
using encode_input_nth[OF i'_lt_rhs2] by simp
show ?thesis
proof (rule ext)
fix x :: 'c
have idx_lt_c: "c_idx x < ?c" using c_idx_lt_card .
have j_ge_u1: "length u1 ≤ i * ?c + c_idx x"
using i_mul n_eq n_le_i by simp
have j_diff: "i * ?c + c_idx x - length u1 = i' * ?c + c_idx x"
using i_mul n_eq by simp
have lhs_lt_iff: "(i * ?c + c_idx x < length (u1 @ u2))
⟷ (i' * ?c + c_idx x < length u2)"
using i_mul n_eq by simp
show "(?lhs ! i) x = ((?rhs1 @ ?rhs2) ! i) x"
proof (cases "i' * ?c + c_idx x < length u2")
case True
hence j_lt_lhs: "i * ?c + c_idx x < length (u1 @ u2)"
using lhs_lt_iff by simp
have lhs_at: "(?lhs ! i) x = u2 ! (i' * ?c + c_idx x)"
proof -
have "(?lhs ! i) x = (u1 @ u2) ! (i * ?c + c_idx x)"
using lhs_step[of x] j_lt_lhs by (simp add: Let_def)
also have "… = u2 ! (i * ?c + c_idx x - length u1)"
using j_ge_u1 by (simp add: nth_append)
also have "… = u2 ! (i' * ?c + c_idx x)" using j_diff by simp
finally show ?thesis .
qed
have rhs2_at: "(?rhs2 ! i') x = u2 ! (i' * ?c + c_idx x)"
using rhs2_step[of x] True by (simp add: Let_def)
show ?thesis using lhs_at rhs2_at rhs_split by simp
next
case False
hence j_ge_lhs: "¬ (i * ?c + c_idx x < length (u1 @ u2))"
using lhs_lt_iff by simp
have lhs_at: "(?lhs ! i) x = bl"
using lhs_step[of x] j_ge_lhs by (simp add: Let_def)
have rhs2_at: "(?rhs2 ! i') x = bl"
using rhs2_step[of x] False by (simp add: Let_def)
show ?thesis using lhs_at rhs2_at rhs_split by simp
qed
qed
qed
qed
qed
text ‹Encoding the empty input gives the empty block list.
Trivial corollary of ‹length_encode_input›: ‹(0 + c - 1) div c
= 0› regardless of ‹c›'s value (zero in nat division when
‹c = 0›; zero by ‹div_less› when ‹c > 0›).›
lemma encode_input_empty:
"(encode_input bl ([] :: 'a list) :: ('c :: enum ⇒ 'a) list) = []"
proof -
have c_pos: "0 < card (UNIV :: 'c set)"
proof -
have "(c_first :: 'c) ∈ UNIV" by simp
thus ?thesis by (simp add: card_gt_0_iff)
qed
have div_zero: "(card (UNIV :: 'c set) - Suc 0)
div card (UNIV :: 'c set) = 0"
using c_pos by (simp add: div_less)
have "length (encode_input bl ([] :: 'a list)
:: ('c ⇒ 'a) list)
= (0 + card (UNIV :: 'c set) - 1)
div card (UNIV :: 'c set)"
using length_encode_input[where bl = bl and u = "[] :: 'a list"]
by simp
also have "… = 0" using div_zero by simp
finally have "length (encode_input bl ([] :: 'a list)
:: ('c ⇒ 'a) list) = 0" .
thus ?thesis by simp
qed
text ‹If a snoc-list is well-formed, every cell of the prefix is
pure: well-formedness allows the *last* cell to be padded, but
the prefix's last cell is no longer last after the snoc, so it
must be pure. Bridges the ‹rev_induct› step in
‹encode_decode_round_trip›: the IH applies to the prefix only
if the prefix is itself well-formed (which follows from
per-cell purity).›
lemma ae_input_well_formed_init_pure:
fixes xs :: "('c :: enum ⇒ 'a) list"
and y :: "'c ⇒ 'a"
assumes "ae_input_well_formed bl (xs @ [y])"
shows "∀i < length xs. is_pure_block bl (xs ! i)"
proof (intro allI impI)
fix i assume i_lt_xs: "i < length xs"
have i_lt: "i < length (xs @ [y])" using i_lt_xs by simp
have not_last: "i ≠ length (xs @ [y]) - 1" using i_lt_xs by simp
have at_i: "is_pure_block bl ((xs @ [y]) ! i)
∨ (i = length (xs @ [y]) - 1
∧ is_padded_block bl ((xs @ [y]) ! i))"
using assms i_lt unfolding ae_input_well_formed_def by blast
hence pure: "is_pure_block bl ((xs @ [y]) ! i)" using not_last by blast
have nth_eq: "(xs @ [y]) ! i = xs ! i"
using i_lt_xs by (simp add: nth_append)
show "is_pure_block bl (xs ! i)" using pure nth_eq by simp
qed
text ‹Encoder image lies inside ‹gamma_block (Σ_M ∪ {bl_M})›:
every cell of every block in ‹encode_input bl_M u› is
either an input symbol from ‹u› (in ‹Σ_M›) or the
blank ‹bl_M›.›
lemma encode_input_in_gamma_block:
fixes M :: "('q, 'a) mttm"
and u :: "'a list"
assumes u_sub: "set u ⊆ Sigma_tm M"
shows "set (encode_input (bl_tm M) u :: ('c :: enum ⇒ 'a) list)
⊆ gamma_block (Sigma_tm M ∪ {bl_tm M})"
proof
fix f :: "'c ⇒ 'a"
assume f_in: "f ∈ set (encode_input (bl_tm M) u :: ('c ⇒ 'a) list)"
let ?c = "card (UNIV :: 'c set)"
from f_in obtain i where
i_lt: "i < (length u + ?c - 1) div ?c"
and f_eq: "f = (λx. let j = i * ?c + c_idx x in
if j < length u then u ! j else bl_tm M)"
unfolding encode_input_def Let_def by auto
have "range f ⊆ Sigma_tm M ∪ {bl_tm M}"
proof
fix v
assume "v ∈ range f"
then obtain x :: 'c where v_eq: "v = f x" by auto
show "v ∈ Sigma_tm M ∪ {bl_tm M}"
proof (cases "i * ?c + c_idx x < length u")
case True
hence fx: "f x = u ! (i * ?c + c_idx x)"
using f_eq by (simp add: Let_def)
have "u ! (i * ?c + c_idx x) ∈ set u" using True by simp
hence "u ! (i * ?c + c_idx x) ∈ Sigma_tm M" using u_sub by auto
thus ?thesis using v_eq fx by auto
next
case False
hence "f x = bl_tm M" using f_eq by (simp add: Let_def)
thus ?thesis using v_eq by auto
qed
qed
thus "f ∈ gamma_block (Sigma_tm M ∪ {bl_tm M})"
unfolding gamma_block_def by simp
qed
text ‹The all-blank block never appears in ‹encode_input
bl_M u›: the cell at the canonical zero offset (‹c_idx x = 0›)
always lies in the input range and so is in ‹Σ_M›, not
‹bl_M›. Discharges the ‹bl_block bl_M ∉ set w›
hypothesis for upstream call sites that feed an encoded word
into the noncanonical / steps-bound lemmas.›
lemma encode_input_no_bl_block:
fixes M :: "('q, 'a) mttm"
and u :: "'a list"
assumes vM: "valid_mttm M"
and u_sub: "set u ⊆ Sigma_tm M"
shows "bl_block (bl_tm M)
∉ set (encode_input (bl_tm M) u :: ('c :: enum ⇒ 'a) list)"
proof
let ?c = "card (UNIV :: 'c set)"
let ?N = "(length u + ?c - 1) div ?c"
let ?xs = "enum_class.enum :: 'c list"
let ?x0 = "?xs ! 0"
assume bl_in: "bl_block (bl_tm M)
∈ set (encode_input (bl_tm M) u :: ('c ⇒ 'a) list)"
from bl_in obtain i where
i_lt: "i < ?N"
and f_eq: "(bl_block (bl_tm M) :: 'c ⇒ 'a)
= (λx. let j = i * ?c + c_idx x in
if j < length u then u ! j else bl_tm M)"
unfolding encode_input_def Let_def by auto
have c_pos: "0 < ?c"
proof -
have "(c_first :: 'c) ∈ UNIV" by simp
thus ?thesis by (simp add: card_gt_0_iff)
qed
have N_pos: "0 < ?N" using i_lt by simp
have u_nonempty: "0 < length u"
proof (rule ccontr)
assume "¬ 0 < length u"
hence "length u = 0" by simp
hence "?N = (?c - 1) div ?c" by simp
also have "… = 0" using c_pos by simp
finally have "?N = 0" .
thus False using N_pos by simp
qed
have N_times_c_le: "?N * ?c ≤ length u + ?c - 1"
using c_pos by (metis div_times_less_eq_dividend)
have ic_lt: "i * ?c < length u"
proof -
have suc_le: "Suc i ≤ ?N" using i_lt by simp
have "Suc i * ?c ≤ ?N * ?c" by (rule mult_le_mono1[OF suc_le])
hence "i * ?c + ?c ≤ ?N * ?c" by simp
also have "… ≤ length u + ?c - 1" by (rule N_times_c_le)
finally have "i * ?c + ?c ≤ length u + ?c - 1" .
thus ?thesis using c_pos u_nonempty by linarith
qed
have len_xs_eq: "length ?xs = ?c"
using card_eq_length_enum[where 'c = 'c] by simp
have len_xs: "0 < length ?xs" using c_pos len_xs_eq by simp
have x0_idx: "c_idx ?x0 = 0"
using c_idx_enum_nth[OF len_xs] .
have eval_f: "(λx. let j = i * ?c + c_idx x in
if j < length u then u ! j else bl_tm M) ?x0
= u ! (i * ?c)"
using ic_lt x0_idx by (simp add: Let_def)
have eval_bl: "(bl_block (bl_tm M) :: 'c ⇒ 'a) ?x0 = bl_tm M"
unfolding bl_block_def by simp
have "u ! (i * ?c) ∈ set u" using ic_lt by simp
hence "u ! (i * ?c) ∈ Sigma_tm M" using u_sub by auto
hence "u ! (i * ?c) ≠ bl_tm M"
using bl_tm_notin_Sigma_tm[OF vM] by auto
thus False using f_eq eval_f eval_bl by metis
qed
text ‹Encoder output excludes the LE-block. Mirror of
‹encode_input_no_bl_block›: the first filled position
‹i ⋅ c› of any block-index ‹i < ⌈|u|/c⌉› is
strictly less than ‹|u|›, so the block's value at
‹c_first› equals ‹u ! (i ⋅ c) ∈ Σ_M›; since
‹le_tm M ∉ Σ_tm M› (substrate's ‹valid_mttm_LE_not_Sigma›),
that value differs from ‹le_tm M›, so the block is not the
constant-‹le› function. Used by ‹alphabet_enlarge_language›
to show ‹set (encode_input ...) ⊆ Sigma_tm
(alphabet_enlarge M)›.›
lemma encode_input_no_LE_block:
fixes M :: "('q, 'a) mttm"
and u :: "'a list"
assumes vM: "valid_mttm M"
and u_sub: "set u ⊆ Sigma_tm M"
shows "LE_block (le_tm M)
∉ set (encode_input (bl_tm M) u :: ('c :: enum ⇒ 'a) list)"
proof
let ?c = "card (UNIV :: 'c set)"
let ?N = "(length u + ?c - 1) div ?c"
let ?xs = "enum_class.enum :: 'c list"
let ?x0 = "?xs ! 0"
assume LE_in: "LE_block (le_tm M)
∈ set (encode_input (bl_tm M) u :: ('c ⇒ 'a) list)"
from LE_in obtain i where
i_lt: "i < ?N"
and f_eq: "(LE_block (le_tm M) :: 'c ⇒ 'a)
= (λx. let j = i * ?c + c_idx x in
if j < length u then u ! j else bl_tm M)"
unfolding encode_input_def Let_def by auto
have c_pos: "0 < ?c"
proof -
have "(c_first :: 'c) ∈ UNIV" by simp
thus ?thesis by (simp add: card_gt_0_iff)
qed
have N_pos: "0 < ?N" using i_lt by simp
have u_nonempty: "0 < length u"
proof (rule ccontr)
assume "¬ 0 < length u"
hence "length u = 0" by simp
hence "?N = (?c - 1) div ?c" by simp
also have "… = 0" using c_pos by simp
finally have "?N = 0" .
thus False using N_pos by simp
qed
have N_times_c_le: "?N * ?c ≤ length u + ?c - 1"
using c_pos by (metis div_times_less_eq_dividend)
have ic_lt: "i * ?c < length u"
proof -
have suc_le: "Suc i ≤ ?N" using i_lt by simp
have "Suc i * ?c ≤ ?N * ?c" by (rule mult_le_mono1[OF suc_le])
hence "i * ?c + ?c ≤ ?N * ?c" by simp
also have "… ≤ length u + ?c - 1" by (rule N_times_c_le)
finally have "i * ?c + ?c ≤ length u + ?c - 1" .
thus ?thesis using c_pos u_nonempty by linarith
qed
have len_xs_eq: "length ?xs = ?c"
using card_eq_length_enum[where 'c = 'c] by simp
have len_xs: "0 < length ?xs" using c_pos len_xs_eq by simp
have x0_idx: "c_idx ?x0 = 0"
using c_idx_enum_nth[OF len_xs] .
have eval_f: "(λx. let j = i * ?c + c_idx x in
if j < length u then u ! j else bl_tm M) ?x0
= u ! (i * ?c)"
using ic_lt x0_idx by (simp add: Let_def)
have eval_LE: "(LE_block (le_tm M) :: 'c ⇒ 'a) ?x0 = le_tm M"
unfolding LE_block_def by simp
have "u ! (i * ?c) ∈ set u" using ic_lt by simp
hence "u ! (i * ?c) ∈ Sigma_tm M" using u_sub by auto
hence "u ! (i * ?c) ≠ le_tm M"
using valid_mttm_LE_not_Sigma[OF vM] by auto
thus False using f_eq eval_f eval_LE by metis
qed
subsubsection ‹Encoder well-formedness and round-trip›
text ‹Encoder output is well-formed: every block except
possibly the last is pure; the last is either pure (when
‹length u› divides evenly) or trailing-padded (when it
doesn't). Discharges the ‹ae_input_well_formed› precondition
for upstream call sites that feed an encoded word into the
steps-bound lemma.
This is the forward arm of the characterisation biconditional
‹ae_validation_canonical_iff_encoder_image› below: every
encoder image is well-formed. The reverse arm of that
biconditional — every well-formed AE-input is in the encoder
image — is not consumed by the headline language theorems
(which are quantified only over explicit encoder-image
inputs) and is retained there as a structural completeness
result.›
lemma encode_input_well_formed:
fixes M :: "('q, 'a) mttm"
and u :: "'a list"
assumes vM: "valid_mttm M"
and u_sub: "set u ⊆ Sigma_tm M"
shows "ae_input_well_formed (bl_tm M)
(encode_input (bl_tm M) u :: ('c :: enum ⇒ 'a) list)"
proof -
let ?c = "card (UNIV :: 'c set)"
let ?N = "(length u + ?c - 1) div ?c"
let ?xs = "enum_class.enum :: 'c list"
let ?w = "encode_input (bl_tm M) u :: ('c ⇒ 'a) list"
have len_w: "length ?w = ?N" by (rule length_encode_input)
have c_pos: "0 < ?c"
proof -
have "(c_first :: 'c) ∈ UNIV" by simp
thus ?thesis by (simp add: card_gt_0_iff)
qed
have len_xs: "length ?xs = ?c"
using card_eq_length_enum[where 'c = 'c] by simp
have bl_notin_Sigma: "bl_tm M ∉ Sigma_tm M"
using bl_tm_notin_Sigma_tm[OF vM] .
show ?thesis
unfolding ae_input_well_formed_def
proof (intro allI impI)
fix s :: nat
assume s_lt: "s < length ?w"
hence s_lt_N: "s < ?N" using len_w by simp
have at_s: "?w ! s
= (λx. let j = s * ?c + c_idx x in
if j < length u then u ! j else bl_tm M)"
proof -
have s_lt_range: "s < length [0 ..< ?N]" using s_lt_N by simp
have nth_range: "[0 ..< ?N] ! s = s" using s_lt_N by simp
have map_unfold:
"?w = map (λi. (λx. let j = i * ?c + c_idx x in
if j < length u then u ! j
else bl_tm M))
[0 ..< ?N]"
unfolding encode_input_def Let_def by simp
show ?thesis
using nth_map[OF s_lt_range,
where f = "λi. (λx. let j = i * ?c + c_idx x in
if j < length u then u ! j
else bl_tm M)"]
nth_range map_unfold
by simp
qed
consider (non_last) "Suc s < ?N"
| (last) "Suc s = ?N"
using s_lt_N by linarith
thus "is_pure_block (bl_tm M) (?w ! s)
∨ (s = length ?w - 1 ∧ is_padded_block (bl_tm M) (?w ! s))"
proof cases
case non_last
have all_pure: "∀x :: 'c. (?w ! s) x ≠ bl_tm M"
proof
fix x :: 'c
have idx_lt_c: "c_idx x < ?c" by (rule c_idx_lt_card)
have suc2: "Suc (Suc s) ≤ ?N" using non_last by simp
have "Suc (Suc s) * ?c ≤ ?N * ?c"
by (rule mult_le_mono1[OF suc2])
also have "… ≤ length u + ?c - 1"
using c_pos by (metis div_times_less_eq_dividend)
finally have suc2_bound: "Suc (Suc s) * ?c ≤ length u + ?c - 1" .
have prod_lt: "s * ?c + c_idx x < length u"
using suc2_bound idx_lt_c c_pos by simp
hence "(?w ! s) x = u ! (s * ?c + c_idx x)"
using at_s by (simp add: Let_def)
moreover have "u ! (s * ?c + c_idx x) ∈ set u"
using prod_lt by simp
ultimately have "(?w ! s) x ∈ Sigma_tm M" using u_sub by auto
thus "(?w ! s) x ≠ bl_tm M" using bl_notin_Sigma by auto
qed
hence "is_pure_block (bl_tm M) (?w ! s)"
unfolding is_pure_block_def by simp
thus ?thesis ..
next
case last
have s_eq: "s = length ?w - 1"
using last len_w by simp
have s_eq_pred: "s = ?N - 1" using last by simp
have N_pos: "1 ≤ ?N" using last by simp
have s_times_c: "s * ?c = ?N * ?c - ?c"
proof -
have "s * ?c = (?N - 1) * ?c" using s_eq_pred by simp
also have "… = ?N * ?c - 1 * ?c"
by (rule diff_mult_distrib)
also have "… = ?N * ?c - ?c" by simp
finally show ?thesis .
qed
show ?thesis
proof (cases "length u mod ?c = 0")
case True
have N_eq: "?N * ?c = length u"
proof -
let ?q = "length u div ?c"
have c_neq_0: "?c ≠ 0" using c_pos by simp
have lu_eq: "length u = ?q * ?c"
using True
by (metis div_mult_mod_eq add.right_neutral)
have c_minus_1_lt: "?c - 1 < ?c" using c_pos by simp
have div_minus: "(?c - 1) div ?c = 0"
using c_minus_1_lt by simp
have add_assoc: "length u + ?c - 1 = length u + (?c - 1)"
using c_pos by simp
have "?N = (length u + (?c - 1)) div ?c"
using add_assoc by simp
also have "… = (?q * ?c + (?c - 1)) div ?c"
using lu_eq by simp
also have "… = ?q + (?c - 1) div ?c"
by simp
also have "… = ?q" using div_minus by simp
finally have "?N = ?q" .
thus ?thesis using lu_eq by simp
qed
have all_pure: "∀x :: 'c. (?w ! s) x ≠ bl_tm M"
proof
fix x :: 'c
have idx_lt_c: "c_idx x < ?c" by (rule c_idx_lt_card)
have N_pos_times: "?c ≤ ?N * ?c"
using N_pos by simp
have prod_lt_N: "s * ?c + c_idx x < ?N * ?c"
using s_times_c idx_lt_c c_pos N_pos_times by linarith
hence prod_lt: "s * ?c + c_idx x < length u" using N_eq by simp
hence "(?w ! s) x = u ! (s * ?c + c_idx x)"
using at_s by (simp add: Let_def)
moreover have "u ! (s * ?c + c_idx x) ∈ set u"
using prod_lt by simp
ultimately have "(?w ! s) x ∈ Sigma_tm M" using u_sub by auto
thus "(?w ! s) x ≠ bl_tm M" using bl_notin_Sigma by auto
qed
hence "is_pure_block (bl_tm M) (?w ! s)"
unfolding is_pure_block_def by simp
thus ?thesis ..
next
case False
let ?k = "length u mod ?c"
let ?q = "length u div ?c"
have k_pos: "1 ≤ ?k" using False by simp
have k_lt_c: "?k < ?c" using c_pos by simp
have k_lt_xs: "?k < length ?xs" using k_lt_c len_xs by simp
have lu_eq: "length u = ?q * ?c + ?k"
by simp
have N_eq: "?N = ?q + 1"
proof -
have plus_eq: "length u + ?c - 1 = ?q * ?c + (?k + ?c - 1)"
using lu_eq k_pos by linarith
have N_step: "?N = (?q * ?c + (?k + ?c - 1)) div ?c"
using plus_eq by simp
have div_step: "(?q * ?c + (?k + ?c - 1)) div ?c
= ?q + (?k + ?c - 1) div ?c"
by simp
have k_plus_c_minus_1_lt: "?k + ?c - 1 < 2 * ?c"
using k_lt_c c_pos by linarith
have k_plus_c_minus_1_ge: "?c ≤ ?k + ?c - 1"
using k_pos c_pos by linarith
have div_kpc_eq_1: "(?k + ?c - 1) div ?c = 1"
proof -
have rewrite: "?k + ?c - 1 = (?k - 1) + ?c"
using k_pos c_pos by linarith
have small: "(?k - 1) div ?c = 0"
using k_lt_c by simp
have c_neq_0: "?c ≠ 0" using c_pos by simp
have "((?k - 1) + ?c) div ?c = (?k - 1) div ?c + 1"
by (rule div_add_self2[OF c_neq_0])
also have "… = 1" using small by simp
finally show ?thesis using rewrite by simp
qed
show ?thesis using N_step div_step div_kpc_eq_1 by simp
qed
have s_eq_q: "s = ?q" using last N_eq by simp
have s_times_c_eq: "s * ?c = length u - ?k"
proof -
have q_eq: "length u div ?c * ?c = length u - length u mod ?c"
by (metis add_diff_cancel_right' div_mod_decomp)
show ?thesis using s_eq_q q_eq by simp
qed
have padded: "is_padded_block (bl_tm M) (?w ! s)"
unfolding is_padded_block_def
proof (intro exI[of _ ?k] conjI)
show "1 ≤ ?k" by (rule k_pos)
show "?k < length ?xs" by (rule k_lt_xs)
show "∀x :: 'c. c_idx x < ?k ⟶ (?w ! s) x ≠ bl_tm M"
proof (intro allI impI)
fix x :: 'c
assume idx_lt_k: "c_idx x < ?k"
have k_le_lu: "?k ≤ length u"
using lu_eq by linarith
have prod_lt: "s * ?c + c_idx x < length u"
using s_times_c_eq idx_lt_k k_le_lu by linarith
hence "(?w ! s) x = u ! (s * ?c + c_idx x)"
using at_s by (simp add: Let_def)
moreover have "u ! (s * ?c + c_idx x) ∈ set u"
using prod_lt by simp
ultimately have "(?w ! s) x ∈ Sigma_tm M"
using u_sub by auto
thus "(?w ! s) x ≠ bl_tm M" using bl_notin_Sigma by auto
qed
show "∀x :: 'c. ?k ≤ c_idx x ⟶ (?w ! s) x = bl_tm M"
proof (intro allI impI)
fix x :: 'c
assume idx_ge_k: "?k ≤ c_idx x"
have "s * ?c + c_idx x ≥ length u"
using s_times_c_eq idx_ge_k by linarith
hence "¬ s * ?c + c_idx x < length u" by simp
thus "(?w ! s) x = bl_tm M"
using at_s by (simp add: Let_def)
qed
qed
thus ?thesis using s_eq by simp
qed
qed
qed
qed
text ‹Encoder ‹∘› decoder is the identity on a single canonical
block: pure cell ‹→› ‹encode_input bl (map x enum) = [x]›;
padded cell with witness ‹k› ‹→›
‹encode_input bl (map x (take k enum)) = [x]›. Per-cell
identity for the round-trip.›
lemma encode_decode_block:
fixes x :: "'c :: enum ⇒ 'a"
assumes "is_canonical_block bl x"
shows "encode_input bl (ae_decode_block bl x)
= ([x] :: ('c ⇒ 'a) list)"
proof (cases "is_pure_block bl x")
case True
have decode: "ae_decode_block bl x
= map x (enum_class.enum :: 'c list)"
by (rule ae_decode_block_pure[OF True])
show ?thesis using decode encode_input_map_enum by simp
next
case False
hence padded: "is_padded_block bl x"
using assms unfolding is_canonical_block_def by blast
obtain k where k_pos: "1 ≤ k"
and k_lt: "k < length (enum_class.enum :: 'c list)"
and prefix: "∀y. c_idx y < k ⟶ x y ≠ bl"
and suffix: "∀y. c_idx y ≥ k ⟶ x y = bl"
using padded unfolding is_padded_block_def by blast
have decode: "ae_decode_block bl x
= map x (take k (enum_class.enum :: 'c list))"
by (rule ae_decode_block_padded[OF k_lt prefix suffix])
have m_pos: "0 < k" using k_pos by simp
have m_le: "k ≤ length (enum_class.enum :: 'c list)" using k_lt by simp
have enc: "encode_input bl (map x (take k (enum_class.enum :: 'c list)))
= ([x] :: ('c ⇒ 'a) list)"
using encode_input_map_take_enum[OF m_pos m_le suffix] .
show ?thesis using decode enc by simp
qed
text ‹The round-trip: for any well-formed block list ‹w›,
encoding the decoder's output reproduces ‹w›. By ‹rev_induct›:
the empty case is trivial; the snoc step uses
‹encode_input_append_div› (the prefix decodes to a length
divisible by ‹c›, since all of ‹w›'s prefix cells are pure)
plus ‹encode_decode_block› on the last block.›
lemma encode_decode_round_trip:
fixes w :: "('c :: enum ⇒ 'a) list"
assumes wf: "ae_input_well_formed bl w"
shows "encode_input bl (ae_decode_input bl w) = w"
proof -
have main: "ae_input_well_formed bl w
⟶ encode_input bl (ae_decode_input bl w) = w"
proof (induct w rule: rev_induct)
case Nil
show ?case
by (simp add: ae_decode_input_def encode_input_empty)
next
case (snoc x xs)
show ?case
proof
assume wf_snoc: "ae_input_well_formed bl (xs @ [x])"
have all_pure: "∀i < length xs. is_pure_block bl (xs ! i)"
using wf_snoc by (rule ae_input_well_formed_init_pure)
have wf_xs: "ae_input_well_formed bl xs"
unfolding ae_input_well_formed_def using all_pure by blast
have ih: "encode_input bl (ae_decode_input bl xs) = xs"
using snoc.hyps wf_xs by blast
have x_canonical: "is_canonical_block bl x"
proof -
let ?n = "length (xs @ [x]) - 1"
have x_eq: "(xs @ [x]) ! ?n = x" by simp
have n_lt: "?n < length (xs @ [x])" by simp
have "is_pure_block bl ((xs @ [x]) ! ?n)
∨ (?n = length (xs @ [x]) - 1
∧ is_padded_block bl ((xs @ [x]) ! ?n))"
using wf_snoc n_lt unfolding ae_input_well_formed_def by blast
hence "is_pure_block bl x ∨ is_padded_block bl x"
using x_eq by simp
thus ?thesis unfolding is_canonical_block_def .
qed
have block_eq: "encode_input bl (ae_decode_block bl x) = [x]"
by (rule encode_decode_block[OF x_canonical])
have len_xs_decode:
"card (UNIV :: 'c set) dvd length (ae_decode_input bl xs)"
proof -
have "length (ae_decode_input bl xs)
= length xs * card (UNIV :: 'c set)"
by (rule length_ae_decode_input_pure[OF all_pure])
thus ?thesis by simp
qed
have decode_split:
"ae_decode_input bl (xs @ [x])
= ae_decode_input bl xs @ ae_decode_block bl x"
by (simp add: ae_decode_input_def)
have "encode_input bl (ae_decode_input bl (xs @ [x]))
= encode_input bl
(ae_decode_input bl xs @ ae_decode_block bl x)"
using decode_split by simp
also have "(encode_input bl
(ae_decode_input bl xs @ ae_decode_block bl x)
:: ('c ⇒ 'a) list)
= encode_input bl (ae_decode_input bl xs)
@ encode_input bl (ae_decode_block bl x)"
by (rule encode_input_append_div[OF len_xs_decode])
also have "… = xs @ encode_input bl (ae_decode_block bl x)"
using ih by simp
also have "… = xs @ [x]" using block_eq by simp
finally show "encode_input bl (ae_decode_input bl (xs @ [x]))
= xs @ [x]" .
qed
qed
show ?thesis using main wf by blast
qed
end