Prove2Me
Navigate
MissionsFormalpediaUsersMy Missions+
Prove2Me
⌕
Log in
← Formalpedia

sgl_fold3_walk

Definition

by Henry Yuen · Jul 30, 2026 · Mathlib c5ea003 (Lean v4.30.0)

Definition code
import Definitions.Def_sgl_fold_walk
import Definitions.Def_sgl_ss_tapes

/-!
# The three-to-three fold walk

Three input tapes advance in step; each stride combines their heads and
deposits the result round-robin onto three output tapes.  The round-robin
needs no state: a stride writes to the first output whose head is blank,
and after the third write all three outputs advance together, exposing
fresh blanks.  Output `k` therefore collects every third combined value —
exactly the distribution the next cascade level consumes.
-/

namespace SipserGacsLautemann

variable {tapes : Nat}

/-- Every third element, starting at the first. -/
def third0 : List TapeSymbol → List TapeSymbol
  | x :: _ :: _ :: r => x :: third0 r
  | x :: _ :: [] => [x]
  | x :: [] => [x]
  | [] => []

/-- Every third element, starting at the second. -/
def third1 : List TapeSymbol → List TapeSymbol
  | _ :: y :: _ :: r => y :: third1 r
  | _ :: y :: [] => [y]
  | _ :: [] => []
  | [] => []

/-- Every third element, starting at the third. -/
def third2 : List TapeSymbol → List TapeSymbol
  | _ :: _ :: z :: r => z :: third2 r
  | _ :: _ :: [] => []
  | _ :: [] => []
  | [] => []

/-- One stride: combine the three input heads, deposit round-robin. -/
def fold3Step (f : Bool → Bool → Bool → Bool)
    (a b c a2 b2 c2 : Fin tapes) (T : Fin tapes → Tape) : Fin tapes → Tape :=
  fun i =>
    if (T a2).head = TapeSymbol.blank then
      if i = a2 then
        Tape.write (T a2)
          (TapeSymbol.bit (f (cellBool (T a).head) (cellBool (T b).head)
            (cellBool (T c).head)))
      else if i = a then moveDir HeadMove.right (T a)
      else if i = b then moveDir HeadMove.right (T b)
      else if i = c then moveDir HeadMove.right (T c)
      else T i
    else if (T b2).head = TapeSymbol.blank then
      if i = b2 then
        Tape.write (T b2)
          (TapeSymbol.bit (f (cellBool (T a).head) (cellBool (T b).head)
            (cellBool (T c).head)))
      else if i = a then moveDir HeadMove.right (T a)
      else if i = b then moveDir HeadMove.right (T b)
      else if i = c then moveDir HeadMove.right (T c)
      else T i
    else
      if i = c2 then
        moveDir HeadMove.right (Tape.write (T c2)
          (TapeSymbol.bit (f (cellBool (T a).head) (cellBool (T b).head)
            (cellBool (T c).head))))
      else if i = a2 then moveDir HeadMove.right (T a2)
      else if i = b2 then moveDir HeadMove.right (T b2)
      else if i = a then moveDir HeadMove.right (T a)
      else if i = b then moveDir HeadMove.right (T b)
      else if i = c then moveDir HeadMove.right (T c)
      else T i

/-- The stride's action. -/
def fold3Action (f : Bool → Bool → Bool → Bool)
    (a b c a2 b2 c2 : Fin tapes) :
    (Fin tapes → TapeSymbol) → Fin tapes → TapeSymbol × HeadMove :=
  fun symbols i =>
    if symbols a2 = TapeSymbol.blank then
      if i = a2 then
        (TapeSymbol.bit (f (cellBool (symbols a)) (cellBool (symbols b))
          (cellBool (symbols c))), HeadMove.stay)
      else if i = a then (symbols i, HeadMove.right)
      else if i = b then (symbols i, HeadMove.right)
      else if i = c then (symbols i, HeadMove.right)
      else (symbols i, HeadMove.stay)
    else if symbols b2 = TapeSymbol.blank then
      if i = b2 then
        (TapeSymbol.bit (f (cellBool (symbols a)) (cellBool (symbols b))
          (cellBool (symbols c))), HeadMove.stay)
      else if i = a then (symbols i, HeadMove.right)
      else if i = b then (symbols i, HeadMove.right)
      else if i = c then (symbols i, HeadMove.right)
      else (symbols i, HeadMove.stay)
    else
      if i = c2 then
        (TapeSymbol.bit (f (cellBool (symbols a)) (cellBool (symbols b))
          (cellBool (symbols c))), HeadMove.right)
      else if i = a2 then (symbols i, HeadMove.right)
      else if i = b2 then (symbols i, HeadMove.right)
      else if i = a then (symbols i, HeadMove.right)
      else if i = b then (symbols i, HeadMove.right)
      else if i = c then (symbols i, HeadMove.right)
      else (symbols i, HeadMove.stay)

/-- One round of the cascade level. -/
def fold3Body (f : Bool → Bool → Bool → Bool)
    (a b c a2 b2 c2 : Fin tapes) :=
  (TypedMachine.test (notBlankAt a)).andThen fun v =>
    if v then
      (TypedMachine.act (fold3Action f a b c a2 b2 c2)).andThen fun _ =>
        TypedMachine.halt (tapes := tapes) true
    else
      (TypedMachine.act (fun symbols i => (symbols i, HeadMove.stay))).andThen
        fun _ => TypedMachine.halt (tapes := tapes) false

/-- The cascade level: one stride per mark on the first input tape. -/
noncomputable def fold3Walk (f : Bool → Bool → Bool → Bool)
    (a b c a2 b2 c2 : Fin tapes) :=
  (fold3Body f a b c a2 b2 c2).repeatUntilFalse


