Theory AlphabetReduction_Codec

theory AlphabetReduction_Codec
  imports "Multitape_TM_Substrate.Multitape_Substrate"
begin

section ‹Alphabet reduction›

text ‹This theory opens the alphabet-reduction development.  The
  alphabet_reduce› combinator takes a well-formed substrate
  machine over an arbitrary finite alphabet 'a› (with
  tape-alphabet cardinality ≥› 4) and produces a
  well-formed substrate machine over the fixed four-element alphabet
  sym4›, preserving the accepted language, determinism, and
  tape count.

  This is the elementary determinism-preserving per-symbol multi-cell
  encoding.

  The development is layered: this first theory fixes the output
  alphabet sym4› and the per-symbol binary codec; the
  simulator, its delta, and the headline theorems
  (alphabet_reduce_wf›, alphabet_reduce_language›,
  alphabet_reduce_time›, alphabet_reduce_det›) are
  built across the theories that follow.
  Throughout, b› abbreviates block_width Γ› (the per-symbol cell width).›

text ‹The fixed four-element output alphabet:
  BLANK4› (blank), LE4› (left endmarker),
  BIT0› (binary 0), BIT1› (binary 1).›

datatype sym4 = BLANK4 | LE4 | BIT0 | BIT1

lemma sym4_UNIV: "(UNIV :: sym4 set) = {BLANK4, LE4, BIT0, BIT1}"
  using sym4.exhaust by blast

lemma sym4_card: "card (UNIV :: sym4 set) = 4"
proof -
  have "(UNIV :: sym4 set) = {BLANK4, LE4, BIT0, BIT1}"
    by (rule sym4_UNIV)
  moreover have "card {BLANK4, LE4, BIT0, BIT1} = 4"
    by simp
  ultimately show ?thesis by simp
qed

subsection ‹Per-symbol encoder helpers›

text ‹The per-source-symbol block length: the number of sym4›
  cells that encode one Γ›-symbol, and the slowdown factor of
  the reduction.  At least 1› (to avoid degenerate empty
  encodings even for trivial Γ›), and otherwise
  ⌈log2 (card Γ)⌉›, the fewest binary digits that
  index all of Γ›.  Defined combinatorially via LEAST›
  on the predicate card Γ ≤ 2 ^ n›; existence of such an
  n› follows from card Γ ≤ 2 ^ card Γ›
  (HOL.Power.less_exp›).

  ‹What is counted.›  The index space {..< card Γ}›
  covers ‹every› symbol of Γ›: the blank at index
  0› (the anchor gamma_enum› relies on --- see below), and
  the left endmarker le› as an ordinary coded symbol, since
  valid_mttm› permits a machine to write le› at any tape
  position (its only special handling is the single LE4› cell at
  position 0›).  So block_width› is genuinely the width of
  this uniform code, not a loose bound on a smaller one --- but it is not
  the ‹coding› minimum.  Reserving index 0› for the
  blank (so an all-BLANK4› block reads back through the same
  accumulator) puts it one digit above
  ⌈log2 (card Γ - 1)⌉›, the width for the
  card Γ - 1› non-blank symbols; the two agree except just
  past a power of two.  Detecting the all-BLANK4› block directly
  would recover that digit, and additionally giving le› its own
  marker block would reach ⌈log2 (card Γ - 2)⌉›
  --- but each costs a marker-detection branch in the read and write
  phases, so that constant-factor gain is deliberately not taken.›

definition block_width :: "'a set  nat" where
  "block_width Γ = max 1 (LEAST n. card Γ  2 ^ n)"

text ‹A ‹blank-anchored› enumeration of Γ› as a
  bijection to {..< card Γ}› that additionally sends the
  blank bl› to 0›.  The anchor is load-bearing for the
  read phase: a blank source cell is encoded as a b›-cell
  all-BLANK4› block (bit_value = 0›), so the read fold
  is a fixpoint at gamma_unenum Γ bl 0›, which must equal
  bl› — and the decode round-trips force that to
  gamma_enum Γ bl bl = 0›.  Existence of such a bijection
  is unconditional on finite Γ› for ‹any›
  bl› (ex_bij_betw_anchor› below: swap 0› with
  g bl› when bl ∈ Γ›, otherwise free off-domain),
  so the carried bl› needs no bl ∈ Γ› side
  condition.  Downstream proofs reason about bij_betw›- and
  anchor-derived facts only; the Hilbert choice picks one specific
  anchored bijection.›

definition gamma_enum :: "'a set  'a  'a  nat" where
  "gamma_enum Γ bl =
     (SOME f. bij_betw f Γ {..< card Γ}  f bl = 0)"

