# mathlibdocumentation

computability.turing_machine

# Turing machines #

This file defines a sequence of simple machine languages, starting with Turing machines and working up to more complex languages based on Wang B-machines.

## Naming conventions #

Each model of computation in this file shares a naming convention for the elements of a model of computation. These are the parameters for the language:

• `Γ` is the alphabet on the tape.
• `Λ` is the set of labels, or internal machine states.
• `σ` is the type of internal memory, not on the tape. This does not exist in the TM0 model, and later models achieve this by mixing it into `Λ`.
• `K` is used in the TM2 model, which has multiple stacks, and denotes the number of such stacks.

All of these variables denote "essentially finite" types, but for technical reasons it is convenient to allow them to be infinite anyway. When using an infinite type, we will be interested to prove that only finitely many values of the type are ever interacted with.

Given these parameters, there are a few common structures for the model that arise:

• `stmt` is the set of all actions that can be performed in one step. For the TM0 model this set is finite, and for later models it is an infinite inductive type representing "possible program texts".
• `cfg` is the set of instantaneous configurations, that is, the state of the machine together with its environment.
• `machine` is the set of all machines in the model. Usually this is approximately a function `Λ → stmt`, although different models have different ways of halting and other actions.
• `step : cfg → option cfg` is the function that describes how the state evolves over one step. If `step c = none`, then `c` is a terminal state, and the result of the computation is read off from `c`. Because of the type of `step`, these models are all deterministic by construction.
• `init : input → cfg` sets up the initial state. The type `input` depends on the model; in most cases it is `list Γ`.
• `eval : machine → input → part output`, given a machine `M` and input `i`, starts from `init i`, runs `step` until it reaches an output, and then applies a function `cfg → output` to the final state to obtain the result. The type `output` depends on the model.
• `supports : machine → finset Λ → Prop` asserts that a machine `M` starts in `S : finset Λ`, and can only ever jump to other states inside `S`. This implies that the behavior of `M` on any input cannot depend on its values outside `S`. We use this to allow `Λ` to be an infinite set when convenient, and prove that only finitely many of these states are actually accessible. This formalizes "essentially finite" mentioned above.
def turing.blank_extends {Γ : Type u_1} [inhabited Γ] (l₁ l₂ : list Γ) :
Prop

The `blank_extends` partial order holds of `l₁` and `l₂` if `l₂` is obtained by adding blanks (`default : Γ`) to the end of `l₁`.

