Theory AlphabetEnlargement_Substeps

theory AlphabetEnlargement_Substeps
  imports AlphabetEnlargement_Defs
begin

subsection ‹Per-substep transition relations›

text δ'› is decomposed into named per-substep relations for the
  validation and simulation phases.  Validation: 8 relations
  (forward LE-skip / pure / padded / non-canonical-reject /
  end-of-input; padded end-of-input / padded-reject; return
  step / return-LE-to-Sim).  Simulation: 8 relations
  (SS1→›SS2, …›, SS7→›SS8, SS8→›SS1).
  Total: 16 relations.

  The substrate transition shape is
  (q, a, q', a', d)› with state 'q × ae_stage›, tape
  symbol 'c ⇒ 'a›.  Source-state constraint
  q ∈ Q ∧ q ≠ t ∧ q ≠ r› matches substrate δ_set›
  for VFwd-source relations (whose substep_idx› coincides with
  t', r'›'s); for the other phases the substep_idx› mismatch
  itself rules out source = t'› / r'›.›

text ‹Validation, VFwd, advance: read either LE_block le_M›
  (initial step from position 0) or a pure block
  (no blanks); R move on tape 0; N moves on other tapes;
  phase stays VFwd›.  No write change.›

definition ae_delta_val_fwd_advance ::
  "('q, 'a) mttm
     (('q × ('a, 'c :: enum) ae_stage)
        × (nat  ('c  'a))
        × ('q × ('a, 'c) ae_stage)
        × (nat  ('c  'a))
        × (nat  dir)) set" where
  "ae_delta_val_fwd_advance M =
    {((q, ofs, buf, dest, VFwd), a,
       (q, ofs, buf, dest, VFwd), a, d) |
     q ofs buf dest a d.
       q  Q_tm M  q  t_tm M  q  r_tm M
        (a 0 = LE_block (le_tm M)
            is_pure_block (bl_tm M) (a 0))
        (jk_tm M. a j = bl_block (bl_tm M))
        d = (λk. if k = 0 then dir.R else dir.N)}"

text ‹Validation, VFwd →› VFwdPad: read a trailing-padded
  block on tape 0; R move on tape 0; N moves on other
  tapes; phase becomes VFwdPad.  No write change.›

definition ae_delta_val_fwd_to_padded ::
  "('q, 'a) mttm
     (('q × ('a, 'c :: enum) ae_stage)
        × (nat  ('c  'a))
        × ('q × ('a, 'c) ae_stage)
        × (nat  ('c  'a))
        × (nat  dir)) set" where
  "ae_delta_val_fwd_to_padded M =
    {((q, ofs, buf, dest, VFwd), a,
       (q, ofs, buf, dest, VFwdPad), a, d) |
     q ofs buf dest a d.
       q  Q_tm M  q  t_tm M  q  r_tm M
        is_padded_block (bl_tm M) (a 0)
        (jk_tm M. a j = bl_block (bl_tm M))
        d = (λk. if k = 0 then dir.R else dir.N)}"

text ‹Validation, VFwd reject: read a non-canonical block
  on tape 0 (in Σ'› but neither pure nor padded — blanks in
  non-trailing positions); N moves uniformly; transition to
  r_M'› (= the canonical reject state of M'›).›

definition ae_delta_val_fwd_reject ::
  "('q, 'a) mttm
     (('q × ('a, 'c :: enum) ae_stage)
        × (nat  ('c  'a))
        × ('q × ('a, 'c) ae_stage)
        × (nat  ('c  'a))
        × (nat  dir)) set" where
  "ae_delta_val_fwd_reject M =
    {((q, ofs, buf, dest, VFwd), a,
       (r_tm M, init_stage (le_tm M)), a, d) |
     q ofs buf dest a d.
       q  Q_tm M  q  t_tm M  q  r_tm M
        a 0  LE_block (le_tm M)
        a 0  bl_block (bl_tm M)
        ¬ is_canonical_block (bl_tm M) (a 0)
        (jk_tm M. a j = bl_block (bl_tm M))
        d = (λ_. dir.N)}"

text ‹Validation, VFwd end-of-input: read bl_block bl_M›
  (past the encoded input); N moves; transition to VRet to
  begin the return scan.›

definition ae_delta_val_fwd_to_ret ::
  "('q, 'a) mttm
     (('q × ('a, 'c :: enum) ae_stage)
        × (nat  ('c  'a))
        × ('q × ('a, 'c) ae_stage)
        × (nat  ('c  'a))
        × (nat  dir)) set" where
  "ae_delta_val_fwd_to_ret M =
    {((q, ofs, buf, dest, VFwd), a,
       (q, ofs, buf, dest, VRet), a, d) |
     q ofs buf dest a d.
       q  Q_tm M  q  t_tm M  q  r_tm M
        a 0 = bl_block (bl_tm M)
        (jk_tm M. a j = bl_block (bl_tm M))
        d = (λ_. dir.N)}"

text ‹Validation, VFwdPad end-of-input: read bl_block bl_M›;
  N moves; transition to VRet.  Same as the VFwd version
  except the source phase.›

definition ae_delta_val_pad_to_ret ::
  "('q, 'a) mttm
     (('q × ('a, 'c :: enum) ae_stage)
        × (nat  ('c  'a))
        × ('q × ('a, 'c) ae_stage)
        × (nat  ('c  'a))
        × (nat  dir)) set" where
  "ae_delta_val_pad_to_ret M =
    {((q, ofs, buf, dest, VFwdPad), a,
       (q, ofs, buf, dest, VRet), a, d) |
     q ofs buf dest a d.
       q  Q_tm M
        a 0 = bl_block (bl_tm M)
        (jk_tm M. a j = bl_block (bl_tm M))
        d = (λ_. dir.N)}"

text ‹Validation, VFwdPad reject: read anything on tape 0 other
  than bl_block bl_M›.  This signals a non-blank block
  appearing after the trailing-padded block.  N moves;
  transition to r_M'›.›

definition ae_delta_val_pad_reject ::
  "('q, 'a) mttm
     (('q × ('a, 'c :: enum) ae_stage)
        × (nat  ('c  'a))
        × ('q × ('a, 'c) ae_stage)
        × (nat  ('c  'a))
        × (nat  dir)) set" where
  "ae_delta_val_pad_reject M =
    {((q, ofs, buf, dest, VFwdPad), a,
       (r_tm M, init_stage (le_tm M)), a, d) |
     q ofs buf dest a d.
       q  Q_tm M
        a 0  bl_block (bl_tm M)
        (jk_tm M. a j = bl_block (bl_tm M))
        d = (λ_. dir.N)}"

text ‹Validation, VRet step: read a non-LE block on tape 0
  (during the return scan); L move on tape 0; N moves on other
  tapes; phase stays VRet.›

definition ae_delta_val_ret_step ::
  "('q, 'a) mttm
     (('q × ('a, 'c :: enum) ae_stage)
        × (nat  ('c  'a))
        × ('q × ('a, 'c) ae_stage)
        × (nat  ('c  'a))
        × (nat  dir)) set" where
  "ae_delta_val_ret_step M =
    {((q, ofs, buf, dest, VRet), a,
       (q, ofs, buf, dest, VRet), a, d) |
     q ofs buf dest a d.
       q  Q_tm M
        a 0  LE_block (le_tm M)
        (jk_tm M. a j = bl_block (bl_tm M))
        d = (λk. if k = 0 then dir.L else dir.N)}"

text ‹Validation, VRet →› Sim: read LE_block le_M› on tape 0
  (return scan reached position 0); N moves uniformly (head
  stays at position 0, the LE-block); phase becomes Sim with
  substep_idx› = SS1.

  The N move on tape 0 — rather than R — leaves M'›'s head at
  block 0 (LE_M'›) post-validation, so the simulation
  phase's first stage runs in LE-stage mode
  (home = LE_M'›) and the c-fold compute correctly simulates
  M›'s first step from (s_M, LE)›.  See bp_advance_le›
  below for how the c-fold compute handles M's first R-move out
  of LE.›

definition ae_delta_val_ret_to_sim ::
  "('q, 'a) mttm
     (('q × ('a, 'c :: enum) ae_stage)
        × (nat  ('c  'a))
        × ('q × ('a, 'c) ae_stage)
        × (nat  ('c  'a))
        × (nat  dir)) set" where
  "ae_delta_val_ret_to_sim M =
    {((q, ofs, buf, dest, VRet), a,
       (q, ofs, buf, dest, SS1), a, d) |
     q ofs buf dest a d.
       q  Q_tm M
        a 0 = LE_block (le_tm M)
        (jk_tm M. a j = bl_block (bl_tm M))
        d = (λ_. dir.N)}"

text ‹SS1 →› SS2: read home into buffer; per-tape move L
  (steady-state) or N (LE-stage).  No write change
  (a' = a›); no M›-state advance.  Buffer-phase substep 1.›

definition ae_delta_ss1_ss2 ::
  "('q, 'a) mttm
     (('q × ('a, 'c :: enum) ae_stage)
        × (nat  ('c  'a))
        × ('q × ('a, 'c) ae_stage)
        × (nat  ('c  'a))
        × (nat  dir)) set" where
  "ae_delta_ss1_ss2 M =
    {((q, ofs, buf, dest, SS1), a,
       (q, ofs, buf', dest, SS2), a, d) |
     q ofs buf dest a buf' d.
       q  Q_tm M  q  t_tm M  q  r_tm M
        (jk_tm M. a j = bl_block (bl_tm M))
        buf' = (λk. if k < k_tm M
                     then (fst (buf k), a k, snd (snd (buf k)))
                     else init_buffer (le_tm M) k)
        d = (λk. if k < k_tm M
                  then (if a k = LE_block (le_tm M) then dir.N else dir.L)
                  else dir.N)}"

text ‹SS2 →› SS3: read left into buffer (or placeholder for
  LE-stage); per-tape move R (steady-state, returning to home)
  or N (LE-stage, staying at home).  Buffer-phase substep 2.

  In LE-stage (buf k.home = LE_M'›), the read is again
  LE_M'› (head didn't move at SS1) and buf' k.left› is set to
  bl_block bl_M› as a semantic placeholder.›

definition ae_delta_ss2_ss3 ::
  "('q, 'a) mttm
     (('q × ('a, 'c :: enum) ae_stage)
        × (nat  ('c  'a))
        × ('q × ('a, 'c) ae_stage)
        × (nat  ('c  'a))
        × (nat  dir)) set" where
  "ae_delta_ss2_ss3 M =
    {((q, ofs, buf, dest, SS2), a,
       (q, ofs, buf', dest, SS3), a, d) |
     q ofs buf dest a buf' d.
       q  Q_tm M  q  t_tm M  q  r_tm M
        (jk_tm M. a j = bl_block (bl_tm M))
        buf' = (λk. if k < k_tm M
                     then (let (l, h, r) = buf k in
                            (if h = LE_block (le_tm M)
                               then bl_block (bl_tm M)
                               else a k,
                             h, r))
                     else init_buffer (le_tm M) k)
        d = (λk. if k < k_tm M
                  then (if (fst (snd (buf k))) = LE_block (le_tm M)
                         then dir.N else dir.R)
                  else dir.N)}"

text ‹SS3 →› SS4: uniform R move per tape (no buffer update,
  no M›-state advance).  Both steady-state and LE-stage move
  R (steady-state from home to right; LE-stage from home (= LE)
  to right neighbour, satisfying δLE› since R is allowed
  from LE).  Buffer-phase substep 3.›

definition ae_delta_ss3_ss4 ::
  "('q, 'a) mttm
     (('q × ('a, 'c :: enum) ae_stage)
        × (nat  ('c  'a))
        × ('q × ('a, 'c) ae_stage)
        × (nat  ('c  'a))
        × (nat  dir)) set" where
  "ae_delta_ss3_ss4 M =
    {((q, ofs, buf, dest, SS3), a,
       (q, ofs, buf, dest, SS4), a, d) |
     q ofs buf dest a d.
       q  Q_tm M  q  t_tm M  q  r_tm M
        (jk_tm M. a j = bl_block (bl_tm M))
        d = (λk. if k < k_tm M then dir.R else dir.N)}"

text ‹Buffered head position: which buffer slot
  (AE_Left› / AE_Home› / AE_Right›) and which offset
  within that slot.  Internal to the compute substep; the
  output of m_steps_buffered› projects this onto
  (nat ⇒ 'c) × (nat ⇒ ae_dest)›.›

type_synonym 'c bp = "ae_dest × 'c"

text ‹Read the symbol at a buffered head position.›

definition read_bp ::
  "(('c :: enum  'a) × ('c  'a) × ('c  'a))
     'c bp  'a" where
  "read_bp blocks p =
     (let (b, off) = p; (l, h, r) = blocks in
        case b of AE_Left  l off
                | AE_Home  h off
                | AE_Right  r off)"

text ‹Write a symbol at a buffered head position.›

definition write_bp ::
  "(('c :: enum  'a) × ('c  'a) × ('c  'a))
     'c bp  'a
     (('c  'a) × ('c  'a) × ('c  'a))" where
  "write_bp blocks p x =
     (let (b, off) = p; (l, h, r) = blocks in
        case b of AE_Left   (l(off := x), h, r)
                | AE_Home   (l, h(off := x), r)
                | AE_Right  (l, h, r(off := x)))"

text ‹Advance a buffered head position by a direction.  Returns
  None› if the head would exit the 3-block buffer (which by the
  per-c›-step head-displacement bound of Hopcroft--Ullman
  cite‹Theorem 12.3› in "Hopcroft1979:introduction" cannot
  happen during a single c›-step compute starting from
  (AE_Home, ofs)›; relations using bp_advance› filter the
  None› case as a vacuous side condition).›

definition bp_advance ::
  "('c :: enum) bp  dir  'c bp option" where
  "bp_advance p d =
     (let (b, off) = p in
        case d of
          dir.N  Some (b, off)
        | dir.R 
            (case c_succ off of
               Some off'  Some (b, off')
             | None 
                 (case b of
                    AE_Left   Some (AE_Home,  c_first)
                  | AE_Home   Some (AE_Right, c_first)
                  | AE_Right  None))
        | dir.L 
            (case c_pred off of
               Some off'  Some (b, off')
             | None 
                 (case b of
                    AE_Right  Some (AE_Home, c_last)
                  | AE_Home   Some (AE_Left, c_last)
                  | AE_Left   None)))"

text ‹LE-aware buffered head advance.  Wraps bp_advance›
  with the substrate-induced LE-skip rule: when the read symbol
  is le› (i.e., the head is positioned within an LE_M'›
  block) and the move is R, jump to (AE_Right, c_first)›
  rather than advancing within the home block.  This corresponds
  to M›'s actual head crossing from position 0 (LE) to
  position 1 (the first input cell), which the simulation
  correspondence requires as a single bp-step rather than a
  sequence of c within-block bp-steps.

  Rationale: the
  LE_M'› block is a c-tuple but only its slot 0 represents
  a real M›-cell; slots 1..c-1 are structural padding.
  bp_advance›'s standard offset arithmetic would walk through
  these padding slots, which doesn't correspond to any
  M›-step.  N stays in place (consistent with M›
  staying at LE on N), L is forbidden by δLE› at LE so
  the case is unreachable.›

definition bp_advance_le ::
  "'a  'a  ('c :: enum) bp  dir  'c bp option" where
  "bp_advance_le le a p d =
     (if a = le  fst p = AE_Home  d = dir.R
        then Some (AE_Right, c_first)
        else bp_advance p d)"

text ‹Single buffered M›-step: applies M›'s δ› to the
  current per-tape buffered reads, writes the post-symbols
  back into the buffer, and advances each per-tape head
  position via bp_advance_le› (the LE-aware wrapper around
  bp_advance›).  The relation is empty
  for configurations whose source M›-state is halting
  (M›'s δ› excludes those) or whose head movement would
  exit the 3-block buffer on any tape.›

definition m_step_buffered ::
  "('q, 'a) mttm
     (('q
         × (nat  ('c :: enum  'a) × ('c  'a) × ('c  'a))
         × (nat  'c bp))
        × ('q
           × (nat  ('c  'a) × ('c  'a) × ('c  'a))
           × (nat  'c bp))) set" where
  "m_step_buffered M =
     {((q, blocks, pos), (q', blocks', pos')) |
        q blocks pos q' blocks' pos' a a' d.
          (q, a, q', a', d)  delta_tm M
           (k. a k = read_bp (blocks k) (pos k))
           (k. blocks' k = write_bp (blocks k) (pos k) (a' k))
           (k. bp_advance_le (le_tm M) (a k) (pos k) (d k)
                  = Some (pos' k))}"

text ‹Introduction rule for m_step_buffered›: package the
  four ingredients (δ›-tuple, read-from-buffer match,
  write-back, head-advance) into the relational membership
  claim.  Used by the inductive step of
  ae_m_steps_buffered_correct› (AlphabetEnlargement.thy›)
  to extend a coupled run by one step.›

lemma m_step_bufferedI:
  fixes M :: "('q, 'a) mttm"
    and a a' :: "nat  'a"
    and d :: "nat  dir"
    and blocks blocks' ::
          "nat  (('c :: enum  'a) × ('c  'a) × ('c  'a))"
    and pos pos' :: "nat  'c bp"
  assumes "(q, a, q', a', d)  delta_tm M"
      and "k. a k = read_bp (blocks k) (pos k)"
      and "k. blocks' k = write_bp (blocks k) (pos k) (a' k)"
      and "k. bp_advance_le (le_tm M) (a k) (pos k) (d k)
              = Some (pos' k)"
  shows "((q, blocks, pos), (q', blocks', pos'))  m_step_buffered M"
  using assms unfolding m_step_buffered_def by blast

text ‹Auxiliary: up-to-c›-step composition of M›'s δ› on
  the buffered representation.  Captures the cumulative effect
  of either c› consecutive M›-steps, or fewer if M›
  reaches a halting state (t_M› / r_M›) earlier.

  Defined as the union of n›-fold relational compositions of
  m_step_buffered M› for n ≤ c = card (UNIV :: 'c set)›,
  filtered to enforce the early-stop discipline (the run runs
  the full c› steps unless M› halts).  This is the
  semantic core of the compute substep (SS4→›SS5).›

definition m_steps_buffered ::
  "('q, 'a) mttm
     (('q
         × (nat  ('c :: enum  'a) × ('c  'a) × ('c  'a))
         × (nat  'c bp))
        × ('q
           × (nat  ('c  'a) × ('c  'a) × ('c  'a))
           × (nat  'c bp))) set" where
  "m_steps_buffered M =
     {(s, s') | s s' n.
        n  card (UNIV :: 'c set)
         (s, s')  (m_step_buffered M) ^^ n
         (n = card (UNIV :: 'c set)
             fst s'  {t_tm M, r_tm M})}"

text ‹Functionality of m_step_buffered› under
  det_mttm M›: a single buffered M-step from a fixed source
  determines the target uniquely.  Threaded through
  m_step_buffered_relpow_functional› and
  m_steps_buffered_functional› below into the
  SS4→›SS5 compute substep's functionality lemma — the
  16th entry in the per-substep functionality cluster, completing
  it under det_mttm M›.›

lemma m_step_buffered_functional:
  fixes M :: "('q, 'a) mttm"
  assumes det: "det_mttm M"
    and h1: "((q, blocks, pos), (q1, blocks1, pos1))  m_step_buffered M"
    and h2: "((q, blocks, pos), (q2, blocks2, pos2))  m_step_buffered M"
  shows "(q1, blocks1, pos1) = (q2, blocks2, pos2)"
proof -
  from h1 obtain a1 a1' d1 where
      t1: "(q, a1, q1, a1', d1)  delta_tm M"
    and r1: "k. a1 k = read_bp (blocks k) (pos k)"
    and w1: "k. blocks1 k = write_bp (blocks k) (pos k) (a1' k)"
    and p1: "k. bp_advance_le (le_tm M) (a1 k) (pos k) (d1 k)
                    = Some (pos1 k)"
    unfolding m_step_buffered_def by blast
  from h2 obtain a2 a2' d2 where
      t2: "(q, a2, q2, a2', d2)  delta_tm M"
    and r2: "k. a2 k = read_bp (blocks k) (pos k)"
    and w2: "k. blocks2 k = write_bp (blocks k) (pos k) (a2' k)"
    and p2: "k. bp_advance_le (le_tm M) (a2 k) (pos k) (d2 k)
                    = Some (pos2 k)"
    unfolding m_step_buffered_def by blast
  have a_eq: "a1 = a2"
    using r1 r2 by (intro ext) auto
  have t2': "(q, a1, q2, a2', d2)  delta_tm M"
    using t2 a_eq by simp
  from det t1 t2'
  have qad: "(q1, a1', d1) = (q2, a2', d2)"
    unfolding det_mttm_def by blast
  hence q_eq: "q1 = q2" and a'_eq: "a1' = a2'" and d_eq: "d1 = d2"
    by auto
  have b_eq: "blocks1 = blocks2"
    using w1 w2 a'_eq by (intro ext) auto
  have p_eq: "pos1 = pos2"
  proof (intro ext)
    fix k
    have "Some (pos1 k) = Some (pos2 k)"
      using p1 p2 a_eq d_eq by metis
    thus "pos1 k = pos2 k" by simp
  qed
  show ?thesis using q_eq b_eq p_eq by simp
qed

text ‹Relational-power lift of m_step_buffered_functional›:
  under det_mttm M›, n-fold composition is functional too.
  Standard induction-on-n proof using the single-step lemma.›

lemma m_step_buffered_relpow_functional:
  fixes M :: "('q, 'a) mttm"
  assumes det: "det_mttm M"
      and h1: "(s, s1)  (m_step_buffered M) ^^ n"
      and h2: "(s, s2)  (m_step_buffered M) ^^ n"
  shows "s1 = s2"
  using h1 h2
proof (induction n arbitrary: s s1 s2)
  case 0
  thus ?case by simp
next
  case (Suc n)
  from Suc.prems(1) obtain s1' where
      step1: "(s, s1')  m_step_buffered M"
    and rest1: "(s1', s1)  (m_step_buffered M) ^^ n"
    by (meson relpow_Suc_D2)
  from Suc.prems(2) obtain s2' where
      step2: "(s, s2')  m_step_buffered M"
    and rest2: "(s2', s2)  (m_step_buffered M) ^^ n"
    by (meson relpow_Suc_D2)
  obtain q b p where s_eq: "s = (q, b, p)" by (cases s)
  obtain q1' b1' p1' where s1'_eq: "s1' = (q1', b1', p1')" by (cases s1')
  obtain q2' b2' p2' where s2'_eq: "s2' = (q2', b2', p2')" by (cases s2')
  from step1 s_eq s1'_eq
  have st1: "((q, b, p), (q1', b1', p1'))  m_step_buffered M"
    by simp
  from step2 s_eq s2'_eq
  have st2: "((q, b, p), (q2', b2', p2'))  m_step_buffered M"
    by simp
  from m_step_buffered_functional[OF det st1 st2]
  have "(q1', b1', p1') = (q2', b2', p2')" .
  hence s'_eq: "s1' = s2'" using s1'_eq s2'_eq by simp
  show ?case using Suc.IH[OF rest1] rest2 s'_eq by simp
qed

text ‹From a halt state, no buffered M-step is possible.  Follows
  from valid_mttm M›'s structural constraint that
  delta_tm M› has no transitions originating in
  {t_tm M, r_tm M}›.  Used below to rule out the case
  where the two witnesses of m_steps_buffered›'s
  functionality argument use different step counts.›

lemma m_step_buffered_no_halt:
  fixes M :: "('q, 'a) mttm"
  assumes vM: "valid_mttm M"
      and halt: "q  {t_tm M, r_tm M}"
  shows "((q, blocks, pos), s')  m_step_buffered M"
proof
  assume "((q, blocks, pos), s')  m_step_buffered M"
  then obtain a q' a' d where
      st: "(q, a, q', a', d)  delta_tm M"
    unfolding m_step_buffered_def by blast
  from valid_mttm_delta_set[OF vM] st
  have "q  Q_tm M - {t_tm M, r_tm M}" by auto
  with halt show False by auto
qed

text ‹Functionality of m_steps_buffered› under
  det_mttm M›: the bounded-and-halt-truncated buffered
  M-run is functional in its source.

  Argument: from membership we obtain step counts n1, n2›
  with n_i ≤ c› and n_i = c ∨ fst s_i ∈ {t, r}›.
  First show n1 = n2›: if n1 < n2› the prefix run
  determines the n1-step state to be s1› (by
  m_step_buffered_relpow_functional›), and the
  remaining n2 - n1 ≥ 1› steps require a transition
  from s1›.  The disjunction on s1› forces either
  n1 = c› (contradicting n1 < n2 ≤ c›) or
  fst s1 ∈ {t, r}›, the latter ruled out by
  m_step_buffered_no_halt›.  Symmetric for n2 < n1›.
  With n1 = n2›, the relpow functional lemma finishes.›

lemma m_steps_buffered_functional:
  fixes M :: "('q, 'a) mttm"
    and s s1 s2 :: "'q
                      × (nat  ('c :: enum  'a)
                              × ('c  'a) × ('c  'a))
                      × (nat  'c bp)"
  assumes vM:  "valid_mttm M"
      and det: "det_mttm M"
      and h1:  "(s, s1)  m_steps_buffered M"
      and h2:  "(s, s2)  m_steps_buffered M"
  shows "s1 = s2"
proof -
  let ?c = "card (UNIV :: 'c set)"
  let ?R = "m_step_buffered M"
  from h1 obtain n1 where
      n1_bnd:  "n1  ?c"
    and n1_run: "(s, s1)  ?R ^^ n1"
    and n1_halt: "n1 = ?c  fst s1  {t_tm M, r_tm M}"
    unfolding m_steps_buffered_def by blast
  from h2 obtain n2 where
      n2_bnd:  "n2  ?c"
    and n2_run: "(s, s2)  ?R ^^ n2"
    and n2_halt: "n2 = ?c  fst s2  {t_tm M, r_tm M}"
    unfolding m_steps_buffered_def by blast

  ― ‹Sub-lemma: under m < n›, the run from the m-step
      state cannot extend, contradicting the halt-or-full
      disjunction at the m-step state.›
  have no_extend:
    "m n s_m s_n.
        (s, s_m)  ?R ^^ m
         (s, s_n)  ?R ^^ n
         m < n
         n  ?c
         m = ?c  fst s_m  {t_tm M, r_tm M}
         False"
  proof -
    fix m n :: nat and s_m s_n
    assume rm: "(s, s_m)  ?R ^^ m"
       and rn: "(s, s_n)  ?R ^^ n"
       and mn: "m < n"
       and nc: "n  ?c"
       and hl: "m = ?c  fst s_m  {t_tm M, r_tm M}"
    define k where "k = n - m - 1"
    have k_eq: "n = m + Suc k"
      unfolding k_def using mn by simp
    from rn k_eq have rn': "(s, s_n)  ?R ^^ (m + Suc k)" by simp
    from rn'[unfolded relpow_add]
    obtain s' where
        rpre: "(s, s')  ?R ^^ m"
      and rpost: "(s', s_n)  ?R ^^ Suc k"
      by blast
    from m_step_buffered_relpow_functional[OF det rm rpre]
    have s_eq: "s_m = s'" .
    from rpost obtain s'' where step: "(s', s'')  ?R"
      by (meson relpow_Suc_D2)
    with s_eq have step_sm: "(s_m, s'')  ?R" by simp
    from hl show False
    proof
      assume "m = ?c"
      with k_eq nc show False by linarith
    next
      assume halt: "fst s_m  {t_tm M, r_tm M}"
      obtain q b p where sm_eq: "s_m = (q, b, p)" by (cases s_m)
      from halt sm_eq have q_halt: "q  {t_tm M, r_tm M}" by simp
      from step_sm sm_eq have "((q, b, p), s'')  ?R" by simp
      with m_step_buffered_no_halt[OF vM q_halt] show False by blast
    qed
  qed

  ― ‹The step counts must be equal.›
  have n_eq: "n1 = n2"
  proof (rule ccontr)
    assume "n1  n2"
    then consider (lt) "n1 < n2" | (gt) "n2 < n1" by linarith
    thus False
    proof cases
      case lt
      from no_extend[OF n1_run n2_run lt n2_bnd n1_halt] show False .
    next
      case gt
      from no_extend[OF n2_run n1_run gt n1_bnd n2_halt] show False .
    qed
  qed
  from n1_run n2_run n_eq
  show ?thesis
    using m_step_buffered_relpow_functional[OF det] by metis
qed

text ‹Linearisation helpers for the 3-block buffer.  These map
  (block, offset)›-pairs to indices in [0, 3c)› and read
  the symbol at a linearised buffer index.  Used to express the
  buffered compute's correctness as a contiguous-tape-window
  match.›

definition bp_linear :: "('c :: enum) bp  nat" where
  "bp_linear p =
     (case fst p of
        AE_Left   0
      | AE_Home   card (UNIV :: 'c set)
      | AE_Right  2 * card (UNIV :: 'c set))
     + c_idx (snd p)"

definition buf_lin_at ::
  "(('c :: enum  'a) × ('c  'a) × ('c  'a))
     nat  'a" where
  "buf_lin_at blocks i =
     (let c = card (UNIV :: 'c set);
          enum_c = (enum_class.enum :: 'c list);
          (l, h, r) = blocks in
        if i < c then l (enum_c ! i)
        else if i < 2 * c then h (enum_c ! (i - c))
        else r (enum_c ! (i - 2 * c)))"

text ‹Window invariant on a 3-block buffer plus buffered head:
  (1) the buffer's linearisation matches an M-tape window of
  3c contiguous positions starting at p_start›; (2) the
  buffered head decodes to the actual M-head position via
  bp_linear›; (3) the entire window lies in the non-LE
  region of the M-tape (p_start ≥ 1›).  The non-LE
  precondition keeps the predicate steady-state; the LE-edge
  case (window intersects M-position 0) is handled by a
  separate lemma at the simulation level.›

definition ae_window_invariant ::
  "(nat  'a)  nat
     ('c :: enum) bp
     (('c  'a) × ('c  'a) × ('c  'a))
     nat  bool" where
  "ae_window_invariant tM nM bp blocks p_start 
     p_start  1
      nM = p_start + bp_linear bp
      (i. i < 3 * card (UNIV :: 'c set)
               tM (p_start + i) = buf_lin_at blocks i)"

text ‹LE-edge analogue of ae_window_invariant› for the
  le1› sub-case: M'›'s head is at block 1 (the first
  content block), so the buffer's left slot is
  LE_block le› (block 0's content under
  ae_init_config›) and M's tape position lies in
  {0, …, 2c}›.  The home and right buffer slots linearise
  to an M-tape window starting at position 1; the left buffer
  slot is LE_block› by construction, with no claim about
  M-tape positions 0, …, c-1› beyond tM 0 = le›.  Under
  δLE› on delta_tm M›, M's buffered trajectory in this
  setting visits only (AE_Left, c_last)› within the left
  block (when reading LE), never the other left slots, so
  the buffer↔›tape mismatch there is harmless.

  The buffered head bp› decodes to the actual M-tape position
  by a three-way case-split (AE_Left c_last ↦ 0›,
  AE_Home ↦ 1 + c_idx ofs›,
  AE_Right ↦ 1 + c + c_idx ofs›) so the predicate serves
  as both SS4 entry condition (bp = (AE_Home, ofs)›) and
  post-compute condition (bp› anywhere in the three-block
  buffer except AE_Left› at non-c_last› offsets, which the
  δLE›-respecting buffered trajectory cannot reach).›

definition ae_window_invariant_le1 ::
  "(nat  'a)  nat
     ('c :: enum) bp
     (('c  'a) × ('c  'a) × ('c  'a))
     'a  bool" where
  "ae_window_invariant_le1 tM nM bp blocks le 
     fst blocks = LE_block le
      tM 0 = le
      (case fst bp of
          AE_Left   snd bp = c_last  nM = 0
        | AE_Home   nM = Suc (c_idx (snd bp))
        | AE_Right  nM = Suc (card (UNIV :: 'c set) + c_idx (snd bp)))
      (i. i < 2 * card (UNIV :: 'c set)
               tM (Suc i)
                    = buf_lin_at blocks (card (UNIV :: 'c set) + i))"

text ‹LE-edge analogue of ae_window_invariant› for the
  le0› sub-case: M'›'s head is at block 0 (the LE
  block itself).  Buffer shape: the home slot is
  LE_block le› (block 0's content), the right slot is
  block 1's content (real input/blank), and the left slot
  holds an arbitrary sentinel (SS2 →› SS3 installs
  bl_block (bl_tm M)›; the predicate doesn't constrain it
  because M's buffered trajectory cannot reach the left
  block under δLE› — M starts at home reading LE,
  can only move N or R, and any R-move from home reading LE
  jumps via bp_advance_le›'s special case to
  (AE_Right, c_first)›, bypassing the rest of home and
  never visiting left).

  The buffered head bp› decodes:
  AE_Home ↦ 0› (M sits at LE regardless of
  snd bp›, since home is all-LE) and
  AE_Right ↦ 1 + c_idx ofs› (M is in block 1's
  range).  AE_Left› is excluded.

  Only the right slot's linearisation is asserted: the home
  slot is fully LE (so linearisation matches tM› only at
  position 0, which tM 0 = le› covers; the rest of home's
  linearisation is fake but unread).›

definition ae_window_invariant_le0 ::
  "(nat  'a)  nat
     ('c :: enum) bp
     (('c  'a) × ('c  'a) × ('c  'a))
     'a  bool" where
  "ae_window_invariant_le0 tM nM bp blocks le 
     fst (snd blocks) = LE_block le
      tM 0 = le
      (case fst bp of
          AE_Home   nM = 0
        | AE_Right  nM = Suc (c_idx (snd bp))
        | AE_Left   False)
      (i. i < card (UNIV :: 'c set)
               tM (Suc i)
                    = buf_lin_at blocks (2 * card (UNIV :: 'c set) + i))"

text ‹Per-tape unified window invariant for the SS4→›SS5
  trace toolkit.  Hybrid encapsulation of the three regime-specific
  sibling predicates: one extra parameter pos› (the per-tape
  mt_pos c' k› at SS4 entry, a frozen value during the
  buffered M›-side run) selects which sibling fires.  Each
  regime is expressed as an implication, so the dispatch is by
  partition of pos :: nat› into 0› / 1› /
  ≥ 2› — exactly one implication is non-vacuous on any
  fixed pos›.

  Design choice rationale: the conjunction-of-implications form
  avoids forcing consumers to disjunction-eliminate before getting
  at the relevant conjunct, while the indexing by pos›
  (rather than a uniform regime tag) lets the predicate be
  instantiated directly from the consumer's per-tape
  mt_pos c' k› without an extra dispatch parameter.  The
  fixed-regime-per-tape property — pos› doesn't change
  during the buffered run because m_step_buffered› is
  parameterised by (q, blocks, posbp)› with no
  c'› in scope — makes per-tape regime selection commute
  with the induction in ae_coupled_run_aux_general›.›

definition ae_window_invariant_general ::
  "(nat  'a)  nat
     ('c :: enum) bp
     (('c  'a) × ('c  'a) × ('c  'a))
     nat  'a  bool" where
  "ae_window_invariant_general tM nM bp blocks pos le 
     (pos = 0  ae_window_invariant_le0 tM nM bp blocks le)
      (pos = 1  ae_window_invariant_le1 tM nM bp blocks le)
      (pos  2
           ae_window_invariant tM nM bp blocks
                ((pos - 2) * card (UNIV :: 'c set) + 1))"

text ‹SS4 →› SS5: read right into buffer; apply the compute
  (c-fold composition of M›'s δ›) to determine the
  post-stage M›-state, the per-tape destination indicator,
  the modified buffer slots, and the new per-tape offset; per-tape
  move L back to home.  Buffer-phase substep 4 (compute folded in).

  The substrate write a' = a› is a no-op (SS4 reads the right
  block but does not modify the on-tape contents; modifications
  are materialised during the write-back phase SS5→›SS8).
  The compute happens in the state component: m_steps_buffered›
  consumes the (fully buffered) blocks plus (AE_Home, ofs)›
  starting position and produces the post-compute
  (q', buf', end_pos)›; the new offset and destination are
  projected from end_pos›.›

definition ae_delta_ss4_ss5 ::
  "('q, 'a) mttm
     (('q × ('a, 'c :: enum) ae_stage)
        × (nat  ('c  'a))
        × ('q × ('a, 'c) ae_stage)
        × (nat  ('c  'a))
        × (nat  dir)) set" where
  "ae_delta_ss4_ss5 M =
     {((q, ofs, buf, dest_old, SS4), a,
        (q', ofs', buf', dest', SS5), a, d) |
        q ofs buf dest_old a q' ofs' buf' dest' d
        buf_full end_pos bufC.
          q  Q_tm M  q  t_tm M  q  r_tm M
           (jk_tm M. a j = bl_block (bl_tm M))
           buf_full = (λk. (fst (buf k),
                              if k < k_tm M then fst (snd (buf k)) else a k,
                              a k))
           ((q, buf_full, λk. (AE_Home, ofs k)),
             (q', bufC, end_pos))  m_steps_buffered M
           ofs' = (λk. if k < k_tm M then snd (end_pos k) else init_offset k)
           buf' = (λk. if k < k_tm M then bufC k else init_buffer (le_tm M) k)
           dest' = (λk. if k < k_tm M then fst (end_pos k) else init_dest k)
           d = (λk. if k < k_tm M
                       then (if a k = LE_block (le_tm M)
                               then dir.N else dir.L)
                       else dir.N)}"

text ‹Per-tape (write, move) action helpers for the four
  write-back substeps (SS5→›SS6, SS6→›SS7,
  SS7→›SS8, SS8→›SS1).  Each takes
  (le, a, buf, dest)› and returns (a', d)›: the per-tape
  written block and direction.

  Convention: buf k = (l, h, r)› is the post-compute buffer.
  LE-stage ⟷› h = LE_block le›.  In steady-state, dest›
  ranges over {AE_Left, AE_Home, AE_Right}›; in LE-stage, the
  compute restricts dest› to {AE_Home, AE_Right}› but the
  helpers handle the AE_Left› branch as a vacuous fall-through
  (will never fire under the compute's invariants).›

text ‹**Head trajectory through the writeback chain.**  The
  direction rules in the action helpers below thread M'›'s
  head through a specific sequence of block positions
  across SS5 →› SS8.  Starting from the SS4
  →› SS5 transition (which moves L when reading
  non-LE, so SS5 entry is at block s›, the original
  home), the chain walks:

   SS5: at block s›; writes h›; moves L
    (dest ≠ AE_Left›) or R (dest = AE_Left›).
   SS6: at block s-1› or s+1›; writes
    l›/r› depending on dest›; moves back toward home.
   SS7: at block s›; writes a› (idempotent);
    moves L (dest = AE_Left›) or R (otherwise).
   SS8: at block s+1› (dest ∈ {AE_Home,
    AE_Right}›) or s-1› (dest = AE_Left›); writes
    r›/l›; lands at block s + dest_offset›
    for the next stage.

  **Why this matters for the LE-edge cases.**  For
  steady-state (s ≥ 2›), the walk stays in data
  territory and the LE-guard never fires.  For le1
  (s = 1›), the walk reaches **block 0 — the LE
  position** at SS6 entry (when dest ≠ AE_Left›) or
  at SS8 entry (when dest = AE_Left›).  At those moments
  the LE-guard branch fires (head reads LE_block›),
  writing LE_block› back idempotently.  The
  side-band invariant ae_position_link› records that
  these LE-guard firings happen at exactly the substeps where
  the default branch would otherwise write the buffer's
  l›-slot (which is LE_block› in le1, having been
  loaded from block 0) to a non-LE position — the
  pre-emption that keeps the encoding consistent.›

text ‹**LE-guard prefix and δLE› compatibility.**
  Each action helper prepends an LE-guard
  if a = LE_block le then (a, N) else …› for syntactic
  δLE›-compatibility: the per-substep relations are over
  all (state, a, …)› tuples, not just reachable ones, and
  the substrate's δLE› well-formedness conjunct (a
  valid_mttm› clause) is universal.
  In reachable executions a = h› at SS5→›SS8 (head at
  the home position), so the guard agrees with the
  h = LE_block le› branch; in unreachable tuples
  (a = LE_block le› but h ≠ LE_block le›), the guard forces
  a δLE›-safe (a, N)› output rather than the
  buffer-driven (…, L)› that would violate δLE›.

  This guard serves a double purpose: substrate compatibility
  (the immediate concern above) AND the LE-pre-emption used
  by le1/le0.  In those regimes the head genuinely reaches
  block 0, the LE-guard's a = LE_block›
  antecedent is true, and the guard fires the LE-block-write
  branch in preference to the default — protecting block
  0 from being clobbered by a non-LE buffer slot.›

fun ae_ss5_action ::
  "'a  ('c :: enum  'a)
     (('c  'a) × ('c  'a) × ('c  'a))
     ae_dest  (('c  'a) × dir)" where
  "ae_ss5_action le a (l, h, r) ds =
     (if a = LE_block le then (a, dir.N)
      else if h = LE_block le then (a, dir.N)
      else (h, if ds = AE_Left then dir.R else dir.L))"

fun ae_ss6_action ::
  "'a  ('c :: enum  'a)
     (('c  'a) × ('c  'a) × ('c  'a))
     ae_dest  (('c  'a) × dir)" where
  "ae_ss6_action le a (l, h, r) ds =
     (if a = LE_block le then (a, dir.R)
      else if h = LE_block le then (a, dir.R)
      else if ds = AE_Left then (r, dir.L) else (l, dir.R))"

fun ae_ss7_action ::
  "'a  ('c :: enum  'a)
     (('c  'a) × ('c  'a) × ('c  'a))
     ae_dest  (('c  'a) × dir)" where
  "ae_ss7_action le a (l, h, r) ds =
     (if a = LE_block le then (a, dir.N)
      else if h = LE_block le
        then (r, if ds = AE_Right then dir.N else dir.L)
      else (a, if ds = AE_Left then dir.L else dir.R))"

fun ae_ss8_action ::
  "'a  ('c :: enum  'a)
     (('c  'a) × ('c  'a) × ('c  'a))
     ae_dest  (('c  'a) × dir)" where
  "ae_ss8_action le a (l, h, r) ds =
     (if a = LE_block le then (a, dir.N)
      else if h = LE_block le
        then (if ds = AE_Right then (r, dir.N) else (a, dir.N))
      else (case ds of
              AE_Left   (l, dir.N)
            | AE_Home   (r, dir.L)
            | AE_Right  (r, dir.N)))"

text ‹SS5 →› SS6: write home block; per-tape move depends on
  (stage_kind k, dest k)›.  Write-back substep 5.›

definition ae_delta_ss5_ss6 ::
  "('q, 'a) mttm
     (('q × ('a, 'c :: enum) ae_stage)
        × (nat  ('c  'a))
        × ('q × ('a, 'c) ae_stage)
        × (nat  ('c  'a))
        × (nat  dir)) set" where
  "ae_delta_ss5_ss6 M =
    {((q, ofs, buf, dest, SS5), a,
       (q, ofs, buf, dest, SS6), a', d) |
     q ofs buf dest a a' d.
       q  Q_tm M
        (jk_tm M. a j = bl_block (bl_tm M))
        a' = (λk. if k < k_tm M
                     then fst (ae_ss5_action (le_tm M) (a k) (buf k) (dest k))
                     else bl_block (bl_tm M))
        d  = (λk. if k < k_tm M
                     then snd (ae_ss5_action (le_tm M) (a k) (buf k) (dest k))
                     else dir.N)}"

text ‹SS6 →› SS7: write left (steady-state, dest ∈ {AE_Home,
  AE_Right}›) or right (steady-state, dest = AE_Left›) or
  home (LE-stage); per-tape move per dest.  Write-back substep 6.›

definition ae_delta_ss6_ss7 ::
  "('q, 'a) mttm
     (('q × ('a, 'c :: enum) ae_stage)
        × (nat  ('c  'a))
        × ('q × ('a, 'c) ae_stage)
        × (nat  ('c  'a))
        × (nat  dir)) set" where
  "ae_delta_ss6_ss7 M =
    {((q, ofs, buf, dest, SS6), a,
       (q, ofs, buf, dest, SS7), a', d) |
     q ofs buf dest a a' d.
       q  Q_tm M
        (jk_tm M. a j = bl_block (bl_tm M))
        a' = (λk. if k < k_tm M
                     then fst (ae_ss6_action (le_tm M) (a k) (buf k) (dest k))
                     else bl_block (bl_tm M))
        d  = (λk. if k < k_tm M
                     then snd (ae_ss6_action (le_tm M) (a k) (buf k) (dest k))
                     else dir.N)}"

text ‹SS7 →› SS8: idempotent home re-write (steady-state) or
  right write (LE-stage); per-tape move per dest.  Write-back
  substep 7.›

definition ae_delta_ss7_ss8 ::
  "('q, 'a) mttm
     (('q × ('a, 'c :: enum) ae_stage)
        × (nat  ('c  'a))
        × ('q × ('a, 'c) ae_stage)
        × (nat  ('c  'a))
        × (nat  dir)) set" where
  "ae_delta_ss7_ss8 M =
    {((q, ofs, buf, dest, SS7), a,
       (q, ofs, buf, dest, SS8), a', d) |
     q ofs buf dest a a' d.
       q  Q_tm M
        (jk_tm M. a j = bl_block (bl_tm M))
        a' = (λk. if k < k_tm M
                     then fst (ae_ss7_action (le_tm M) (a k) (buf k) (dest k))
                     else bl_block (bl_tm M))
        d  = (λk. if k < k_tm M
                     then snd (ae_ss7_action (le_tm M) (a k) (buf k) (dest k))
                     else dir.N)}"

text ‹SS8 →› SS1: write final block at destination; head ends
  at dest› position; substep counter resets to SS1.  End of
  write-back phase.

  Halt-aware destination: if q› reached a halting state
  (t_tm M› / r_tm M›) during the compute substep, the
  end-of-stage state is forced to (q, init_stage le_M)›,
  which equals t_M'› / r_M'› by construction.  This makes
  M' actually reach its canonical accept / reject state when M
  halts mid-stage, rather than stalling at SS5.  For non-halting
  q›, the (offset, buffer) pair is preserved for the next
  stage; dest› resets to AE_Home›.›

definition ae_delta_ss8_ss1 ::
  "('q, 'a) mttm
     (('q × ('a, 'c :: enum) ae_stage)
        × (nat  ('c  'a))
        × ('q × ('a, 'c) ae_stage)
        × (nat  ('c  'a))
        × (nat  dir)) set" where
  "ae_delta_ss8_ss1 M =
    {((q, ofs, buf, dest, SS8), a,
       (q, stage'), a', d) |
     q ofs buf dest a stage' a' d.
       q  Q_tm M
        (jk_tm M. a j = bl_block (bl_tm M))
        stage' = (if q  {t_tm M, r_tm M}
                    then init_stage (le_tm M)
                    else (ofs, buf, init_dest, SS1))
        a' = (λk. if k < k_tm M
                     then fst (ae_ss8_action (le_tm M) (a k) (buf k) (dest k))
                     else bl_block (bl_tm M))
        d  = (λk. if k < k_tm M
                     then snd (ae_ss8_action (le_tm M) (a k) (buf k) (dest k))
                     else dir.N)}"

end