Imports
/- Copyright (c) 2026 Terence Rokop. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Terence Rokop -/ module public import Geb.Prototypes.Computability.BitTree.BinaryMachine.Execution public import Cslib.Computability.Machines.Turing.MultiTape.TapeLemmas
set_option doc.verso true

Linear-time recognition with logarithmic work space

The binary-counter machine decides the unified tree-and-payload grammar using linear time and work space bounded by a constant multiple of the input length's binary size. Each input bit is consumed once. Carry and return steps leave the input head stationary.

Main statements

  • computableInTimeAndSpace_validBool gives explicit simultaneous time and space bounds in CSLib's finite-state, finite-alphabet machine model.

Implementation notes

The bound counts every visited work-tape cell, including blank cells. The read-only input tape is excluded by CSLib's space definition. Binary size is Nat.­size, the number of digits in the binary representation.

Tags

binary tree, bitstring, Turing machine, linear time, logarithmic space

@[expose] public sectionnamespace Geb.BitTree.BinaryMachineopen Turing MultiTapeTM

After all input macros, one final transition halts the machine.

theorem halts_at (w : List Bool) : (machine.configs (machine.initCfg (w.map boolEmb)) (2 + runCost w + 1)).state = none := w:List Bool(configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w + 1)).state = none w:List Boolh:have cost := 2 + runCost (List.take w.length w); have now := configs (initCfg (List.map (⇑boolEmb) w)) cost; have s := account (List.take w.length w); Represents (w.length + 2).size now (modeState s.mode) s.forks s.leaves now.inputPos = w.length + 1 machine.outputString (initCfg (List.map (⇑boolEmb) w)) cost = [] u cost, HeadBound (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) u)(configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w + 1)).state = none w:List Boolh:Represents (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w)) (modeState (account w).mode) (account w).forks (account w).leaves (configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w)).inputPos = w.length + 1 machine.outputString (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w) = [] u 2 + runCost w, HeadBound (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) u)(configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w + 1)).state = none w:List Boolhr:Represents (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w)) (modeState (account w).mode) (account w).forks (account w).leaveshp:(configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w)).inputPos = w.length + 1left✝:machine.outputString (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w) = []right✝: u 2 + runCost w, HeadBound (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) u)(configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w + 1)).state = none w:List Boolhr:Represents (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w)) (modeState (account w).mode) (account w).forks (account w).leaveshp:(configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w)).inputPos = w.length + 1left✝:machine.outputString (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w) = []right✝: u 2 + runCost w, HeadBound (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) u)hi:(configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w)).inputSymbol = none(configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w + 1)).state = none All goals completed! 🐙

The sole emitted bit is the decision of the unified scanner.

theorem outputString_eq (w : List Bool) : machine.outputString (machine.initCfg (w.map boolEmb)) (2 + runCost w + 1) = [boolEmb (validBool w)] := w:List Boolmachine.outputString (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w + 1) = [boolEmb (validBool w)] w:List Boolh:have cost := 2 + runCost (List.take w.length w); have now := configs (initCfg (List.map (⇑boolEmb) w)) cost; have s := account (List.take w.length w); Represents (w.length + 2).size now (modeState s.mode) s.forks s.leaves now.inputPos = w.length + 1 machine.outputString (initCfg (List.map (⇑boolEmb) w)) cost = [] u cost, HeadBound (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) u)machine.outputString (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w + 1) = [boolEmb (validBool w)] w:List Boolh:Represents (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w)) (modeState (account w).mode) (account w).forks (account w).leaves (configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w)).inputPos = w.length + 1 machine.outputString (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w) = [] u 2 + runCost w, HeadBound (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) u)machine.outputString (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w + 1) = [boolEmb (validBool w)] w:List Boolhr:Represents (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w)) (modeState (account w).mode) (account w).forks (account w).leaveshp:(configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w)).inputPos = w.length + 1ho:machine.outputString (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w) = []right✝: u 2 + runCost w, HeadBound (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) u)machine.outputString (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w + 1) = [boolEmb (validBool w)] w:List Boolhr:Represents (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w)) (modeState (account w).mode) (account w).forks (account w).leaveshp:(configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w)).inputPos = w.length + 1ho:machine.outputString (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w) = []right✝: u 2 + runCost w, HeadBound (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) u)hi:(configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w)).inputSymbol = nonemachine.outputString (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w + 1) = [boolEmb (validBool w)] w:List Boolhr:Represents (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w)) (modeState (account w).mode) (account w).forks (account w).leaveshp:(configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w)).inputPos = w.length + 1ho:machine.outputString (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w) = []right✝: u 2 + runCost w, HeadBound (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) u)hi:(configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w)).inputSymbol = nonehm:(account w).mode = (scan w).1machine.outputString (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w + 1) = [boolEmb (validBool w)] w:List Boolhr:Represents (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w)) (modeState (account w).mode) (account w).forks (account w).leaveshp:(configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w)).inputPos = w.length + 1ho:machine.outputString (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w) = []right✝: u 2 + runCost w, HeadBound (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) u)hi:(configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w)).inputSymbol = nonehm:(account w).mode = (scan w).1[] ++ (some (boolEmb (decide ((scan w).1 = Mode.done)))).toList = [boolEmb (validBool w)] All goals completed! 🐙

