CREATE TABLE input (
  input BYTEA
);

INSERT INTO input VALUES ('aabbbaa'::BYTEA || '\x00'::BYTEA);

CREATE TABLE program (
  state INT,
  read INT,
  next_state INT,
  write INT,
  move INT,
  accept BOOL DEFAULT false,
  reject BOOL DEFAULT false
);

-- This program is a binary palindrome recognizer, using the value 'a' and 'b'.
INSERT INTO program VALUES 
  -- search right
  (000,        0  , 100,        0  , 0, false, false), -- input is empty; accept
  (000, ASCII('a'), 011,        0  , 1, false, false),
  (000, ASCII('b'), 016,        0  , 1, false, false),
  
  -- search right for 'a' 
  (011,        0  , 012,        0  , -1, false, false), -- end found
  (011, ASCII('a'), 011, ASCII('a'),  1, false, false),
  (011, ASCII('b'), 011, ASCII('b'),  1, false, false),
  
  -- search right for 'a': verify value
  (012,        0  , 100,        0  ,  0, false, false),
  (012, ASCII('a'), 001,        0  , -1, false, false),
  (012, ASCII('b'), 101,        0  ,  0, false, false), -- 'a' expected, 'b' found
  
  -- search right for 'b'
  (016,        0  , 017,        0  , -1, false, false), -- end found
  (016, ASCII('a'), 016, ASCII('a'),  1, false, false),
  (016, ASCII('b'), 016, ASCII('b'),  1, false, false),
  
  -- search right for 'b': verify value
  (017,        0  , 100,        0  ,  0, false, false),
  (017, ASCII('a'), 101,        0  ,  0, false, false), -- 'b' expected, 'a' found
  (017, ASCII('b'), 001,        0  , -1, false, false), 
  
  
  -- search left
  (001,        0  , 100,        0  ,  0, false, false), -- input is empty; accept
  (001, ASCII('a'), 021,        0  , -1, false, false),
  (001, ASCII('b'), 026,        0  , -1, false, false),
    
  -- search left for 'a' 
  (021,        0  , 022,        0  ,  1, false, false), -- end found
  (021, ASCII('a'), 021, ASCII('a'), -1, false, false),
  (021, ASCII('b'), 021, ASCII('b'), -1, false, false),
  
  -- search left for 'a': verify value
  (022,        0  , 100,        0  ,  0, false, false),
  (022, ASCII('a'), 000,        0  ,  1, false, false),
  (022, ASCII('b'), 101,        0  ,  0, false, false), -- 'a' expected, 'b' found
  
  -- search left for 'b'
  (026,        0  , 027,        0  ,  1, false, false), -- end found
  (026, ASCII('a'), 026, ASCII('a'), -1, false, false),
  (026, ASCII('b'), 026, ASCII('b'), -1, false, false),
  
  -- search left for 'b': verify value
  (027,        0  , 100,        0  ,  0, false, false),
  (027, ASCII('a'), 101,        0  ,  0, false, false), -- 'b' expected, 'a' found
  (027, ASCII('b'), 000,        0  ,  1, false, false), 
  
  
  -- accept state
  (100,        0  , 100,        0  ,  0, true, false),
  (100, ASCII('a'), 100,        0  ,  0, true, false),
  (100, ASCII('b'), 100,        0  ,  0, true, false),
  
  -- reject state
  (101,        0  , 101,        0  ,  0, false, true),
  (101, ASCII('a'), 101,        0  ,  0, false, true),
  (101, ASCII('b'), 101,        0  ,  0, false, true)
;


WITH RECURSIVE run_state (iteration, state, position, band, accept, reject) AS (
  SELECT 
    0, 000, 0 , input, false, false
  FROM input

  UNION ALL
  
  SELECT 
    run_state.iteration + 1,
    program.next_state,
    run_state.position + program.move,
    set_byte(run_state.band, run_state.position, program.write),
    program.accept,
    program.reject
  FROM run_state
  INNER JOIN program
    ON  program.state = run_state.state
    AND program.read = get_byte(run_state.band, run_state.position)
  WHERE 
        NOT run_state.accept 
    AND NOT run_state.reject
)
SELECT 
  CASE
    WHEN run.accept THEN 'Input was accepted in ' || run.iteration || ' iterations.'
    WHEN run.reject THEN 'Input was rejected in ' || run.iteration || ' iterations.'
    ELSE '[insert confused meme here]'
  END
FROM run_state AS run
WHERE 
     run.accept
  OR run.reject;