Equations
• l₂ = ∃ (n : ), l₂ =
@[refl]
theorem turing.blank_extends.refl {Γ : Type u_1} [inhabited Γ] (l : list Γ) :
@[trans]
theorem turing.blank_extends.trans {Γ : Type u_1} [inhabited Γ] {l₁ l₂ l₃ : list Γ} :
l₂ l₃ l₃
theorem turing.blank_extends.below_of_le {Γ : Type u_1} [inhabited Γ] {l l₁ l₂ : list Γ} :
l₁.length l₂.length l₂
def turing.blank_extends.above {Γ : Type u_1} [inhabited Γ] {l l₁ l₂ : list Γ} (h₁ : l₁) (h₂ : l₂) :
{l' // l' l'}

Any two extensions by blank `l₁,l₂` of `l` have a common join (which can be taken to be the longer of `l₁` and `l₂`).

Equations
theorem turing.blank_extends.above_of_le {Γ : Type u_1} [inhabited Γ] {l l₁ l₂ : list Γ} :
l₁.length l₂.length l₂
def turing.blank_rel {Γ : Type u_1} [inhabited Γ] (l₁ l₂ : list Γ) :
Prop

`blank_rel` is the symmetric closure of `blank_extends`, turning it into an equivalence relation. Two lists are related by `blank_rel` if one extends the other by blanks.

Equations
• l₂ = l₂ l₁)
@[refl]
theorem turing.blank_rel.refl {Γ : Type u_1} [inhabited Γ] (l : list Γ) :
@[symm]
theorem turing.blank_rel.symm {Γ : Type u_1} [inhabited Γ] {l₁ l₂ : list Γ} :
l₂ l₁
@[trans]
theorem turing.blank_rel.trans {Γ : Type u_1} [inhabited Γ] {l₁ l₂ l₃ : list Γ} :
l₂ l₃ l₃
def turing.blank_rel.above {Γ : Type u_1} [inhabited Γ] {l₁ l₂ : list Γ} (h : l₂) :
{l // l}

Given two `blank_rel` lists, there exists (constructively) a common join.

Equations
def turing.blank_rel.below {Γ : Type u_1} [inhabited Γ] {l₁ l₂ : list Γ} (h : l₂) :
{l // l₂}

Given two `blank_rel` lists, there exists (constructively) a common meet.

Equations
theorem turing.blank_rel.equivalence (Γ : Type u_1) [inhabited Γ] :
def turing.blank_rel.setoid (Γ : Type u_1) [inhabited Γ] :

Construct a setoid instance for `blank_rel`.

Equations
def turing.list_blank (Γ : Type u_1) [inhabited Γ] :
Type u_1

A `list_blank Γ` is a quotient of `list Γ` by extension by blanks at the end. This is used to represent half-tapes of a Turing machine, so that we can pretend that the list continues infinitely with blanks.

Equations
Instances for `turing.list_blank`
@[protected, instance]
def turing.list_blank.inhabited {Γ : Type u_1} [inhabited Γ] :
Equations
@[protected, instance]
def turing.list_blank.has_emptyc {Γ : Type u_1} [inhabited Γ] :
Equations
@[protected, reducible]
def turing.list_blank.lift_on {Γ : Type u_1} [inhabited Γ] {α : Sort u_2} (l : turing.list_blank Γ) (f : list Γ → α) (H : ∀ (a b : list Γ), f a = f b) :
α

A modified version of `quotient.lift_on'` specialized for `list_blank`, with the stronger precondition `blank_extends` instead of `blank_rel`.

Equations
def turing.list_blank.mk {Γ : Type u_1} [inhabited Γ] :

The quotient map turning a `list` into a `list_blank`.

Equations
@[protected]
theorem turing.list_blank.induction_on {Γ : Type u_1} [inhabited Γ] {p : → Prop} (q : turing.list_blank Γ) (h : ∀ (a : list Γ), p ) :
p q
def turing.list_blank.head {Γ : Type u_1} [inhabited Γ] (l : turing.list_blank Γ) :
Γ

The head of a `list_blank` is well defined.

Equations
@[simp]
theorem turing.list_blank.head_mk {Γ : Type u_1} [inhabited Γ] (l : list Γ) :
def turing.list_blank.tail {Γ : Type u_1} [inhabited Γ] (l : turing.list_blank Γ) :

The tail of a `list_blank` is well defined (up to the tail of blanks).

Equations
@[simp]
theorem turing.list_blank.tail_mk {Γ : Type u_1} [inhabited Γ] (l : list Γ) :
def turing.list_blank.cons {Γ : Type u_1} [inhabited Γ] (a : Γ) (l : turing.list_blank Γ) :

We can cons an element onto a `list_blank`.

Equations
@[simp]
theorem turing.list_blank.cons_mk {Γ : Type u_1} [inhabited Γ] (a : Γ) (l : list Γ) :
@[simp]
theorem turing.list_blank.head_cons {Γ : Type u_1} [inhabited Γ] (a : Γ) (l : turing.list_blank Γ) :
@[simp]
theorem turing.list_blank.tail_cons {Γ : Type u_1} [inhabited Γ] (a : Γ) (l : turing.list_blank Γ) :
.tail = l
@[simp]
theorem turing.list_blank.cons_head_tail {Γ : Type u_1} [inhabited Γ] (l : turing.list_blank Γ) :

The `cons` and `head`/`tail` functions are mutually inverse, unlike in the case of `list` where this only holds for nonempty lists.

theorem turing.list_blank.exists_cons {Γ : Type u_1} [inhabited Γ] (l : turing.list_blank Γ) :
∃ (a : Γ) (l' : , l =

The `cons` and `head`/`tail` functions are mutually inverse, unlike in the case of `list` where this only holds for nonempty lists.

def turing.list_blank.nth {Γ : Type u_1} [inhabited Γ] (l : turing.list_blank Γ) (n : ) :
Γ

The n-th element of a `list_blank` is well defined for all `n : ℕ`, unlike in a `list`.

Equations
@[simp]
theorem turing.list_blank.nth_mk {Γ : Type u_1} [inhabited Γ] (l : list Γ) (n : ) :
n = l.inth n
@[simp]
theorem turing.list_blank.nth_zero {Γ : Type u_1} [inhabited Γ] (l : turing.list_blank Γ) :
@[simp]
theorem turing.list_blank.nth_succ {Γ : Type u_1} [inhabited Γ] (l : turing.list_blank Γ) (n : ) :
l.nth (n + 1) = l.tail.nth n
@[ext]
theorem turing.list_blank.ext {Γ : Type u_1} [inhabited Γ] {L₁ L₂ : turing.list_blank Γ} :
(∀ (i : ), L₁.nth i = L₂.nth i)L₁ = L₂
@[simp]
def turing.list_blank.modify_nth {Γ : Type u_1} [inhabited Γ] (f : Γ → Γ) :

Apply a function to a value stored at the nth position of the list.

Equations
theorem turing.list_blank.nth_modify_nth {Γ : Type u_1} [inhabited Γ] (f : Γ → Γ) (n i : ) (L : turing.list_blank Γ) :
L).nth i = ite (i = n) (f (L.nth i)) (L.nth i)
structure turing.pointed_map (Γ : Type u) (Γ' : Type v) [inhabited Γ] [inhabited Γ'] :
Type (max u v)
• f : Γ → Γ'
• map_pt' :

A pointed map of `inhabited` types is a map that sends one default value to the other.

Instances for `turing.pointed_map`
@[protected, instance]
def turing.pointed_map.inhabited {Γ : Type u_1} {Γ' : Type u_2} [inhabited Γ] [inhabited Γ'] :
Equations
@[protected, instance]
def turing.pointed_map.has_coe_to_fun {Γ : Type u_1} {Γ' : Type u_2} [inhabited Γ] [inhabited Γ'] :
(λ (_x : Γ'), Γ → Γ')
Equations
@[simp]
theorem turing.pointed_map.mk_val {Γ : Type u_1} {Γ' : Type u_2} [inhabited Γ] [inhabited Γ'] (f : Γ → Γ')  :
{f := f, map_pt' := pt} = f
@[simp]
theorem turing.pointed_map.map_pt {Γ : Type u_1} {Γ' : Type u_2} [inhabited Γ] [inhabited Γ'] (f : Γ') :
@[simp]
theorem turing.pointed_map.head_map {Γ : Type u_1} {Γ' : Type u_2} [inhabited Γ] [inhabited Γ'] (f : Γ') (l : list Γ) :
def turing.list_blank.map {Γ : Type u_1} {Γ' : Type u_2} [inhabited Γ] [inhabited Γ'] (f : Γ') (l : turing.list_blank Γ) :

The `map` function on lists is well defined on `list_blank`s provided that the map is pointed.

Equations
@[simp]
theorem turing.list_blank.map_mk {Γ : Type u_1} {Γ' : Type u_2} [inhabited Γ] [inhabited Γ'] (f : Γ') (l : list Γ) :
@[simp]
theorem turing.list_blank.head_map {Γ : Type u_1} {Γ' : Type u_2} [inhabited Γ] [inhabited Γ'] (f : Γ') (l : turing.list_blank Γ) :
@[simp]
theorem turing.list_blank.tail_map {Γ : Type u_1} {Γ' : Type u_2} [inhabited Γ] [inhabited Γ'] (f : Γ') (l : turing.list_blank Γ) :
@[simp]
theorem turing.list_blank.map_cons {Γ : Type u_1} {Γ' : Type u_2} [inhabited Γ] [inhabited Γ'] (f : Γ') (l : turing.list_blank Γ) (a : Γ) :
=
@[simp]
theorem turing.list_blank.nth_map {Γ : Type u_1} {Γ' : Type u_2} [inhabited Γ] [inhabited Γ'] (f : Γ') (l : turing.list_blank Γ) (n : ) :
.nth n = f (l.nth n)
def turing.proj {ι : Type u_1} {Γ : ι → Type u_2} [Π (i : ι), inhabited (Γ i)] (i : ι) :
turing.pointed_map (Π (i : ι), Γ i) (Γ i)

The `i`-th projection as a pointed map.

Equations
• = {f := λ (a : Π (i : ι), Γ i), a i, map_pt' := _}
theorem turing.proj_map_nth {ι : Type u_1} {Γ : ι → Type u_2} [Π (i : ι), inhabited (Γ i)] (i : ι) (L : turing.list_blank (Π (i : ι), Γ i)) (n : ) :
L).nth n = L.nth n i
theorem turing.list_blank.map_modify_nth {Γ : Type u_1} {Γ' : Type u_2} [inhabited Γ] [inhabited Γ'] (F : Γ') (f : Γ → Γ) (f' : Γ' → Γ') (H : ∀ (x : Γ), F (f x) = f' (F x)) (n : ) (L : turing.list_blank Γ) :
@[simp]
def turing.list_blank.append {Γ : Type u_1} [inhabited Γ] :
list Γ

Append a list on the left side of a list_blank.

Equations
@[simp]
theorem turing.list_blank.append_mk {Γ : Type u_1} [inhabited Γ] (l₁ l₂ : list Γ) :
theorem turing.list_blank.append_assoc {Γ : Type u_1} [inhabited Γ] (l₁ l₂ : list Γ) (l₃ : turing.list_blank Γ) :
turing.list_blank.append (l₁ ++ l₂) l₃ =
def turing.list_blank.bind {Γ : Type u_1} {Γ' : Type u_2} [inhabited Γ] [inhabited Γ'] (l : turing.list_blank Γ) (f : Γ → list Γ') (hf : ∃ (n : ), ) :

The `bind` function on lists is well defined on `list_blank`s provided that the default element is sent to a sequence of default elements.

Equations
@[simp]
theorem turing.list_blank.bind_mk {Γ : Type u_1} {Γ' : Type u_2} [inhabited Γ] [inhabited Γ'] (l : list Γ) (f : Γ → list Γ') (hf : ∃ (n : ), ) :
@[simp]
theorem turing.list_blank.cons_bind {Γ : Type u_1} {Γ' : Type u_2} [inhabited Γ] [inhabited Γ'] (a : Γ) (l : turing.list_blank Γ) (f : Γ → list Γ') (hf : ∃ (n : ), ) :
.bind f hf = (l.bind f hf)
structure turing.tape (Γ : Type u_1) [inhabited Γ] :
Type u_1
• left :
• right :

The tape of a Turing machine is composed of a head element (which we imagine to be the current position of the head), together with two `list_blank`s denoting the portions of the tape going off to the left and right. When the Turing machine moves right, an element is pulled from the right side and becomes the new head, while the head element is consed onto the left side.

Instances for `turing.tape`
@[protected, instance]
def turing.tape.inhabited {Γ : Type u_1} [inhabited Γ] :
Equations
@[protected, instance]
@[protected, instance]
inductive turing.dir  :
Type

A direction for the turing machine `move` command, either left or right.

Instances for `turing.dir`
def turing.tape.left₀ {Γ : Type u_1} [inhabited Γ] (T : turing.tape Γ) :

The "inclusive" left side of the tape, including both `left` and `head`.

Equations
def turing.tape.right₀ {Γ : Type u_1} [inhabited Γ] (T : turing.tape Γ) :

The "inclusive" right side of the tape, including both `right` and `head`.

Equations
def turing.tape.move {Γ : Type u_1} [inhabited Γ] :

Move the tape in response to a motion of the Turing machine. Note that `T.move dir.left` makes `T.left` smaller; the Turing machine is moving left and the tape is moving right.

Equations
@[simp]
theorem turing.tape.move_left_right {Γ : Type u_1} [inhabited Γ] (T : turing.tape Γ) :
@[simp]
theorem turing.tape.move_right_left {Γ : Type u_1} [inhabited Γ] (T : turing.tape Γ) :
def turing.tape.mk' {Γ : Type u_1} [inhabited Γ] (L R : turing.list_blank Γ) :

Construct a tape from a left side and an inclusive right side.

Equations
@[simp]
theorem turing.tape.mk'_left {Γ : Type u_1} [inhabited Γ] (L R : turing.list_blank Γ) :
R).left = L
@[simp]
theorem turing.tape.mk'_head {Γ : Type u_1} [inhabited Γ] (L R : turing.list_blank Γ) :
@[simp]
theorem turing.tape.mk'_right {Γ : Type u_1} [inhabited Γ] (L R : turing.list_blank Γ) :
R).right = R.tail
@[simp]
theorem turing.tape.mk'_right₀ {Γ : Type u_1} [inhabited Γ] (L R : turing.list_blank Γ) :
R).right₀ = R
@[simp]
theorem turing.tape.mk'_left_right₀ {Γ : Type u_1} [inhabited Γ] (T : turing.tape Γ) :
= T
theorem turing.tape.exists_mk' {Γ : Type u_1} [inhabited Γ] (T : turing.tape Γ) :
∃ (L R : , T =
@[simp]
theorem turing.tape.move_left_mk' {Γ : Type u_1} [inhabited Γ] (L R : turing.list_blank Γ) :
@[simp]
theorem turing.tape.move_right_mk' {Γ : Type u_1} [inhabited Γ] (L R : turing.list_blank Γ) :
def turing.tape.mk₂ {Γ : Type u_1} [inhabited Γ] (L R : list Γ) :

Construct a tape from a left side and an inclusive right side.

Equations
def turing.tape.mk₁ {Γ : Type u_1} [inhabited Γ] (l : list Γ) :

Construct a tape from a list, with the head of the list at the TM head and the rest going to the right.

Equations
def turing.tape.nth {Γ : Type u_1} [inhabited Γ] (T : turing.tape Γ) :
→ Γ

The `nth` function of a tape is integer-valued, with index `0` being the head, negative indexes on the left and positive indexes on the right. (Picture a number line.)

Equations
@[simp]
theorem turing.tape.nth_zero {Γ : Type u_1} [inhabited Γ] (T : turing.tape Γ) :
theorem turing.tape.right₀_nth {Γ : Type u_1} [inhabited Γ] (T : turing.tape Γ) (n : ) :
@[simp]
theorem turing.tape.mk'_nth_nat {Γ : Type u_1} [inhabited Γ] (L R : turing.list_blank Γ) (n : ) :
R).nth n = R.nth n
@[simp]
theorem turing.tape.move_left_nth {Γ : Type u_1} [inhabited Γ] (T : turing.tape Γ) (i : ) :
= T.nth (i - 1)
@[simp]
theorem turing.tape.move_right_nth {Γ : Type u_1} [inhabited Γ] (T : turing.tape Γ) (i : ) :
= T.nth (i + 1)
@[simp]
theorem turing.tape.move_right_n_head {Γ : Type u_1} [inhabited Γ] (T : turing.tape Γ) (i : ) :
= T.nth i
def turing.tape.write {Γ : Type u_1} [inhabited Γ] (b : Γ) (T : turing.tape Γ) :

Replace the current value of the head on the tape.

Equations
@[simp]
theorem turing.tape.write_self {Γ : Type u_1} [inhabited Γ] (T : turing.tape Γ) :
= T
@[simp]
theorem turing.tape.write_nth {Γ : Type u_1} [inhabited Γ] (b : Γ) (T : turing.tape Γ) {i : } :
T).nth i = ite (i = 0) b (T.nth i)
@[simp]
theorem turing.tape.write_mk' {Γ : Type u_1} [inhabited Γ] (a b : Γ) (L R : turing.list_blank Γ) :
=
def turing.tape.map {Γ : Type u_1} {Γ' : Type u_2} [inhabited Γ] [inhabited Γ'] (f : Γ') (T : turing.tape Γ) :

Apply a pointed map to a tape to change the alphabet.

Equations
@[simp]
theorem turing.tape.map_fst {Γ : Type u_1} {Γ' : Type u_2} [inhabited Γ] [inhabited Γ'] (f : Γ') (T : turing.tape Γ) :
@[simp]
theorem turing.tape.map_write {Γ : Type u_1} {Γ' : Type u_2} [inhabited Γ] [inhabited Γ'] (f : Γ') (b : Γ) (T : turing.tape Γ) :
T) = turing.tape.write (f b) T)
@[simp]
theorem turing.tape.write_move_right_n {Γ : Type u_1} [inhabited Γ] (f : Γ → Γ) (L R : turing.list_blank Γ) (n : ) :
theorem turing.tape.map_move {Γ : Type u_1} {Γ' : Type u_2} [inhabited Γ] [inhabited Γ'] (f : Γ') (T : turing.tape Γ) (d : turing.dir) :
T) = T)
theorem turing.tape.map_mk' {Γ : Type u_1} {Γ' : Type u_2} [inhabited Γ] [inhabited Γ'] (f : Γ') (L R : turing.list_blank Γ) :
R) =
theorem turing.tape.map_mk₂ {Γ : Type u_1} {Γ' : Type u_2} [inhabited Γ] [inhabited Γ'] (f : Γ') (L R : list Γ) :
theorem turing.tape.map_mk₁ {Γ : Type u_1} {Γ' : Type u_2} [inhabited Γ] [inhabited Γ'] (f : Γ') (l : list Γ) :
def turing.eval {σ : Type u_1} (f : σ → ) :
σ → part σ

Run a state transition function `σ → option σ` "to completion". The return value is the last state returned before a `none` result. If the state transition function always returns `some`, then the computation diverges, returning `part.none`.

Equations
def turing.reaches {σ : Type u_1} (f : σ → ) :
σ → σ → Prop

The reflexive transitive closure of a state transition function. `reaches f a b` means there is a finite sequence of steps `f a = some a₁`, `f a₁ = some a₂`, ... such that `aₙ = b`. This relation permits zero steps of the state transition function.

Equations
def turing.reaches₁ {σ : Type u_1} (f : σ → ) :
σ → σ → Prop

The transitive closure of a state transition function. `reaches₁ f a b` means there is a nonempty finite sequence of steps `f a = some a₁`, `f a₁ = some a₂`, ... such that `aₙ = b`. This relation does not permit zero steps of the state transition function.

Equations
theorem turing.reaches₁_eq {σ : Type u_1} {f : σ → } {a b c : σ} (h : f a = f b) :
c c
theorem turing.reaches_total {σ : Type u_1} {f : σ → } {a b c : σ} (hab : b) (hac : c) :
c b
theorem turing.reaches₁_fwd {σ : Type u_1} {f : σ → } {a b c : σ} (h₁ : c) (h₂ : b f a) :
c
def turing.reaches₀ {σ : Type u_1} (f : σ → ) (a b : σ) :
Prop

A variation on `reaches`. `reaches₀ f a b` holds if whenever `reaches₁ f b c` then `reaches₁ f a c`. This is a weaker property than `reaches` and is useful for replacing states with equivalent states without taking a step.

Equations
• b = ∀ (c : σ), c c
theorem turing.reaches₀.trans {σ : Type u_1} {f : σ → } {a b c : σ} (h₁ : b) (h₂ : c) :
c
@[refl]
theorem turing.reaches₀.refl {σ : Type u_1} {f : σ → } (a : σ) :
a
theorem turing.reaches₀.single {σ : Type u_1} {f : σ → } {a b : σ} (h : b f a) :
b
theorem turing.reaches₀.head {σ : Type u_1} {f : σ → } {a b c : σ} (h : b f a) (h₂ : c) :
c
theorem turing.reaches₀.tail {σ : Type u_1} {f : σ → } {a b c : σ} (h₁ : b) (h : c f b) :
c
theorem turing.reaches₀_eq {σ : Type u_1} {f : σ → } {a b : σ} (e : f a = f b) :
b
theorem turing.reaches₁.to₀ {σ : Type u_1} {f : σ → } {a b : σ} (h : b) :
b
theorem turing.reaches.to₀ {σ : Type u_1} {f : σ → } {a b : σ} (h : b) :
b
theorem turing.reaches₀.tail' {σ : Type u_1} {f : σ → } {a b c : σ} (h : b) (h₂ : c f b) :
c
def turing.eval_induction {σ : Type u_1} {f : σ → } {b : σ} {C : σ → Sort u_2} {a : σ} (h : b a) (H : Π (a : σ), b a(Π (a' : σ), f a = C a')C a) :
C a

(co-)Induction principle for `eval`. If a property `C` holds of any point `a` evaluating to `b` which is either terminal (meaning `a = b`) or where the next point also satisfies `C`, then it holds of any point where `eval f a` evaluates to `b`. This formalizes the notion that if `eval f a` evaluates to `b` then it reaches terminal state `b` in finitely many steps.

Equations
theorem turing.mem_eval {σ : Type u_1} {f : σ → } {a b : σ} :
b a b f b = option.none
theorem turing.eval_maximal₁ {σ : Type u_1} {f : σ → } {a b : σ} (h : b a) (c : σ) :
¬ c
theorem turing.eval_maximal {σ : Type u_1} {f : σ → } {a b : σ} (h : b a) {c : σ} :
c c = b
theorem turing.reaches_eval {σ : Type u_1} {f : σ → } {a b : σ} (ab : b) :
a = b
def turing.respects {σ₁ : Type u_1} {σ₂ : Type u_2} (f₁ : σ₁ → option σ₁) (f₂ : σ₂ → option σ₂) (tr : σ₁ → σ₂ → Prop) :
Prop

Given a relation `tr : σ₁ → σ₂ → Prop` between state spaces, and state transition functions `f₁ : σ₁ → option σ₁` and `f₂ : σ₂ → option σ₂`, `respects f₁ f₂ tr` means that if `tr a₁ a₂` holds initially and `f₁` takes a step to `a₂` then `f₂` will take one or more steps before reaching a state `b₂` satisfying `tr a₂ b₂`, and if `f₁ a₁` terminates then `f₂ a₂` also terminates. Such a relation `tr` is also known as a refinement.

Equations
• f₂ tr = ∀ ⦃a₁ : σ₁⦄ ⦃a₂ : σ₂⦄, tr a₁ a₂turing.respects._match_1 f₂ tr (f₁ a₁)
• turing.respects._match_1 f₂ tr (option.some b₁) = ∃ (b₂ : σ₂), tr b₁ b₂ a₂ b₂
• turing.respects._match_1 f₂ tr option.none = (f₂ a₂ = option.none)
theorem turing.tr_reaches₁ {σ₁ : Type u_1} {σ₂ : Type u_2} {f₁ : σ₁ → option σ₁} {f₂ : σ₂ → option σ₂} {tr : σ₁ → σ₂ → Prop} (H : f₂ tr) {a₁ : σ₁} {a₂ : σ₂} (aa : tr a₁ a₂) {b₁ : σ₁} (ab : a₁ b₁) :
∃ (b₂ : σ₂), tr b₁ b₂ a₂ b₂
theorem turing.tr_reaches {σ₁ : Type u_1} {σ₂ : Type u_2} {f₁ : σ₁ → option σ₁} {f₂ : σ₂ → option σ₂} {tr : σ₁ → σ₂ → Prop} (H : f₂ tr) {a₁ : σ₁} {a₂ : σ₂} (aa : tr a₁ a₂) {b₁ : σ₁} (ab : a₁ b₁) :
∃ (b₂ : σ₂), tr b₁ b₂ a₂ b₂
theorem turing.tr_reaches_rev {σ₁ : Type u_1} {σ₂ : Type u_2} {f₁ : σ₁ → option σ₁} {f₂ : σ₂ → option σ₂} {tr : σ₁ → σ₂ → Prop} (H : f₂ tr) {a₁ : σ₁} {a₂ : σ₂} (aa : tr a₁ a₂) {b₂ : σ₂} (ab : a₂ b₂) :
∃ (c₁ : σ₁) (c₂ : σ₂), b₂ c₂ tr c₁ c₂ a₁ c₁
theorem turing.tr_eval {σ₁ : Type u_1} {σ₂ : Type u_2} {f₁ : σ₁ → option σ₁} {f₂ : σ₂ → option σ₂} {tr : σ₁ → σ₂ → Prop} (H : f₂ tr) {a₁ b₁ : σ₁} {a₂ : σ₂} (aa : tr a₁ a₂) (ab : b₁ a₁) :
∃ (b₂ : σ₂), tr b₁ b₂ b₂ a₂
theorem turing.tr_eval_rev {σ₁ : Type u_1} {σ₂ : Type u_2} {f₁ : σ₁ → option σ₁} {f₂ : σ₂ → option σ₂} {tr : σ₁ → σ₂ → Prop} (H : f₂ tr) {a₁ : σ₁} {b₂ a₂ : σ₂} (aa : tr a₁ a₂) (ab : b₂ a₂) :
∃ (b₁ : σ₁), tr b₁ b₂ b₁ a₁
theorem turing.tr_eval_dom {σ₁ : Type u_1} {σ₂ : Type u_2} {f₁ : σ₁ → option σ₁} {f₂ : σ₂ → option σ₂} {tr : σ₁ → σ₂ → Prop} (H : f₂ tr) {a₁ : σ₁} {a₂ : σ₂} (aa : tr a₁ a₂) :
(turing.eval f₂ a₂).dom (turing.eval f₁ a₁).dom
def turing.frespects {σ₁ : Type u_1} {σ₂ : Type u_2} (f₂ : σ₂ → option σ₂) (tr : σ₁ → σ₂) (a₂ : σ₂) :
option σ₁ → Prop

A simpler version of `respects` when the state transition relation `tr` is a function.

Equations
theorem turing.frespects_eq {σ₁ : Type u_1} {σ₂ : Type u_2} {f₂ : σ₂ → option σ₂} {tr : σ₁ → σ₂} {a₂ b₂ : σ₂} (h : f₂ a₂ = f₂ b₂) {b₁ : option σ₁} :
tr a₂ b₁ tr b₂ b₁
theorem turing.fun_respects {σ₁ : Type u_1} {σ₂ : Type u_2} {f₁ : σ₁ → option σ₁} {f₂ : σ₂ → option σ₂} {tr : σ₁ → σ₂} :
f₂ (λ (a : σ₁) (b : σ₂), tr a = b) ∀ ⦃a₁ : σ₁⦄, tr (tr a₁) (f₁ a₁)
theorem turing.tr_eval' {σ₁ σ₂ : Type u_1} (f₁ : σ₁ → option σ₁) (f₂ : σ₂ → option σ₂) (tr : σ₁ → σ₂) (H : f₂ (λ (a : σ₁) (b : σ₂), tr a = b)) (a₁ : σ₁) :
(tr a₁) = tr <\$> a₁

## The TM0 model #

A TM0 turing machine is essentially a Post-Turing machine, adapted for type theory.

A Post-Turing machine with symbol type `Γ` and label type `Λ` is a function `Λ → Γ → option (Λ × stmt)`, where a `stmt` can be either `move left`, `move right` or `write a` for `a : Γ`. The machine works over a "tape", a doubly-infinite sequence of elements of `Γ`, and an instantaneous configuration, `cfg`, is a label `q : Λ` indicating the current internal state of the machine, and a `tape Γ` (which is essentially `ℤ →₀ Γ`). The evolution is described by the `step` function:

• If `M q T.head = none`, then the machine halts.
• If `M q T.head = some (q', s)`, then the machine performs action `s : stmt` and then transitions to state `q'`.

The initial state takes a `list Γ` and produces a `tape Γ` where the head of the list is the head of the tape and the rest of the list extends to the right, with the left side all blank. The final state takes the entire right side of the tape right or equal to the current position of the machine. (This is actually a `list_blank Γ`, not a `list Γ`, because we don't know, at this level of generality, where the output ends. If equality to `default : Γ` is decidable we can trim the list to remove the infinite tail of blanks.)

inductive turing.TM0.stmt (Γ : Type u_1) [inhabited Γ] :
Type u_1
• move : Π {Γ : Type u_1} [_inst_1 : ,
• write : Π {Γ : Type u_1} [_inst_1 : , Γ →

A Turing machine "statement" is just a command to either move left or right, or write a symbol on the tape.

Instances for `turing.TM0.stmt`
@[protected, instance]
def turing.TM0.stmt.inhabited (Γ : Type u_1) [inhabited Γ] :
Equations
@[nolint]
def turing.TM0.machine (Γ : Type u_1) [inhabited Γ] (Λ : Type u_2) [inhabited Λ] :
Type (max u_2 u_1)

A Post-Turing machine with symbol type `Γ` and label type `Λ` is a function which, given the current state `q : Λ` and the tape head `a : Γ`, either halts (returns `none`) or returns a new state `q' : Λ` and a `stmt` describing what to do, either a move left or right, or a write command.

Both `Λ` and `Γ` are required to be inhabited; the default value for `Γ` is the "blank" tape value, and the default value of `Λ` is the initial state.

Equations
Instances for `turing.TM0.machine`
@[protected, instance]
def turing.TM0.machine.inhabited (Γ : Type u_1) [inhabited Γ] (Λ : Type u_2) [inhabited Λ] :
Equations
structure turing.TM0.cfg (Γ : Type u_1) [inhabited Γ] (Λ : Type u_2) [inhabited Λ] :
Type (max u_1 u_2)
• q : Λ
• tape :

The configuration state of a Turing machine during operation consists of a label (machine state), and a tape, represented in the form `(a, L, R)` meaning the tape looks like `L.rev ++ [a] ++ R` with the machine currently reading the `a`. The lists are automatically extended with blanks as the machine moves around.

Instances for `turing.TM0.cfg`
@[protected, instance]
def turing.TM0.cfg.inhabited (Γ : Type u_1) [inhabited Γ] (Λ : Type u_2) [inhabited Λ] :
Equations
def turing.TM0.step {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} [inhabited Λ] (M : Λ) :
option Λ)

Execution semantics of the Turing machine.

Equations
• {q := q, tape := T} = option.map (λ (_x : Λ × , turing.TM0.step._match_2 T _x) (M q T.head)
• turing.TM0.step._match_2 T (q', a) = {q := q', tape := turing.TM0.step._match_1 T a}
• turing.TM0.step._match_1 T =
• turing.TM0.step._match_1 T =
def turing.TM0.reaches {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} [inhabited Λ] (M : Λ) :
→ Prop

The statement `reaches M s₁ s₂` means that `s₂` is obtained starting from `s₁` after a finite number of steps from `s₂`.

Equations
def turing.TM0.init {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} [inhabited Λ] (l : list Γ) :

The initial configuration.

Equations
def turing.TM0.eval {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} [inhabited Λ] (M : Λ) (l : list Γ) :

Evaluate a Turing machine on initial input to a final state, if it terminates.

Equations
def turing.TM0.supports {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} [inhabited Λ] (M : Λ) (S : set Λ) :
Prop

The raw definition of a Turing machine does not require that `Γ` and `Λ` are finite, and in practice we will be interested in the infinite `Λ` case. We recover instead a notion of "effectively finite" Turing machines, which only make use of a finite subset of their states. We say that a set `S ⊆ Λ` supports a Turing machine `M` if `S` is closed under the transition function and contains the initial state.

Equations
• = ∀ {q : Λ} {a : Γ} {q' : Λ} {s : , (q', s) M q aq Sq' S)
theorem turing.TM0.step_supports {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} [inhabited Λ] (M : Λ) {S : set Λ} (ss : S) {c c' : Λ} :
c' c.q Sc'.q S
theorem turing.TM0.univ_supports {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} [inhabited Λ] (M : Λ) :
def turing.TM0.stmt.map {Γ : Type u_1} [inhabited Γ] {Γ' : Type u_2} [inhabited Γ'] (f : Γ') :

Map a TM statement across a function. This does nothing to move statements and maps the write values.

Equations
def turing.TM0.cfg.map {Γ : Type u_1} [inhabited Γ] {Γ' : Type u_2} [inhabited Γ'] {Λ : Type u_3} [inhabited Λ] {Λ' : Type u_4} [inhabited Λ'] (f : Γ') (g : Λ → Λ') :
Λ'

Map a configuration across a function, given `f : Γ → Γ'` a map of the alphabets and `g : Λ → Λ'` a map of the machine states.

Equations
def turing.TM0.machine.map {Γ : Type u_1} [inhabited Γ] {Γ' : Type u_2} [inhabited Γ'] {Λ : Type u_3} [inhabited Λ] {Λ' : Type u_4} [inhabited Λ'] (M : Λ) (f₁ : Γ') (f₂ : Γ) (g₁ : Λ → Λ') (g₂ : Λ' → Λ) :
Λ'

Because the state transition function uses the alphabet and machine states in both the input and output, to map a machine from one alphabet and machine state space to another we need functions in both directions, essentially an `equiv` without the laws.

Equations
theorem turing.TM0.machine.map_step {Γ : Type u_1} [inhabited Γ] {Γ' : Type u_2} [inhabited Γ'] {Λ : Type u_3} [inhabited Λ] {Λ' : Type u_4} [inhabited Λ'] (M : Λ) (f₁ : Γ') (f₂ : Γ) (g₁ : Λ → Λ') (g₂ : Λ' → Λ) {S : set Λ} (f₂₁ : f₂) (g₂₁ : ∀ (q : Λ), q Sg₂ (g₁ q) = q) (c : Λ) :
c.q Soption.map g₁) c) = turing.TM0.step (M.map f₁ f₂ g₁ g₂) g₁ c)
theorem turing.TM0.map_init {Γ : Type u_1} [inhabited Γ] {Γ' : Type u_2} [inhabited Γ'] {Λ : Type u_3} [inhabited Λ] {Λ' : Type u_4} [inhabited Λ'] (f₁ : Γ') (g₁ : Λ') (l : list Γ) :
theorem turing.TM0.machine.map_respects {Γ : Type u_1} [inhabited Γ] {Γ' : Type u_2} [inhabited Γ'] {Λ : Type u_3} [inhabited Λ] {Λ' : Type u_4} [inhabited Λ'] (M : Λ) (f₁ : Γ') (f₂ : Γ) (g₁ : Λ') (g₂ : Λ' → Λ) {S : set Λ} (ss : S) (f₂₁ : f₂) (g₂₁ : ∀ (q : Λ), q Sg₂ (g₁ q) = q) :
(turing.TM0.step (M.map f₁ f₂ g₁ g₂)) (λ (a : Λ) (b : Λ'), a.q S g₁ a = b)

## The TM1 model #

The TM1 model is a simplification and extension of TM0 (Post-Turing model) in the direction of Wang B-machines. The machine's internal state is extended with a (finite) store `σ` of variables that may be accessed and updated at any time.

A machine is given by a `Λ` indexed set of procedures or functions. Each function has a body which is a `stmt`. Most of the regular commands are allowed to use the current value `a` of the local variables and the value `T.head` on the tape to calculate what to write or how to change local state, but the statements themselves have a fixed structure. The `stmt`s can be as follows:

• `move d q`: move left or right, and then do `q`
• `write (f : Γ → σ → Γ) q`: write `f a T.head` to the tape, then do `q`
• `load (f : Γ → σ → σ) q`: change the internal state to `f a T.head`
• `branch (f : Γ → σ → bool) qtrue qfalse`: If `f a T.head` is true, do `qtrue`, else `qfalse`
• `goto (f : Γ → σ → Λ)`: Go to label `f a T.head`
• `halt`: Transition to the halting state, which halts on the following step

Note that here most statements do not have labels; `goto` commands can only go to a new function. Only the `goto` and `halt` statements actually take a step; the rest is done by recursion on statements and so take 0 steps. (There is a uniform bound on many statements can be executed before the next `goto`, so this is an `O(1)` speedup with the constant depending on the machine.)

The `halt` command has a one step stutter before actually halting so that any changes made before the halt have a chance to be "committed", since the `eval` relation uses the final configuration before the halt as the output, and `move` and `write` etc. take 0 steps in this model.

inductive turing.TM1.stmt (Γ : Type u_1) [inhabited Γ] (Λ : Type u_2) (σ : Type u_3) :
Type (max u_1 u_2 u_3)
• move : Π {Γ : Type u_1} [_inst_1 : {Λ : Type u_2} {σ : Type u_3}, turing.dir σ σ
• write : Π {Γ : Type u_1} [_inst_1 : {Λ : Type u_2} {σ : Type u_3}, (Γ → σ → Γ) σ σ
• load : Π {Γ : Type u_1} [_inst_1 : {Λ : Type u_2} {σ : Type u_3}, (Γ → σ → σ) σ σ
• branch : Π {Γ : Type u_1} [_inst_1 : {Λ : Type u_2} {σ : Type u_3}, (Γ → σ → bool) σ σ σ
• goto : Π {Γ : Type u_1} [_inst_1 : {Λ : Type u_2} {σ : Type u_3}, (Γ → σ → Λ) σ
• halt : Π {Γ : Type u_1} [_inst_1 : {Λ : Type u_2} {σ : Type u_3}, σ

The TM1 model is a simplification and extension of TM0 (Post-Turing model) in the direction of Wang B-machines. The machine's internal state is extended with a (finite) store `σ` of variables that may be accessed and updated at any time. A machine is given by a `Λ` indexed set of procedures or functions. Each function has a body which is a `stmt`, which can either be a `move` or `write` command, a `branch` (if statement based on the current tape value), a `load` (set the variable value), a `goto` (call another function), or `halt`. Note that here most statements do not have labels; `goto` commands can only go to a new function. All commands have access to the variable value and current tape value.

Instances for `turing.TM1.stmt`
@[protected, instance]
def turing.TM1.stmt.inhabited (Γ : Type u_1) [inhabited Γ] (Λ : Type u_2) (σ : Type u_3) :
inhabited Λ σ)
Equations
structure turing.TM1.cfg (Γ : Type u_1) [inhabited Γ] (Λ : Type u_2) (σ : Type u_3) :
Type (max u_1 u_2 u_3)
• l :
• var : σ
• tape :

The configuration of a TM1 machine is given by the currently evaluating statement, the variable store value, and the tape.

Instances for `turing.TM1.cfg`
@[protected, instance]
def turing.TM1.cfg.inhabited (Γ : Type u_1) [inhabited Γ] (Λ : Type u_2) (σ : Type u_3) [inhabited σ] :
inhabited Λ σ)
Equations
def turing.TM1.step_aux {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} {σ : Type u_3} :
σσ → σ

The semantics of TM1 evaluation.

Equations
def turing.TM1.step {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} {σ : Type u_3} (M : Λ → σ) :
σoption Λ σ)

The state transition function.

Equations
def turing.TM1.supports_stmt {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} {σ : Type u_3} (S : finset Λ) :
σ → Prop

A set `S` of labels supports the statement `q` if all the `goto` statements in `q` refer only to other functions in `S`.

Equations
• = ∀ (a : Γ) (v : σ), l a v S
• q₂) =
noncomputable def turing.TM1.stmts₁ {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} {σ : Type u_3} :
σfinset Λ σ)

The subterm closure of a statement.

Equations
theorem turing.TM1.stmts₁_self {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} {σ : Type u_3} {q : σ} :
theorem turing.TM1.stmts₁_trans {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} {σ : Type u_3} {q₁ q₂ : σ} :
q₁
theorem turing.TM1.stmts₁_supports_stmt_mono {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} {σ : Type u_3} {S : finset Λ} {q₁ q₂ : σ} (h : q₁ ) (hs : q₂) :
noncomputable def turing.TM1.stmts {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} {σ : Type u_3} (M : Λ → σ) (S : finset Λ) :
finset (option Λ σ))

The set of all statements in a turing machine, plus one extra value `none` representing the halt state. This is used in the TM1 to TM0 reduction.

Equations
theorem turing.TM1.stmts_trans {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} {σ : Type u_3} {M : Λ → σ} {S : finset Λ} {q₁ q₂ : σ} (h₁ : q₁ ) :
def turing.TM1.supports {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} {σ : Type u_3} [inhabited Λ] (M : Λ → σ) (S : finset Λ) :
Prop

A set `S` of labels supports machine `M` if all the `goto` statements in the functions in `S` refer only to other functions in `S`.

Equations
• = ∀ (q : Λ), q S (M q))
theorem turing.TM1.stmts_supports_stmt {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} {σ : Type u_3} [inhabited Λ] {M : Λ → σ} {S : finset Λ} {q : σ} (ss : S) :
theorem turing.TM1.step_supports {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} {σ : Type u_3} [inhabited Λ] (M : Λ → σ) {S : finset Λ} (ss : S) {c c' : σ} :
c'
def turing.TM1.init {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} {σ : Type u_3} [inhabited Λ] [inhabited σ] (l : list Γ) :
σ

The initial state, given a finite input that is placed on the tape starting at the TM head and going to the right.

Equations
def turing.TM1.eval {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} {σ : Type u_3} [inhabited Λ] [inhabited σ] (M : Λ → σ) (l : list Γ) :

Evaluate a TM to completion, resulting in an output list on the tape (with an indeterminate number of blanks on the end).

Equations

## TM1 emulator in TM0 #

To prove that TM1 computable functions are TM0 computable, we need to reduce each TM1 program to a TM0 program. So suppose a TM1 program is given. We take the following:

• The alphabet `Γ` is the same for both TM1 and TM0
• The set of states `Λ'` is defined to be `option stmt₁ × σ`, that is, a TM1 statement or `none` representing halt, and the possible settings of the internal variables. Note that this is an infinite set, because `stmt₁` is infinite. This is okay because we assume that from the initial TM1 state, only finitely many other labels are reachable, and there are only finitely many statements that appear in all of these functions.

Even though `stmt₁` contains a statement called `halt`, we must separate it from `none` (`some halt` steps to `none` and `none` actually halts) because there is a one step stutter in the TM1 semantics.

@[nolint]
def turing.TM1to0.Λ' {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} [inhabited Λ] {σ : Type u_3} [inhabited σ] (M : Λ → σ) :
Type (max (max u_1 u_2 u_3) u_3)

The base machine state space is a pair of an `option stmt₁` representing the current program to be executed, or `none` for the halt state, and a `σ` which is the local state (stored in the TM, not the tape). Because there are an infinite number of programs, this state space is infinite, but for a finitely supported TM1 machine and a finite type `σ`, only finitely many of these states are reachable.

Equations
Instances for `turing.TM1to0.Λ'`
@[protected, instance]
def turing.TM1to0.Λ'.inhabited {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} [inhabited Λ] {σ : Type u_3} [inhabited σ] (M : Λ → σ) :
Equations
def turing.TM1to0.tr_aux {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} [inhabited Λ] {σ : Type u_3} [inhabited σ] (M : Λ → σ) (s : Γ) :
σσ →

The core TM1 → TM0 translation function. Here `s` is the current value on the tape, and the `stmt₁` is the TM1 statement to translate, with local state `v : σ`. We evaluate all regular instructions recursively until we reach either a `move` or `write` command, or a `goto`; in the latter case we emit a dummy `write s` step and transition to the new target location.

Equations
def turing.TM1to0.tr {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} [inhabited Λ] {σ : Type u_3} [inhabited σ] (M : Λ → σ) :

The translated TM0 machine (given the TM1 machine input).

Equations
def turing.TM1to0.tr_cfg {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} [inhabited Λ] {σ : Type u_3} [inhabited σ] (M : Λ → σ) :
σ

Translate configurations from TM1 to TM0.

Equations
theorem turing.TM1to0.tr_respects {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} [inhabited Λ] {σ : Type u_3} [inhabited σ] (M : Λ → σ) :
(λ (c₁ : σ) (c₂ : , = c₂)
theorem turing.TM1to0.tr_eval {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} [inhabited Λ] {σ : Type u_3} [inhabited σ] (M : Λ → σ) (l : list Γ) :
noncomputable def turing.TM1to0.tr_stmts {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} [inhabited Λ] {σ : Type u_3} [inhabited σ] (M : Λ → σ) [fintype σ] (S : finset Λ) :

Given a finite set of accessible `Λ` machine states, there is a finite set of accessible machine states in the target (even though the type `Λ'` is infinite).

Equations
theorem turing.TM1to0.tr_supports {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} [inhabited Λ] {σ : Type u_3} [inhabited σ] (M : Λ → σ) [fintype σ] {S : finset Λ} (ss : S) :

## TM1(Γ) emulator in TM1(bool) #

The most parsimonious Turing machine model that is still Turing complete is `TM0` with `Γ = bool`. Because our construction in the previous section reducing `TM1` to `TM0` doesn't change the alphabet, we can do the alphabet reduction on `TM1` instead of `TM0` directly.

The basic idea is to use a bijection between `Γ` and a subset of `vector bool n`, where `n` is a fixed constant. Each tape element is represented as a block of `n` bools. Whenever the machine wants to read a symbol from the tape, it traverses over the block, performing `n` `branch` instructions to each any of the `2^n` results.

For the `write` instruction, we have to use a `goto` because we need to follow a different code path depending on the local state, which is not available in the TM1 model, so instead we jump to a label computed using the read value and the local state, which performs the writing and returns to normal execution.

Emulation overhead is `O(1)`. If not for the above `write` behavior it would be 1-1 because we are exploiting the 0-step behavior of regular commands to avoid taking steps, but there are nevertheless a bounded number of `write` calls between `goto` statements because TM1 statements are finitely long.

theorem turing.TM1to1.exists_enc_dec {Γ : Type u_1} [inhabited Γ] [fintype Γ] :
∃ (n : ) (enc : Γ → n) (dec : → Γ), ∀ (a : Γ), dec (enc a) = a
inductive turing.TM1to1.Λ' {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} [inhabited Λ] {σ : Type u_3} [inhabited σ] :
Type (max u_1 u_2 u_3)
• normal : Π {Γ : Type u_1} [_inst_1 : {Λ : Type u_2} [_inst_2 : {σ : Type u_3} [_inst_3 : ,
• write : Π {Γ : Type u_1} [_inst_1 : {Λ : Type u_2} [_inst_2 : {σ : Type u_3} [_inst_3 : , Γ →

The configuration state of the TM.

Instances for `turing.TM1to1.Λ'`
@[protected, instance]
def turing.TM1to1.Λ'.inhabited {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} [inhabited Λ] {σ : Type u_3} [inhabited σ] :
Equations
def turing.TM1to1.read_aux {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} [inhabited Λ] {σ : Type u_3} [inhabited σ] (n : ) :

Read a vector of length `n` from the tape.

Equations
def turing.TM1to1.move {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} [inhabited Λ] {σ : Type u_3} [inhabited σ] {n : } (d : turing.dir)  :

A move left or right corresponds to `n` moves across the super-cell.

Equations
def turing.TM1to1.read {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} [inhabited Λ] {σ : Type u_3} [inhabited σ] {n : } (dec : → Γ) (f : Γ → ) :

To read a symbol from the tape, we use `read_aux` to traverse the symbol, then return to the original position with `n` moves to the left.

Equations
• f = (λ (v : n), (f (dec v)))
def turing.TM1to1.write {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} [inhabited Λ] {σ : Type u_3} [inhabited σ] :

Write a list of bools on the tape.

Equations
def turing.TM1to1.tr_normal {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} [inhabited Λ] {σ : Type u_3} [inhabited σ] {n : } (dec : → Γ) :

Translate a normal instruction. For the `write` command, we use a `goto` indirection so that we can access the current value of the tape.

Equations
theorem turing.TM1to1.step_aux_move {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} [inhabited Λ] {σ : Type u_3} [inhabited σ] {n : } (d : turing.dir) (v : σ) (T : turing.tape bool) :
T = T)
theorem turing.TM1to1.supports_stmt_move {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} [inhabited Λ] {σ : Type u_3} [inhabited σ] {n : } {d : turing.dir}  :
theorem turing.TM1to1.supports_stmt_write {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} [inhabited Λ] {σ : Type u_3} [inhabited σ] {l : list bool}  :
theorem turing.TM1to1.supports_stmt_read {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} [inhabited Λ] {σ : Type u_3} [inhabited σ] {n : } (dec : → Γ) {f : Γ → } :
(∀ (a : Γ), (f a))
def turing.TM1to1.tr_tape' {Γ : Type u_1} [inhabited Γ] {n : } {enc : Γ → } (enc0 : = ) (L R : turing.list_blank Γ) :

The low level tape corresponding to the given tape over alphabet `Γ`.

Equations
def turing.TM1to1.tr_tape {Γ : Type u_1} [inhabited Γ] {n : } {enc : Γ → } (enc0 : = ) (T : turing.tape Γ) :

The low level tape corresponding to the given tape over alphabet `Γ`.

Equations
theorem turing.TM1to1.tr_tape_mk' {Γ : Type u_1} [inhabited Γ] {n : } {enc : Γ → } (enc0 : = ) (L R : turing.list_blank Γ) :
R) = L R
def turing.TM1to1.tr {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} [inhabited Λ] {σ : Type u_3} [inhabited σ] {n : } (enc : Γ → ) (dec : → Γ) (M : Λ → σ) :

The top level program.

Equations
def turing.TM1to1.tr_cfg {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} [inhabited Λ] {σ : Type u_3} [inhabited σ] {n : } {enc : Γ → } (enc0 : = ) :

The machine configuration translation.

Equations
theorem turing.TM1to1.tr_tape'_move_left {Γ : Type u_1} [inhabited Γ] {n : } {enc : Γ → } (enc0 : = ) (L R : turing.list_blank Γ) :
= L.tail
theorem turing.TM1to1.tr_tape'_move_right {Γ : Type u_1} [inhabited Γ] {n : } {enc : Γ → } (enc0 : = ) (L R : turing.list_blank Γ) :
= R.tail
theorem turing.TM1to1.step_aux_write {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} [inhabited Λ] {σ : Type u_3} [inhabited σ] {n : } {enc : Γ → } (enc0 : = ) (v : σ) (a b : Γ) (L R : turing.list_blank Γ) :
theorem turing.TM1to1.step_aux_read {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} [inhabited Λ] {σ : Type u_3} [inhabited σ] {n : } {enc : Γ → } (dec : → Γ) (enc0 : = ) (encdec : ∀ (a : Γ), dec (enc a) = a) (f : Γ → ) (v : σ) (L R : turing.list_blank Γ) :
v L R) = turing.TM1.step_aux (f R.head) v L R)
theorem turing.TM1to1.tr_respects {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} [inhabited Λ] {σ : Type u_3} [inhabited σ] {n : } {enc : Γ → } (dec : → Γ) (enc0 : = ) (M : Λ → σ) (encdec : ∀ (a : Γ), dec (enc a) = a) :
(turing.TM1.step (turing.TM1to1.tr enc dec M)) (λ (c₁ : σ) (c₂ : , c₁ = c₂)
noncomputable def turing.TM1to1.writes {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} [inhabited Λ] {σ : Type u_3} [inhabited σ] [fintype Γ] :

The set of accessible `Λ'.write` machine states.

Equations
noncomputable def turing.TM1to1.tr_supp {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} [inhabited Λ] {σ : Type u_3} [inhabited σ] (M : Λ → σ) [fintype Γ] (S : finset Λ) :

The set of accessible machine states, assuming that the input machine is supported on `S`, are the normal states embedded from `S`, plus all write states accessible from these states.

Equations
theorem turing.TM1to1.tr_supports {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} [inhabited Λ] {σ : Type u_3} [inhabited σ] {n : } {enc : Γ → } (dec : → Γ) (M : Λ → σ) [fintype Γ] {S : finset Λ} (ss : S) :

## TM0 emulator in TM1 #

To establish that TM0 and TM1 are equivalent computational models, we must also have a TM0 emulator in TM1. The main complication here is that TM0 allows an action to depend on the value at the head and local state, while TM1 doesn't (in order to have more programming language-like semantics). So we use a computed `goto` to go to a state that performes the desired action and then returns to normal execution.

One issue with this is that the `halt` instruction is supposed to halt immediately, not take a step to a halting state. To resolve this we do a check for `halt` first, then `goto` (with an unreachable branch).

inductive turing.TM0to1.Λ' {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} [inhabited Λ] :
Type (max u_1 u_2)
• normal : Π {Γ : Type u_1} [_inst_1 : {Λ : Type u_2} [_inst_2 : ,
• act : Π {Γ : Type u_1} [_inst_1 : {Λ : Type u_2} [_inst_2 : ,

The machine states for a TM1 emulating a TM0 machine. States of the TM0 machine are embedded as `normal q` states, but the actual operation is split into two parts, a jump to `act s q` followed by the action and a jump to the next `normal` state.

Instances for `turing.TM0to1.Λ'`
@[protected, instance]
def turing.TM0to1.Λ'.inhabited {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} [inhabited Λ] :
Equations
def turing.TM0to1.tr {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} [inhabited Λ] (M : Λ) :

The program.

Equations
def turing.TM0to1.tr_cfg {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} [inhabited Λ] (M : Λ) :

The configuration translation.

Equations
theorem turing.TM0to1.tr_respects {Γ : Type u_1} [inhabited Γ] {Λ : Type u_2} [inhabited Λ] (M : Λ) :
(λ (a : Λ) (b : , = b)

## The TM2 model #

The TM2 model removes the tape entirely from the TM1 model, replacing it with an arbitrary (finite) collection of stacks, each with elements of different types (the alphabet of stack `k : K` is `Γ k`). The statements are:

• `push k (f : σ → Γ k) q` puts `f a` on the `k`-th stack, then does `q`.
• `pop k (f : σ → option (Γ k) → σ) q` changes the state to `f a (S k).head`, where `S k` is the value of the `k`-th stack, and removes this element from the stack, then does `q`.
• `peek k (f : σ → option (Γ k) → σ) q` changes the state to `f a (S k).head`, where `S k` is the value of the `k`-th stack, then does `q`.
• `load (f : σ → σ) q` reads nothing but applies `f` to the internal state, then does `q`.
• `branch (f : σ → bool) qtrue qfalse` does `qtrue` or `qfalse` according to `f a`.
• `goto (f : σ → Λ)` jumps to label `f a`.
• `halt` halts on the next step.

The configuration is a tuple `(l, var, stk)` where `l : option Λ` is the current label to run or `none` for the halting state, `var : σ` is the (finite) internal state, and `stk : ∀ k, list (Γ k)` is the collection of stacks. (Note that unlike the `TM0` and `TM1` models, these are not `list_blank`s, they have definite ends that can be detected by the `pop` command.)

Given a designated stack `k` and a value `L : list (Γ k)`, the initial configuration has all the stacks empty except the designated "input" stack; in `eval` this designated stack also functions as the output stack.

inductive turing.TM2.stmt {K : Type u_1} [decidable_eq K] (Γ : K → Type u_2) (Λ : Type u_3) (σ : Type u_4) :
Type (max u_1 u_2 u_3 u_4)
• push : Π {K : Type u_1} [_inst_1 : {Γ : K → Type u_2} {Λ : Type u_3} {σ : Type u_4} (k : K), (σ → Γ k) σ σ
• peek : Π {K : Type u_1} [_inst_1 : {Γ : K → Type u_2} {Λ : Type u_3} {σ : Type u_4} (k : K), (σ → option (Γ k) → σ) σ σ
• pop : Π {K : Type u_1} [_inst_1 : {Γ : K → Type u_2} {Λ : Type u_3} {σ : Type u_4} (k : K), (σ → option (Γ k) → σ) σ σ
• load : Π {K : Type u_1} [_inst_1 : {Γ : K → Type u_2} {Λ : Type u_3} {σ : Type u_4}, (σ → σ) σ σ
• branch : Π {K : Type u_1} [_inst_1 : {Γ : K → Type u_2} {Λ : Type u_3} {σ : Type u_4}, (σ → bool) σ σ σ
• goto : Π {K : Type u_1} [_inst_1 : {Γ : K → Type u_2} {Λ : Type u_3} {σ : Type u_4}, (σ → Λ) σ
• halt : Π {K : Type u_1} [_inst_1 : {Γ : K → Type u_2} {Λ : Type u_3} {σ : Type u_4}, σ

The TM2 model removes the tape entirely from the TM1 model, replacing it with an arbitrary (finite) collection of stacks. The operation `push` puts an element on one of the stacks, and `pop` removes an element from a stack (and modifying the internal state based on the result). `peek` modifies the internal state but does not remove an element.

Instances for `turing.TM2.stmt`
@[protected, instance]
def turing.TM2.stmt.inhabited {K : Type u_1} [decidable_eq K] (Γ : K → Type u_2) (Λ : Type u_3) (σ : Type u_4) :
inhabited Λ σ)
Equations
structure turing.TM2.cfg {K : Type u_1} [decidable_eq K] (Γ : K → Type u_2) (Λ : Type u_3) (σ : Type u_4) :
Type (max u_1 u_2 u_3 u_4)
• l :
• var : σ
• stk : Π (k : K), list (Γ k)

A configuration in the TM2 model is a label (or `none` for the halt state), the state of local variables, and the stacks. (Note that the stacks are not `list_blank`s, they have a definite size.)

Instances for `turing.TM2.cfg`
@[protected, instance]
def turing.TM2.cfg.inhabited {K : Type u_1} [decidable_eq K] (Γ : K → Type u_2) (Λ : Type u_3) (σ : Type u_4) [inhabited σ] :
inhabited Λ σ)
Equations
@[simp]
def turing.TM2.step_aux {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} {Λ : Type u_3} {σ : Type u_4} :
σσ → (Π (k : K), list (Γ k)) σ

The step function for the TM2 model.

Equations
@[simp]
def turing.TM2.step {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} {Λ : Type u_3} {σ : Type u_4} (M : Λ → σ) :
σoption Λ σ)

The step function for the TM2 model.

Equations
def turing.TM2.reaches {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} {Λ : Type u_3} {σ : Type u_4} (M : Λ → σ) :
σ σ → Prop

The (reflexive) reachability relation for the TM2 model.

Equations
def turing.TM2.supports_stmt {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} {Λ : Type u_3} {σ : Type u_4} (S : finset Λ) :
σ → Prop

Given a set `S` of states, `support_stmt S q` means that `q` only jumps to states in `S`.

Equations
• = ∀ (v : σ), l v S
• q₂) =
noncomputable def turing.TM2.stmts₁ {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} {Λ : Type u_3} {σ : Type u_4} :
σfinset Λ σ)

The set of subtree statements in a statement.

Equations
theorem turing.TM2.stmts₁_self {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} {Λ : Type u_3} {σ : Type u_4} {q : σ} :
theorem turing.TM2.stmts₁_trans {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} {Λ : Type u_3} {σ : Type u_4} {q₁ q₂ : σ} :
q₁
theorem turing.TM2.stmts₁_supports_stmt_mono {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} {Λ : Type u_3} {σ : Type u_4} {S : finset Λ} {q₁ q₂ : σ} (h : q₁ ) (hs : q₂) :
noncomputable def turing.TM2.stmts {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} {Λ : Type u_3} {σ : Type u_4} (M : Λ → σ) (S : finset Λ) :
finset (option Λ σ))

The set of statements accessible from initial set `S` of labels.

Equations
theorem turing.TM2.stmts_trans {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} {Λ : Type u_3} {σ : Type u_4} {M : Λ → σ} {S : finset Λ} {q₁ q₂ : σ} (h₁ : q₁ ) :
def turing.TM2.supports {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} {Λ : Type u_3} {σ : Type u_4} [inhabited Λ] (M : Λ → σ) (S : finset Λ) :
Prop

Given a TM2 machine `M` and a set `S` of states, `supports M S` means that all states in `S` jump only to other states in `S`.

Equations
• = ∀ (q : Λ), q S (M q))
theorem turing.TM2.stmts_supports_stmt {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} {Λ : Type u_3} {σ : Type u_4} [inhabited Λ] {M : Λ → σ} {S : finset Λ} {q : σ} (ss : S) :
theorem turing.TM2.step_supports {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} {Λ : Type u_3} {σ : Type u_4} [inhabited Λ] (M : Λ → σ) {S : finset Λ} (ss : S) {c c' : σ} :
c'
def turing.TM2.init {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} {Λ : Type u_3} {σ : Type u_4} [inhabited Λ] [inhabited σ] (k : K) (L : list (Γ k)) :
σ

The initial state of the TM2 model. The input is provided on a designated stack.

Equations
def turing.TM2.eval {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} {Λ : Type u_3} {σ : Type u_4} [inhabited Λ] [inhabited σ] (M : Λ → σ) (k : K) (L : list (Γ k)) :
part (list (Γ k))

Evaluates a TM2 program to completion, with the output on the same stack as the input.

Equations

## TM2 emulator in TM1 #

To prove that TM2 computable functions are TM1 computable, we need to reduce each TM2 program to a TM1 program. So suppose a TM2 program is given. This program has to maintain a whole collection of stacks, but we have only one tape, so we must "multiplex" them all together. Pictorially, if stack 1 contains `[a, b]` and stack 2 contains `[c, d, e, f]` then the tape looks like this:

`````` bottom:  ... | _ | T | _ | _ | _ | _ | ...
stack 1: ... | _ | b | a | _ | _ | _ | ...
stack 2: ... | _ | f | e | d | c | _ | ...
``````

where a tape element is a vertical slice through the diagram. Here the alphabet is `Γ' := bool × ∀ k, option (Γ k)`, where:

• `bottom : bool` is marked only in one place, the initial position of the TM, and represents the tail of all stacks. It is never modified.
• `stk k : option (Γ k)` is the value of the `k`-th stack, if in range, otherwise `none` (which is the blank value). Note that the head of the stack is at the far end; this is so that push and pop don't have to do any shifting.

In "resting" position, the TM is sitting at the position marked `bottom`. For non-stack actions, it operates in place, but for the stack actions `push`, `peek`, and `pop`, it must shuttle to the end of the appropriate stack, make its changes, and then return to the bottom. So the states are:

• `normal (l : Λ)`: waiting at `bottom` to execute function `l`
• `go k (s : st_act k) (q : stmt₂)`: travelling to the right to get to the end of stack `k` in order to perform stack action `s`, and later continue with executing `q`
• `ret (q : stmt₂)`: travelling to the left after having performed a stack action, and executing `q` once we arrive

Because of the shuttling, emulation overhead is `O(n)`, where `n` is the current maximum of the length of all stacks. Therefore a program that takes `k` steps to run in TM2 takes `O((m+k)k)` steps to run when emulated in TM1, where `m` is the length of the input.

theorem turing.TM2to1.stk_nth_val {K : Type u_1} {Γ : K → Type u_2} {L : turing.list_blank (Π (k : K), option (Γ k))} {k : K} {S : list (Γ k)} (n : ) (hL : = ) :
L.nth n k = S.reverse.nth n
@[nolint]
def turing.TM2to1.Γ' {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} :
Type (max u_1 u_2)

The alphabet of the TM2 simulator on TM1 is a marker for the stack bottom, plus a vector of stack elements for each stack, or none if the stack does not extend this far.

Equations
Instances for `turing.TM2to1.Γ'`
@[protected, instance]
def turing.TM2to1.Γ'.inhabited {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} :
Equations
@[protected, instance]
def turing.TM2to1.Γ'.fintype {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} [fintype K] [Π (k : K), fintype (Γ k)] :
Equations
def turing.TM2to1.add_bottom {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} (L : turing.list_blank (Π (k : K), option (Γ k))) :

The bottom marker is fixed throughout the calculation, so we use the `add_bottom` function to express the program state in terms of a tape with only the stacks themselves.

Equations
theorem turing.TM2to1.add_bottom_map {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} (L : turing.list_blank (Π (k : K), option (Γ k))) :
turing.list_blank.map {f := prod.snd (Π (k : K), option (Γ k)), map_pt' := _} = L
theorem turing.TM2to1.add_bottom_modify_nth {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} (f : (Π (k : K), option (Γ k))Π (k : K), option (Γ k)) (L : turing.list_blank (Π (k : K), option (Γ k))) (n : ) :
theorem turing.TM2to1.add_bottom_nth_snd {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} (L : turing.list_blank (Π (k : K), option (Γ k))) (n : ) :
n).snd = L.nth n
theorem turing.TM2to1.add_bottom_nth_succ_fst {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} (L : turing.list_blank (Π (k : K), option (Γ k))) (n : ) :
(n + 1)).fst = bool.ff
theorem turing.TM2to1.add_bottom_head_fst {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} (L : turing.list_blank (Π (k : K), option (Γ k))) :
inductive turing.TM2to1.st_act {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} {σ : Type u_4} [inhabited σ] (k : K) :
Type (max u_2 u_4)
• push : Π {K : Type u_1} [_inst_1 : {Γ : K → Type u_2} {σ : Type u_4} [_inst_3 : {k : K}, (σ → Γ k)
• peek : Π {K : Type u_1} [_inst_1 : {Γ : K → Type u_2} {σ : Type u_4} [_inst_3 : {k : K}, (σ → option (Γ k) → σ)
• pop : Π {K : Type u_1} [_inst_1 : {Γ : K → Type u_2} {σ : Type u_4} [_inst_3 : {k : K}, (σ → option (Γ k) → σ)

A stack action is a command that interacts with the top of a stack. Our default position is at the bottom of all the stacks, so we have to hold on to this action while going to the end to modify the stack.

Instances for `turing.TM2to1.st_act`
@[protected, instance]
def turing.TM2to1.st_act.inhabited {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} {σ : Type u_4} [inhabited σ] {k : K} :
Equations
@[nolint]
def turing.TM2to1.st_run {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} {Λ : Type u_3} [inhabited Λ] {σ : Type u_4} [inhabited σ] {k : K} :
σ σ

The TM2 statement corresponding to a stack action.

Equations
def turing.TM2to1.st_var {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} {σ : Type u_4} [inhabited σ] {k : K} (v : σ) (l : list (Γ k)) :
→ σ

The effect of a stack action on the local variables, given the value of the stack.

Equations
def turing.TM2to1.st_write {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} {σ : Type u_4} [inhabited σ] {k : K} (v : σ) (l : list (Γ k)) :
list (Γ k)

The effect of a stack action on the stack.

Equations
def turing.TM2to1.stmt_st_rec {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} {Λ : Type u_3} [inhabited Λ] {σ : Type u_4} [inhabited σ] {C : σSort l} (H₁ : Π (k : K) (s : (q : σ), C qC q)) (H₂ : Π (a : σ → σ) (q : σ), C qC q)) (H₃ : Π (p : σ → bool) (q₁ q₂ : σ), C q₁C q₂C q₂)) (H₄ : Π (l : σ → Λ), C ) (H₅ : C turing.TM2.stmt.halt) (n : σ) :
C n

We have partitioned the TM2 statements into "stack actions", which require going to the end of the stack, and all other actions, which do not. This is a modified recursor which lumps the stack actions into one.

Equations
• H₃ H₄ H₅ turing.TM2.stmt.halt = H₅
• H₃ H₄ H₅ = H₄ l
• H₃ H₄ H₅ q₂) = H₃ a q₁ q₂ H₃ H₄ H₅ q₁) H₃ H₄ H₅ q₂)
• H₃ H₄ H₅ q) = H₂ a q H₃ H₄ H₅ q)
• H₃ H₄ H₅ q) = H₁ k q H₃ H₄ H₅ q)
• H₃ H₄ H₅ q) = H₁ k q H₃ H₄ H₅ q)
• H₃ H₄ H₅ q) = H₁ k q H₃ H₄ H₅ q)
theorem turing.TM2to1.supports_run {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} {Λ : Type u_3} [inhabited Λ] {σ : Type u_4} [inhabited σ] (S : finset Λ) {k : K} (s : turing.TM2to1.st_act k) (q : σ) :
inductive turing.TM2to1.Λ' {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} {Λ : Type u_3} [inhabited Λ] {σ : Type u_4} [inhabited σ] :
Type (max u_1 u_2 u_3 u_4)
• normal : Π {K : Type u_1} [_inst_1 : {Γ : K → Type u_2} {Λ : Type u_3} [_inst_2 : {σ : Type u_4} [_inst_3 : ,
• go : Π {K : Type u_1} [_inst_1 : {Γ : K → Type u_2} {Λ : Type u_3} [_inst_2 : {σ : Type u_4} [_inst_3 : (k : K),
• ret : Π {K : Type u_1} [_inst_1 : {Γ : K → Type u_2} {Λ : Type u_3} [_inst_2 : {σ : Type u_4} [_inst_3 : ,

The machine states of the TM2 emulator. We can either be in a normal state when waiting for the next TM2 action, or we can be in the "go" and "return" states to go to the top of the stack and return to the bottom, respectively.

Instances for `turing.TM2to1.Λ'`
@[protected, instance]
def turing.TM2to1.Λ'.inhabited {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} {Λ : Type u_3} [inhabited Λ] {σ : Type u_4} [inhabited σ] :
Equations
def turing.TM2to1.tr_st_act {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} {Λ : Type u_3} [inhabited Λ] {σ : Type u_4} [inhabited σ] {k : K}  :

The program corresponding to state transitions at the end of a stack. Here we start out just after the top of the stack, and should end just after the new top of the stack.

Equations
def turing.TM2to1.tr_init {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} (k : K) (L : list (Γ k)) :

The initial state for the TM2 emulator, given an initial TM2 state. All stacks start out empty except for the input stack, and the stack bottom mark is set at the head.

Equations
theorem turing.TM2to1.step_run {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} {Λ : Type u_3} [inhabited Λ] {σ : Type u_4} [inhabited σ] {k : K} (q : σ) (v : σ) (S : Π (k : K), list (Γ k)) (s : turing.TM2to1.st_act k) :
S = (S k) s) k (S k) s))
def turing.TM2to1.tr_normal {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} {Λ : Type u_3} [inhabited Λ] {σ : Type u_4} [inhabited σ] :

The translation of TM2 statements to TM1 statements. regular actions have direct equivalents, but stack actions are deferred by going to the corresponding `go` state, so that we can find the appropriate stack top.

Equations
theorem turing.TM2to1.tr_normal_run {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} {Λ : Type u_3} [inhabited Λ] {σ : Type u_4} [inhabited σ] {k : K} (s : turing.TM2to1.st_act k) (q : σ) :
= turing.TM1.stmt.goto (λ (_x : turing.TM2to1.Γ') (_x : σ), q)
noncomputable def turing.TM2to1.tr_stmts₁ {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} {Λ : Type u_3} [inhabited Λ] {σ : Type u_4} [inhabited σ] :

The set of machine states accessible from an initial TM2 statement.

Equations
theorem turing.TM2to1.tr_stmts₁_run {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} {Λ : Type u_3} [inhabited Λ] {σ : Type u_4} [inhabited σ] {k : K} {s : turing.TM2to1.st_act k} {q : σ} :
theorem turing.TM2to1.tr_respects_aux₂ {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} {Λ : Type u_3} [inhabited Λ] {σ : Type u_4} [inhabited σ] {k : K} {v : σ} {S : Π (k : K), list (Γ k)} {L : turing.list_blank (Π (k : K), option (Γ k))} (hL : ∀ (k : K), ) (o : turing.TM2to1.st_act k) :
let v' : σ := (S k) o, Sk' : list (Γ k) := (S k) o, S' : Π (a : K), list (Γ a) := Sk' in ∃ (L' : turing.list_blank (Π (k : K), option (Γ k))), (∀ (k : K), = turing.list_blank.mk (S' k)).reverse)
def turing.TM2to1.tr {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} {Λ : Type u_3} [inhabited Λ] {σ : Type u_4} [inhabited σ] (M : Λ → σ) :

The TM2 emulator machine states written as a TM1 program. This handles the `go` and `ret` states, which shuttle to and from a stack top.

Equations
inductive turing.TM2to1.tr_cfg {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} {Λ : Type u_3} [inhabited Λ] {σ : Type u_4} [inhabited σ] (M : Λ → σ) :
σ
• mk : ∀ {K : Type u_1} [_inst_1 : {Γ : K → Type u_2} {Λ : Type u_3} [_inst_2 : {σ : Type u_4} [_inst_3 : {M : Λ → σ} {q : option Λ} {v : σ} {S : Π (k : K), list (Γ k)} (L : turing.list_blank (Π (k : K), option (Γ k))), (∀ (k : K), {l := q, var := v, stk := S}

The relation between TM2 configurations and TM1 configurations of the TM2 emulator.

theorem turing.TM2to1.tr_respects_aux₁ {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} {Λ : Type u_3} [inhabited Λ] {σ : Type u_4} [inhabited σ] (M : Λ → σ) {k : K} (o : turing.TM2to1.st_act k) (q : σ) (v : σ) {S : list (Γ k)} {L : turing.list_blank (Π (k : K), option (Γ k))} (hL : = ) (n : ) (H : n S.length) :
{l := option.some q), var := v, tape := {l := option.some q), var := v, tape :=
theorem turing.TM2to1.tr_respects_aux₃ {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} {Λ : Type u_3} [inhabited Λ] {σ : Type u_4} [inhabited σ] (M : Λ → σ) {q : σ} {v : σ} {L : turing.list_blank (Π (k : K), option (Γ k))} (n : ) :
{l := , var := v, tape :=
theorem turing.TM2to1.tr_respects_aux {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} {Λ : Type u_3} [inhabited Λ] {σ : Type u_4} [inhabited σ] (M : Λ → σ) {q : σ} {v : σ} {T : turing.list_blank (Π (i : K), option (Γ i))} {k : K} {S : Π (k : K), list (Γ k)} (hT : ∀ (k : K), ) (o : turing.TM2to1.st_act k) (IH : ∀ {v : σ} {S : Π (k : K), list (Γ k)} {T : turing.list_blank (Π (k : K), option (Γ k))}, (∀ (k : K), (∃ (b : , S) b ) :
∃ (b : , S) b
theorem turing.TM2to1.tr_respects {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} {Λ : Type u_3} [inhabited Λ] {σ : Type u_4} [inhabited σ] (M : Λ → σ) :
theorem turing.TM2to1.tr_cfg_init {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} {Λ : Type u_3} [inhabited Λ] {σ : Type u_4} [inhabited σ] (M : Λ → σ) (k : K) (L : list (Γ k)) :
theorem turing.TM2to1.tr_eval_dom {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} {Λ : Type u_3} [inhabited Λ] {σ : Type u_4} [inhabited σ] (M : Λ → σ) (k : K) (L : list (Γ k)) :
L)).dom k L).dom
theorem turing.TM2to1.tr_eval {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} {Λ : Type u_3} [inhabited Λ] {σ : Type u_4} [inhabited σ] (M : Λ → σ) (k : K) (L : list (Γ k)) {L₂ : list (Γ k)} (H₁ : L₁ ) (H₂ : L₂ L) :
∃ (S : Π (k : K), list (Γ k)) (L' : turing.list_blank (Π (k : K), option (Γ k))), (∀ (k : K), = S k = L₂
noncomputable def turing.TM2to1.tr_supp {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} {Λ : Type u_3} [inhabited Λ] {σ : Type u_4} [inhabited σ] (M : Λ → σ) (S : finset Λ) :

The support of a set of TM2 states in the TM2 emulator.

Equations
theorem turing.TM2to1.tr_supports {K : Type u_1} [decidable_eq K] {Γ : K → Type u_2} {Λ : Type u_3} [inhabited Λ] {σ : Type u_4} [inhabited σ] (M : Λ → σ) {S : finset Λ} (ss : S) :