theorem fold3Step_eq (f : Bool → Bool → Bool → Bool)
    (a b c a2 b2 c2 : Fin tapes)
    (hab : a ≠ b) (hac : a ≠ c) (hbc : b ≠ c)
    (h2ab : a2 ≠ b2) (h2ac : a2 ≠ c2) (h2bc : b2 ≠ c2)
    (ha2a : a2 ≠ a) (ha2b : a2 ≠ b) (ha2c : a2 ≠ c)
    (hb2a : b2 ≠ a) (hb2b : b2 ≠ b) (hb2c : b2 ≠ c)
    (hc2a : c2 ≠ a) (hc2b : c2 ≠ b) (hc2c : c2 ≠ c)
    (T : Fin tapes → Tape) :
    applyAction T (fold3Action f a b c a2 b2 c2) =
      fold3Step f a b c a2 b2 c2 T := by
  funext i
  simp only [fold3Step]
  by_cases hpa : (T a2).head = TapeSymbol.blank
  · rw [if_pos hpa]
    by_cases hia : i = a2
    · subst hia
      simp [applyAction, fold3Action, hpa, moveDir, Tape.move]
    · rw [if_neg hia]
      by_cases hi1 : i = a
      · subst hi1
        simp [applyAction, fold3Action, hpa, hia, Tape.write_head_self,
          moveDir]
      · rw [if_neg hi1]
        by_cases hi2 : i = b
        · subst hi2
          simp [applyAction, fold3Action, hpa, hia, hi1,
            Tape.write_head_self, moveDir]
        · rw [if_neg hi2]
          by_cases hi3 : i = c
          · subst hi3
            simp [applyAction, fold3Action, hpa, hia, hi1, hi2,
              Tape.write_head_self, moveDir]
          · rw [if_neg hi3]
            simp [applyAction, fold3Action, hpa, hia, hi1, hi2, hi3,
              Tape.write_head_self, Tape.move]
  · rw [if_neg hpa]
    by_cases hpb : (T b2).head = TapeSymbol.blank
    · rw [if_pos hpb]
      by_cases hib : i = b2
      · subst hib
        simp [applyAction, fold3Action, hpa, hpb, moveDir, Tape.move]
      · rw [if_neg hib]
        by_cases hi1 : i = a
        · subst hi1
          simp [applyAction, fold3Action, hpa, hpb, hib,
            Tape.write_head_self, moveDir]
        · rw [if_neg hi1]
          by_cases hi2 : i = b
          · subst hi2
            simp [applyAction, fold3Action, hpa, hpb, hib, hi1,
              Tape.write_head_self, moveDir]
          · rw [if_neg hi2]
            by_cases hi3 : i = c
            · subst hi3
              simp [applyAction, fold3Action, hpa, hpb, hib, hi1, hi2,
                Tape.write_head_self, moveDir]
            · rw [if_neg hi3]
              simp [applyAction, fold3Action, hpa, hpb, hib, hi1, hi2,
                hi3, Tape.write_head_self, Tape.move]
    · rw [if_neg hpb]
      by_cases hic : i = c2
      · subst hic
        simp [applyAction, fold3Action, hpa, hpb, moveDir]
      · rw [if_neg hic]
        by_cases hja : i = a2
        · subst hja
          simp [applyAction, fold3Action, hpa, hpb, hic,
            Tape.write_head_self, moveDir]
        · rw [if_neg hja]
          by_cases hjb : i = b2
          · subst hjb
            simp [applyAction, fold3Action, hpa, hpb, hic, hja,
              Tape.write_head_self, moveDir]
          · rw [if_neg hjb]
            by_cases hi1 : i = a
            · subst hi1
              simp [applyAction, fold3Action, hpa, hpb, hic, hja, hjb,
                Tape.write_head_self, moveDir]
            · rw [if_neg hi1]
              by_cases hi2 : i = b
              · subst hi2
                simp [applyAction, fold3Action, hpa, hpb, hic, hja, hjb,
                  hi1, Tape.write_head_self, moveDir]
              · rw [if_neg hi2]
                by_cases hi3 : i = c
                · subst hi3
                  simp [applyAction, fold3Action, hpa, hpb, hic, hja,
                    hjb, hi1, hi2, Tape.write_head_self, moveDir]
                · rw [if_neg hi3]
                  simp [applyAction, fold3Action, hpa, hpb, hic, hja,
                    hjb, hi1, hi2, hi3, Tape.write_head_self, Tape.move]


