File: Mathlib/Computability/TMToPartrec.lean

full_name: Turing.ToPartrec.Code.tail
code: /-- The type of codes for primitive recursive functions. Unlike `Nat.Partrec.Code`, this uses a set
of operations on `List ℕ`. See `Code.eval` for a description of the behavior of the primitives. -/
inductive Code
  | zero'
  | succ
  | tail
  | cons : Code → Code → Code
  | comp : Code → Code → Code
  | case : Code → Code → Code
  | fix : Code → Code
  deriving DecidableEq, Inhabited
goal: Turing.ToPartrec.Code

full_name: Turing.ToPartrec.Code.zero'
code: /-- The type of codes for primitive recursive functions. Unlike `Nat.Partrec.Code`, this uses a set
of operations on `List ℕ`. See `Code.eval` for a description of the behavior of the primitives. -/
inductive Code
  | zero'
  | succ
  | tail
  | cons : Code → Code → Code
  | comp : Code → Code → Code
  | case : Code → Code → Code
  | fix : Code → Code
  deriving DecidableEq, Inhabited
goal: Turing.ToPartrec.Code

full_name: Turing.ToPartrec.Code.fix
code: /-- The type of codes for primitive recursive functions. Unlike `Nat.Partrec.Code`, this uses a set
of operations on `List ℕ`. See `Code.eval` for a description of the behavior of the primitives. -/
inductive Code
  | zero'
  | succ
  | tail
  | cons : Code → Code → Code
  | comp : Code → Code → Code
  | case : Code → Code → Code
  | fix : Code → Code
  deriving DecidableEq, Inhabited
goal: Turing.ToPartrec.Code → Turing.ToPartrec.Code

full_name: Turing.ToPartrec.instInhabitedCode
code: /-- The type of codes for primitive recursive functions. Unlike `Nat.Partrec.Code`, this uses a set
of operations on `List ℕ`. See `Code.eval` for a description of the behavior of the primitives. -/
inductive Code
  | zero'
  | succ
  | tail
  | cons : Code → Code → Code
  | comp : Code → Code → Code
  | case : Code → Code → Code
  | fix : Code → Code
  deriving DecidableEq, Inhabited
goal: Inhabited Turing.ToPartrec.Code

full_name: Turing.ToPartrec.Code.case
code: /-- The type of codes for primitive recursive functions. Unlike `Nat.Partrec.Code`, this uses a set
of operations on `List ℕ`. See `Code.eval` for a description of the behavior of the primitives. -/
inductive Code
  | zero'
  | succ
  | tail
  | cons : Code → Code → Code
  | comp : Code → Code → Code
  | case : Code → Code → Code
  | fix : Code → Code
  deriving DecidableEq, Inhabited
goal: Turing.ToPartrec.Code → Turing.ToPartrec.Code → Turing.ToPartrec.Code

full_name: Turing.ToPartrec.Code.succ
code: /-- The type of codes for primitive recursive functions. Unlike `Nat.Partrec.Code`, this uses a set
of operations on `List ℕ`. See `Code.eval` for a description of the behavior of the primitives. -/
inductive Code
  | zero'
  | succ
  | tail
  | cons : Code → Code → Code
  | comp : Code → Code → Code
  | case : Code → Code → Code
  | fix : Code → Code
  deriving DecidableEq, Inhabited
goal: Turing.ToPartrec.Code

full_name: Turing.ToPartrec.Code.cons
code: /-- The type of codes for primitive recursive functions. Unlike `Nat.Partrec.Code`, this uses a set
of operations on `List ℕ`. See `Code.eval` for a description of the behavior of the primitives. -/
inductive Code
  | zero'
  | succ
  | tail
  | cons : Code → Code → Code
  | comp : Code → Code → Code
  | case : Code → Code → Code
  | fix : Code → Code
  deriving DecidableEq, Inhabited
goal: Turing.ToPartrec.Code → Turing.ToPartrec.Code → Turing.ToPartrec.Code

full_name: Turing.ToPartrec.Code.comp
code: /-- The type of codes for primitive recursive functions. Unlike `Nat.Partrec.Code`, this uses a set
of operations on `List ℕ`. See `Code.eval` for a description of the behavior of the primitives. -/
inductive Code
  | zero'
  | succ
  | tail
  | cons : Code → Code → Code
  | comp : Code → Code → Code
  | case : Code → Code → Code
  | fix : Code → Code
  deriving DecidableEq, Inhabited
goal: Turing.ToPartrec.Code → Turing.ToPartrec.Code → Turing.ToPartrec.Code

full_name: Turing.ToPartrec.Code
code: /-- The type of codes for primitive recursive functions. Unlike `Nat.Partrec.Code`, this uses a set
of operations on `List ℕ`. See `Code.eval` for a description of the behavior of the primitives. -/
inductive Code
  | zero'
  | succ
  | tail
  | cons : Code → Code → Code
  | comp : Code → Code → Code
  | case : Code → Code → Code
  | fix : Code → Code
  deriving DecidableEq, Inhabited
goal: Type

full_name: Turing.ToPartrec.Code.eval
code: /-- The semantics of the `Code` primitives, as partial functions `List ℕ →. List ℕ`. By convention
we functions that return a single result return a singleton `[n]`, or in some cases `n :: v` where
`v` will be ignored by a subsequent function.

* `zero'` appends a `0` to the input. That is, `zero' v = 0 :: v`.
* `succ` returns the successor of the head of the input, defaulting to zero if there is no head:
  * `succ [] = [1]`
  * `succ (n :: v) = [n + 1]`
* `tail` returns the tail of the input
  * `tail [] = []`
  * `tail (n :: v) = v`
* `cons f fs` calls `f` and `fs` on the input and conses the results:
  * `cons f fs v = (f v).head :: fs v`
* `comp f g` calls `f` on the output of `g`:
  * `comp f g v = f (g v)`
* `case f g` cases on the head of the input, calling `f` or `g` depending on whether it is zero or
  a successor (similar to `Nat.casesOn`).
  * `case f g [] = f []`
  * `case f g (0 :: v) = f v`
  * `case f g (n+1 :: v) = g (n :: v)`
* `fix f` calls `f` repeatedly, using the head of the result of `f` to decide whether to call `f`
  again or finish:
  * `fix f v = []` if `f v = []`
  * `fix f v = w` if `f v = 0 :: w`
  * `fix f v = fix f w` if `f v = n+1 :: w` (the exact value of `n` is discarded)
-/
def Code.eval : Code → List ℕ →. List ℕ
  
goal: Turing.ToPartrec.Code → List ℕ →. List ℕ

full_name: Turing.ToPartrec.Code.zero'_eval
code: @[simp]
theorem zero'_eval : zero'.eval = fun v => pure (0 :: v) 
goal: Turing.ToPartrec.Code.zero'.eval = fun v => pure (0 :: v)

full_name: Turing.ToPartrec.Code.succ_eval
code: @[simp]
theorem succ_eval : succ.eval = fun v => pure [v.headI.succ] 
goal: Turing.ToPartrec.Code.succ.eval = fun v => pure [v.headI.succ]

full_name: Turing.ToPartrec.Code.tail_eval
code: @[simp]
theorem tail_eval : tail.eval = fun v => pure v.tail 
goal: Turing.ToPartrec.Code.tail.eval = fun v => pure v.tail

full_name: Turing.ToPartrec.Code.cons_eval
code: @[simp]
theorem cons_eval (f fs) : (cons f fs).eval = fun v => do {
    let n ← Code.eval f v
    let ns ← Code.eval fs v
    pure (n.headI :: ns) } 
goal: (f.cons fs).eval = fun v => do
  let n ← f.eval v
  let ns ← fs.eval v
  pure (n.headI :: ns)

full_name: Turing.ToPartrec.Code.comp_eval
code: @[simp]
theorem comp_eval (f g) : (comp f g).eval = fun v => g.eval v >>= f.eval 
goal: (f.comp g).eval = fun v => g.eval v >>= f.eval

full_name: Turing.ToPartrec.Code.case_eval
code: @[simp]
theorem case_eval (f g) :
    (case f g).eval = fun v => v.headI.rec (f.eval v.tail) fun y _ => g.eval (y::v.tail) 
goal: (f.case g).eval = fun v => Nat.rec (f.eval v.tail) (fun y x => g.eval (y :: v.tail)) v.headI

full_name: Turing.ToPartrec.Code.fix_eval
code: @[simp]
theorem fix_eval (f) : (fix f).eval =
    PFun.fix fun v => (f.eval v).map fun v =>
      if v.headI = 0 then Sum.inl v.tail else Sum.inr v.tail 
goal: f.fix.eval = PFun.fix fun v => Part.map (fun v => if v.headI = 0 then Sum.inl v.tail else Sum.inr v.tail) (f.eval v)

full_name: Turing.ToPartrec.Code.nil
code: /-- `nil` is the constant nil function: `nil v = []`. -/
def nil : Code 
goal: Turing.ToPartrec.Code

full_name: Turing.ToPartrec.Code.nil_eval
code: @[simp]
theorem nil_eval (v) : nil.eval v = pure [] 
goal: Turing.ToPartrec.Code.nil.eval v = pure []

full_name: Turing.ToPartrec.Code.id
code: /-- `id` is the identity function: `id v = v`. -/
def id : Code 
goal: Turing.ToPartrec.Code

full_name: Turing.ToPartrec.Code.id_eval
code: @[simp]
theorem id_eval (v) : id.eval v = pure v 
goal: Turing.ToPartrec.Code.id.eval v = pure v

full_name: Turing.ToPartrec.Code.head
code: /-- `head` gets the head of the input list: `head [] = [0]`, `head (n :: v) = [n]`. -/
def head : Code 
goal: Turing.ToPartrec.Code

full_name: Turing.ToPartrec.Code.head_eval
code: @[simp]
theorem head_eval (v) : head.eval v = pure [v.headI] 
goal: Turing.ToPartrec.Code.head.eval v = pure [v.headI]

full_name: Turing.ToPartrec.Code.zero
code: /-- `zero` is the constant zero function: `zero v = [0]`. -/
def zero : Code 
goal: Turing.ToPartrec.Code

full_name: Turing.ToPartrec.Code.zero_eval
code: @[simp]
theorem zero_eval (v) : zero.eval v = pure [0] 
goal: Turing.ToPartrec.Code.zero.eval v = pure [0]

full_name: Turing.ToPartrec.Code.pred
code: /-- `pred` returns the predecessor of the head of the input:
`pred [] = [0]`, `pred (0 :: v) = [0]`, `pred (n+1 :: v) = [n]`. -/
def pred : Code 
goal: Turing.ToPartrec.Code

full_name: Turing.ToPartrec.Code.pred_eval
code: @[simp]
theorem pred_eval (v) : pred.eval v = pure [v.headI.pred] 
goal: Turing.ToPartrec.Code.pred.eval v = pure [v.headI.pred]

full_name: Turing.ToPartrec.Code.rfind
code: /-- `rfind f` performs the function of the `rfind` primitive of partial recursive functions.
`rfind f v` returns the smallest `n` such that `(f (n :: v)).head = 0`.

It is implemented as:

    rfind f v = pred (fix (fun (n::v) => f (n::v) :: n+1 :: v) (0 :: v))

The idea is that the initial state is `0 :: v`, and the `fix` keeps `n :: v` as its internal state;
it calls `f (n :: v)` as the exit test and `n+1 :: v` as the next state. At the end we get
`n+1 :: v` where `n` is the desired output, and `pred (n+1 :: v) = [n]` returns the result.
 -/
def rfind (f : Code) : Code 
goal: Turing.ToPartrec.Code

full_name: Turing.ToPartrec.Code.prec
code: /-- `prec f g` implements the `prec` (primitive recursion) operation of partial recursive
functions. `prec f g` evaluates as:

* `prec f g [] = [f []]`
* `prec f g (0 :: v) = [f v]`
* `prec f g (n+1 :: v) = [g (n :: prec f g (n :: v) :: v)]`

It is implemented as:

    G (a :: b :: IH :: v) = (b :: a+1 :: b-1 :: g (a :: IH :: v) :: v)
    F (0 :: f_v :: v) = (f_v :: v)
    F (n+1 :: f_v :: v) = (fix G (0 :: n :: f_v :: v)).tail.tail
    prec f g (a :: v) = [(F (a :: f v :: v)).head]