text ‹Render a natural number as a fixed-length sym4›
  bit list (BIT0› / BIT1› only).  The list has
  shape [bitb-1, …, bit1, bit0]›:
  most-significant bit at the head, least-significant bit at
  the tail.  For n ≥ 2 ^ b› the high bits are dropped
  (only the low b› bits are rendered).›

fun nat_to_bits :: "nat  nat  sym4 list" where
  "nat_to_bits 0 _ = []"
| "nat_to_bits (Suc k) n =
     nat_to_bits k (n div 2)
       @ [if n mod 2 = 0 then BIT0 else BIT1]"

subsection ‹Per-symbol encoder›

text ‹Encoding a single 'a›-symbol as a fixed-length list of
  sym4›-symbols, parameterised over the source alphabet
  Γ› (a finite set, passed explicitly as a value rather
  than through a type-class constraint, so the construction is uniform
  over all source alphabets).  The
  length b› is the slowdown factor; the encoding is
  injective on Γ›.  The two bit symbols BIT0›,
  BIT1› carry the payload; LE4› and BLANK4›
  do not appear in any encoder image.  For x ∉ Γ› the
  encoder produces a value that's structurally well-shaped (a
  b›-cell bit list) but semantically arbitrary —
  downstream proofs always operate on x ∈ Γ›.›

definition encode_symbol ::
  "'a set  'a  'a  sym4 list" where
  "encode_symbol Γ bl x = nat_to_bits (block_width Γ) (gamma_enum Γ bl x)"

text ‹Encoding an input word as a flat list of sym4›-symbols
  (concatenation of per-symbol encodings under the given Γ›).›

definition encode_input_ar ::
  "'a set  'a  'a list  sym4 list" where
  "encode_input_ar Γ bl w = concat (map (encode_symbol Γ bl) w)"

subsection ‹Per-symbol encoder lemmas›

text ‹The encoded list has length b›, by induction
  on the block width.  Total — no x ∈ Γ› precondition is
  needed.›

lemma length_nat_to_bits [simp]:
  "length (nat_to_bits k n) = k"
  by (induct k arbitrary: n) auto

lemma length_encode_symbol [simp]:
  "length (encode_symbol Γ bl x) = block_width Γ"
  unfolding encode_symbol_def by simp

text ‹Every cell of an encoded symbol is in the bit alphabet
  {BIT0, BIT1}›.  In particular, the substrate-reserved
  markers LE4› and BLANK4› never appear in an
  encoder image.›

lemma set_nat_to_bits:
  "set (nat_to_bits k n)  {BIT0, BIT1}"
  by (induct k arbitrary: n) auto

lemma encode_symbol_cell_domain:
  "set (encode_symbol Γ bl x)  {BIT0, BIT1}"
  unfolding encode_symbol_def
  by (rule set_nat_to_bits)

text ‹The enumeration gamma_enum Γ bl› is a genuine bijection
  from Γ› to {..< card Γ}› whenever Γ› is
  finite.  Existence is library
  (ex_bij_betw_finite_nat›); the Hilbert-choice operator
  picks one such bijection.›

text ‹Existence of a blank-anchored bijection
  Γ → {..< card Γ}› sending bl› to 0›,
  unconditional on finite Γ› for any bl›.  Take any
  bijection g› (library ex_bij_betw_finite_nat›); if
  bl ∈ Γ›, post-compose with the nat-level transposition of
  0› and g bl› (both < card Γ›), which is a
  bij_betw› of {..< card Γ}› onto itself by
  endo_inj_surj›; if bl ∉ Γ›, just override
  g› at bl› (off-domain, so bij_betw› is
  unchanged by bij_betw_cong›).›

lemma ex_bij_betw_anchor:
  fixes Γ :: "'a set" and bl :: 'a
  assumes finG: "finite Γ"
  shows "f. bij_betw f Γ {..< card Γ}  f bl = 0"