A bounded interval for every work head bounds the total visited work space.

theorem spaceUsed_le_of_headBound {input : List (Fin 4)} (cfg : Cfg 3 (Fin 4) (Fin 10) input) (width t : ) (h : u t, HeadBound width (machine.configs cfg u)) : machine.spaceUsed cfg t 3 * (width + 1) := input:List (Fin 4)cfg:Cfg 3 (Fin 4) (Fin 10) inputwidth:t:h: u t, HeadBound width (configs cfg u)spaceUsed cfg t 3 * (width + 1) input:List (Fin 4)cfg:Cfg 3 (Fin 4) (Fin 10) inputwidth:t:h: u t, HeadBound width (configs cfg u)hsub: (i : Fin 3), visitedByTapeHead cfg t i Finset.image (fun n n) (Finset.range (width + 1))spaceUsed cfg t 3 * (width + 1) input:List (Fin 4)cfg:Cfg 3 (Fin 4) (Fin 10) inputwidth:t:h: u t, HeadBound width (configs cfg u)hsub: (i : Fin 3), visitedByTapeHead cfg t i Finset.image (fun n n) (Finset.range (width + 1))hi: (i : Fin 3), spaceUsedByTape cfg t i width + 1spaceUsed cfg t 3 * (width + 1) calc machine.spaceUsed cfg t _i : Fin 3, (width + 1) := Finset.sum_le_sum (fun i _ hi i) _ = 3 * (width + 1) := input:List (Fin 4)cfg:Cfg 3 (Fin 4) (Fin 10) inputwidth:t:h: u t, HeadBound width (configs cfg u)hsub: (i : Fin 3), visitedByTapeHead cfg t i Finset.image (fun n n) (Finset.range (width + 1))hi: (i : Fin 3), spaceUsedByTape cfg t i width + 1 _i, (width + 1) = 3 * (width + 1) All goals completed! 🐙

All work-head positions remain within the chosen binary width, including the halt step.

