module bb2 where

-- Turing machines in Agda
--
-- 1. Turing machines as programs: the Busy Beaver champions for 2, 3, 4 states
-- 2. Non-termination as a proposition, and a proof that a machine never stops
--
-- Everything is defined from scratch: no library is needed.

------------------------------------------------------------------------
-- Basic data types
------------------------------------------------------------------------

data Nat : Set where
  zero : Nat
  suc  : Nat  Nat

data List (A : Set) : Set where
  []   : List A
  _::_ : A  List A  List A

infixr 5 _::_

-- Dependent pairs (Σ-types); product and sum as special cases

record Σ (A : Set) (B : A  Set) : Set where
  constructor _,_
  field
    fst : A
    snd : B fst

open Σ

infixr 4 _,_

_×_ : Set  Set  Set
A × B = Σ A  _  B)

infixr 4 _×_

data _+_ (A B : Set) : Set where
  inl : A  A + B
  inr : B  A + B

-- A few numbers, to bound the runs below

five : Nat
five = suc (suc (suc (suc (suc zero))))

ten : Nat
ten = suc (suc (suc (suc (suc five))))

twice : Nat  Nat
twice zero    = zero
twice (suc n) = suc (suc (twice n))

twenty : Nat
twenty = twice ten

big : Nat                       -- 320
big = twice (twice (twice (twice twenty)))

------------------------------------------------------------------------
-- Turing machines
------------------------------------------------------------------------

-- Symbols: zero = blank, one = mark
data Symbol : Set where
  zero one : Symbol

-- States, with a distinguished halting state
data State : Set where
  qA qB qC qD qHalt : State

-- Head movements
data Dir : Set where
  L R : Dir

-- The tape: the cells to the left of the head (nearest first), the cell
-- under the head, the cells to the right.  Only finitely many cells are
-- stored; the rest are blank.
record Tape : Set where
  constructor tape
  field
    left  : List Symbol
    head  : Symbol
    right : List Symbol

open Tape

move : Dir  Tape  Tape
move L (tape []        h r) = tape [] zero (h :: r)
move L (tape (l :: ls) h r) = tape ls l (h :: r)
move R (tape l h [])        = tape (h :: l) zero []
move R (tape l h (r :: rs)) = tape (h :: l) r rs

-- A machine is its transition function: from the current state and the
-- symbol under the head, give the new state, the symbol to write, and
-- where to move.
Transition : Set
Transition = State  Symbol  State × Symbol × Dir

-- One step of execution
step : Transition  State  Tape  State × Tape
step δ s t = go (δ s (head t))
  where
    go : State × Symbol × Dir  State × Tape
    go (s' , sym , d) = (s' , move d (tape (left t) sym (right t)))

-- Run for at most n steps, stopping early in the halting state.
-- This always terminates: n decreases at each call.
run : Nat  Transition  State  Tape  State × Tape
run zero    δ s t = (s , t)
run (suc n) δ s t = go (step δ s t)
  where
    go : State × Tape  State × Tape
    go (qHalt , t') = (qHalt , t')
    go (s'    , t') = run n δ s' t'

-- Start in qA on a blank tape
initialTape : Tape
initialTape = tape [] zero []

srun : Nat  Transition  State × Tape
srun n δ = run n δ qA initialTape

------------------------------------------------------------------------
-- The Busy Beaver champions
------------------------------------------------------------------------

-- BB(2) = 6 steps
bb2 : Transition
bb2 qA zero = (qB    , one , R)
bb2 qA one  = (qB    , one , L)
bb2 qB zero = (qA    , one , L)
bb2 qB one  = (qHalt , one , R)
bb2 q  s    = (qHalt , s   , R)      -- unused states

-- BB(3) = 21 steps
bb3 : Transition
bb3 qA zero = (qB    , one , R)
bb3 qA one  = (qC    , one , L)
bb3 qB zero = (qA    , one , L)
bb3 qB one  = (qB    , one , R)
bb3 qC zero = (qB    , one , L)
bb3 qC one  = (qHalt , one , R)
bb3 q  s    = (qHalt , s   , R)

-- BB(4) = 107 steps, leaves 13 marks on the tape (Brady 1983)
bb4 : Transition
bb4 qA zero = (qB    , one  , R)
bb4 qA one  = (qB    , one  , L)
bb4 qB zero = (qA    , one  , L)
bb4 qB one  = (qC    , zero , L)
bb4 qC zero = (qHalt , one  , R)
bb4 qC one  = (qD    , one  , L)
bb4 qD zero = (qD    , one  , R)
bb4 qD one  = (qA    , zero , R)
bb4 q  s    = (qHalt , s    , R)