proof -
  obtain g where g: "bij_betw g Γ {..< card Γ}"
    using ex_bij_betw_finite_nat[OF finG] by (auto simp: atLeast0LessThan)
  show ?thesis
  proof (cases "bl  Γ")
    case False
    have agree: "x. x  Γ  (g(bl := 0)) x = g x"
      using False by auto
    have "bij_betw (g(bl := 0)) Γ {..< card Γ}"
      using g agree by (metis bij_betw_cong)
    moreover have "(g(bl := 0)) bl = 0" by simp
    ultimately show ?thesis by blast
  next
    case True
    have npos: "0 < card Γ" using True finG by (auto simp: card_gt_0_iff)
    have gbl: "g bl < card Γ" using g True by (auto simp: bij_betw_def)
    let ?s = "λi. if i = 0 then g bl else if i = g bl then 0 else i"
    have into: "?s ` {..< card Γ}  {..< card Γ}"
      using gbl npos by auto
    have inj: "inj_on ?s {..< card Γ}"
      by (auto simp: inj_on_def split: if_splits)
    have "bij_betw ?s {..< card Γ} {..< card Γ}"
      unfolding bij_betw_def
      using inj endo_inj_surj[OF finite_lessThan into inj] by blast
    from bij_betw_trans[OF g this]
    have "bij_betw (?s  g) Γ {..< card Γ}" .
    moreover have "(?s  g) bl = 0" using gbl by (auto simp: comp_def)
    ultimately show ?thesis by blast
  qed
qed

lemma gamma_enum_anchor:
  assumes "finite Γ"
  shows "bij_betw (gamma_enum Γ bl) Γ {..< card Γ}
            gamma_enum Γ bl bl = 0"
  unfolding gamma_enum_def
  using ex_bij_betw_anchor[OF assms] by (rule someI_ex)

lemma gamma_enum_bij:
  assumes "finite Γ"
  shows "bij_betw (gamma_enum Γ bl) Γ {..< card Γ}"
  using gamma_enum_anchor[OF assms] by simp

text ‹The anchor: the blank-anchored enumeration sends the blank to
  0›.  This is what makes an all-BLANK4› block decode to
  bl› (decode_blank›).›

lemma gamma_enum_blank:
  assumes "finite Γ"
  shows "gamma_enum Γ bl bl = 0"
  using gamma_enum_anchor[OF assms] by simp

lemma gamma_enum_lt_card:
  assumes "finite Γ" and "x  Γ"
  shows "gamma_enum Γ bl x < card Γ"
proof -
  from gamma_enum_bij[OF finite Γ] x  Γ
  have "gamma_enum Γ bl x  {..< card Γ}"
    by (auto simp: bij_betw_def)
  thus ?thesis by simp
qed

text ‹The cardinality of Γ› fits in b› bits:
  card Γ ≤ 2 ^ b›.  This is the structural
  bound that underwrites injectivity: every enumeration value
  gamma_enum Γ bl x < card Γ› for x ∈ Γ› is in the
  range where nat_to_bits› is injective.›

lemma card_le_two_pow_block_width: "card Γ  2 ^ block_width Γ"
proof -
  have ex: "n. card Γ  2 ^ n"
    using less_exp[of "card Γ"] by (intro exI[of _ "card Γ"]) simp
  let ?m = "LEAST n. card Γ  2 ^ n"
  have m_bound: "card Γ  2 ^ ?m"
    using LeastI_ex[OF ex] .
  have "?m  block_width Γ"
    unfolding block_width_def by simp
  hence "(2::nat) ^ ?m  2 ^ block_width Γ"
    by (rule power_increasing) simp
  with m_bound show ?thesis by linarith
qed

text ‹Minimality (the lower edge): b› is the ‹smallest› width
  that indexes all of Γ›, so once card Γ ≥ 2› one
  bit fewer does not suffice --- 2 ^ (b - 1) < card Γ›.  With
  card_le_two_pow_block_width› this pins b› to
  ⌈log2 (card Γ)⌉› exactly: b› is a ‹step
  function› of card Γ›, constant on each band
  2 ^ (b - 1) < card Γ ≤ 2 ^ b› and jumping by one as
  card Γ› crosses a power of two (4 → 5›,
  8 → 9›, 16 → 17›).  The slowdown factor therefore
  rises in unit steps at the powers of two, not smoothly with alphabet
  size.  This is minimality for the ‹uniform› code that indexes
  every symbol of Γ› with the blank at index 0›; the
  ‹coding› minimum (the non-blank symbols alone) sits one digit
  lower just past each power of two --- see block_width›.›

lemma two_pow_block_width_pred_less_card:
  assumes card2: "2  card Γ"
  shows "2 ^ (block_width Γ - 1) < card Γ"
proof -
  have ex: "n. card Γ  2 ^ n"
    using less_exp[of "card Γ"] by (intro exI[of _ "card Γ"]) simp
  let ?m = "LEAST n. card Γ  2 ^ n"
  have notP0: "¬ card Γ  2 ^ (0::nat)" using card2 by simp
  have m_pos: "0 < ?m"
  proof (rule ccontr)
    assume "¬ 0 < ?m"
    then have "?m = 0" by simp
    then have "card Γ  2 ^ (0::nat)" using LeastI_ex[OF ex] by simp
    with notP0 show False by simp
  qed
  hence bw: "block_width Γ = ?m" unfolding block_width_def by simp
  have "block_width Γ - 1 < ?m" using bw m_pos by simp
  hence "¬ card Γ  2 ^ (block_width Γ - 1)" by (rule not_less_Least)
  thus ?thesis by simp