set_option maxHeartbeats 1000000 in
/-- A round on a marked first input. -/
theorem fold3Body_mark (f : Bool → Bool → Bool → Bool)
    (a b c a2 b2 c2 : Fin tapes)
    (hab : a ≠ b) (hac : a ≠ c) (hbc : b ≠ c)
    (h2ab : a2 ≠ b2) (h2ac : a2 ≠ c2) (h2bc : b2 ≠ c2)
    (ha2a : a2 ≠ a) (ha2b : a2 ≠ b) (ha2c : a2 ≠ c)
    (hb2a : b2 ≠ a) (hb2b : b2 ≠ b) (hb2c : b2 ≠ c)
    (hc2a : c2 ≠ a) (hc2b : c2 ≠ b) (hc2c : c2 ≠ c)
    (T : Fin tapes → Tape)
    (hmark : (T a).head ≠ TapeSymbol.blank) :
    HaltsExactly (fold3Body f a b c a2 b2 c2)
      ((fold3Body f a b c a2 b2 c2).startCfg T) (1 + 1 + (1 + 1 + 0)) true ∧
    (((fold3Body f a b c a2 b2 c2).step^[1 + 1 + (1 + 1 + 0)])
      ((fold3Body f a b c a2 b2 c2).startCfg T)).tape =
      fold3Step f a b c a2 b2 c2 T := by
  have htest := TypedMachine.test_spec (notBlankAt a) T
  rw [notBlankAt_true a T hmark] at htest
  have htestt : (((TypedMachine.test (notBlankAt a)).step^[1])
      ((TypedMachine.test (notBlankAt a)).startCfg T)).tape = T := by
    rw [Function.iterate_one]
    funext j
    simp [TypedMachine.step, TypedMachine.test, TypedMachine.startCfg,
      Tape.write_head_self, Tape.move]
  have hact := TypedMachine.act_spec (fold3Action f a b c a2 b2 c2) T
  have hactt : (((TypedMachine.act (fold3Action f a b c a2 b2 c2)).step^[1])
      ((TypedMachine.act (fold3Action f a b c a2 b2 c2)).startCfg T)).tape =
      fold3Step f a b c a2 b2 c2 T := by
    rw [Function.iterate_one, ← fold3Step_eq f a b c a2 b2 c2 hab hac hbc
      h2ab h2ac h2bc ha2a ha2b ha2c hb2a hb2b hb2c hc2a hc2b hc2c]
    funext j
    simp [TypedMachine.step, TypedMachine.act, TypedMachine.startCfg,
      applyAction]
  have hhalt := TypedMachine.halt_spec (tapes := tapes) true
    ((TypedMachine.halt (tapes := tapes) true).startCfg
      (fold3Step f a b c a2 b2 c2 T))
  have hinner := chainStepC hact hactt hhalt
  have hmain := chainStepD
    (M₂ := fun v =>
      if v then
        (TypedMachine.act (fold3Action f a b c a2 b2 c2)).andThen fun _ =>
          TypedMachine.halt (tapes := tapes) true
      else
        (TypedMachine.act (fun symbols i =>
          (symbols i, HeadMove.stay))).andThen
          fun _ => TypedMachine.halt (tapes := tapes) false)
    htest htestt (by simpa using hinner.1)
  refine ⟨hmain.1, ?_⟩
  have hfin := hmain.2
  have hfin' : ((fold3Body f a b c a2 b2 c2).step^[1 + 1 + (1 + 1 + 0)])
      ((fold3Body f a b c a2 b2 c2).startCfg T) = _ := hfin
  rw [hfin']
  show (TypedConfiguration.inRight true
    ((((TypedMachine.act (fold3Action f a b c a2 b2 c2)).andThen fun _ =>
      TypedMachine.halt (tapes := tapes) true).step^[1 + 1 + 0])
      (((TypedMachine.act (fold3Action f a b c a2 b2 c2)).andThen fun _ =>
        TypedMachine.halt (tapes := tapes) true).startCfg T))).tape =
    fold3Step f a b c a2 b2 c2 T
  have hi := hinner.2
  rw [hi]
  show ((TypedMachine.halt (tapes := tapes) true).step^[0]
    ((TypedMachine.halt (tapes := tapes) true).startCfg
      (fold3Step f a b c a2 b2 c2 T))).tape = fold3Step f a b c a2 b2 c2 T
  rfl

set_option maxHeartbeats 1000000 in
/-- A round on a blank first input. -/
theorem fold3Body_blank (f : Bool → Bool → Bool → Bool)
    (a b c a2 b2 c2 : Fin tapes)
    (T : Fin tapes → Tape)
    (hblank : (T a).head = TapeSymbol.blank) :
    HaltsExactly (fold3Body f a b c a2 b2 c2)
      ((fold3Body f a b c a2 b2 c2).startCfg T) (1 + 1 + (1 + 1 + 0)) false ∧
    (((fold3Body f a b c a2 b2 c2).step^[1 + 1 + (1 + 1 + 0)])
      ((fold3Body f a b c a2 b2 c2).startCfg T)).tape = T := by
  have htest := TypedMachine.test_spec (notBlankAt a) T
  rw [notBlankAt_false a T hblank] at htest
  have htestt : (((TypedMachine.test (notBlankAt a)).step^[1])
      ((TypedMachine.test (notBlankAt a)).startCfg T)).tape = T := by
    rw [Function.iterate_one]
    funext j
    simp [TypedMachine.step, TypedMachine.test, TypedMachine.startCfg,
      Tape.write_head_self, Tape.move]
  have hact := TypedMachine.act_spec
    (fun symbols i => (symbols i, HeadMove.stay)) T
  have hactt : (((TypedMachine.act (fun symbols i =>
      (symbols i, HeadMove.stay))).step^[1])
      ((TypedMachine.act (fun symbols i =>
        (symbols i, HeadMove.stay))).startCfg T)).tape = T := by
    rw [Function.iterate_one]
    funext j
    simp [TypedMachine.step, TypedMachine.act, TypedMachine.startCfg,
      applyAction, Tape.write_head_self, Tape.move]
  have hhalt := TypedMachine.halt_spec (tapes := tapes) false
    ((TypedMachine.halt (tapes := tapes) false).startCfg T)
  have hinner := chainStepC hact hactt hhalt
  have hmain := chainStepD
    (M₂ := fun v =>
      if v then
        (TypedMachine.act (fold3Action f a b c a2 b2 c2)).andThen fun _ =>
          TypedMachine.halt (tapes := tapes) true
      else
        (TypedMachine.act (fun symbols i =>
          (symbols i, HeadMove.stay))).andThen
          fun _ => TypedMachine.halt (tapes := tapes) false)
    htest htestt (by simpa using hinner.1)
  refine ⟨hmain.1, ?_⟩
  have hfin := hmain.2
  have hfin' : ((fold3Body f a b c a2 b2 c2).step^[1 + 1 + (1 + 1 + 0)])
      ((fold3Body f a b c a2 b2 c2).startCfg T) = _ := hfin
  rw [hfin']
  show (TypedConfiguration.inRight false
    ((((TypedMachine.act (fun symbols i =>
      (symbols i, HeadMove.stay))).andThen fun _ =>
      TypedMachine.halt (tapes := tapes) false).step^[1 + 1 + 0])
      (((TypedMachine.act (fun symbols i =>
        (symbols i, HeadMove.stay))).andThen fun _ =>
        TypedMachine.halt (tapes := tapes) false).startCfg T))).tape = T
  have hi := hinner.2
  rw [hi]
  show ((TypedMachine.halt (tapes := tapes) false).step^[0]
    ((TypedMachine.halt (tapes := tapes) false).startCfg T)).tape = T
  rfl

set_option maxHeartbeats 4000000 in
/-- **The cascade level.** -/
theorem fold3Walk_spec (f : Bool → Bool → Bool → Bool)
    (a b c a2 b2 c2 : Fin tapes)
    (hab : a ≠ b) (hac : a ≠ c) (hbc : b ≠ c)
    (h2ab : a2 ≠ b2) (h2ac : a2 ≠ c2) (h2bc : b2 ≠ c2)
    (ha2a : a2 ≠ a) (ha2b : a2 ≠ b) (ha2c : a2 ≠ c)
    (hb2a : b2 ≠ a) (hb2b : b2 ≠ b) (hb2c : b2 ≠ c)
    (hc2a : c2 ≠ a) (hc2b : c2 ≠ b) (hc2c : c2 ≠ c)
    (m : Nat) (T : Fin tapes → Tape)
    (hmarks : ∀ r, r < m →
      ((((fold3Step f a b c a2 b2 c2)^[r]) T) a).head ≠ TapeSymbol.blank)
    (hexit : ((((fold3Step f a b c a2 b2 c2)^[m]) T) a).head =
      TapeSymbol.blank) :
    HaltsExactly (fold3Walk f a b c a2 b2 c2)
      ((fold3Walk f a b c a2 b2 c2).startCfg T) (m * 5 + 4) false ∧
    (((fold3Walk f a b c a2 b2 c2).step^[m * 5 + 4])
      ((fold3Walk f a b c a2 b2 c2).startCfg T)).tape =
      ((fold3Step f a b c a2 b2 c2)^[m]) T := by
  classical
  set body := fold3Body f a b c a2 b2 c2 with hbody
  set cfg : Nat → TypedConfiguration tapes _ :=
    fun r => body.startCfg (((fold3Step f a b c a2 b2 c2)^[r]) T) with hcfg
  have hround : ∀ r, r < m →
      HaltsExactly body (cfg r) 4 true ∧
        cfg (r + 1) = ⟨body.start, ((body.step^[4]) (cfg r)).tape⟩ := by
    intro r hr
    obtain ⟨hh, ht⟩ := fold3Body_mark f a b c a2 b2 c2 hab hac hbc h2ab
      h2ac h2bc ha2a ha2b ha2c hb2a hb2b hb2c hc2a hc2b hc2c
      (((fold3Step f a b c a2 b2 c2)^[r]) T) (hmarks r hr)
    have hh4 : HaltsExactly body (cfg r) 4 true := by
      rw [hcfg, hbody]; simpa using hh
    have ht4 : ((body.step^[4]) (cfg r)).tape =
        ((fold3Step f a b c a2 b2 c2)^[r + 1]) T := by
      rw [hcfg, hbody]
      rw [show (4 : Nat) = 1 + 1 + (1 + 1 + 0) from rfl]
      rw [ht, Function.iterate_succ_apply']
    refine ⟨hh4, ?_⟩
    show body.startCfg (((fold3Step f a b c a2 b2 c2)^[r + 1]) T) = _
    rw [← ht4]
    rfl
  have hexitr : HaltsExactly body (cfg m) 4 false := by
    have := (fold3Body_blank f a b c a2 b2 c2
      (((fold3Step f a b c a2 b2 c2)^[m]) T) hexit).1
    rw [hcfg, hbody]; simpa using this
  have hspec := TypedMachine.repeatUntilFalse_spec body cfg (fun _ => 4) m 4
    hround hexitr
  have hcost : loopCost (fun _ => 4) m + 4 = m * 5 + 4 := by
    rw [loopCost_const]
  rw [hcost] at hspec
  have hrun := TypedMachine.repeatUntilFalse_rounds body cfg (fun _ => 4) m
    hround
  rw [loopCost_const] at hrun
  refine ⟨by rw [hcfg] at hspec; exact hspec, ?_⟩
  have hstart : (fold3Walk f a b c a2 b2 c2).startCfg T = cfg 0 := by
    rw [hcfg]; rfl
  rw [hstart, show m * 5 + 4 = 4 + m * (4 + 1) by omega,
    Function.iterate_add_apply]
  show ((fold3Walk f a b c a2 b2 c2).step^[4]
    (((fold3Walk f a b c a2 b2 c2).step^[m * (4 + 1)]) (cfg 0))).tape = _
  rw [show (fold3Walk f a b c a2 b2 c2) = body.repeatUntilFalse from rfl,
    hrun]
  have htp : ((body.repeatUntilFalse.step^[4]) (cfg m)).tape =
      ((body.step^[4]) (cfg m)).tape := by
    rw [body.repeatUntilFalse_iterate_fresh (cfg m) 4 hexitr.fresh]
  rw [htp, hcfg, hbody]
  exact (fold3Body_blank f a b c a2 b2 c2
    (((fold3Step f a b c a2 b2 c2)^[m]) T) hexit).2


/-! ## Strides, phase by phase -/

/-- The inputs advance every stride, whatever the phase. -/
theorem fold3Step_inputs (f : Bool → Bool → Bool → Bool)
    (a b c a2 b2 c2 : Fin tapes)
    (hab : a ≠ b) (hac : a ≠ c) (hbc : b ≠ c)
    (ha2a : a2 ≠ a) (ha2b : a2 ≠ b) (ha2c : a2 ≠ c)
    (hb2a : b2 ≠ a) (hb2b : b2 ≠ b) (hb2c : b2 ≠ c)
    (hc2a : c2 ≠ a) (hc2b : c2 ≠ b) (hc2c : c2 ≠ c)
    (T : Fin tapes → Tape) :
    (fold3Step f a b c a2 b2 c2 T) a = moveDir HeadMove.right (T a) ∧
    (fold3Step f a b c a2 b2 c2 T) b = moveDir HeadMove.right (T b) ∧
    (fold3Step f a b c a2 b2 c2 T) c = moveDir HeadMove.right (T c) := by
  refine ⟨?_, ?_, ?_⟩ <;> simp only [fold3Step] <;>
    by_cases hpa : (T a2).head = TapeSymbol.blank
  · rw [if_pos hpa, if_neg (Ne.symm ha2a), if_pos trivial]
  · rw [if_neg hpa]
    by_cases hpb : (T b2).head = TapeSymbol.blank
    · rw [if_pos hpb, if_neg (Ne.symm hb2a), if_pos trivial]
    · rw [if_neg hpb, if_neg (Ne.symm hc2a), if_neg (Ne.symm ha2a),
        if_neg (Ne.symm hb2a), if_pos trivial]
  · rw [if_pos hpa, if_neg (Ne.symm ha2b), if_neg (Ne.symm hab), if_pos trivial]
  · rw [if_neg hpa]
    by_cases hpb : (T b2).head = TapeSymbol.blank
    · rw [if_pos hpb, if_neg (Ne.symm hb2b), if_neg (Ne.symm hab),
        if_pos trivial]
    · rw [if_neg hpb, if_neg (Ne.symm hc2b), if_neg (Ne.symm ha2b),
        if_neg (Ne.symm hb2b), if_neg (Ne.symm hab), if_pos trivial]
  · rw [if_pos hpa, if_neg (Ne.symm ha2c), if_neg (Ne.symm hac),
      if_neg (Ne.symm hbc), if_pos trivial]
  · rw [if_neg hpa]
    by_cases hpb : (T b2).head = TapeSymbol.blank
    · rw [if_pos hpb, if_neg (Ne.symm hb2c), if_neg (Ne.symm hac),
        if_neg (Ne.symm hbc), if_pos trivial]
    · rw [if_neg hpb, if_neg (Ne.symm hc2c), if_neg (Ne.symm ha2c),
        if_neg (Ne.symm hb2c), if_neg (Ne.symm hac), if_neg (Ne.symm hbc),
        if_pos trivial]

/-- Tapes other than the six are untouched by a stride. -/
theorem fold3Step_other_one (f : Bool → Bool → Bool → Bool)
    (a b c a2 b2 c2 : Fin tapes) (T : Fin tapes → Tape) (j : Fin tapes)
    (hja : j ≠ a) (hjb : j ≠ b) (hjc : j ≠ c)
    (hja2 : j ≠ a2) (hjb2 : j ≠ b2) (hjc2 : j ≠ c2) :
    (fold3Step f a b c a2 b2 c2 T) j = T j := by
  simp only [fold3Step]
  by_cases hpa : (T a2).head = TapeSymbol.blank
  · rw [if_pos hpa, if_neg hja2, if_neg hja, if_neg hjb, if_neg hjc]
  · rw [if_neg hpa]
    by_cases hpb : (T b2).head = TapeSymbol.blank
    · rw [if_pos hpb, if_neg hjb2, if_neg hja, if_neg hjb, if_neg hjc]
    · rw [if_neg hpb, if_neg hjc2, if_neg hja2, if_neg hjb2, if_neg hja,
        if_neg hjb, if_neg hjc]

theorem fold3Step_other (f : Bool → Bool → Bool → Bool)
    (a b c a2 b2 c2 : Fin tapes) :
    ∀ (m : Nat) (T : Fin tapes → Tape) (j : Fin tapes),
      j ≠ a → j ≠ b → j ≠ c → j ≠ a2 → j ≠ b2 → j ≠ c2 →
      (((fold3Step f a b c a2 b2 c2)^[m]) T) j = T j := by
  intro m
  induction m with
  | zero => intro T j _ _ _ _ _ _; rfl
  | succ m ih =>
      intro T j hja hjb hjc hja2 hjb2 hjc2
      rw [Function.iterate_succ_apply,
        ih (fold3Step f a b c a2 b2 c2 T) j hja hjb hjc hja2 hjb2 hjc2,
        fold3Step_other_one f a b c a2 b2 c2 T j hja hjb hjc hja2 hjb2 hjc2]

/-- Phase 0: the first output's head is blank; it receives the value and
stays; the other outputs are untouched. -/
theorem fold3Step_phase0 (f : Bool → Bool → Bool → Bool)
    (a b c a2 b2 c2 : Fin tapes)
    (h2ab : a2 ≠ b2) (h2ac : a2 ≠ c2) (h2bc : b2 ≠ c2)
    (ha2a : a2 ≠ a) (ha2b : a2 ≠ b) (ha2c : a2 ≠ c)
    (hb2a : b2 ≠ a) (hb2b : b2 ≠ b) (hb2c : b2 ≠ c)
    (hc2a : c2 ≠ a) (hc2b : c2 ≠ b) (hc2c : c2 ≠ c)
    (T : Fin tapes → Tape)
    (hpa : (T a2).head = TapeSymbol.blank) :
    (fold3Step f a b c a2 b2 c2 T) a2 =
      Tape.write (T a2)
        (TapeSymbol.bit (f (cellBool (T a).head) (cellBool (T b).head)
          (cellBool (T c).head))) ∧
    (fold3Step f a b c a2 b2 c2 T) b2 = T b2 ∧
    (fold3Step f a b c a2 b2 c2 T) c2 = T c2 := by
  refine ⟨?_, ?_, ?_⟩ <;> simp only [fold3Step]
  · rw [if_pos hpa, if_pos trivial]
  · rw [if_pos hpa, if_neg (Ne.symm h2ab), if_neg hb2a, if_neg hb2b,
      if_neg hb2c]
  · rw [if_pos hpa, if_neg (Ne.symm h2ac), if_neg hc2a, if_neg hc2b,
      if_neg hc2c]

/-- Phase 1: first output occupied, second blank. -/
theorem fold3Step_phase1 (f : Bool → Bool → Bool → Bool)
    (a b c a2 b2 c2 : Fin tapes)
    (h2ab : a2 ≠ b2) (h2ac : a2 ≠ c2) (h2bc : b2 ≠ c2)
    (ha2a : a2 ≠ a) (ha2b : a2 ≠ b) (ha2c : a2 ≠ c)
    (hb2a : b2 ≠ a) (hb2b : b2 ≠ b) (hb2c : b2 ≠ c)
    (hc2a : c2 ≠ a) (hc2b : c2 ≠ b) (hc2c : c2 ≠ c)
    (T : Fin tapes → Tape)
    (hpa : (T a2).head ≠ TapeSymbol.blank)
    (hpb : (T b2).head = TapeSymbol.blank) :
    (fold3Step f a b c a2 b2 c2 T) a2 = T a2 ∧
    (fold3Step f a b c a2 b2 c2 T) b2 =
      Tape.write (T b2)
        (TapeSymbol.bit (f (cellBool (T a).head) (cellBool (T b).head)
          (cellBool (T c).head))) ∧
    (fold3Step f a b c a2 b2 c2 T) c2 = T c2 := by
  refine ⟨?_, ?_, ?_⟩ <;> simp only [fold3Step]
  · rw [if_neg hpa, if_pos hpb, if_neg h2ab, if_neg ha2a, if_neg ha2b,
      if_neg ha2c]
  · rw [if_neg hpa, if_pos hpb, if_pos trivial]
  · rw [if_neg hpa, if_pos hpb, if_neg (Ne.symm h2bc), if_neg hc2a,
      if_neg hc2b, if_neg hc2c]

/-- Phase 2: both leading outputs occupied — write the third and advance all
three outputs. -/
theorem fold3Step_phase2 (f : Bool → Bool → Bool → Bool)
    (a b c a2 b2 c2 : Fin tapes)
    (h2ab : a2 ≠ b2) (h2ac : a2 ≠ c2) (h2bc : b2 ≠ c2)
    (ha2a : a2 ≠ a) (ha2b : a2 ≠ b) (ha2c : a2 ≠ c)
    (hb2a : b2 ≠ a) (hb2b : b2 ≠ b) (hb2c : b2 ≠ c)
    (hc2a : c2 ≠ a) (hc2b : c2 ≠ b) (hc2c : c2 ≠ c)
    (T : Fin tapes → Tape)
    (hpa : (T a2).head ≠ TapeSymbol.blank)
    (hpb : (T b2).head ≠ TapeSymbol.blank) :
    (fold3Step f a b c a2 b2 c2 T) a2 = moveDir HeadMove.right (T a2) ∧
    (fold3Step f a b c a2 b2 c2 T) b2 = moveDir HeadMove.right (T b2) ∧
    (fold3Step f a b c a2 b2 c2 T) c2 =
      moveDir HeadMove.right (Tape.write (T c2)
        (TapeSymbol.bit (f (cellBool (T a).head) (cellBool (T b).head)
          (cellBool (T c).head)))) := by
  refine ⟨?_, ?_, ?_⟩ <;> simp only [fold3Step]
  · rw [if_neg hpa, if_neg hpb, if_neg h2ac, if_pos trivial]
  · rw [if_neg hpa, if_neg hpb, if_neg h2bc, if_neg (Ne.symm h2ab),
      if_pos trivial]
  · rw [if_neg hpa, if_neg hpb, if_pos trivial]


/-! ## Three strides make a group -/

set_option maxHeartbeats 2000000 in
/-- **One group.**  From an aligned state — outputs at their frontiers —
three strides consume one cell of each input and deposit one combined cell
on each output, realigning. -/
theorem fold3Group (f : Bool → Bool → Bool → Bool)
    (a b c a2 b2 c2 : Fin tapes)
    (hab : a ≠ b) (hac : a ≠ c) (hbc : b ≠ c)
    (h2ab : a2 ≠ b2) (h2ac : a2 ≠ c2) (h2bc : b2 ≠ c2)
    (ha2a : a2 ≠ a) (ha2b : a2 ≠ b) (ha2c : a2 ≠ c)
    (hb2a : b2 ≠ a) (hb2b : b2 ≠ b) (hb2c : b2 ≠ c)
    (hc2a : c2 ≠ a) (hc2b : c2 ≠ b) (hc2c : c2 ≠ c)
    (T : Fin tapes → Tape)
    (x0 x1 x2 y0 y1 y2 z0 z1 z2 : TapeSymbol)
    (La Ra Lb Rb Lc Rc La2 Lb2 Lc2 : List TapeSymbol)
    (ha : T a = cellsTape La (x0 :: x1 :: x2 :: Ra))
    (hb : T b = cellsTape Lb (y0 :: y1 :: y2 :: Rb))
    (hc : T c = cellsTape Lc (z0 :: z1 :: z2 :: Rc))
    (ha2 : T a2 = cellsTape La2 [])
    (hb2 : T b2 = cellsTape Lb2 [])
    (hc2 : T c2 = cellsTape Lc2 []) :
    (((fold3Step f a b c a2 b2 c2)^[3]) T) a =
      cellsTape (x2 :: x1 :: x0 :: La) Ra ∧
    (((fold3Step f a b c a2 b2 c2)^[3]) T) b =
      cellsTape (y2 :: y1 :: y0 :: Lb) Rb ∧
    (((fold3Step f a b c a2 b2 c2)^[3]) T) c =
      cellsTape (z2 :: z1 :: z0 :: Lc) Rc ∧
    (((fold3Step f a b c a2 b2 c2)^[3]) T) a2 =
      cellsTape (TapeSymbol.bit (f (cellBool x0) (cellBool y0)
        (cellBool z0)) :: La2) [] ∧
    (((fold3Step f a b c a2 b2 c2)^[3]) T) b2 =
      cellsTape (TapeSymbol.bit (f (cellBool x1) (cellBool y1)
        (cellBool z1)) :: Lb2) [] ∧
    (((fold3Step f a b c a2 b2 c2)^[3]) T) c2 =
      cellsTape (TapeSymbol.bit (f (cellBool x2) (cellBool y2)
        (cellBool z2)) :: Lc2) [] := by
  have hI := fold3Step_inputs f a b c a2 b2 c2 hab hac hbc ha2a ha2b ha2c
    hb2a hb2b hb2c hc2a hc2b hc2c
  -- stride 1: phase 0
  have hph0 : (T a2).head = TapeSymbol.blank := by rw [ha2]; rfl
  obtain ⟨p0a2, p0b2, p0c2⟩ := fold3Step_phase0 f a b c a2 b2 c2 h2ab h2ac
    h2bc ha2a ha2b ha2c hb2a hb2b hb2c hc2a hc2b hc2c T hph0
  have s1a : (fold3Step f a b c a2 b2 c2 T) a =
      cellsTape (x0 :: La) (x1 :: x2 :: Ra) := by
    rw [(hI T).1, ha, cellsTape_moveRight_headD]
    rfl
  have s1b : (fold3Step f a b c a2 b2 c2 T) b =
      cellsTape (y0 :: Lb) (y1 :: y2 :: Rb) := by
    rw [(hI T).2.1, hb, cellsTape_moveRight_headD]
    rfl
  have s1c : (fold3Step f a b c a2 b2 c2 T) c =
      cellsTape (z0 :: Lc) (z1 :: z2 :: Rc) := by
    rw [(hI T).2.2, hc, cellsTape_moveRight_headD]
    rfl
  have s1a2 : (fold3Step f a b c a2 b2 c2 T) a2 =
      cellsTape La2 [TapeSymbol.bit (f (cellBool x0) (cellBool y0)
        (cellBool z0))] := by
    rw [p0a2, ha, hb, hc, ha2, write_cellsTape]
    rfl
  have s1b2 : (fold3Step f a b c a2 b2 c2 T) b2 = cellsTape Lb2 [] := by
    rw [p0b2, hb2]
  have s1c2 : (fold3Step f a b c a2 b2 c2 T) c2 = cellsTape Lc2 [] := by
    rw [p0c2, hc2]
  -- stride 2: phase 1
  set S1 := fold3Step f a b c a2 b2 c2 T with hS1
  have hph1a : (S1 a2).head ≠ TapeSymbol.blank := by
    rw [s1a2]; simp [cellsTape]
  have hph1b : (S1 b2).head = TapeSymbol.blank := by rw [s1b2]; rfl
  obtain ⟨p1a2, p1b2, p1c2⟩ := fold3Step_phase1 f a b c a2 b2 c2 h2ab h2ac
    h2bc ha2a ha2b ha2c hb2a hb2b hb2c hc2a hc2b hc2c S1 hph1a hph1b
  have s2a : (fold3Step f a b c a2 b2 c2 S1) a =
      cellsTape (x1 :: x0 :: La) (x2 :: Ra) := by
    rw [(hI S1).1, s1a, cellsTape_moveRight_headD]
    rfl
  have s2b : (fold3Step f a b c a2 b2 c2 S1) b =
      cellsTape (y1 :: y0 :: Lb) (y2 :: Rb) := by
    rw [(hI S1).2.1, s1b, cellsTape_moveRight_headD]
    rfl
  have s2c : (fold3Step f a b c a2 b2 c2 S1) c =
      cellsTape (z1 :: z0 :: Lc) (z2 :: Rc) := by
    rw [(hI S1).2.2, s1c, cellsTape_moveRight_headD]
    rfl
  have s2a2 : (fold3Step f a b c a2 b2 c2 S1) a2 =
      cellsTape La2 [TapeSymbol.bit (f (cellBool x0) (cellBool y0)
        (cellBool z0))] := by
    rw [p1a2, s1a2]
  have s2b2 : (fold3Step f a b c a2 b2 c2 S1) b2 =
      cellsTape Lb2 [TapeSymbol.bit (f (cellBool x1) (cellBool y1)
        (cellBool z1))] := by
    rw [p1b2, s1a, s1b, s1c, s1b2, write_cellsTape]
    rfl
  have s2c2 : (fold3Step f a b c a2 b2 c2 S1) c2 = cellsTape Lc2 [] := by
    rw [p1c2, s1c2]
  -- stride 3: phase 2
  set S2 := fold3Step f a b c a2 b2 c2 S1 with hS2
  have hph2a : (S2 a2).head ≠ TapeSymbol.blank := by
    rw [s2a2]; simp [cellsTape]
  have hph2b : (S2 b2).head ≠ TapeSymbol.blank := by
    rw [s2b2]; simp [cellsTape]
  obtain ⟨p2a2, p2b2, p2c2⟩ := fold3Step_phase2 f a b c a2 b2 c2 h2ab h2ac
    h2bc ha2a ha2b ha2c hb2a hb2b hb2c hc2a hc2b hc2c S2 hph2a hph2b
  have hit3 : ((fold3Step f a b c a2 b2 c2)^[3]) T =
      fold3Step f a b c a2 b2 c2 S2 := by
    rw [hS2, hS1]
    rw [show (3 : Nat) = 2 + 1 from rfl, Function.iterate_succ_apply',
      show (2 : Nat) = 1 + 1 from rfl, Function.iterate_succ_apply',
      Function.iterate_one]
  rw [hit3]
  refine ⟨?_, ?_, ?_, ?_, ?_, ?_⟩
  · rw [(hI S2).1, s2a, cellsTape_moveRight_headD]
    rfl
  · rw [(hI S2).2.1, s2b, cellsTape_moveRight_headD]
    rfl
  · rw [(hI S2).2.2, s2c, cellsTape_moveRight_headD]
    rfl
  · rw [p2a2, s2a2, cellsTape_moveRight_headD]
    rfl
  · rw [p2b2, s2b2, cellsTape_moveRight_headD]
    rfl
  · rw [p2c2, s2a, s2b, s2c, s2c2, write_cellsTape,
      cellsTape_moveRight_headD]
    rfl


/-- A list of length `3(n+1)` starts with a triple. -/
theorem triple_dest (l : List TapeSymbol) (n : Nat)
    (hl : l.length = 3 * (n + 1)) :
    ∃ u0 u1 u2 l', l = u0 :: u1 :: u2 :: l' ∧ l'.length = 3 * n := by
  rcases l with _ | ⟨u0, _ | ⟨u1, _ | ⟨u2, l'⟩⟩⟩
  · simp at hl
  · simp at hl; omega
  · simp at hl; omega
  · exact ⟨u0, u1, u2, l', rfl, by simp at hl; omega⟩

set_option maxHeartbeats 4000000 in
/-- **The cascade level, accumulated.**  Over `t` groups the inputs are
consumed and each output collects every third combined cell. -/
theorem fold3_iterate (f : Bool → Bool → Bool → Bool)
    (a b c a2 b2 c2 : Fin tapes)
    (hab : a ≠ b) (hac : a ≠ c) (hbc : b ≠ c)
    (h2ab : a2 ≠ b2) (h2ac : a2 ≠ c2) (h2bc : b2 ≠ c2)
    (ha2a : a2 ≠ a) (ha2b : a2 ≠ b) (ha2c : a2 ≠ c)
    (hb2a : b2 ≠ a) (hb2b : b2 ≠ b) (hb2c : b2 ≠ c)
    (hc2a : c2 ≠ a) (hc2b : c2 ≠ b) (hc2c : c2 ≠ c) :
    ∀ (t : Nat) (as bs cs : List TapeSymbol) (T : Fin tapes → Tape)
      (La Ra Lb Rb Lc Rc La2 Lb2 Lc2 : List TapeSymbol),
      as.length = 3 * t → bs.length = 3 * t → cs.length = 3 * t →
      T a = cellsTape La (as ++ Ra) → T b = cellsTape Lb (bs ++ Rb) →
      T c = cellsTape Lc (cs ++ Rc) →
      T a2 = cellsTape La2 [] → T b2 = cellsTape Lb2 [] →
      T c2 = cellsTape Lc2 [] →
      (((fold3Step f a b c a2 b2 c2)^[3 * t]) T) a =
        cellsTape (as.reverse ++ La) Ra ∧
      (((fold3Step f a b c a2 b2 c2)^[3 * t]) T) b =
        cellsTape (bs.reverse ++ Lb) Rb ∧
      (((fold3Step f a b c a2 b2 c2)^[3 * t]) T) c =
        cellsTape (cs.reverse ++ Lc) Rc ∧
      (((fold3Step f a b c a2 b2 c2)^[3 * t]) T) a2 =
        cellsTape ((third0 (foldCells f as bs cs)).reverse ++ La2) [] ∧
      (((fold3Step f a b c a2 b2 c2)^[3 * t]) T) b2 =
        cellsTape ((third1 (foldCells f as bs cs)).reverse ++ Lb2) [] ∧
      (((fold3Step f a b c a2 b2 c2)^[3 * t]) T) c2 =
        cellsTape ((third2 (foldCells f as bs cs)).reverse ++ Lc2) [] := by
  intro t
  induction t with
  | zero =>
      intro as bs cs T La Ra Lb Rb Lc Rc La2 Lb2 Lc2 hla hlb hlc ha hb hc
        ha2 hb2 hc2
      have hae : as = [] := List.length_eq_zero_iff.mp (by simpa using hla)
      have hbe : bs = [] := List.length_eq_zero_iff.mp (by simpa using hlb)
      have hce : cs = [] := List.length_eq_zero_iff.mp (by simpa using hlc)
      subst hae
      subst hbe
      subst hce
      simp only [List.nil_append] at ha hb hc
      simp only [Nat.mul_zero, Function.iterate_zero_apply, List.reverse_nil,
        List.nil_append, foldCells_nil, third0, third1, third2]
      exact ⟨ha, hb, hc, ha2, hb2, hc2⟩
  | succ t ih =>
      intro as bs cs T La Ra Lb Rb Lc Rc La2 Lb2 Lc2 hla hlb hlc ha hb hc
        ha2 hb2 hc2
      obtain ⟨x0, x1, x2, as', rfl, hla'⟩ := triple_dest as t hla
      obtain ⟨y0, y1, y2, bs', rfl, hlb'⟩ := triple_dest bs t hlb
      obtain ⟨z0, z1, z2, cs', rfl, hlc'⟩ := triple_dest cs t hlc
      obtain ⟨ga, gb, gc, ga2, gb2, gc2⟩ := fold3Group f a b c a2 b2 c2
        hab hac hbc h2ab h2ac h2bc ha2a ha2b ha2c hb2a hb2b hb2c hc2a hc2b
        hc2c T x0 x1 x2 y0 y1 y2 z0 z1 z2 La (as' ++ Ra) Lb (bs' ++ Rb)
        Lc (cs' ++ Rc) La2 Lb2 Lc2
        (by rw [ha]; rfl) (by rw [hb]; rfl) (by rw [hc]; rfl) ha2 hb2 hc2
      obtain ⟨ka, kb, kc, ka2, kb2, kc2⟩ := ih as' bs' cs'
        (((fold3Step f a b c a2 b2 c2)^[3]) T)
        (x2 :: x1 :: x0 :: La) Ra (y2 :: y1 :: y0 :: Lb) Rb
        (z2 :: z1 :: z0 :: Lc) Rc
        (TapeSymbol.bit (f (cellBool x0) (cellBool y0) (cellBool z0)) :: La2)
        (TapeSymbol.bit (f (cellBool x1) (cellBool y1) (cellBool z1)) :: Lb2)
        (TapeSymbol.bit (f (cellBool x2) (cellBool y2) (cellBool z2)) :: Lc2)
        hla' hlb' hlc' ga gb gc ga2 gb2 gc2
      have hsplit : ((fold3Step f a b c a2 b2 c2)^[3 * (t + 1)]) T =
          ((fold3Step f a b c a2 b2 c2)^[3 * t])
            (((fold3Step f a b c a2 b2 c2)^[3]) T) := by
        rw [show 3 * (t + 1) = 3 * t + 3 by ring,
          Function.iterate_add_apply]
      rw [hsplit]
      refine ⟨?_, ?_, ?_, ?_, ?_, ?_⟩
      · rw [ka]; simp
      · rw [kb]; simp
      · rw [kc]; simp
      · rw [ka2]
        show cellsTape _ _ = cellsTape _ _
        congr 1
        show (third0 (foldCells f as' bs' cs')).reverse ++ _ :: La2 = _
        rw [show foldCells f (x0 :: x1 :: x2 :: as') (y0 :: y1 :: y2 :: bs')
          (z0 :: z1 :: z2 :: cs') =
          TapeSymbol.bit (f (cellBool x0) (cellBool y0) (cellBool z0)) ::
          TapeSymbol.bit (f (cellBool x1) (cellBool y1) (cellBool z1)) ::
          TapeSymbol.bit (f (cellBool x2) (cellBool y2) (cellBool z2)) ::
          foldCells f as' bs' cs' from rfl]
        rw [show third0 (TapeSymbol.bit (f (cellBool x0) (cellBool y0)
          (cellBool z0)) ::
          TapeSymbol.bit (f (cellBool x1) (cellBool y1) (cellBool z1)) ::
          TapeSymbol.bit (f (cellBool x2) (cellBool y2) (cellBool z2)) ::
          foldCells f as' bs' cs') =
          TapeSymbol.bit (f (cellBool x0) (cellBool y0) (cellBool z0)) ::
          third0 (foldCells f as' bs' cs') from rfl]
        simp
      · rw [kb2]
        show cellsTape _ _ = cellsTape _ _
        congr 1
        show (third1 (foldCells f as' bs' cs')).reverse ++ _ :: Lb2 = _
        rw [show foldCells f (x0 :: x1 :: x2 :: as') (y0 :: y1 :: y2 :: bs')
          (z0 :: z1 :: z2 :: cs') =
          TapeSymbol.bit (f (cellBool x0) (cellBool y0) (cellBool z0)) ::
          TapeSymbol.bit (f (cellBool x1) (cellBool y1) (cellBool z1)) ::
          TapeSymbol.bit (f (cellBool x2) (cellBool y2) (cellBool z2)) ::
          foldCells f as' bs' cs' from rfl]
        rw [show third1 (TapeSymbol.bit (f (cellBool x0) (cellBool y0)
          (cellBool z0)) ::
          TapeSymbol.bit (f (cellBool x1) (cellBool y1) (cellBool z1)) ::
          TapeSymbol.bit (f (cellBool x2) (cellBool y2) (cellBool z2)) ::
          foldCells f as' bs' cs') =
          TapeSymbol.bit (f (cellBool x1) (cellBool y1) (cellBool z1)) ::
          third1 (foldCells f as' bs' cs') from rfl]
        simp
      · rw [kc2]
        show cellsTape _ _ = cellsTape _ _
        congr 1
        show (third2 (foldCells f as' bs' cs')).reverse ++ _ :: Lc2 = _
        rw [show foldCells f (x0 :: x1 :: x2 :: as') (y0 :: y1 :: y2 :: bs')
          (z0 :: z1 :: z2 :: cs') =
          TapeSymbol.bit (f (cellBool x0) (cellBool y0) (cellBool z0)) ::
          TapeSymbol.bit (f (cellBool x1) (cellBool y1) (cellBool z1)) ::
          TapeSymbol.bit (f (cellBool x2) (cellBool y2) (cellBool z2)) ::
          foldCells f as' bs' cs' from rfl]
        rw [show third2 (TapeSymbol.bit (f (cellBool x0) (cellBool y0)
          (cellBool z0)) ::
          TapeSymbol.bit (f (cellBool x1) (cellBool y1) (cellBool z1)) ::
          TapeSymbol.bit (f (cellBool x2) (cellBool y2) (cellBool z2)) ::
          foldCells f as' bs' cs') =
          TapeSymbol.bit (f (cellBool x2) (cellBool y2) (cellBool z2)) ::
          third2 (foldCells f as' bs' cs') from rfl]
        simp


/-- The first input's evolution is a plain rightward walk. -/
theorem fold3_a_iter (f : Bool → Bool → Bool → Bool)
    (a b c a2 b2 c2 : Fin tapes)
    (hab : a ≠ b) (hac : a ≠ c) (hbc : b ≠ c)
    (ha2a : a2 ≠ a) (ha2b : a2 ≠ b) (ha2c : a2 ≠ c)
    (hb2a : b2 ≠ a) (hb2b : b2 ≠ b) (hb2c : b2 ≠ c)
    (hc2a : c2 ≠ a) (hc2b : c2 ≠ b) (hc2c : c2 ≠ c) :
    ∀ (r : Nat) (T : Fin tapes → Tape),
      (((fold3Step f a b c a2 b2 c2)^[r]) T) a =
        ((Tape.move · HeadMove.right)^[r]) (T a) := by
  intro r
  induction r with
  | zero => intro T; rfl
  | succ r ih =>
      intro T
      rw [Function.iterate_succ_apply, Function.iterate_succ_apply,
        ih (fold3Step f a b c a2 b2 c2 T),
        (fold3Step_inputs f a b c a2 b2 c2 hab hac hbc ha2a ha2b ha2c
          hb2a hb2b hb2c hc2a hc2b hc2c T).1]
      rfl

/-- The marks hypothesis: the first input reads non-blank until its run
ends, and blank at its end. -/
theorem fold3_marks (f : Bool → Bool → Bool → Bool)
    (a b c a2 b2 c2 : Fin tapes)
    (hab : a ≠ b) (hac : a ≠ c) (hbc : b ≠ c)
    (ha2a : a2 ≠ a) (ha2b : a2 ≠ b) (ha2c : a2 ≠ c)
    (hb2a : b2 ≠ a) (hb2b : b2 ≠ b) (hb2c : b2 ≠ c)
    (hc2a : c2 ≠ a) (hc2b : c2 ≠ b) (hc2c : c2 ≠ c)
    (cs La Ra : List TapeSymbol) (T : Fin tapes → Tape)
    (hnb : ∀ x ∈ cs, x ≠ TapeSymbol.blank)
    (ha : T a = cellsTape La (cs ++ TapeSymbol.blank :: Ra)) :
    (∀ r, r < cs.length →
      ((((fold3Step f a b c a2 b2 c2)^[r]) T) a).head ≠
        TapeSymbol.blank) ∧
    ((((fold3Step f a b c a2 b2 c2)^[cs.length]) T) a).head =
      TapeSymbol.blank := by
  have hev := fold3_a_iter f a b c a2 b2 c2 hab hac hbc ha2a ha2b ha2c
    hb2a hb2b hb2c hc2a hc2b hc2c
  constructor
  · intro r hr
    rw [hev r T, ha]
    obtain ⟨L', hL'⟩ := moveRight_iterate_exists r La
      (cs ++ TapeSymbol.blank :: Ra)
    rw [hL']
    rw [List.drop_append_of_le_length (by omega)]
    have hlen : (cs.drop r).length = cs.length - r := List.length_drop
    cases hcons : cs.drop r with
    | nil => rw [hcons] at hlen; simp at hlen; omega
    | cons hd tl =>
        show ((hd :: tl ++ TapeSymbol.blank :: Ra).headD
          TapeSymbol.blank) ≠ TapeSymbol.blank
        have hmem : hd ∈ cs := by
          have : hd ∈ cs.drop r := by rw [hcons]; simp
          exact List.mem_of_mem_drop this
        exact hnb hd hmem
  · rw [hev cs.length T, ha]
    obtain ⟨L', hL'⟩ := moveRight_iterate_exists cs.length La
      (cs ++ TapeSymbol.blank :: Ra)
    rw [hL']
    rw [List.drop_append_of_le_length (by omega)]
    rw [List.drop_length]
    rfl


/-- Lengths of the three residue classes, on triple-length lists. -/
theorem third_lengths : ∀ (t : Nat) (l : List TapeSymbol),
    l.length = 3 * t →
    (third0 l).length = t ∧ (third1 l).length = t ∧
      (third2 l).length = t := by
  intro t
  induction t with
  | zero =>
      intro l hl
      have : l = [] := List.length_eq_zero_iff.mp (by simpa using hl)
      subst this
      exact ⟨rfl, rfl, rfl⟩
  | succ t ih =>
      intro l hl
      obtain ⟨u0, u1, u2, l', rfl, hl'⟩ := triple_dest l t hl
      obtain ⟨h0, h1, h2⟩ := ih l' hl'
      refine ⟨?_, ?_, ?_⟩
      · show (u0 :: third0 l').length = t + 1
        simp [h0]
      · show (u1 :: third1 l').length = t + 1
        simp [h1]
      · show (u2 :: third2 l').length = t + 1
        simp [h2]

/-- Residue-class members come from the list. -/
theorem third_mem : ∀ (l : List TapeSymbol) (x : TapeSymbol),
    (x ∈ third0 l → x ∈ l) ∧ (x ∈ third1 l → x ∈ l) ∧
      (x ∈ third2 l → x ∈ l) := by
  intro l
  induction l using third0.induct with
  | case1 a d e r ih =>
      intro x
      obtain ⟨i0, i1, i2⟩ := ih x
      refine ⟨?_, ?_, ?_⟩
      · intro hx
        rcases List.mem_cons.mp hx with h | h
        · simp [h]
        · have := i0 h
          simp [this]
      · intro hx
        rcases List.mem_cons.mp hx with h | h
        · simp [h]
        · have := i1 h
          simp [this]
      · intro hx
        rcases List.mem_cons.mp hx with h | h
        · simp [h]
        · have := i2 h
          simp [this]
  | case2 a d =>
      intro x
      refine ⟨?_, ?_, ?_⟩
      · intro hx
        rcases List.mem_cons.mp hx with h | h
        · simp [h]
        · simp at h
      · intro hx
        rcases List.mem_cons.mp hx with h | h
        · simp [h]
        · simp at h
      · intro hx
        simp [third2] at hx
  | case3 a =>
      intro x
      refine ⟨?_, ?_, ?_⟩
      · intro hx
        rcases List.mem_cons.mp hx with h | h
        · simp [h]
        · simp at h
      · intro hx
        simp [third1] at hx
      · intro hx
        simp [third2] at hx
  | case4 =>
      intro x
      refine ⟨?_, ?_, ?_⟩ <;> intro hx <;> simp [third0, third1, third2] at hx

/-- Every combined cell is a bit. -/
theorem foldCells_nonblank (f : Bool → Bool → Bool → Bool) :
    ∀ (cs bs ds : List TapeSymbol), ∀ x ∈ foldCells f cs bs ds,
      x ≠ TapeSymbol.blank := by
  intro cs
  induction cs with
  | nil => intro bs ds x hx; simp [foldCells] at hx
  | cons c cs ih =>
      intro bs ds x hx
      rw [show foldCells f (c :: cs) bs ds =
        TapeSymbol.bit (f (cellBool c) (cellBool (bs.headD TapeSymbol.blank))
          (cellBool (ds.headD TapeSymbol.blank))) ::
          foldCells f cs bs.tail ds.tail from rfl] at hx
      rcases List.mem_cons.mp hx with h | h
      · rw [h]; simp
      · exact ih bs.tail ds.tail x h

end SipserGacsLautemann

View graph

Get started

Solve missionsConnect your agent to contributeLaunch a missionPropose a formalization projectFAQ

About Prove2Me

Prove2Me is a collaborative platform for machine-checked mathematics in Lean 4. Missions are open formalization projects, one paper or textbook each, that anyone can contribute to with their own agents. Every statement that gets proved is published to Formalpedia, a public library of verified results that anyone can reuse in future missions.

How Prove2Me works
SKILL.mdTourFAQContactJoin Slack© 2026 Prove2Me