-- Evaluate these with C-c C-n
runBB2 : State × Tape
runBB2 = srun big bb2

runBB3 : State × Tape
runBB3 = srun big bb3

runBB4 : State × Tape
runBB4 = srun big bb4

-- Exercise: write a function counting the number of steps until the machine
-- halts, and check that bb4 halts after 107 steps.

------------------------------------------------------------------------
-- Machines that never stop
------------------------------------------------------------------------

-- The simplest one: stay in qA and move right forever
forever : Transition
forever qA zero = (qA , zero , R)
forever q  s    = (q  , s    , R)

-- A slightly less trivial one: alternate between qA and qB
alternate : Transition
alternate qA s = (qB , one  , R)
alternate qB s = (qA , zero , R)
alternate q  s = (qHalt , s , R)

-- Running them just gives back a state and a tape; no amount of running
-- can show that a machine never stops.  For that we need a proof.
runForever : State × Tape
runForever = srun big forever

------------------------------------------------------------------------
-- Propositions as types
------------------------------------------------------------------------

-- The false proposition: no proof
data  : Set where

-- The true proposition: one proof
data  : Set where
  tt : 

-- Negation: a proof of ¬ A is a function from proofs of A to proofs of ⊥
¬ : Set  Set
¬ A = A  

-- "The state s is the halting state", as a proposition depending on s
Stop : State  Set
Stop qHalt = 
Stop _     = 

-- "The state s is qA"
QA : State  Set
QA qA = 
QA _  = 

-- A state cannot be both qA and halting.  Proof by cases on the state;
-- in each case the proof is trivial once the types have computed.
QA-notStop : (s : State)  QA s  ¬ (Stop s)
QA-notStop qA    p h = h
QA-notStop qB    p h = p
QA-notStop qC    p h = p
QA-notStop qD    p h = p
QA-notStop qHalt p h = p

-- "The machine δ never stops": for every n, after n steps we are not halted
neverStop : Transition  Set
neverStop δ = (n : Nat)  ¬ (Stop (fst (srun n δ)))

-- "The machine δ stops": for some n, after n steps we are halted
willStop : Transition  Set
willStop δ = Σ Nat  n  Stop (fst (srun n δ)))

-- The machine `forever` stays in qA, whatever the tape to the left.
-- Proof by induction on n.  Note that the statement has to be generalised
-- to an arbitrary tape on the left, since each step adds one cell there.
staysInQA : (n : Nat) (l : List Symbol)  QA (fst (run n forever qA (tape l zero [])))
staysInQA zero    l = tt
staysInQA (suc n) l = staysInQA n (zero :: l)

theorem : neverStop forever
theorem n = QA-notStop (fst (srun n forever)) (staysInQA n [])

-- Exercise: prove  neverStop alternate.
-- Hint: define QB, and prove by induction that after n steps the machine is
-- in qA or in qB.

------------------------------------------------------------------------
-- What we cannot expect to prove
------------------------------------------------------------------------

-- For a given machine, deciding which of the two holds is the halting
-- problem.  So we do not expect to prove the following for all δ.
Halting : Transition  Set
Halting δ = neverStop δ + willStop δ

------------------------------------------------------------------------
-- Equality, and proofs by computation
------------------------------------------------------------------------

data _≡_ {A : Set} (a : A) : A  Set where
  refl : a  a

-- These are checked by the type checker itself: it runs the machine.
bb2-halts : fst runBB2  qHalt
bb2-halts = refl

bb4-halts : fst runBB4  qHalt
bb4-halts = refl

forever-runs : fst runForever  qA
forever-runs = refl

--------------------------------------------------------------------------------
-- Follow-up exercises on this file:
--
-- 1. Prove that neverStop is logically equivalent to the negation of willStop.
--    What about the opposite direction: Is willStop the negation of neverStop?
--
-- 2. Refactor the code so that State does not contain qHalt,
--    but instead the Transition function is partial,
--
--       State → Symbol → Maybe (State × Symbol × Dir)
--
--    with the standard definition of Maybe:
--
--       data Maybe (A : Set) : Set where
--         nothing : Maybe A
--         just    : A → Maybe A
--
--    This refactoring may force you to adapt many of the definitions.
--
--    Try to make a judgement:
--    Is the refactored code more clear or the original one?
--
--------------------------------------------------------------------------------