qed

text ‹The bit-renderer is injective on inputs bounded by
  2 ^ b›: two values with the same b›-bit
  representation are equal.  Proven by induction on b›:
  the head of the bit list determines the high bit n div 2›
  (recursive case), and the tail single element determines the
  low bit n mod 2›; combining via
  n = 2 ⋅ (n div 2) + n mod 2› gives equality.›

lemma nat_to_bits_inj_bounded:
  assumes "n1 < 2 ^ k"
      and "n2 < 2 ^ k"
      and "nat_to_bits k n1 = nat_to_bits k n2"
  shows "n1 = n2"
  using assms
proof (induct k arbitrary: n1 n2)
  case 0
  thus ?case by simp
next
  case (Suc k)
  from nat_to_bits (Suc k) n1 = nat_to_bits (Suc k) n2
  have eq:
    "nat_to_bits k (n1 div 2)
       @ [if n1 mod 2 = 0 then BIT0 else BIT1]
     = nat_to_bits k (n2 div 2)
       @ [if n2 mod 2 = 0 then BIT0 else BIT1]"
    by simp
  hence pref_eq:
    "nat_to_bits k (n1 div 2) = nat_to_bits k (n2 div 2)"
    and suf_eq:
    "(if n1 mod 2 = 0 then BIT0 else BIT1)
       = (if n2 mod 2 = 0 then BIT0 else BIT1)"
    by simp_all
  from suf_eq have mod_zero_iff:
    "n1 mod 2 = 0  n2 mod 2 = 0"
    by (auto split: if_split_asm)
  have "n1 mod 2 < 2" and "n2 mod 2 < 2" by simp_all
  with mod_zero_iff have mod_eq: "n1 mod 2 = n2 mod 2"
    by (cases "n1 mod 2 = 0"; cases "n2 mod 2 = 0") auto
  from n1 < 2 ^ Suc k have lt1: "n1 div 2 < 2 ^ k" by simp
  from n2 < 2 ^ Suc k have lt2: "n2 div 2 < 2 ^ k" by simp
  from Suc.hyps[OF lt1 lt2 pref_eq]
  have div_eq: "n1 div 2 = n2 div 2" .
  have "n1 = 2 * (n1 div 2) + n1 mod 2" by simp
  also from div_eq mod_eq have " = 2 * (n2 div 2) + n2 mod 2"
    by simp
  also have " = n2" by simp
  finally show ?case .
qed

text ‹Injectivity-on-Γ›: distinct source symbols in Γ›
  produce distinct encoder images.  This is the load-bearing
  property for language preservation: no two source symbols
  collide under the encoder.›

lemma encode_symbol_inj_on_Gamma:
  assumes "finite Γ"
      and "x  Γ" and "y  Γ"
      and "encode_symbol Γ bl x = encode_symbol Γ bl y"
  shows "x = y"
proof -
  from encode_symbol Γ bl x = encode_symbol Γ bl y
  have bits_eq:
    "nat_to_bits (block_width Γ) (gamma_enum Γ bl x)
       = nat_to_bits (block_width Γ) (gamma_enum Γ bl y)"
    by (simp add: encode_symbol_def)
  have lt_x: "gamma_enum Γ bl x < 2 ^ block_width Γ"
    using gamma_enum_lt_card[OF finite Γ x  Γ, of bl]
          card_le_two_pow_block_width[of Γ]
    by linarith
  have lt_y: "gamma_enum Γ bl y < 2 ^ block_width Γ"
    using gamma_enum_lt_card[OF finite Γ y  Γ, of bl]
          card_le_two_pow_block_width[of Γ]
    by linarith
  from nat_to_bits_inj_bounded[OF lt_x lt_y bits_eq]
  have enum_eq: "gamma_enum Γ bl x = gamma_enum Γ bl y" .
  have "inj_on (gamma_enum Γ bl) Γ"
    using gamma_enum_bij[OF finite Γ]
    by (simp add: bij_betw_def)
  from this enum_eq x  Γ y  Γ show ?thesis
    by (rule inj_onD)
qed

text ‹Structural properties of the input-word encoder.  These
  follow directly from the concat (map (encode_symbol Γ bl)
  …)› definition and are useful for the validation-phase
  reach lemmas in later slices.›