theorem headBound_run (w : List Bool) : u 2 + runCost w + 1, HeadBound (w.length + 2).size (machine.configs (machine.initCfg (w.map boolEmb)) u) := w:List Bool u 2 + runCost w + 1, HeadBound (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) u) w:List Boolh:have cost := 2 + runCost (List.take w.length w); have now := configs (initCfg (List.map (⇑boolEmb) w)) cost; have s := account (List.take w.length w); Represents (w.length + 2).size now (modeState s.mode) s.forks s.leaves now.inputPos = w.length + 1 machine.outputString (initCfg (List.map (⇑boolEmb) w)) cost = [] u cost, HeadBound (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) u) u 2 + runCost w + 1, HeadBound (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) u) w:List Boolh:Represents (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w)) (modeState (account w).mode) (account w).forks (account w).leaves (configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w)).inputPos = w.length + 1 machine.outputString (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w) = [] u 2 + runCost w, HeadBound (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) u) u 2 + runCost w + 1, HeadBound (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) u) w:List Boolhr:Represents (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w)) (modeState (account w).mode) (account w).forks (account w).leaveshp:(configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w)).inputPos = w.length + 1left✝:machine.outputString (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w) = []hh: u 2 + runCost w, HeadBound (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) u) u 2 + runCost w + 1, HeadBound (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) u) w:List Boolhr:Represents (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w)) (modeState (account w).mode) (account w).forks (account w).leaveshp:(configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w)).inputPos = w.length + 1left✝:machine.outputString (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w) = []hh: u 2 + runCost w, HeadBound (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) u)u:hu:u 2 + runCost w + 1HeadBound (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) u) w:List Boolhr:Represents (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w)) (modeState (account w).mode) (account w).forks (account w).leaveshp:(configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w)).inputPos = w.length + 1left✝:machine.outputString (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w) = []hh: u 2 + runCost w, HeadBound (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) u)u:hu:u 2 + runCost w + 1he:u 2 + runCost wHeadBound (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) u)w:List Boolhr:Represents (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w)) (modeState (account w).mode) (account w).forks (account w).leaveshp:(configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w)).inputPos = w.length + 1left✝:machine.outputString (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w) = []hh: u 2 + runCost w, HeadBound (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) u)u:hu:u 2 + runCost w + 1he:¬u 2 + runCost wHeadBound (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) u) w:List Boolhr:Represents (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w)) (modeState (account w).mode) (account w).forks (account w).leaveshp:(configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w)).inputPos = w.length + 1left✝:machine.outputString (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w) = []hh: u 2 + runCost w, HeadBound (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) u)u:hu:u 2 + runCost w + 1he:u 2 + runCost wHeadBound (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) u) All goals completed! 🐙 w:List Boolhr:Represents (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w)) (modeState (account w).mode) (account w).forks (account w).leaveshp:(configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w)).inputPos = w.length + 1left✝:machine.outputString (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w) = []hh: u 2 + runCost w, HeadBound (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) u)u:hu:u 2 + runCost w + 1he:¬u 2 + runCost wHeadBound (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) u) w:List Boolhr:Represents (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w)) (modeState (account w).mode) (account w).forks (account w).leaveshp:(configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w)).inputPos = w.length + 1left✝:machine.outputString (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w) = []hh: u 2 + runCost w, HeadBound (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) u)u:hu:u 2 + runCost w + 1he:¬u 2 + runCost whu':u = 2 + runCost w + 1HeadBound (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) u) w:List Boolhr:Represents (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w)) (modeState (account w).mode) (account w).forks (account w).leaveshp:(configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w)).inputPos = w.length + 1left✝:machine.outputString (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w) = []hh: u 2 + runCost w, HeadBound (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) u)hu:2 + runCost w + 1 2 + runCost w + 1he:¬2 + runCost w + 1 2 + runCost wHeadBound (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w + 1)) w:List Boolhr:Represents (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w)) (modeState (account w).mode) (account w).forks (account w).leaveshp:(configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w)).inputPos = w.length + 1left✝:machine.outputString (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w) = []hh: u 2 + runCost w, HeadBound (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) u)hu:2 + runCost w + 1 2 + runCost w + 1he:¬2 + runCost w + 1 2 + runCost whi:(configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w)).inputSymbol = noneHeadBound (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w + 1)) w:List Boolhr:Represents (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w)) (modeState (account w).mode) (account w).forks (account w).leaveshp:(configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w)).inputPos = w.length + 1left✝:machine.outputString (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w) = []hh: u 2 + runCost w, HeadBound (w.length + 2).size (configs (initCfg (List.map (⇑boolEmb) w)) u)hu:2 + runCost w + 1 2 + runCost w + 1he:¬2 + runCost w + 1 2 + runCost whi:(configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w)).inputSymbol = noneHeadBound (w.length + 2).size { state := none, inputPos := (configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w)).inputPos, workTapes := (configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w)).workTapes, workTapePos := (configs (initCfg (List.map (⇑boolEmb) w)) (2 + runCost w)).workTapePos } All goals completed! 🐙

Recognition takes linear time and logarithmic work space simultaneously.

theorem computableInTimeAndSpace_validBool : ComputableInTimeAndSpace (fun w : List Bool [validBool w]) (fun n 6 * n + 5) (fun n 3 * ((n + 2).size + 1)) := ComputableInTimeAndSpace (fun w [validBool w]) (fun n 6 * n + 5) fun n 3 * ((n + 2).size + 1) w:List Bool t' (fun n 6 * n + 5) w.length, s' (fun n 3 * ((n + 2).size + 1)) w.length, machine.ComputesInTimeAndSpace (List.map (⇑boolEmb) w) (List.map (⇑boolEmb) ((fun w [validBool w]) w)) t' s' w:List Bool2 + runCost w + 1 (fun n 6 * n + 5) w.length w:List Bool2 + runCost w + 1 6 * w.length + 5 w:List Boolh:runCost w 6 * w.length + 22 + runCost w + 1 6 * w.length + 5 All goals completed! 🐙
end Geb.BitTree.BinaryMachine