Because `fix` always evaluates its body at least once, we must special case the `0` case to avoid
calling `g` more times than necessary (which could be bad if `g` diverges). If the input is
`0 :: v`, then `F (0 :: f v :: v) = (f v :: v)` so we return `[f v]`. If the input is `n+1 :: v`,
we evaluate the function from the bottom up, with initial state `0 :: n :: f v :: v`. The first
number counts up, providing arguments for the applications to `g`, while the second number counts
down, providing the exit condition (this is the initial `b` in the return value of `G`, which is
stripped by `fix`). After the `fix` is complete, the final state is `n :: 0 :: res :: v` where
`res` is the desired result, and the rest reduces this to `[res]`. -/
def prec (f g : Code) : Code 
goal: Turing.ToPartrec.Code

full_name: Turing.ToPartrec.Code.exists_code.comp
code: theorem exists_code.comp {m n} {f : Vector ℕ n →. ℕ} {g : Fin n → Vector ℕ m →. ℕ}
    (hf : ∃ c : Code, ∀ v : Vector ℕ n, c.eval v.1 = pure <$> f v)
    (hg : ∀ i, ∃ c : Code, ∀ v : Vector ℕ m, c.eval v.1 = pure <$> g i v) :
    ∃ c : Code, ∀ v : Vector ℕ m, c.eval v.1 = pure <$> ((Vector.mOfFn fun i => g i v) >>= f) 
goal: ∃ c, ∀ (v : Mathlib.Vector ℕ m), c.eval ↑v = pure <$> ((Mathlib.Vector.mOfFn fun i => g i v) >>= f)