lemma encode_input_ar_Nil [simp]:
  "encode_input_ar Γ bl [] = []"
  unfolding encode_input_ar_def by simp

lemma encode_input_ar_append:
  "encode_input_ar Γ bl (w1 @ w2)
     = encode_input_ar Γ bl w1 @ encode_input_ar Γ bl w2"
  unfolding encode_input_ar_def by simp

lemma length_encode_input_ar:
  "length (encode_input_ar Γ bl w) = block_width Γ * length w"
  unfolding encode_input_ar_def
  by (induct w) auto

text ‹Uniform-width block indexing: when every block f x›
  has the same length K›, the (i ⋅ K + j)›-th cell
  of concat (map f xs)› is the j›-th cell of the
  i›-th block.  The list-level fact underlying the
  initial-tape correspondence: encode_input_ar› is exactly
  such a uniform concatenation, every block b›
  wide by length_encode_symbol›.›
lemma nth_concat_map_uniform:
  assumes K: "x. length (f x) = K"
      and i: "i < length xs"
      and j: "j < K"
  shows "concat (map f xs) ! (i * K + j) = f (xs ! i) ! j"
  using i
proof (induct xs arbitrary: i)
  case Nil
  then show ?case by simp
next
  case (Cons x xs)
  show ?case
  proof (cases i)
    case 0
    have "concat (map f (x # xs)) ! (i * K + j)
            = (f x @ concat (map f xs)) ! j"
      using 0 by simp
    also have " = f x ! j"
      using j K[of x] by (simp add: nth_append)
    finally show ?thesis using 0 by simp
  next
    case (Suc i')
    have i'len: "i' < length xs" using Cons.prems Suc by simp
    have idx: "i * K + j = length (f x) + (i' * K + j)"
      using Suc K[of x] by simp
    have "concat (map f (x # xs)) ! (i * K + j)
            = concat (map f xs) ! (i' * K + j)"
      using idx by (simp add: nth_append)
    also have " = f (xs ! i') ! j"
      using Cons.hyps[OF i'len] .
    finally show ?thesis using Suc by simp
  qed
qed

lemma nth_encode_input_ar:
  assumes "i < length w" and "j < block_width Γ"
  shows "encode_input_ar Γ bl w ! (i * block_width Γ + j)
           = encode_symbol Γ bl (w ! i) ! j"
  unfolding encode_input_ar_def
  by (rule nth_concat_map_uniform[OF length_encode_symbol assms])

subsection ‹Per-symbol decoder helpers›

text ‹Bit value of a sym4› cell.  BIT1› contributes
  1, every other symbol contributes 0; the non-bit symbols
  (BIT0›, LE4›, BLANK4›) are conflated to
  zero — junk-in, junk-out — so the decoder produces a
  well-defined nat even on malformed input.  Validity checking
  is performed separately by decode_symbol›'s cell-domain
  test.›

fun bit_value :: "sym4  nat" where
  "bit_value BIT0 = 0"
| "bit_value BIT1 = 1"
| "bit_value LE4 = 0"
| "bit_value BLANK4 = 0"

text ‹Read a sym4› list as a binary number, MSB at the
  head (matches nat_to_bits›'s output shape).  Each
  position contributes bit_value› times the appropriate
  power of two.›

fun bits_to_nat :: "sym4 list  nat" where
  "bits_to_nat [] = 0"
| "bits_to_nat (b # bs) = bit_value b * 2 ^ length bs + bits_to_nat bs"

text ‹Snoc-form of bits_to_nat›: appending a low bit
  shifts the existing value left and adds the new bit.  This
  is the inductive workhorse for the round-trip lemma — it
  matches nat_to_bits›'s recursive shape (which appends
  the LSB at the tail).›

lemma bits_to_nat_snoc:
  "bits_to_nat (xs @ [b]) = 2 * bits_to_nat xs + bit_value b"
  by (induct xs) auto

text ‹Round-trip on bounded naturals: rendering n› as
  a b›-bit list and reading it back recovers n›,
  provided n < 2 ^ b›.  Proof by induction on b›:
  the snoc-form of bits_to_nat› peels off the
  trailing bit, the IH handles the prefix n div 2›, and
  n = 2 ⋅ (n div 2) + n mod 2› reassembles.›

lemma bit_value_low_bit:
  "bit_value (if n mod 2 = 0 then BIT0 else BIT1) = n mod 2"
  using mod_less_divisor[of 2 n] by (auto split: if_split)

lemma bits_to_nat_nat_to_bits:
  assumes "n < 2 ^ k"
  shows "bits_to_nat (nat_to_bits k n) = n"
  using assms
proof (induct k arbitrary: n)
  case 0
  thus ?case by simp
next
  case (Suc k)
  have lt: "n div 2 < 2 ^ k"
    using n < 2 ^ Suc k by simp
  have "bits_to_nat (nat_to_bits (Suc k) n)
          = bits_to_nat
              (nat_to_bits k (n div 2)
                 @ [if n mod 2 = 0 then BIT0 else BIT1])"
    by simp
  also have "
          = 2 * bits_to_nat (nat_to_bits k (n div 2))
              + bit_value (if n mod 2 = 0 then BIT0 else BIT1)"
    by (rule bits_to_nat_snoc)
  also have " = 2 * (n div 2) + (n mod 2)"
    using Suc.hyps[OF lt] bit_value_low_bit[of n] by simp
  also have " = n" by presburger
  finally show ?case .
qed

subsection ‹Per-symbol decoder›

text ‹Partial inverse of encode_symbol›.  Returns
  Some x› when the input list:
   has length b› (correct cell count);
   contains only BIT0› / BIT1› cells (no
    substrate-reserved markers); and
   has binary interpretation strictly below
    card Γ› (in the enumeration range).

  Otherwise returns None›.  The validation phase in
  later slices implements this check as a sequence of substep
  transitions; the abstract decode_symbol› partial
  function is the specification target.›

definition decode_symbol ::
  "'a set  'a  sym4 list  'a option" where
  "decode_symbol Γ bl ys =
     (if length ys = block_width Γ
               set ys  {BIT0, BIT1}
               bits_to_nat ys < card Γ
      then Some (inv_into Γ (gamma_enum Γ bl) (bits_to_nat ys))
      else None)"

text ‹Round-trip: decoding an encoded symbol from
  Γ› recovers the source value.  All three validity
  preconditions of decode_symbol› are satisfied by the
  encoder image; inv_into› resolves to x› via
  gamma_enum›'s injectivity-on-Γ›.›

lemma decode_symbol_encode_symbol:
  assumes "finite Γ" and "x  Γ"
  shows "decode_symbol Γ bl (encode_symbol Γ bl x) = Some x"
proof -
  let ?ys = "encode_symbol Γ bl x"
  have len: "length ?ys = block_width Γ" by simp
  have cd: "set ?ys  {BIT0, BIT1}"
    by (rule encode_symbol_cell_domain)
  have lt_x: "gamma_enum Γ bl x < 2 ^ block_width Γ"
    using gamma_enum_lt_card[OF finite Γ x  Γ, of bl]
          card_le_two_pow_block_width[of Γ]
    by linarith
  have bn: "bits_to_nat ?ys = gamma_enum Γ bl x"
    unfolding encode_symbol_def
    by (rule bits_to_nat_nat_to_bits[OF lt_x])
  have lt_card: "bits_to_nat ?ys < card Γ"
    using bn gamma_enum_lt_card[OF finite Γ x  Γ] by simp
  have inj: "inj_on (gamma_enum Γ bl) Γ"
    using gamma_enum_bij[OF finite Γ]
    by (simp add: bij_betw_def)
  have inv_eq: "inv_into Γ (gamma_enum Γ bl) (bits_to_nat ?ys) = x"
    using bn inv_into_f_f[OF inj x  Γ] by simp
  show ?thesis
    unfolding decode_symbol_def
    using len cd lt_card inv_eq by simp
qed

text ‹A total form of the symbol decoder, indexed by a
  natural number rather than a bit list.  For n < card
  Γ›, returns the unique x ∈ Γ› with
  gamma_enum Γ bl x = n› (via inv_into›);
  for n ≥ card Γ›, returns the blank
  bl› as a defensive fallback.  Used by
  ar_delta_read›'s per-bit accumulator to maintain
  buf› as a partial-decoded 'a› value: at
  every per-bit substep before the boundary, the partial nat is
  strictly less than 2 ^ (b - 1) < card Γ›
  (since b› is the *least* n›
  with card Γ ≤ 2 ^ n›), so the fallback is
  never substantively reached for valid inputs; only the
  boundary substep can land out of range under
  non-encoder-image inputs, where the language theorem doesn't
  care.›

definition gamma_unenum :: "'a set  'a  nat  'a" where
  "gamma_unenum Γ bl n =
     (if n < card Γ
      then inv_into Γ (gamma_enum Γ bl) n
      else bl)"

text ‹Enumeration round-trips between gamma_enum› and its
  total inverse gamma_unenum›, the arithmetic core the read
  phase's incremental decode rests on.  gamma_enum ∘
  gamma_unenum› is the identity on the in-range index set
  ⦃0..<card Γ⦄› (where gamma_unenum› resolves
  to inv_into› and gamma_enum› is surjective onto that
  set), and gamma_unenum ∘ gamma_enum› is the identity on
  Γ› (where gamma_enum› is injective and lands
  below card Γ›).›

lemma gamma_enum_gamma_unenum:
  assumes "finite Γ" and "n < card Γ"
  shows "gamma_enum Γ bl (gamma_unenum Γ bl n) = n"
proof -
  have img: "gamma_enum Γ bl ` Γ = {..<card Γ}"
    using gamma_enum_bij[OF assms(1)] by (simp add: bij_betw_def)
  have "n  gamma_enum Γ bl ` Γ" using assms(2) img by simp
  hence "gamma_enum Γ bl (inv_into Γ (gamma_enum Γ bl) n) = n"
    by (rule f_inv_into_f)
  thus ?thesis using assms(2) by (simp add: gamma_unenum_def)
qed

lemma gamma_unenum_gamma_enum:
  assumes "finite Γ" and "x  Γ"
  shows "gamma_unenum Γ bl (gamma_enum Γ bl x) = x"
proof -
  have lt: "gamma_enum Γ bl x < card Γ"
    by (rule gamma_enum_lt_card[OF assms])
  have inj: "inj_on (gamma_enum Γ bl) Γ"
    using gamma_enum_bij[OF assms(1)] by (simp add: bij_betw_def)
  have "inv_into Γ (gamma_enum Γ bl) (gamma_enum Γ bl x) = x"
    by (rule inv_into_f_f[OF inj assms(2)])
  thus ?thesis using lt by (simp add: gamma_unenum_def)
qed

text ‹Reading an encoded symbol back via bits_to_nat›
  recovers its enumeration index, the named form of the local
  bn› fact inside decode_symbol_encode_symbol›.  Needed
  by the read-phase accumulator below.›

lemma bits_to_nat_encode_symbol:
  assumes "finite Γ" and "x  Γ"
  shows "bits_to_nat (encode_symbol Γ bl x) = gamma_enum Γ bl x"
proof -
  have lt_x: "gamma_enum Γ bl x < 2 ^ block_width Γ"
    using gamma_enum_lt_card[OF assms, of bl] card_le_two_pow_block_width[of Γ]
    by linarith
  show ?thesis
    unfolding encode_symbol_def by (rule bits_to_nat_nat_to_bits[OF lt_x])
qed

text ‹The read-phase accumulator: one bit-cell folded into the
  running symbol.  Mirrors the per-bit update the read substeps
  perform — buf := gamma_unenum (2 ⋅ gamma_enum buf +
  bit_value c)› — reading the b›-cell block most-significant
  bit first.›

definition ar_acc :: "'a set  'a  'a  sym4  'a" where
  "ar_acc Γ bl b c = gamma_unenum Γ bl (2 * gamma_enum Γ bl b + bit_value c)"

text ‹Folding the accumulator from the seed gamma_unenum 0›
  over a bit list whose value is in enumeration range yields exactly
  gamma_unenum (bits_to_nat ys)›.  Proof by rev_induct›:
  appending a low bit shifts the running value left and adds it
  (bits_to_nat_snoc›), the prefix value stays in range
  (bits_to_nat zs ≤ bits_to_nat (zs @ [b])›, no separate
  take›-bound lemma needed), and the gamma_enum ∘
  gamma_unenum› round-trip cancels at each step.›

lemma foldl_ar_acc_eq:
  assumes "finite Γ"
  shows "set ys  {BIT0, BIT1}  bits_to_nat ys < card Γ
           foldl (ar_acc Γ bl) (gamma_unenum Γ bl 0) ys
                = gamma_unenum Γ bl (bits_to_nat ys)"
proof (induct ys rule: rev_induct)
  case Nil
  show ?case by simp
next
  case (snoc b zs)
  have set_zs: "set zs  {BIT0, BIT1}" using snoc.prems(1) by simp
  have snoc_eq: "bits_to_nat (zs @ [b]) = 2 * bits_to_nat zs + bit_value b"
    by (rule bits_to_nat_snoc)
  have bound_zs: "bits_to_nat zs < card Γ"
    using snoc.prems(2) snoc_eq by linarith
  have IH: "foldl (ar_acc Γ bl) (gamma_unenum Γ bl 0) zs
              = gamma_unenum Γ bl (bits_to_nat zs)"
    using snoc.hyps set_zs bound_zs by blast
  have ge_zs: "gamma_enum Γ bl
                 (foldl (ar_acc Γ bl) (gamma_unenum Γ bl 0) zs)
               = bits_to_nat zs"
    using IH gamma_enum_gamma_unenum[OF assms bound_zs] by simp
  have "foldl (ar_acc Γ bl) (gamma_unenum Γ bl 0) (zs @ [b])
          = ar_acc Γ bl
              (foldl (ar_acc Γ bl) (gamma_unenum Γ bl 0) zs) b"
    by simp
  also have " = gamma_unenum Γ bl (2 * bits_to_nat zs + bit_value b)"
    by (simp add: ar_acc_def ge_zs)
  also have " = gamma_unenum Γ bl (bits_to_nat (zs @ [b]))"
    using snoc_eq by simp
  finally show ?case .
qed

text ‹Read-phase decode correctness: folding the accumulator over
  a symbol's encoding recovers the symbol.  This is the arithmetic
  content the read phase delivers — every tape's buf› field,
  after walking its b›-cell block, holds the source cell's
  value.›

lemma foldl_ar_acc_encode_symbol:
  assumes "finite Γ" and "x  Γ"
  shows "foldl (ar_acc Γ bl) (gamma_unenum Γ bl 0) (encode_symbol Γ bl x) = x"
proof -
  have cd: "set (encode_symbol Γ bl x)  {BIT0, BIT1}"
    by (rule encode_symbol_cell_domain)
  have bn: "bits_to_nat (encode_symbol Γ bl x) = gamma_enum Γ bl x"
    by (rule bits_to_nat_encode_symbol[OF assms])
  have lt: "bits_to_nat (encode_symbol Γ bl x) < card Γ"
    using bn gamma_enum_lt_card[OF assms] by simp
  have "foldl (ar_acc Γ bl) (gamma_unenum Γ bl 0) (encode_symbol Γ bl x)
          = gamma_unenum Γ bl (bits_to_nat (encode_symbol Γ bl x))"
    by (rule foldl_ar_acc_eq[OF assms(1) cd lt])
  also have " = gamma_unenum Γ bl (gamma_enum Γ bl x)" using bn by simp
  also have " = x" by (rule gamma_unenum_gamma_enum[OF assms])
  finally show ?thesis .
qed

text ‹The decoder's neutral element is the blank: gamma_unenum
  Γ bl 0 = bl›, since the anchor puts bl› at index
  0› and the enumeration is injective.›

lemma gamma_unenum_zero:
  assumes "finite Γ" and "bl  Γ"
  shows "gamma_unenum Γ bl 0 = bl"
proof -
  have npos: "0 < card Γ" using assms by (auto simp: card_gt_0_iff)
  have inj: "inj_on (gamma_enum Γ bl) Γ"
    using gamma_enum_bij[OF assms(1)] by (simp add: bij_betw_def)
  have "gamma_unenum Γ bl 0 = inv_into Γ (gamma_enum Γ bl) 0"
    using npos by (simp add: gamma_unenum_def)
  also have "
        = inv_into Γ (gamma_enum Γ bl) (gamma_enum Γ bl bl)"
    using gamma_enum_blank[OF assms(1)] by simp
  also have " = bl" by (rule inv_into_f_f[OF inj assms(2)])
  finally show ?thesis .
qed

text ‹An all-BLANK4› block decodes to the blank: bl› is a
  fixpoint of the accumulator on a BLANK4› cell (2 ⋅
  gamma_enum bl + 0 = 0›, decoding back to bl›), so folding
  any number of BLANK4› cells from bl› stays bl›.
  This is the read-phase decode correctness for the blank cell, the
  BLANK4› counterpart of foldl_ar_acc_encode_symbol›.›

lemma foldl_ar_acc_blank:
  assumes "finite Γ" and "bl  Γ"
  shows "foldl (ar_acc Γ bl) bl (replicate k BLANK4) = bl"
proof (induct k)
  case 0
  show ?case by simp
next
  case (Suc k)
  have step: "ar_acc Γ bl bl BLANK4 = bl"
    unfolding ar_acc_def
    using gamma_enum_blank[OF assms(1)] gamma_unenum_zero[OF assms] by simp
  have "foldl (ar_acc Γ bl) bl (replicate (Suc k) BLANK4)
          = foldl (ar_acc Γ bl) (ar_acc Γ bl bl BLANK4)
                  (replicate k BLANK4)"
    by (simp add: replicate_Suc)
  also have " = foldl (ar_acc Γ bl) bl (replicate k BLANK4)"
    using step by simp
  also have " = bl" by (rule Suc.hyps)
  finally show ?case .
qed

end