full_name: Turing.ToPartrec.Code.exists_code
code: theorem exists_code {n} {f : Vector ℕ n →. ℕ} (hf : Nat.Partrec' f) :
    ∃ c : Code, ∀ v : Vector ℕ n, c.eval v.1 = pure <$> f v 
goal: ∃ c, ∀ (v : Mathlib.Vector ℕ n), c.eval ↑v = pure <$> f v

full_name: Turing.ToPartrec.Cont.comp
code: /-- The type of continuations, built up during evaluation of a `Code` expression. -/
inductive Cont
  | halt
  | cons₁ : Code → List ℕ → Cont → Cont
  | cons₂ : List ℕ → Cont → Cont
  | comp : Code → Cont → Cont
  | fix : Code → Cont → Cont
  deriving Inhabited
goal: Turing.ToPartrec.Code → Turing.ToPartrec.Cont → Turing.ToPartrec.Cont

full_name: Turing.ToPartrec.Cont
code: /-- The type of continuations, built up during evaluation of a `Code` expression. -/
inductive Cont
  | halt
  | cons₁ : Code → List ℕ → Cont → Cont
  | cons₂ : List ℕ → Cont → Cont
  | comp : Code → Cont → Cont
  | fix : Code → Cont → Cont
  deriving Inhabited
goal: Type

full_name: Turing.ToPartrec.instInhabitedCont
code: /-- The type of continuations, built up during evaluation of a `Code` expression. -/
inductive Cont
  | halt
  | cons₁ : Code → List ℕ → Cont → Cont
  | cons₂ : List ℕ → Cont → Cont
  | comp : Code → Cont → Cont
  | fix : Code → Cont → Cont
  deriving Inhabited
goal: Inhabited Turing.ToPartrec.Cont

full_name: Turing.ToPartrec.Cont.halt
code: /-- The type of continuations, built up during evaluation of a `Code` expression. -/
inductive Cont
  | halt
  | cons₁ : Code → List ℕ → Cont → Cont
  | cons₂ : List ℕ → Cont → Cont
  | comp : Code → Cont → Cont
  | fix : Code → Cont → Cont
  deriving Inhabited
goal: Turing.ToPartrec.Cont

full_name: Turing.ToPartrec.Cont.cons₁
code: /-- The type of continuations, built up during evaluation of a `Code` expression. -/
inductive Cont
  | halt
  | cons₁ : Code → List ℕ → Cont → Cont
  | cons₂ : List ℕ → Cont → Cont
  | comp : Code → Cont → Cont
  | fix : Code → Cont → Cont
  deriving Inhabited
goal: Turing.ToPartrec.Code → List ℕ → Turing.ToPartrec.Cont → Turing.ToPartrec.Cont

full_name: Turing.ToPartrec.Cont.fix
code: /-- The type of continuations, built up during evaluation of a `Code` expression. -/
inductive Cont
  | halt
  | cons₁ : Code → List ℕ → Cont → Cont
  | cons₂ : List ℕ → Cont → Cont
  | comp : Code → Cont → Cont
  | fix : Code → Cont → Cont
  deriving Inhabited
goal: Turing.ToPartrec.Code → Turing.ToPartrec.Cont → Turing.ToPartrec.Cont

full_name: Turing.ToPartrec.Cont.cons₂
code: /-- The type of continuations, built up during evaluation of a `Code` expression. -/
inductive Cont
  | halt
  | cons₁ : Code → List ℕ → Cont → Cont
  | cons₂ : List ℕ → Cont → Cont
  | comp : Code → Cont → Cont
  | fix : Code → Cont → Cont
  deriving Inhabited
goal: List ℕ → Turing.ToPartrec.Cont → Turing.ToPartrec.Cont

full_name: Turing.ToPartrec.Cont.eval
code: /-- The semantics of a continuation. -/
def Cont.eval : Cont → List ℕ →. List ℕ
  
goal: Turing.ToPartrec.Cont → List ℕ →. List ℕ

full_name: Turing.ToPartrec.Cfg.ret
code: /-- The set of configurations of the machine:

* `halt v`: The machine is about to stop and `v : List ℕ` is the result.
* `ret k v`: The machine is about to pass `v : List ℕ` to continuation `k : cont`.

We don't have a state corresponding to normal evaluation because these are evaluated immediately
to a `ret` "in zero steps" using the `stepNormal` function. -/
inductive Cfg
  | halt : List ℕ → Cfg
  | ret : Cont → List ℕ → Cfg
  deriving Inhabited
goal: Turing.ToPartrec.Cont → List ℕ → Turing.ToPartrec.Cfg

full_name: Turing.ToPartrec.Cfg
code: /-- The set of configurations of the machine:

* `halt v`: The machine is about to stop and `v : List ℕ` is the result.
* `ret k v`: The machine is about to pass `v : List ℕ` to continuation `k : cont`.

We don't have a state corresponding to normal evaluation because these are evaluated immediately
to a `ret` "in zero steps" using the `stepNormal` function. -/
inductive Cfg
  | halt : List ℕ → Cfg
  | ret : Cont → List ℕ → Cfg
  deriving Inhabited
goal: Type

full_name: Turing.ToPartrec.instInhabitedCfg
code: /-- The set of configurations of the machine:

* `halt v`: The machine is about to stop and `v : List ℕ` is the result.
* `ret k v`: The machine is about to pass `v : List ℕ` to continuation `k : cont`.

We don't have a state corresponding to normal evaluation because these are evaluated immediately
to a `ret` "in zero steps" using the `stepNormal` function. -/
inductive Cfg
  | halt : List ℕ → Cfg
  | ret : Cont → List ℕ → Cfg
  deriving Inhabited
goal: Inhabited Turing.ToPartrec.Cfg

full_name: Turing.ToPartrec.Cfg.halt
code: /-- The set of configurations of the machine:

* `halt v`: The machine is about to stop and `v : List ℕ` is the result.
* `ret k v`: The machine is about to pass `v : List ℕ` to continuation `k : cont`.

We don't have a state corresponding to normal evaluation because these are evaluated immediately
to a `ret` "in zero steps" using the `stepNormal` function. -/
inductive Cfg
  | halt : List ℕ → Cfg
  | ret : Cont → List ℕ → Cfg
  deriving Inhabited
goal: List ℕ → Turing.ToPartrec.Cfg

full_name: Turing.ToPartrec.stepNormal
code: /-- Evaluating `c : Code` in a continuation `k : Cont` and input `v : List ℕ`. This goes by
recursion on `c`, building an augmented continuation and a value to pass to it.

* `zero' v = 0 :: v` evaluates immediately, so we return it to the parent continuation
* `succ v = [v.headI.succ]` evaluates immediately, so we return it to the parent continuation
* `tail v = v.tail` evaluates immediately, so we return it to the parent continuation
* `cons f fs v = (f v).headI :: fs v` requires two sub-evaluations, so we evaluate
  `f v` in the continuation `k (_.headI :: fs v)` (called `Cont.cons₁ fs v k`)
* `comp f g v = f (g v)` requires two sub-evaluations, so we evaluate
  `g v` in the continuation `k (f _)` (called `Cont.comp f k`)
* `case f g v = v.head.casesOn (f v.tail) (fun n => g (n :: v.tail))` has the information needed
  to evaluate the case statement, so we do that and transition to either
  `f v` or `g (n :: v.tail)`.
* `fix f v = let v' := f v; if v'.headI = 0 then k v'.tail else fix f v'.tail`
  needs to first evaluate `f v`, so we do that and leave the rest for the continuation (called
  `Cont.fix f k`)
-/
def stepNormal : Code → Cont → List ℕ → Cfg
  
goal: Turing.ToPartrec.Code → Turing.ToPartrec.Cont → List ℕ → Turing.ToPartrec.Cfg

full_name: Turing.ToPartrec.stepRet
code: /-- Evaluating a continuation `k : Cont` on input `v : List ℕ`. This is the second part of
evaluation, when we receive results from continuations built by `stepNormal`.

* `Cont.halt v = v`, so we are done and transition to the `Cfg.halt v` state
* `Cont.cons₁ fs as k v = k (v.headI :: fs as)`, so we evaluate `fs as` now with the continuation
  `k (v.headI :: _)` (called `cons₂ v k`).
* `Cont.cons₂ ns k v = k (ns.headI :: v)`, where we now have everything we need to evaluate
  `ns.headI :: v`, so we return it to `k`.
* `Cont.comp f k v = k (f v)`, so we call `f v` with `k` as the continuation.
* `Cont.fix f k v = k (if v.headI = 0 then k v.tail else fix f v.tail)`, where `v` is a value,
  so we evaluate the if statement and either call `k` with `v.tail`, or call `fix f v` with `k` as
  the continuation (which immediately calls `f` with `Cont.fix f k` as the continuation).
-/
def stepRet : Cont → List ℕ → Cfg
  
goal: Turing.ToPartrec.Cont → List ℕ → Turing.ToPartrec.Cfg

full_name: Turing.ToPartrec.step
code: /-- If we are not done (in `Cfg.halt` state), then we must be still stuck on a continuation, so
this main loop calls `stepRet` with the new continuation. The overall `step` function transitions
from one `Cfg` to another, only halting at the `Cfg.halt` state. -/
def step : Cfg → Option Cfg
  
goal: Turing.ToPartrec.Cfg → Option Turing.ToPartrec.Cfg

full_name: Turing.ToPartrec.Cont.then
code: /-- In order to extract a compositional semantics from the sequential execution behavior of
configurations, we observe that continuations have a monoid structure, with `Cont.halt` as the unit
and `Cont.then` as the multiplication. `Cont.then k₁ k₂` runs `k₁` until it halts, and then takes
the result of `k₁` and passes it to `k₂`.

We will not prove it is associative (although it is), but we are instead interested in the
associativity law `k₂ (eval c k₁) = eval c (k₁.then k₂)`. This holds at both the sequential and
compositional levels, and allows us to express running a machine without the ambient continuation
and relate it to the original machine's evaluation steps. In the literature this is usually
where one uses Turing machines embedded inside other Turing machines, but this approach allows us
to avoid changing the ambient type `Cfg` in the middle of the recursion.
-/
def Cont.then : Cont → Cont → Cont
  
goal: Turing.ToPartrec.Cont → Turing.ToPartrec.Cont → Turing.ToPartrec.Cont

full_name: Turing.ToPartrec.Cont.then_eval
code: theorem Cont.then_eval {k k' : Cont} {v} : (k.then k').eval v = k.eval v >>= k'.eval 
goal: (k.then k').eval v = k.eval v >>= k'.eval

full_name: Turing.ToPartrec.Cfg.then
code: /-- The `then k` function is a "configuration homomorphism". Its operation on states is to append
`k` to the continuation of a `Cfg.ret` state, and to run `k` on `v` if we are in the `Cfg.halt v`
state. -/
def Cfg.then : Cfg → Cont → Cfg
  
goal: Turing.ToPartrec.Cfg → Turing.ToPartrec.Cont → Turing.ToPartrec.Cfg

full_name: Turing.ToPartrec.stepNormal_then
code: /-- The `stepNormal` function respects the `then k'` homomorphism. Note that this is an exact
equality, not a simulation; the original and embedded machines move in lock-step until the
embedded machine reaches the halt state. -/
theorem stepNormal_then (c) (k k' : Cont) (v) :
    stepNormal c (k.then k') v = (stepNormal c k v).then k' 
goal: Turing.ToPartrec.stepNormal c (k.then k') v = (Turing.ToPartrec.stepNormal c k v).then k'

full_name: Turing.ToPartrec.stepRet_then
code: /-- The `stepRet` function respects the `then k'` homomorphism. Note that this is an exact
equality, not a simulation; the original and embedded machines move in lock-step until the
embedded machine reaches the halt state. -/
theorem stepRet_then {k k' : Cont} {v} : stepRet (k.then k') v = (stepRet k v).then k' 
goal: Turing.ToPartrec.stepRet (k.then k') v = (Turing.ToPartrec.stepRet k v).then k'

full_name: Turing.ToPartrec.Code.Ok
code: /-- This is a temporary definition, because we will prove in `code_is_ok` that it always holds.
It asserts that `c` is semantically correct; that is, for any `k` and `v`,
`eval (stepNormal c k v) = eval (Cfg.ret k (Code.eval c v))`, as an equality of partial values
(so one diverges iff the other does).

In particular, we can let `k = Cont.halt`, and then this asserts that `stepNormal c Cont.halt v`
evaluates to `Cfg.halt (Code.eval c v)`. -/
def Code.Ok (c : Code) 
goal: Prop

full_name: Turing.ToPartrec.Code.Ok.zero
code: theorem Code.Ok.zero {c} (h : Code.Ok c) {v} :
    Turing.eval step (stepNormal c Cont.halt v) = Cfg.halt <$> Code.eval c v 
goal: Turing.eval Turing.ToPartrec.step (Turing.ToPartrec.stepNormal c Turing.ToPartrec.Cont.halt v) =
  Turing.ToPartrec.Cfg.halt <$> c.eval v

full_name: Turing.ToPartrec.stepNormal.is_ret
code: theorem stepNormal.is_ret (c k v) : ∃ k' v', stepNormal c k v = Cfg.ret k' v' 
goal: ∃ k' v', Turing.ToPartrec.stepNormal c k v = Turing.ToPartrec.Cfg.ret k' v'

full_name: Turing.ToPartrec.cont_eval_fix
code: theorem cont_eval_fix {f k v} (fok : Code.Ok f) :
    Turing.eval step (stepNormal f (Cont.fix f k) v) =
      f.fix.eval v >>= fun v => Turing.eval step (Cfg.ret k v) 
goal: Turing.eval Turing.ToPartrec.step (Turing.ToPartrec.stepNormal f (Turing.ToPartrec.Cont.fix f k) v) = do
  let v ← f.fix.eval v
  Turing.eval Turing.ToPartrec.step (Turing.ToPartrec.Cfg.ret k v)

full_name: Turing.ToPartrec.code_is_ok
code: theorem code_is_ok (c) : Code.Ok c 
goal: c.Ok

full_name: Turing.ToPartrec.stepNormal_eval
code: theorem stepNormal_eval (c v) : eval step (stepNormal c Cont.halt v) = Cfg.halt <$> c.eval v 
goal: Turing.eval Turing.ToPartrec.step (Turing.ToPartrec.stepNormal c Turing.ToPartrec.Cont.halt v) =
  Turing.ToPartrec.Cfg.halt <$> c.eval v

full_name: Turing.ToPartrec.stepRet_eval
code: theorem stepRet_eval {k v} : eval step (stepRet k v) = Cfg.halt <$> k.eval v 
goal: Turing.eval Turing.ToPartrec.step (Turing.ToPartrec.stepRet k v) = Turing.ToPartrec.Cfg.halt <$> k.eval v

full_name: Turing.PartrecToTM2.Γ'.bit0
code: /-- The alphabet for the stacks in the program. `bit0` and `bit1` are used to represent `ℕ` values
as lists of binary digits, `cons` is used to separate `List ℕ` values, and `consₗ` is used to
separate `List (List ℕ)` values. See the section documentation. -/
inductive Γ'
  | consₗ
  | cons
  | bit0
  | bit1
  deriving DecidableEq, Inhabited, Fintype
goal: Turing.PartrecToTM2.Γ'

full_name: Turing.PartrecToTM2.Γ'
code: /-- The alphabet for the stacks in the program. `bit0` and `bit1` are used to represent `ℕ` values
as lists of binary digits, `cons` is used to separate `List ℕ` values, and `consₗ` is used to
separate `List (List ℕ)` values. See the section documentation. -/
inductive Γ'
  | consₗ
  | cons
  | bit0
  | bit1
  deriving DecidableEq, Inhabited, Fintype
goal: Type

full_name: Turing.PartrecToTM2.Γ'.bit1
code: /-- The alphabet for the stacks in the program. `bit0` and `bit1` are used to represent `ℕ` values
as lists of binary digits, `cons` is used to separate `List ℕ` values, and `consₗ` is used to
separate `List (List ℕ)` values. See the section documentation. -/
inductive Γ'
  | consₗ
  | cons
  | bit0
  | bit1
  deriving DecidableEq, Inhabited, Fintype
goal: Turing.PartrecToTM2.Γ'

full_name: Turing.PartrecToTM2.instDecidableEqΓ'
code: /-- The alphabet for the stacks in the program. `bit0` and `bit1` are used to represent `ℕ` values
as lists of binary digits, `cons` is used to separate `List ℕ` values, and `consₗ` is used to
separate `List (List ℕ)` values. See the section documentation. -/
inductive Γ'
  | consₗ
  | cons
  | bit0
  | bit1
  deriving DecidableEq, Inhabited, Fintype
goal: DecidableEq Turing.PartrecToTM2.Γ'

full_name: Turing.PartrecToTM2.Γ'.cons
code: /-- The alphabet for the stacks in the program. `bit0` and `bit1` are used to represent `ℕ` values
as lists of binary digits, `cons` is used to separate `List ℕ` values, and `consₗ` is used to
separate `List (List ℕ)` values. See the section documentation. -/
inductive Γ'
  | consₗ
  | cons
  | bit0
  | bit1
  deriving DecidableEq, Inhabited, Fintype
goal: Turing.PartrecToTM2.Γ'

full_name: Turing.PartrecToTM2.Γ'.consₗ
code: /-- The alphabet for the stacks in the program. `bit0` and `bit1` are used to represent `ℕ` values
as lists of binary digits, `cons` is used to separate `List ℕ` values, and `consₗ` is used to
separate `List (List ℕ)` values. See the section documentation. -/
inductive Γ'
  | consₗ
  | cons
  | bit0
  | bit1
  deriving DecidableEq, Inhabited, Fintype
goal: Turing.PartrecToTM2.Γ'

full_name: Turing.PartrecToTM2.instFintypeΓ'
code: /-- The alphabet for the stacks in the program. `bit0` and `bit1` are used to represent `ℕ` values
as lists of binary digits, `cons` is used to separate `List ℕ` values, and `consₗ` is used to
separate `List (List ℕ)` values. See the section documentation. -/
inductive Γ'
  | consₗ
  | cons
  | bit0
  | bit1
  deriving DecidableEq, Inhabited, Fintype
goal: Fintype Turing.PartrecToTM2.Γ'

full_name: Turing.PartrecToTM2.instInhabitedΓ'
code: /-- The alphabet for the stacks in the program. `bit0` and `bit1` are used to represent `ℕ` values
as lists of binary digits, `cons` is used to separate `List ℕ` values, and `consₗ` is used to
separate `List (List ℕ)` values. See the section documentation. -/
inductive Γ'
  | consₗ
  | cons
  | bit0
  | bit1
  deriving DecidableEq, Inhabited, Fintype
goal: Inhabited Turing.PartrecToTM2.Γ'

full_name: Turing.PartrecToTM2.instInhabitedK'
code: /-- The four stacks used by the program. `main` is used to store the input value in `trNormal`
mode and the output value in `Λ'.ret` mode, while `stack` is used to keep all the data for the
continuations. `rev` is used to store reversed lists when transferring values between stacks, and
`aux` is only used once in `cons₁`. See the section documentation. -/
inductive K'
  | main
  | rev
  | aux
  | stack
  deriving DecidableEq, Inhabited
goal: Inhabited Turing.PartrecToTM2.K'

full_name: Turing.PartrecToTM2.K'.aux
code: /-- The four stacks used by the program. `main` is used to store the input value in `trNormal`
mode and the output value in `Λ'.ret` mode, while `stack` is used to keep all the data for the
continuations. `rev` is used to store reversed lists when transferring values between stacks, and
`aux` is only used once in `cons₁`. See the section documentation. -/
inductive K'
  | main
  | rev
  | aux
  | stack
  deriving DecidableEq, Inhabited
goal: Turing.PartrecToTM2.K'

full_name: Turing.PartrecToTM2.K'.main
code: /-- The four stacks used by the program. `main` is used to store the input value in `trNormal`
mode and the output value in `Λ'.ret` mode, while `stack` is used to keep all the data for the
continuations. `rev` is used to store reversed lists when transferring values between stacks, and
`aux` is only used once in `cons₁`. See the section documentation. -/
inductive K'
  | main
  | rev
  | aux
  | stack
  deriving DecidableEq, Inhabited
goal: Turing.PartrecToTM2.K'

full_name: Turing.PartrecToTM2.K'.rev
code: /-- The four stacks used by the program. `main` is used to store the input value in `trNormal`
mode and the output value in `Λ'.ret` mode, while `stack` is used to keep all the data for the
continuations. `rev` is used to store reversed lists when transferring values between stacks, and
`aux` is only used once in `cons₁`. See the section documentation. -/
inductive K'
  | main
  | rev
  | aux
  | stack
  deriving DecidableEq, Inhabited
goal: Turing.PartrecToTM2.K'

full_name: Turing.PartrecToTM2.K'
code: /-- The four stacks used by the program. `main` is used to store the input value in `trNormal`
mode and the output value in `Λ'.ret` mode, while `stack` is used to keep all the data for the
continuations. `rev` is used to store reversed lists when transferring values between stacks, and
`aux` is only used once in `cons₁`. See the section documentation. -/
inductive K'
  | main
  | rev
  | aux
  | stack
  deriving DecidableEq, Inhabited
goal: Type

full_name: Turing.PartrecToTM2.K'.stack
code: /-- The four stacks used by the program. `main` is used to store the input value in `trNormal`
mode and the output value in `Λ'.ret` mode, while `stack` is used to keep all the data for the
continuations. `rev` is used to store reversed lists when transferring values between stacks, and
`aux` is only used once in `cons₁`. See the section documentation. -/
inductive K'
  | main
  | rev
  | aux
  | stack
  deriving DecidableEq, Inhabited
goal: Turing.PartrecToTM2.K'

full_name: Turing.PartrecToTM2.instDecidableEqK'
code: /-- The four stacks used by the program. `main` is used to store the input value in `trNormal`
mode and the output value in `Λ'.ret` mode, while `stack` is used to keep all the data for the
continuations. `rev` is used to store reversed lists when transferring values between stacks, and
`aux` is only used once in `cons₁`. See the section documentation. -/
inductive K'
  | main
  | rev
  | aux
  | stack
  deriving DecidableEq, Inhabited
goal: DecidableEq Turing.PartrecToTM2.K'

full_name: Turing.PartrecToTM2.Cont'.cons₁
code: /-- Continuations as in `ToPartrec.Cont` but with the data removed. This is done because we want
the set of all continuations in the program to be finite (so that it can ultimately be encoded into
the finite state machine of a Turing machine), but a continuation can handle a potentially infinite
number of data values during execution. -/
inductive Cont'
  | halt
  | cons₁ : Code → Cont' → Cont'
  | cons₂ : Cont' → Cont'
  | comp : Code → Cont' → Cont'
  | fix : Code → Cont' → Cont'
  deriving DecidableEq, Inhabited
goal: Turing.ToPartrec.Code → Turing.PartrecToTM2.Cont' → Turing.PartrecToTM2.Cont'

full_name: Turing.PartrecToTM2.Cont'.cons₂
code: /-- Continuations as in `ToPartrec.Cont` but with the data removed. This is done because we want
the set of all continuations in the program to be finite (so that it can ultimately be encoded into
the finite state machine of a Turing machine), but a continuation can handle a potentially infinite
number of data values during execution. -/
inductive Cont'
  | halt
  | cons₁ : Code → Cont' → Cont'
  | cons₂ : Cont' → Cont'
  | comp : Code → Cont' → Cont'
  | fix : Code → Cont' → Cont'
  deriving DecidableEq, Inhabited
goal: Turing.PartrecToTM2.Cont' → Turing.PartrecToTM2.Cont'

full_name: Turing.PartrecToTM2.Cont'.fix
code: /-- Continuations as in `ToPartrec.Cont` but with the data removed. This is done because we want
the set of all continuations in the program to be finite (so that it can ultimately be encoded into
the finite state machine of a Turing machine), but a continuation can handle a potentially infinite
number of data values during execution. -/
inductive Cont'
  | halt
  | cons₁ : Code → Cont' → Cont'
  | cons₂ : Cont' → Cont'
  | comp : Code → Cont' → Cont'
  | fix : Code → Cont' → Cont'
  deriving DecidableEq, Inhabited
goal: Turing.ToPartrec.Code → Turing.PartrecToTM2.Cont' → Turing.PartrecToTM2.Cont'

full_name: Turing.PartrecToTM2.instInhabitedCont'
code: /-- Continuations as in `ToPartrec.Cont` but with the data removed. This is done because we want
the set of all continuations in the program to be finite (so that it can ultimately be encoded into
the finite state machine of a Turing machine), but a continuation can handle a potentially infinite
number of data values during execution. -/
inductive Cont'
  | halt
  | cons₁ : Code → Cont' → Cont'
  | cons₂ : Cont' → Cont'
  | comp : Code → Cont' → Cont'
  | fix : Code → Cont' → Cont'
  deriving DecidableEq, Inhabited
goal: Inhabited Turing.PartrecToTM2.Cont'

full_name: Turing.PartrecToTM2.Cont'.comp
code: /-- Continuations as in `ToPartrec.Cont` but with the data removed. This is done because we want
the set of all continuations in the program to be finite (so that it can ultimately be encoded into
the finite state machine of a Turing machine), but a continuation can handle a potentially infinite
number of data values during execution. -/
inductive Cont'
  | halt
  | cons₁ : Code → Cont' → Cont'
  | cons₂ : Cont' → Cont'
  | comp : Code → Cont' → Cont'
  | fix : Code → Cont' → Cont'
  deriving DecidableEq, Inhabited
goal: Turing.ToPartrec.Code → Turing.PartrecToTM2.Cont' → Turing.PartrecToTM2.Cont'

full_name: Turing.PartrecToTM2.Cont'
code: /-- Continuations as in `ToPartrec.Cont` but with the data removed. This is done because we want
the set of all continuations in the program to be finite (so that it can ultimately be encoded into
the finite state machine of a Turing machine), but a continuation can handle a potentially infinite
number of data values during execution. -/
inductive Cont'
  | halt
  | cons₁ : Code → Cont' → Cont'
  | cons₂ : Cont' → Cont'
  | comp : Code → Cont' → Cont'
  | fix : Code → Cont' → Cont'
  deriving DecidableEq, Inhabited
goal: Type

full_name: Turing.PartrecToTM2.Cont'.halt
code: /-- Continuations as in `ToPartrec.Cont` but with the data removed. This is done because we want
the set of all continuations in the program to be finite (so that it can ultimately be encoded into
the finite state machine of a Turing machine), but a continuation can handle a potentially infinite
number of data values during execution. -/
inductive Cont'
  | halt
  | cons₁ : Code → Cont' → Cont'
  | cons₂ : Cont' → Cont'
  | comp : Code → Cont' → Cont'
  | fix : Code → Cont' → Cont'
  deriving DecidableEq, Inhabited
goal: Turing.PartrecToTM2.Cont'

full_name: Turing.PartrecToTM2.Λ'.pred
code: /-- The set of program positions. We make extensive use of inductive types here to let us describe
"subroutines"; for example `clear p k q` is a program that clears stack `k`, then does `q` where
`q` is another label. In order to prevent this from resulting in an infinite number of distinct
accessible states, we are careful to be non-recursive (although loops are okay). See the section
documentation for a description of all the programs. -/
inductive Λ'
  | move (p : Γ' → Bool) (k₁ k₂ : K') (q : Λ')
  | clear (p : Γ' → Bool) (k : K') (q : Λ')
  | copy (q : Λ')
  | push (k : K') (s : Option Γ' → Option Γ') (q : Λ')
  | read (f : Option Γ' → Λ')
  | succ (q : Λ')
  | pred (q₁ q₂ : Λ')
  | ret (k : Cont')
goal: Turing.PartrecToTM2.Λ'

full_name: Turing.PartrecToTM2.Λ'.move
code: /-- The set of program positions. We make extensive use of inductive types here to let us describe
"subroutines"; for example `clear p k q` is a program that clears stack `k`, then does `q` where
`q` is another label. In order to prevent this from resulting in an infinite number of distinct
accessible states, we are careful to be non-recursive (although loops are okay). See the section
documentation for a description of all the programs. -/
inductive Λ'
  | move (p : Γ' → Bool) (k₁ k₂ : K') (q : Λ')
  | clear (p : Γ' → Bool) (k : K') (q : Λ')
  | copy (q : Λ')
  | push (k : K') (s : Option Γ' → Option Γ') (q : Λ')
  | read (f : Option Γ' → Λ')
  | succ (q : Λ')
  | pred (q₁ q₂ : Λ')
  | ret (k : Cont')
goal: Turing.PartrecToTM2.Λ'

full_name: Turing.PartrecToTM2.Λ'
code: /-- The set of program positions. We make extensive use of inductive types here to let us describe
"subroutines"; for example `clear p k q` is a program that clears stack `k`, then does `q` where
`q` is another label. In order to prevent this from resulting in an infinite number of distinct
accessible states, we are careful to be non-recursive (although loops are okay). See the section
documentation for a description of all the programs. -/
inductive Λ'
  | move (p : Γ' → Bool) (k₁ k₂ : K') (q : Λ')
  | clear (p : Γ' → Bool) (k : K') (q : Λ')
  | copy (q : Λ')
  | push (k : K') (s : Option Γ' → Option Γ') (q : Λ')
  | read (f : Option Γ' → Λ')
  | succ (q : Λ')
  | pred (q₁ q₂ : Λ')
  | ret (k : Cont')
goal: Type

full_name: Turing.PartrecToTM2.Λ'.clear
code: /-- The set of program positions. We make extensive use of inductive types here to let us describe
"subroutines"; for example `clear p k q` is a program that clears stack `k`, then does `q` where
`q` is another label. In order to prevent this from resulting in an infinite number of distinct
accessible states, we are careful to be non-recursive (although loops are okay). See the section
documentation for a description of all the programs. -/
inductive Λ'
  | move (p : Γ' → Bool) (k₁ k₂ : K') (q : Λ')
  | clear (p : Γ' → Bool) (k : K') (q : Λ')
  | copy (q : Λ')
  | push (k : K') (s : Option Γ' → Option Γ') (q : Λ')
  | read (f : Option Γ' → Λ')
  | succ (q : Λ')
  | pred (q₁ q₂ : Λ')
  | ret (k : Cont')
goal: Turing.PartrecToTM2.Λ'

full_name: Turing.PartrecToTM2.Λ'.succ
code: /-- The set of program positions. We make extensive use of inductive types here to let us describe
"subroutines"; for example `clear p k q` is a program that clears stack `k`, then does `q` where
`q` is another label. In order to prevent this from resulting in an infinite number of distinct
accessible states, we are careful to be non-recursive (although loops are okay). See the section
documentation for a description of all the programs. -/
inductive Λ'
  | move (p : Γ' → Bool) (k₁ k₂ : K') (q : Λ')
  | clear (p : Γ' → Bool) (k : K') (q : Λ')
  | copy (q : Λ')
  | push (k : K') (s : Option Γ' → Option Γ') (q : Λ')
  | read (f : Option Γ' → Λ')
  | succ (q : Λ')
  | pred (q₁ q₂ : Λ')
  | ret (k : Cont')
goal: Turing.PartrecToTM2.Λ'

full_name: Turing.PartrecToTM2.Λ'.push
code: /-- The set of program positions. We make extensive use of inductive types here to let us describe
"subroutines"; for example `clear p k q` is a program that clears stack `k`, then does `q` where
`q` is another label. In order to prevent this from resulting in an infinite number of distinct
accessible states, we are careful to be non-recursive (although loops are okay). See the section
documentation for a description of all the programs. -/
inductive Λ'
  | move (p : Γ' → Bool) (k₁ k₂ : K') (q : Λ')
  | clear (p : Γ' → Bool) (k : K') (q : Λ')
  | copy (q : Λ')
  | push (k : K') (s : Option Γ' → Option Γ') (q : Λ')
  | read (f : Option Γ' → Λ')
  | succ (q : Λ')
  | pred (q₁ q₂ : Λ')
  | ret (k : Cont')
goal: Turing.PartrecToTM2.Λ'

full_name: Turing.PartrecToTM2.Λ'.read
code: /-- The set of program positions. We make extensive use of inductive types here to let us describe
"subroutines"; for example `clear p k q` is a program that clears stack `k`, then does `q` where
`q` is another label. In order to prevent this from resulting in an infinite number of distinct
accessible states, we are careful to be non-recursive (although loops are okay). See the section
documentation for a description of all the programs. -/
inductive Λ'
  | move (p : Γ' → Bool) (k₁ k₂ : K') (q : Λ')
  | clear (p : Γ' → Bool) (k : K') (q : Λ')
  | copy (q : Λ')
  | push (k : K') (s : Option Γ' → Option Γ') (q : Λ')
  | read (f : Option Γ' → Λ')
  | succ (q : Λ')
  | pred (q₁ q₂ : Λ')
  | ret (k : Cont')
goal: Turing.PartrecToTM2.Λ'

full_name: Turing.PartrecToTM2.Λ'.ret
code: /-- The set of program positions. We make extensive use of inductive types here to let us describe
"subroutines"; for example `clear p k q` is a program that clears stack `k`, then does `q` where
`q` is another label. In order to prevent this from resulting in an infinite number of distinct
accessible states, we are careful to be non-recursive (although loops are okay). See the section
documentation for a description of all the programs. -/
inductive Λ'
  | move (p : Γ' → Bool) (k₁ k₂ : K') (q : Λ')
  | clear (p : Γ' → Bool) (k : K') (q : Λ')
  | copy (q : Λ')
  | push (k : K') (s : Option Γ' → Option Γ') (q : Λ')
  | read (f : Option Γ' → Λ')
  | succ (q : Λ')
  | pred (q₁ q₂ : Λ')
  | ret (k : Cont')
goal: Turing.PartrecToTM2.Λ'

full_name: Turing.PartrecToTM2.Λ'.copy
code: /-- The set of program positions. We make extensive use of inductive types here to let us describe
"subroutines"; for example `clear p k q` is a program that clears stack `k`, then does `q` where
`q` is another label. In order to prevent this from resulting in an infinite number of distinct
accessible states, we are careful to be non-recursive (although loops are okay). See the section
documentation for a description of all the programs. -/
inductive Λ'
  | move (p : Γ' → Bool) (k₁ k₂ : K') (q : Λ')
  | clear (p : Γ' → Bool) (k : K') (q : Λ')
  | copy (q : Λ')
  | push (k : K') (s : Option Γ' → Option Γ') (q : Λ')
  | read (f : Option Γ' → Λ')
  | succ (q : Λ')
  | pred (q₁ q₂ : Λ')
  | ret (k : Cont')
goal: Turing.PartrecToTM2.Λ'

full_name: Turing.PartrecToTM2.Λ'.instInhabited
code: instance Λ'.instInhabited : Inhabited Λ' 
goal: Inhabited Turing.PartrecToTM2.Λ'

full_name: Turing.PartrecToTM2.Λ'.instDecidableEq
code: instance Λ'.instDecidableEq : DecidableEq Λ' 
goal: DecidableEq Turing.PartrecToTM2.Λ'

full_name: Turing.PartrecToTM2.Stmt'
code: /-- The type of TM2 statements used by this machine. -/
def Stmt' 
goal: Type

full_name: Turing.PartrecToTM2.Cfg'
code: /-- The type of TM2 configurations used by this machine. -/
def Cfg' 
goal: Type

full_name: Turing.PartrecToTM2.natEnd
code: /-- A predicate that detects the end of a natural number, either `Γ'.cons` or `Γ'.consₗ` (or
implicitly the end of the list), for use in predicate-taking functions like `move` and `clear`. -/
@[simp]
def natEnd : Γ' → Bool
  
goal: Turing.PartrecToTM2.Γ' → Bool

full_name: Turing.PartrecToTM2.pop'
code: /-- Pop a value from the stack and place the result in local store. -/
@[simp]
def pop' (k : K') : Stmt' → Stmt' 
goal: Turing.PartrecToTM2.Stmt' → Turing.PartrecToTM2.Stmt'

full_name: Turing.PartrecToTM2.peek'
code: /-- Peek a value from the stack and place the result in local store. -/
@[simp]
def peek' (k : K') : Stmt' → Stmt' 
goal: Turing.PartrecToTM2.Stmt' → Turing.PartrecToTM2.Stmt'

full_name: Turing.PartrecToTM2.push'
code: /-- Push the value in the local store to the given stack. -/
@[simp]
def push' (k : K') : Stmt' → Stmt' 
goal: Turing.PartrecToTM2.Stmt' → Turing.PartrecToTM2.Stmt'

full_name: Turing.PartrecToTM2.unrev
code: /-- Move everything from the `rev` stack to the `main` stack (reversed). -/
def unrev 
goal: Turing.PartrecToTM2.Λ'

full_name: Turing.PartrecToTM2.moveExcl
code: /-- Move elements from `k₁` to `k₂` while `p` holds, with the last element being left on `k₁`. -/
def moveExcl (p k₁ k₂ q) 
goal: Turing.PartrecToTM2.Λ'

full_name: Turing.PartrecToTM2.move₂
code: /-- Move elements from `k₁` to `k₂` without reversion, by performing a double move via the `rev`
stack. -/
def move₂ (p k₁ k₂ q) 
goal: Turing.PartrecToTM2.Λ'

full_name: Turing.PartrecToTM2.head
code: /-- Assuming `trList v` is on the front of stack `k`, remove it, and push `v.headI` onto `main`.
See the section documentation. -/
def head (k : K') (q : Λ') : Λ' 
goal: Turing.PartrecToTM2.Λ'

full_name: Turing.PartrecToTM2.trNormal
code: /-- The program that evaluates code `c` with continuation `k`. This expects an initial state where
`trList v` is on `main`, `trContStack k` is on `stack`, and `aux` and `rev` are empty.
See the section documentation for details. -/
@[simp]
def trNormal : Code → Cont' → Λ'
  
goal: Turing.ToPartrec.Code → Turing.PartrecToTM2.Cont' → Turing.PartrecToTM2.Λ'

full_name: Turing.PartrecToTM2.tr
code: /-- The main program. See the section documentation for details. -/
def tr : Λ' → Stmt'
  
goal: Turing.PartrecToTM2.Λ' → Turing.PartrecToTM2.Stmt'

full_name: Turing.PartrecToTM2.tr_move
code: theorem tr_move (p k₁ k₂ q) : tr (Λ'.move p k₁ k₂ q) =
    pop' k₁ (branch (fun s => s.elim true p) (goto fun _ => q)
      (push' k₂ <| goto fun _ => Λ'.move p k₁ k₂ q)) 
goal: Turing.PartrecToTM2.tr (Turing.PartrecToTM2.Λ'.move p k₁ k₂ q) =
  Turing.PartrecToTM2.pop' k₁
    (Turing.TM2.Stmt.branch (fun s => s.elim true p) (Turing.TM2.Stmt.goto fun x => q)
      (Turing.PartrecToTM2.push' k₂ (Turing.TM2.Stmt.goto fun x => Turing.PartrecToTM2.Λ'.move p k₁ k₂ q)))

full_name: Turing.PartrecToTM2.tr_push
code: theorem tr_push (k f q) : tr (Λ'.push k f q) = branch (fun s => (f s).isSome)
    ((push k fun s => (f s).iget) <| goto fun _ => q) (goto fun _ => q) 
goal: Turing.PartrecToTM2.tr (Turing.PartrecToTM2.Λ'.push k f q) =
  Turing.TM2.Stmt.branch (fun s => (f s).isSome)
    (Turing.TM2.Stmt.push k (fun s => (f s).iget) (Turing.TM2.Stmt.goto fun x => q)) (Turing.TM2.Stmt.goto fun x => q)

full_name: Turing.PartrecToTM2.tr_read
code: theorem tr_read (q) : tr (Λ'.read q) = goto q 
goal: Turing.PartrecToTM2.tr (Turing.PartrecToTM2.Λ'.read q) = Turing.TM2.Stmt.goto q

full_name: Turing.PartrecToTM2.tr_clear
code: theorem tr_clear (p k q) : tr (Λ'.clear p k q) = pop' k (branch
    (fun s => s.elim true p) (goto fun _ => q) (goto fun _ => Λ'.clear p k q)) 
goal: Turing.PartrecToTM2.tr (Turing.PartrecToTM2.Λ'.clear p k q) =
  Turing.PartrecToTM2.pop' k
    (Turing.TM2.Stmt.branch (fun s => s.elim true p) (Turing.TM2.Stmt.goto fun x => q)
      (Turing.TM2.Stmt.goto fun x => Turing.PartrecToTM2.Λ'.clear p k q))

full_name: Turing.PartrecToTM2.tr_copy
code: theorem tr_copy (q) : tr (Λ'.copy q) = pop' rev (branch Option.isSome
    (push' main <| push' stack <| goto fun _ => Λ'.copy q) (goto fun _ => q)) 
goal: Turing.PartrecToTM2.tr q.copy =
  Turing.PartrecToTM2.pop' Turing.PartrecToTM2.K'.rev
    (Turing.TM2.Stmt.branch Option.isSome
      (Turing.PartrecToTM2.push' Turing.PartrecToTM2.K'.main
        (Turing.PartrecToTM2.push' Turing.PartrecToTM2.K'.stack (Turing.TM2.Stmt.goto fun x => q.copy)))
      (Turing.TM2.Stmt.goto fun x => q))

full_name: Turing.PartrecToTM2.tr_succ
code: theorem tr_succ (q) : tr (Λ'.succ q) = pop' main (branch (fun s => s = some Γ'.bit1)
    ((push rev fun _ => Γ'.bit0) <| goto fun _ => Λ'.succ q) <|
      branch (fun s => s = some Γ'.cons)
        ((push main fun _ => Γ'.cons) <| (push main fun _ => Γ'.bit1) <| goto fun _ => unrev q)
        ((push main fun _ => Γ'.bit1) <| goto fun _ => unrev q)) 
goal: Turing.PartrecToTM2.tr q.succ =
  Turing.PartrecToTM2.pop' Turing.PartrecToTM2.K'.main
    (Turing.TM2.Stmt.branch (fun s => decide (s = some Turing.PartrecToTM2.Γ'.bit1))
      (Turing.TM2.Stmt.push Turing.PartrecToTM2.K'.rev (fun x => Turing.PartrecToTM2.Γ'.bit0)
        (Turing.TM2.Stmt.goto fun x => q.succ))
      (Turing.TM2.Stmt.branch (fun s => decide (s = some Turing.PartrecToTM2.Γ'.cons))
        (Turing.TM2.Stmt.push Turing.PartrecToTM2.K'.main (fun x => Turing.PartrecToTM2.Γ'.cons)
          (Turing.TM2.Stmt.push Turing.PartrecToTM2.K'.main (fun x => Turing.PartrecToTM2.Γ'.bit1)
            (Turing.TM2.Stmt.goto fun x => Turing.PartrecToTM2.unrev q)))
        (Turing.TM2.Stmt.push Turing.PartrecToTM2.K'.main (fun x => Turing.PartrecToTM2.Γ'.bit1)
          (Turing.TM2.Stmt.goto fun x => Turing.PartrecToTM2.unrev q))))

full_name: Turing.PartrecToTM2.tr_pred
code: theorem tr_pred (q₁ q₂) : tr (Λ'.pred q₁ q₂) = pop' main (branch (fun s => s = some Γ'.bit0)
    ((push rev fun _ => Γ'.bit1) <| goto fun _ => Λ'.pred q₁ q₂) <|
    branch (fun s => natEnd s.iget) (goto fun _ => q₁)
      (peek' main <|
        branch (fun s => natEnd s.iget) (goto fun _ => unrev q₂)
          ((push rev fun _ => Γ'.bit0) <| goto fun _ => unrev q₂))) 
goal: Turing.PartrecToTM2.tr (q₁.pred q₂) =
  Turing.PartrecToTM2.pop' Turing.PartrecToTM2.K'.main
    (Turing.TM2.Stmt.branch (fun s => decide (s = some Turing.PartrecToTM2.Γ'.bit0))
      (Turing.TM2.Stmt.push Turing.PartrecToTM2.K'.rev (fun x => Turing.PartrecToTM2.Γ'.bit1)
        (Turing.TM2.Stmt.goto fun x => q₁.pred q₂))
      (Turing.TM2.Stmt.branch (fun s => Turing.PartrecToTM2.natEnd s.iget) (Turing.TM2.Stmt.goto fun x => q₁)
        (Turing.PartrecToTM2.peek' Turing.PartrecToTM2.K'.main
          (Turing.TM2.Stmt.branch (fun s => Turing.PartrecToTM2.natEnd s.iget)
            (Turing.TM2.Stmt.goto fun x => Turing.PartrecToTM2.unrev q₂)
            (Turing.TM2.Stmt.push Turing.PartrecToTM2.K'.rev (fun x => Turing.PartrecToTM2.Γ'.bit0)
              (Turing.TM2.Stmt.goto fun x => Turing.PartrecToTM2.unrev q₂))))))

full_name: Turing.PartrecToTM2.tr_ret_cons₁
code: theorem tr_ret_cons₁ (fs k) : tr (Λ'.ret (Cont'.cons₁ fs k)) = goto fun _ =>
    move₂ (fun _ => false) main aux <|
      move₂ (fun s => s = Γ'.consₗ) stack main <|
        move₂ (fun _ => false) aux stack <| trNormal fs (Cont'.cons₂ k) 
goal: Turing.PartrecToTM2.tr (Turing.PartrecToTM2.Λ'.ret (Turing.PartrecToTM2.Cont'.cons₁ fs k)) =
  Turing.TM2.Stmt.goto fun x =>
    Turing.PartrecToTM2.move₂ (fun x => false) Turing.PartrecToTM2.K'.main Turing.PartrecToTM2.K'.aux
      (Turing.PartrecToTM2.move₂ (fun s => decide (s = Turing.PartrecToTM2.Γ'.consₗ)) Turing.PartrecToTM2.K'.stack
        Turing.PartrecToTM2.K'.main
        (Turing.PartrecToTM2.move₂ (fun x => false) Turing.PartrecToTM2.K'.aux Turing.PartrecToTM2.K'.stack
          (Turing.PartrecToTM2.trNormal fs k.cons₂)))

full_name: Turing.PartrecToTM2.tr_ret_cons₂
code: theorem tr_ret_cons₂ (k) : tr (Λ'.ret (Cont'.cons₂ k)) =
    goto fun _ => head stack <| Λ'.ret k 
goal: Turing.PartrecToTM2.tr (Turing.PartrecToTM2.Λ'.ret k.cons₂) =
  Turing.TM2.Stmt.goto fun x => Turing.PartrecToTM2.head Turing.PartrecToTM2.K'.stack (Turing.PartrecToTM2.Λ'.ret k)

full_name: Turing.PartrecToTM2.tr_ret_comp
code: theorem tr_ret_comp (f k) : tr (Λ'.ret (Cont'.comp f k)) = goto fun _ => trNormal f k 
goal: Turing.PartrecToTM2.tr (Turing.PartrecToTM2.Λ'.ret (Turing.PartrecToTM2.Cont'.comp f k)) =
  Turing.TM2.Stmt.goto fun x => Turing.PartrecToTM2.trNormal f k

full_name: Turing.PartrecToTM2.tr_ret_fix
code: theorem tr_ret_fix (f k) : tr (Λ'.ret (Cont'.fix f k)) = pop' main (goto fun s =>
    cond (natEnd s.iget) (Λ'.ret k) <| Λ'.clear natEnd main <| trNormal f (Cont'.fix f k)) 
goal: Turing.PartrecToTM2.tr (Turing.PartrecToTM2.Λ'.ret (Turing.PartrecToTM2.Cont'.fix f k)) =
  Turing.PartrecToTM2.pop' Turing.PartrecToTM2.K'.main
    (Turing.TM2.Stmt.goto fun s =>
      bif Turing.PartrecToTM2.natEnd s.iget then Turing.PartrecToTM2.Λ'.ret k
      else
        Turing.PartrecToTM2.Λ'.clear Turing.PartrecToTM2.natEnd Turing.PartrecToTM2.K'.main
          (Turing.PartrecToTM2.trNormal f (Turing.PartrecToTM2.Cont'.fix f k)))

full_name: Turing.PartrecToTM2.tr_ret_halt
code: theorem tr_ret_halt : tr (Λ'.ret Cont'.halt) = (load fun _ => none) halt 
goal: Turing.PartrecToTM2.tr (Turing.PartrecToTM2.Λ'.ret Turing.PartrecToTM2.Cont'.halt) =
  Turing.TM2.Stmt.load (fun x => none) Turing.TM2.Stmt.halt

full_name: Turing.PartrecToTM2.trCont
code: /-- Translating a `Cont` continuation to a `Cont'` continuation simply entails dropping all the
data. This data is instead encoded in `trContStack` in the configuration. -/
def trCont : Cont → Cont'
  
goal: Turing.ToPartrec.Cont → Turing.PartrecToTM2.Cont'

full_name: Turing.PartrecToTM2.trPosNum
code: /-- We use `PosNum` to define the translation of binary natural numbers. A natural number is
represented as a little-endian list of `bit0` and `bit1` elements:

    1 = [bit1]
    2 = [bit0, bit1]
    3 = [bit1, bit1]
    4 = [bit0, bit0, bit1]

In particular, this representation guarantees no trailing `bit0`'s at the end of the list. -/
def trPosNum : PosNum → List Γ'
  
goal: PosNum → List Turing.PartrecToTM2.Γ'

full_name: Turing.PartrecToTM2.trNum
code: /-- We use `Num` to define the translation of binary natural numbers. Positive numbers are
translated using `trPosNum`, and `trNum 0 = []`. So there are never any trailing `bit0`'s in
a translated `Num`.

    0 = []
    1 = [bit1]
    2 = [bit0, bit1]
    3 = [bit1, bit1]
    4 = [bit0, bit0, bit1]
-/
def trNum : Num → List Γ'
  
goal: Num → List Turing.PartrecToTM2.Γ'

full_name: Turing.PartrecToTM2.trNat
code: /-- Because we use binary encoding, we define `trNat` in terms of `trNum`, using `Num`, which are
binary natural numbers. (We could also use `Nat.binaryRecOn`, but `Num` and `PosNum` make for
easy inductions.) -/
def trNat (n : ℕ) : List Γ' 
goal: List Turing.PartrecToTM2.Γ'

full_name: Turing.PartrecToTM2.trNat_zero
code: @[simp]
theorem trNat_zero : trNat 0 = [] 
goal: Turing.PartrecToTM2.trNat 0 = []

full_name: Turing.PartrecToTM2.trNat_default
code: theorem trNat_default : trNat default = [] 
goal: Turing.PartrecToTM2.trNat default = []

full_name: Turing.PartrecToTM2.trList
code: /-- Lists are translated with a `cons` after each encoded number.
For example:

    [] = []
    [0] = [cons]
    [1] = [bit1, cons]
    [6, 0] = [bit0, bit1, bit1, cons, cons]
-/
@[simp]
def trList : List ℕ → List Γ'
  
goal: List ℕ → List Turing.PartrecToTM2.Γ'

full_name: Turing.PartrecToTM2.trLList
code: /-- Lists of lists are translated with a `consₗ` after each encoded list.
For example:

    [] = []
    [[]] = [consₗ]
    [[], []] = [consₗ, consₗ]
    [[0]] = [cons, consₗ]
    [[1, 2], [0]] = [bit1, cons, bit0, bit1, cons, consₗ, cons, consₗ]
-/
@[simp]
def trLList : List (List ℕ) → List Γ'
  
goal: List (List ℕ) → List Turing.PartrecToTM2.Γ'

full_name: Turing.PartrecToTM2.contStack
code: /-- The data part of a continuation is a list of lists, which is encoded on the `stack` stack
using `trLList`. -/
@[simp]
def contStack : Cont → List (List ℕ)
  
goal: Turing.ToPartrec.Cont → List (List ℕ)

full_name: Turing.PartrecToTM2.trContStack
code: /-- The data part of a continuation is a list of lists, which is encoded on the `stack` stack
using `trLList`. -/
def trContStack (k : Cont) 
goal: List Turing.PartrecToTM2.Γ'

full_name: Turing.PartrecToTM2.K'.elim
code: /-- This is the nondependent eliminator for `K'`, but we use it specifically here in order to
represent the stack data as four lists rather than as a function `K' → List Γ'`, because this makes
rewrites easier. The theorems `K'.elim_update_main` et. al. show how such a function is updated
after an `update` to one of the components. -/
def K'.elim (a b c d : List Γ') : K' → List Γ'
  
goal: Turing.PartrecToTM2.K' → List Turing.PartrecToTM2.Γ'

full_name: Turing.PartrecToTM2.K'.elim_main
code: theorem K'.elim_main (a b c d) : K'.elim a b c d K'.main = a 
goal: Turing.PartrecToTM2.K'.elim a b c d Turing.PartrecToTM2.K'.main = a

full_name: Turing.PartrecToTM2.K'.elim_rev
code: theorem K'.elim_rev (a b c d) : K'.elim a b c d K'.rev = b 
goal: Turing.PartrecToTM2.K'.elim a b c d Turing.PartrecToTM2.K'.rev = b

full_name: Turing.PartrecToTM2.K'.elim_aux
code: theorem K'.elim_aux (a b c d) : K'.elim a b c d K'.aux = c 
goal: Turing.PartrecToTM2.K'.elim a b c d Turing.PartrecToTM2.K'.aux = c

full_name: Turing.PartrecToTM2.K'.elim_stack
code: theorem K'.elim_stack (a b c d) : K'.elim a b c d K'.stack = d 
goal: Turing.PartrecToTM2.K'.elim a b c d Turing.PartrecToTM2.K'.stack = d

full_name: Turing.PartrecToTM2.K'.elim_update_main
code: @[simp]
theorem K'.elim_update_main {a b c d a'} : update (K'.elim a b c d) main a' = K'.elim a' b c d 
goal: Function.update (Turing.PartrecToTM2.K'.elim a b c d) Turing.PartrecToTM2.K'.main a' =
  Turing.PartrecToTM2.K'.elim a' b c d

full_name: Turing.PartrecToTM2.K'.elim_update_rev
code: @[simp]
theorem K'.elim_update_rev {a b c d b'} : update (K'.elim a b c d) rev b' = K'.elim a b' c d 
goal: Function.update (Turing.PartrecToTM2.K'.elim a b c d) Turing.PartrecToTM2.K'.rev b' =
  Turing.PartrecToTM2.K'.elim a b' c d

full_name: Turing.PartrecToTM2.K'.elim_update_aux
code: @[simp]
theorem K'.elim_update_aux {a b c d c'} : update (K'.elim a b c d) aux c' = K'.elim a b c' d 
goal: Function.update (Turing.PartrecToTM2.K'.elim a b c d) Turing.PartrecToTM2.K'.aux c' =
  Turing.PartrecToTM2.K'.elim a b c' d

full_name: Turing.PartrecToTM2.K'.elim_update_stack
code: @[simp]
theorem K'.elim_update_stack {a b c d d'} :
    update (K'.elim a b c d) stack d' = K'.elim a b c d' 
goal: Function.update (Turing.PartrecToTM2.K'.elim a b c d) Turing.PartrecToTM2.K'.stack d' =
  Turing.PartrecToTM2.K'.elim a b c d'

full_name: Turing.PartrecToTM2.halt
code: /-- The halting state corresponding to a `List ℕ` output value. -/
def halt (v : List ℕ) : Cfg' 
goal: Turing.PartrecToTM2.Cfg'

full_name: Turing.PartrecToTM2.TrCfg
code: /-- The `Cfg` states map to `Cfg'` states almost one to one, except that in normal operation the
local store contains an arbitrary garbage value. To make the final theorem cleaner we explicitly
clear it in the halt state so that there is exactly one configuration corresponding to output `v`.
-/
def TrCfg : Cfg → Cfg' → Prop
  
goal: Turing.ToPartrec.Cfg → Turing.PartrecToTM2.Cfg' → Prop

full_name: Turing.PartrecToTM2.splitAtPred
code: /-- This could be a general list definition, but it is also somewhat specialized to this
application. `splitAtPred p L` will search `L` for the first element satisfying `p`.
If it is found, say `L = l₁ ++ a :: l₂` where `a` satisfies `p` but `l₁` does not, then it returns
`(l₁, some a, l₂)`. Otherwise, if there is no such element, it returns `(L, none, [])`. -/
def splitAtPred {α} (p : α → Bool) : List α → List α × Option α × List α
  
goal: List α → List α × Option α × List α

full_name: Turing.PartrecToTM2.splitAtPred_eq
code: theorem splitAtPred_eq {α} (p : α → Bool) :
    ∀ L l₁ o l₂,
      (∀ x ∈ l₁, p x = false) →
        Option.elim' (L = l₁ ∧ l₂ = []) (fun a => p a = true ∧ L = l₁ ++ a::l₂) o →
          splitAtPred p L = (l₁, o, l₂)
  
goal: (∀ x ∈ l₁, p x = false) →
  Option.elim' (L = l₁ ∧ l₂ = []) (fun a => p a = true ∧ L = l₁ ++ a :: l₂) o →
    Turing.PartrecToTM2.splitAtPred p L = (l₁, o, l₂)

full_name: Turing.PartrecToTM2.splitAtPred_false
code: theorem splitAtPred_false {α} (L : List α) : splitAtPred (fun _ => false) L = (L, none, []) 
goal: Turing.PartrecToTM2.splitAtPred (fun x => false) L = (L, none, [])

full_name: Turing.PartrecToTM2.move_ok
code: theorem move_ok {p k₁ k₂ q s L₁ o L₂} {S : K' → List Γ'} (h₁ : k₁ ≠ k₂)
    (e : splitAtPred p (S k₁) = (L₁, o, L₂)) :
    Reaches₁ (TM2.step tr) ⟨some (Λ'.move p k₁ k₂ q), s, S⟩
      ⟨some q, o, update (update S k₁ L₂) k₂ (L₁.reverseAux (S k₂))⟩ 
goal: Turing.Reaches₁ (Turing.TM2.step Turing.PartrecToTM2.tr)
  { l := some (Turing.PartrecToTM2.Λ'.move p k₁ k₂ q), var := s, stk := S }
  { l := some q, var := o, stk := Function.update (Function.update S k₁ L₂) k₂ (L₁.reverseAux (S k₂)) }

full_name: Turing.PartrecToTM2.unrev_ok
code: theorem unrev_ok {q s} {S : K' → List Γ'} :
    Reaches₁ (TM2.step tr) ⟨some (unrev q), s, S⟩
      ⟨some q, none, update (update S rev []) main (List.reverseAux (S rev) (S main))⟩ 
goal: Turing.Reaches₁ (Turing.TM2.step Turing.PartrecToTM2.tr) { l := some (Turing.PartrecToTM2.unrev q), var := s, stk := S }
  { l := some q, var := none,
    stk :=
      Function.update (Function.update S Turing.PartrecToTM2.K'.rev []) Turing.PartrecToTM2.K'.main
        ((S Turing.PartrecToTM2.K'.rev).reverseAux (S Turing.PartrecToTM2.K'.main)) }

full_name: Turing.PartrecToTM2.move₂_ok
code: theorem move₂_ok {p k₁ k₂ q s L₁ o L₂} {S : K' → List Γ'} (h₁ : k₁ ≠ rev ∧ k₂ ≠ rev ∧ k₁ ≠ k₂)
    (h₂ : S rev = []) (e : splitAtPred p (S k₁) = (L₁, o, L₂)) :
    Reaches₁ (TM2.step tr) ⟨some (move₂ p k₁ k₂ q), s, S⟩
      ⟨some q, none, update (update S k₁ (o.elim id List.cons L₂)) k₂ (L₁ ++ S k₂)⟩ 
goal: Turing.Reaches₁ (Turing.TM2.step Turing.PartrecToTM2.tr)
  { l := some (Turing.PartrecToTM2.move₂ p k₁ k₂ q), var := s, stk := S }
  { l := some q, var := none, stk := Function.update (Function.update S k₁ (o.elim id List.cons L₂)) k₂ (L₁ ++ S k₂) }

full_name: Turing.PartrecToTM2.clear_ok
code: theorem clear_ok {p k q s L₁ o L₂} {S : K' → List Γ'} (e : splitAtPred p (S k) = (L₁, o, L₂)) :
    Reaches₁ (TM2.step tr) ⟨some (Λ'.clear p k q), s, S⟩ ⟨some q, o, update S k L₂⟩ 
goal: Turing.Reaches₁ (Turing.TM2.step Turing.PartrecToTM2.tr)
  { l := some (Turing.PartrecToTM2.Λ'.clear p k q), var := s, stk := S }
  { l := some q, var := o, stk := Function.update S k L₂ }

full_name: Turing.PartrecToTM2.copy_ok
code: theorem copy_ok (q s a b c d) :
    Reaches₁ (TM2.step tr) ⟨some (Λ'.copy q), s, K'.elim a b c d⟩
      ⟨some q, none, K'.elim (List.reverseAux b a) [] c (List.reverseAux b d)⟩ 
goal: Turing.Reaches₁ (Turing.TM2.step Turing.PartrecToTM2.tr)
  { l := some q.copy, var := s, stk := Turing.PartrecToTM2.K'.elim a b c d }
  { l := some q, var := none, stk := Turing.PartrecToTM2.K'.elim (b.reverseAux a) [] c (b.reverseAux d) }

full_name: Turing.PartrecToTM2.trPosNum_natEnd
code: theorem trPosNum_natEnd : ∀ (n), ∀ x ∈ trPosNum n, natEnd x = false
  
goal: x ∈ Turing.PartrecToTM2.trPosNum n → Turing.PartrecToTM2.natEnd x = false

full_name: Turing.PartrecToTM2.trNum_natEnd
code: theorem trNum_natEnd : ∀ (n), ∀ x ∈ trNum n, natEnd x = false
  
goal: x ∈ Turing.PartrecToTM2.trNum n → Turing.PartrecToTM2.natEnd x = false

full_name: Turing.PartrecToTM2.trNat_natEnd
code: theorem trNat_natEnd (n) : ∀ x ∈ trNat n, natEnd x = false 
goal: x ∈ Turing.PartrecToTM2.trNat n → Turing.PartrecToTM2.natEnd x = false

full_name: Turing.PartrecToTM2.trList_ne_consₗ
code: theorem trList_ne_consₗ : ∀ (l), ∀ x ∈ trList l, x ≠ Γ'.consₗ
  
goal: x ∈ Turing.PartrecToTM2.trList l → x ≠ Turing.PartrecToTM2.Γ'.consₗ

full_name: Turing.PartrecToTM2.head_main_ok
code: theorem head_main_ok {q s L} {c d : List Γ'} :
    Reaches₁ (TM2.step tr) ⟨some (head main q), s, K'.elim (trList L) [] c d⟩
      ⟨some q, none, K'.elim (trList [L.headI]) [] c d⟩ 
goal: Turing.Reaches₁ (Turing.TM2.step Turing.PartrecToTM2.tr)
  { l := some (Turing.PartrecToTM2.head Turing.PartrecToTM2.K'.main q), var := s,
    stk := Turing.PartrecToTM2.K'.elim (Turing.PartrecToTM2.trList L) [] c d }
  { l := some q, var := none, stk := Turing.PartrecToTM2.K'.elim (Turing.PartrecToTM2.trList [L.headI]) [] c d }

full_name: Turing.PartrecToTM2.head_stack_ok
code: theorem head_stack_ok {q s L₁ L₂ L₃} :
    Reaches₁ (TM2.step tr)
      ⟨some (head stack q), s, K'.elim (trList L₁) [] [] (trList L₂ ++ Γ'.consₗ :: L₃)⟩
      ⟨some q, none, K'.elim (trList (L₂.headI :: L₁)) [] [] L₃⟩ 
goal: Turing.Reaches₁ (Turing.TM2.step Turing.PartrecToTM2.tr)
  { l := some (Turing.PartrecToTM2.head Turing.PartrecToTM2.K'.stack q), var := s,
    stk :=
      Turing.PartrecToTM2.K'.elim (Turing.PartrecToTM2.trList L₁) [] []
        (Turing.PartrecToTM2.trList L₂ ++ Turing.PartrecToTM2.Γ'.consₗ :: L₃) }
  { l := some q, var := none,
    stk := Turing.PartrecToTM2.K'.elim (Turing.PartrecToTM2.trList (L₂.headI :: L₁)) [] [] L₃ }

full_name: Turing.PartrecToTM2.succ_ok
code: theorem succ_ok {q s n} {c d : List Γ'} :
    Reaches₁ (TM2.step tr) ⟨some (Λ'.succ q), s, K'.elim (trList [n]) [] c d⟩
      ⟨some q, none, K'.elim (trList [n.succ]) [] c d⟩ 
goal: Turing.Reaches₁ (Turing.TM2.step Turing.PartrecToTM2.tr)
  { l := some q.succ, var := s, stk := Turing.PartrecToTM2.K'.elim (Turing.PartrecToTM2.trList [n]) [] c d }
  { l := some q, var := none, stk := Turing.PartrecToTM2.K'.elim (Turing.PartrecToTM2.trList [n.succ]) [] c d }

full_name: Turing.PartrecToTM2.pred_ok
code: theorem pred_ok (q₁ q₂ s v) (c d : List Γ') : ∃ s',
    Reaches₁ (TM2.step tr) ⟨some (Λ'.pred q₁ q₂), s, K'.elim (trList v) [] c d⟩
      (v.headI.rec ⟨some q₁, s', K'.elim (trList v.tail) [] c d⟩ fun n _ =>
        ⟨some q₂, s', K'.elim (trList (n::v.tail)) [] c d⟩) 
goal: ∃ s',
  Turing.Reaches₁ (Turing.TM2.step Turing.PartrecToTM2.tr)
    { l := some (q₁.pred q₂), var := s, stk := Turing.PartrecToTM2.K'.elim (Turing.PartrecToTM2.trList v) [] c d }
    (Nat.rec { l := some q₁, var := s', stk := Turing.PartrecToTM2.K'.elim (Turing.PartrecToTM2.trList v.tail) [] c d }
      (fun n x =>
        { l := some q₂, var := s',
          stk := Turing.PartrecToTM2.K'.elim (Turing.PartrecToTM2.trList (n :: v.tail)) [] c d })
      v.headI)

full_name: Turing.PartrecToTM2.trNormal_respects
code: theorem trNormal_respects (c k v s) :
    ∃ b₂,
      TrCfg (stepNormal c k v) b₂ ∧
        Reaches₁ (TM2.step tr)
          ⟨some (trNormal c (trCont k)), s, K'.elim (trList v) [] [] (trContStack k)⟩ b₂ 
goal: ∃ b₂,
  Turing.PartrecToTM2.TrCfg (Turing.ToPartrec.stepNormal c k v) b₂ ∧
    Turing.Reaches₁ (Turing.TM2.step Turing.PartrecToTM2.tr)
      { l := some (Turing.PartrecToTM2.trNormal c (Turing.PartrecToTM2.trCont k)), var := s,
        stk := Turing.PartrecToTM2.K'.elim (Turing.PartrecToTM2.trList v) [] [] (Turing.PartrecToTM2.trContStack k) }
      b₂

full_name: Turing.PartrecToTM2.tr_ret_respects
code: theorem tr_ret_respects (k v s) : ∃ b₂,
    TrCfg (stepRet k v) b₂ ∧
      Reaches₁ (TM2.step tr)
        ⟨some (Λ'.ret (trCont k)), s, K'.elim (trList v) [] [] (trContStack k)⟩ b₂ 
goal: ∃ b₂,
  Turing.PartrecToTM2.TrCfg (Turing.ToPartrec.stepRet k v) b₂ ∧
    Turing.Reaches₁ (Turing.TM2.step Turing.PartrecToTM2.tr)
      { l := some (Turing.PartrecToTM2.Λ'.ret (Turing.PartrecToTM2.trCont k)), var := s,
        stk := Turing.PartrecToTM2.K'.elim (Turing.PartrecToTM2.trList v) [] [] (Turing.PartrecToTM2.trContStack k) }
      b₂

full_name: Turing.PartrecToTM2.tr_respects
code: theorem tr_respects : Respects step (TM2.step tr) TrCfg
  
goal: Turing.Respects Turing.ToPartrec.step (Turing.TM2.step Turing.PartrecToTM2.tr) Turing.PartrecToTM2.TrCfg

full_name: Turing.PartrecToTM2.init
code: /-- The initial state, evaluating function `c` on input `v`. -/
def init (c : Code) (v : List ℕ) : Cfg' 
goal: Turing.PartrecToTM2.Cfg'

full_name: Turing.PartrecToTM2.tr_init
code: theorem tr_init (c v) :
    ∃ b, TrCfg (stepNormal c Cont.halt v) b ∧ Reaches₁ (TM2.step tr) (init c v) b 
goal: ∃ b,
  Turing.PartrecToTM2.TrCfg (Turing.ToPartrec.stepNormal c Turing.ToPartrec.Cont.halt v) b ∧
    Turing.Reaches₁ (Turing.TM2.step Turing.PartrecToTM2.tr) (Turing.PartrecToTM2.init c v) b

full_name: Turing.PartrecToTM2.tr_eval
code: theorem tr_eval (c v) : eval (TM2.step tr) (init c v) = halt <$> Code.eval c v 
goal: Turing.eval (Turing.TM2.step Turing.PartrecToTM2.tr) (Turing.PartrecToTM2.init c v) =
  Turing.PartrecToTM2.halt <$> c.eval v

full_name: Turing.PartrecToTM2.trStmts₁
code: /-- The set of machine states reachable via downward label jumps, discounting jumps via `ret`. -/
def trStmts₁ : Λ' → Finset Λ'
  
goal: Turing.PartrecToTM2.Λ' → Finset Turing.PartrecToTM2.Λ'

full_name: Turing.PartrecToTM2.trStmts₁_trans
code: theorem trStmts₁_trans {q q'} : q' ∈ trStmts₁ q → trStmts₁ q' ⊆ trStmts₁ q 
goal: q' ∈ Turing.PartrecToTM2.trStmts₁ q → Turing.PartrecToTM2.trStmts₁ q' ⊆ Turing.PartrecToTM2.trStmts₁ q

full_name: Turing.PartrecToTM2.trStmts₁_self
code: theorem trStmts₁_self (q) : q ∈ trStmts₁ q 
goal: q ∈ Turing.PartrecToTM2.trStmts₁ q

full_name: Turing.PartrecToTM2.codeSupp'
code: /-- The (finite!) set of machine states visited during the course of evaluation of `c`,
including the state `ret k` but not any states after that (that is, the states visited while
evaluating `k`). -/
def codeSupp' : Code → Cont' → Finset Λ'
  
goal: Turing.ToPartrec.Code → Turing.PartrecToTM2.Cont' → Finset Turing.PartrecToTM2.Λ'

full_name: Turing.PartrecToTM2.codeSupp'_self
code: @[simp]
theorem codeSupp'_self (c k) : trStmts₁ (trNormal c k) ⊆ codeSupp' c k 
goal: Turing.PartrecToTM2.trStmts₁ (Turing.PartrecToTM2.trNormal c k) ⊆ Turing.PartrecToTM2.codeSupp' c k

full_name: Turing.PartrecToTM2.contSupp
code: /-- The (finite!) set of machine states visited during the course of evaluation of a continuation
`k`, not including the initial state `ret k`. -/
def contSupp : Cont' → Finset Λ'
  
goal: Turing.PartrecToTM2.Cont' → Finset Turing.PartrecToTM2.Λ'

full_name: Turing.PartrecToTM2.codeSupp
code: /-- The (finite!) set of machine states visited during the course of evaluation of `c` in
continuation `k`. This is actually closed under forward simulation (see `tr_supports`), and the
existence of this set means that the machine constructed in this section is in fact a proper
Turing machine, with a finite set of states. -/
def codeSupp (c : Code) (k : Cont') : Finset Λ' 
goal: Finset Turing.PartrecToTM2.Λ'

full_name: Turing.PartrecToTM2.codeSupp_self
code: @[simp]
theorem codeSupp_self (c k) : trStmts₁ (trNormal c k) ⊆ codeSupp c k 
goal: Turing.PartrecToTM2.trStmts₁ (Turing.PartrecToTM2.trNormal c k) ⊆ Turing.PartrecToTM2.codeSupp c k

full_name: Turing.PartrecToTM2.codeSupp_zero
code: @[simp]
theorem codeSupp_zero (k) : codeSupp Code.zero' k = trStmts₁ (trNormal Code.zero' k) ∪ contSupp k 
goal: Turing.PartrecToTM2.codeSupp Turing.ToPartrec.Code.zero' k =
  Turing.PartrecToTM2.trStmts₁ (Turing.PartrecToTM2.trNormal Turing.ToPartrec.Code.zero' k) ∪
    Turing.PartrecToTM2.contSupp k

full_name: Turing.PartrecToTM2.codeSupp_succ
code: @[simp]
theorem codeSupp_succ (k) : codeSupp Code.succ k = trStmts₁ (trNormal Code.succ k) ∪ contSupp k 
goal: Turing.PartrecToTM2.codeSupp Turing.ToPartrec.Code.succ k =
  Turing.PartrecToTM2.trStmts₁ (Turing.PartrecToTM2.trNormal Turing.ToPartrec.Code.succ k) ∪
    Turing.PartrecToTM2.contSupp k

full_name: Turing.PartrecToTM2.codeSupp_tail
code: @[simp]
theorem codeSupp_tail (k) : codeSupp Code.tail k = trStmts₁ (trNormal Code.tail k) ∪ contSupp k 
goal: Turing.PartrecToTM2.codeSupp Turing.ToPartrec.Code.tail k =
  Turing.PartrecToTM2.trStmts₁ (Turing.PartrecToTM2.trNormal Turing.ToPartrec.Code.tail k) ∪
    Turing.PartrecToTM2.contSupp k

full_name: Turing.PartrecToTM2.codeSupp_cons
code: @[simp]
theorem codeSupp_cons (f fs k) :
    codeSupp (Code.cons f fs) k =
      trStmts₁ (trNormal (Code.cons f fs) k) ∪ codeSupp f (Cont'.cons₁ fs k) 
goal: Turing.PartrecToTM2.codeSupp (f.cons fs) k =
  Turing.PartrecToTM2.trStmts₁ (Turing.PartrecToTM2.trNormal (f.cons fs) k) ∪
    Turing.PartrecToTM2.codeSupp f (Turing.PartrecToTM2.Cont'.cons₁ fs k)

full_name: Turing.PartrecToTM2.codeSupp_comp
code: @[simp]
theorem codeSupp_comp (f g k) :
    codeSupp (Code.comp f g) k =
      trStmts₁ (trNormal (Code.comp f g) k) ∪ codeSupp g (Cont'.comp f k) 
goal: Turing.PartrecToTM2.codeSupp (f.comp g) k =
  Turing.PartrecToTM2.trStmts₁ (Turing.PartrecToTM2.trNormal (f.comp g) k) ∪
    Turing.PartrecToTM2.codeSupp g (Turing.PartrecToTM2.Cont'.comp f k)

full_name: Turing.PartrecToTM2.codeSupp_case
code: @[simp]
theorem codeSupp_case (f g k) :
    codeSupp (Code.case f g) k =
      trStmts₁ (trNormal (Code.case f g) k) ∪ (codeSupp f k ∪ codeSupp g k) 
goal: Turing.PartrecToTM2.codeSupp (f.case g) k =
  Turing.PartrecToTM2.trStmts₁ (Turing.PartrecToTM2.trNormal (f.case g) k) ∪
    (Turing.PartrecToTM2.codeSupp f k ∪ Turing.PartrecToTM2.codeSupp g k)

full_name: Turing.PartrecToTM2.codeSupp_fix
code: @[simp]
theorem codeSupp_fix (f k) :
    codeSupp (Code.fix f) k = trStmts₁ (trNormal (Code.fix f) k) ∪ codeSupp f (Cont'.fix f k) 
goal: Turing.PartrecToTM2.codeSupp f.fix k =
  Turing.PartrecToTM2.trStmts₁ (Turing.PartrecToTM2.trNormal f.fix k) ∪
    Turing.PartrecToTM2.codeSupp f (Turing.PartrecToTM2.Cont'.fix f k)

full_name: Turing.PartrecToTM2.contSupp_cons₁
code: @[simp]
theorem contSupp_cons₁ (fs k) :
    contSupp (Cont'.cons₁ fs k) =
      trStmts₁
          (move₂ (fun _ => false) main aux <|
            move₂ (fun s => s = Γ'.consₗ) stack main <|
              move₂ (fun _ => false) aux stack <| trNormal fs (Cont'.cons₂ k)) ∪
        codeSupp fs (Cont'.cons₂ k) 
goal: Turing.PartrecToTM2.contSupp (Turing.PartrecToTM2.Cont'.cons₁ fs k) =
  Turing.PartrecToTM2.trStmts₁
      (Turing.PartrecToTM2.move₂ (fun x => false) Turing.PartrecToTM2.K'.main Turing.PartrecToTM2.K'.aux
        (Turing.PartrecToTM2.move₂ (fun s => decide (s = Turing.PartrecToTM2.Γ'.consₗ)) Turing.PartrecToTM2.K'.stack
          Turing.PartrecToTM2.K'.main
          (Turing.PartrecToTM2.move₂ (fun x => false) Turing.PartrecToTM2.K'.aux Turing.PartrecToTM2.K'.stack
            (Turing.PartrecToTM2.trNormal fs k.cons₂)))) ∪
    Turing.PartrecToTM2.codeSupp fs k.cons₂

full_name: Turing.PartrecToTM2.contSupp_cons₂
code: @[simp]
theorem contSupp_cons₂ (k) :
    contSupp (Cont'.cons₂ k) = trStmts₁ (head stack <| Λ'.ret k) ∪ contSupp k 
goal: Turing.PartrecToTM2.contSupp k.cons₂ =
  Turing.PartrecToTM2.trStmts₁ (Turing.PartrecToTM2.head Turing.PartrecToTM2.K'.stack (Turing.PartrecToTM2.Λ'.ret k)) ∪
    Turing.PartrecToTM2.contSupp k

full_name: Turing.PartrecToTM2.contSupp_comp
code: @[simp]
theorem contSupp_comp (f k) : contSupp (Cont'.comp f k) = codeSupp f k 
goal: Turing.PartrecToTM2.contSupp (Turing.PartrecToTM2.Cont'.comp f k) = Turing.PartrecToTM2.codeSupp f k

full_name: Turing.PartrecToTM2.contSupp_fix
code: theorem contSupp_fix (f k) : contSupp (Cont'.fix f k) = codeSupp f (Cont'.fix f k) 
goal: Turing.PartrecToTM2.contSupp (Turing.PartrecToTM2.Cont'.fix f k) =
  Turing.PartrecToTM2.codeSupp f (Turing.PartrecToTM2.Cont'.fix f k)

full_name: Turing.PartrecToTM2.contSupp_halt
code: @[simp]
theorem contSupp_halt : contSupp Cont'.halt = ∅ 
goal: Turing.PartrecToTM2.contSupp Turing.PartrecToTM2.Cont'.halt = ∅

full_name: Turing.PartrecToTM2.Λ'.Supports
code: /-- The statement `Λ'.Supports S q` means that `contSupp k ⊆ S` for any `ret k`
reachable from `q`.
(This is a technical condition used in the proof that the machine is supported.) -/
def Λ'.Supports (S : Finset Λ') : Λ' → Prop
  
goal: Turing.PartrecToTM2.Λ' → Prop

full_name: Turing.PartrecToTM2.Supports
code: /-- A shorthand for the predicate that we are proving in the main theorems `trStmts₁_supports`,
`codeSupp'_supports`, `contSupp_supports`, `codeSupp_supports`. The set `S` is fixed throughout
the proof, and denotes the full set of states in the machine, while `K` is a subset that we are
currently proving a property about. The predicate asserts that every state in `K` is closed in `S`
under forward simulation, i.e. stepping forward through evaluation starting from any state in `K`
stays entirely within `S`. -/
def Supports (K S : Finset Λ') 
goal: Prop

full_name: Turing.PartrecToTM2.supports_insert
code: theorem supports_insert {K S q} :
    Supports (insert q K) S ↔ TM2.SupportsStmt S (tr q) ∧ Supports K S 
goal: Turing.PartrecToTM2.Supports (insert q K) S ↔
  Turing.TM2.SupportsStmt S (Turing.PartrecToTM2.tr q) ∧ Turing.PartrecToTM2.Supports K S

full_name: Turing.PartrecToTM2.supports_singleton
code: theorem supports_singleton {S q} : Supports {q} S ↔ TM2.SupportsStmt S (tr q) 
goal: Turing.PartrecToTM2.Supports {q} S ↔ Turing.TM2.SupportsStmt S (Turing.PartrecToTM2.tr q)

full_name: Turing.PartrecToTM2.supports_union
code: theorem supports_union {K₁ K₂ S} : Supports (K₁ ∪ K₂) S ↔ Supports K₁ S ∧ Supports K₂ S 
goal: Turing.PartrecToTM2.Supports (K₁ ∪ K₂) S ↔ Turing.PartrecToTM2.Supports K₁ S ∧ Turing.PartrecToTM2.Supports K₂ S

full_name: Turing.PartrecToTM2.supports_biUnion
code: theorem supports_biUnion {K : Option Γ' → Finset Λ'} {S} :
    Supports (Finset.univ.biUnion K) S ↔ ∀ a, Supports (K a) S 
goal: Turing.PartrecToTM2.Supports (Finset.univ.biUnion K) S ↔
  ∀ (a : Option Turing.PartrecToTM2.Γ'), Turing.PartrecToTM2.Supports (K a) S

full_name: Turing.PartrecToTM2.head_supports
code: theorem head_supports {S k q} (H : (q : Λ').Supports S) : (head k q).Supports S 
goal: Turing.PartrecToTM2.Λ'.Supports S (Turing.PartrecToTM2.head k q)

full_name: Turing.PartrecToTM2.ret_supports
code: theorem ret_supports {S k} (H₁ : contSupp k ⊆ S) : TM2.SupportsStmt S (tr (Λ'.ret k)) 
goal: Turing.TM2.SupportsStmt S (Turing.PartrecToTM2.tr (Turing.PartrecToTM2.Λ'.ret k))

full_name: Turing.PartrecToTM2.trStmts₁_supports
code: theorem trStmts₁_supports {S q} (H₁ : (q : Λ').Supports S) (HS₁ : trStmts₁ q ⊆ S) :
    Supports (trStmts₁ q) S 
goal: Turing.PartrecToTM2.Supports (Turing.PartrecToTM2.trStmts₁ q) S

full_name: Turing.PartrecToTM2.trStmts₁_supports'
code: theorem trStmts₁_supports' {S q K} (H₁ : (q : Λ').Supports S) (H₂ : trStmts₁ q ∪ K ⊆ S)
    (H₃ : K ⊆ S → Supports K S) : Supports (trStmts₁ q ∪ K) S 
goal: Turing.PartrecToTM2.Supports (Turing.PartrecToTM2.trStmts₁ q ∪ K) S

full_name: Turing.PartrecToTM2.trNormal_supports
code: theorem trNormal_supports {S c k} (Hk : codeSupp c k ⊆ S) : (trNormal c k).Supports S 
goal: Turing.PartrecToTM2.Λ'.Supports S (Turing.PartrecToTM2.trNormal c k)

full_name: Turing.PartrecToTM2.codeSupp'_supports
code: theorem codeSupp'_supports {S c k} (H : codeSupp c k ⊆ S) : Supports (codeSupp' c k) S 
goal: Turing.PartrecToTM2.Supports (Turing.PartrecToTM2.codeSupp' c k) S

full_name: Turing.PartrecToTM2.contSupp_supports
code: theorem contSupp_supports {S k} (H : contSupp k ⊆ S) : Supports (contSupp k) S 
goal: Turing.PartrecToTM2.Supports (Turing.PartrecToTM2.contSupp k) S

full_name: Turing.PartrecToTM2.codeSupp_supports
code: theorem codeSupp_supports {S c k} (H : codeSupp c k ⊆ S) : Supports (codeSupp c k) S 
goal: Turing.PartrecToTM2.Supports (Turing.PartrecToTM2.codeSupp c k) S

full_name: Turing.PartrecToTM2.tr_supports
code: /-- The set `codeSupp c k` is a finite set that witnesses the effective finiteness of the `tr`
Turing machine. Starting from the initial state `trNormal c k`, forward simulation uses only
states in `codeSupp c k`, so this is a finite state machine. Even though the underlying type of
state labels `Λ'` is infinite, for a given partial recursive function `c` and continuation `k`,
only finitely many states are accessed, corresponding roughly to subterms of `c`. -/
theorem tr_supports (c k) : @TM2.Supports _ _ _ _ ⟨trNormal c k⟩ tr (codeSupp c k) 
goal: Turing.TM2.Supports Turing.PartrecToTM2.tr (Turing.PartrecToTM2.codeSupp c k)