#lang racket
(require rackunit) (provide (all-defined-out))
(define-struct state (data ptr)
#:mutable)
(define (new-state)
(make-state (make-vector 30000 0)
0))
(define (increment-ptr a-state)
(set-state-ptr! a-state (add1 (state-ptr a-state))))
(define (decrement-ptr a-state)
(set-state-ptr! a-state (sub1 (state-ptr a-state))))
(define (increment-byte a-state)
(let ([v (state-data a-state)]
[i (state-ptr a-state)])
(vector-set! v i (add1 (vector-ref v i)))))
(define (decrement-byte a-state)
(let ([v (state-data a-state)]
[i (state-ptr a-state)])
(vector-set! v i (sub1 (vector-ref v i)))))
(define (write-byte-to-stdout a-state)
(let ([v (state-data a-state)]
[i (state-ptr a-state)])
(write-byte (vector-ref v i) (current-output-port))))
(define (read-byte-from-stdin a-state)
(let ([v (state-data a-state)]
[i (state-ptr a-state)])
(vector-set! v i (read-byte (current-input-port)))))
(define-syntax-rule (loop a-state body ...)
(let loop ()
(unless (= (vector-ref (state-data a-state)
(state-ptr a-state))
0)
body ...
(loop))))
(let ([s (new-state)])
(increment-byte s)
(check-equal? 1 (vector-ref (state-data s) 0))
(increment-byte s)
(check-equal? 2 (vector-ref (state-data s) 0))
(decrement-byte s)
(check-equal? 1 (vector-ref (state-data s) 0)))
(let ([s (new-state)])
(increment-ptr s)
(increment-byte s)
(check-equal? 0 (vector-ref (state-data s) 0))
(check-equal? 1 (vector-ref (state-data s) 1))
(decrement-ptr s)
(increment-byte s)
(check-equal? 1 (vector-ref (state-data s) 0))
(check-equal? 1 (vector-ref (state-data s) 1)))
(let ([s (new-state)])
(parameterize ([current-input-port
(open-input-bytes (bytes 3 1 4))])
(read-byte-from-stdin s)
(increment-ptr s)
(read-byte-from-stdin s)
(increment-ptr s)
(read-byte-from-stdin s))
(check-equal? 3 (vector-ref (state-data s) 0))
(check-equal? 1 (vector-ref (state-data s) 1))
(check-equal? 4 (vector-ref (state-data s) 2)))
(let ([s (new-state)])
(set-state-data! s (vector 80 76 84))
(let ([simulated-stdout (open-output-string)])
(parameterize ([current-output-port simulated-stdout])
(write-byte-to-stdout s)
(increment-ptr s)
(write-byte-to-stdout s)
(increment-ptr s)
(write-byte-to-stdout s))
(check-equal? "PLT" (get-output-string simulated-stdout))))
(let ([s (new-state)])
(set-state-data! s (vector 0 29 92 14 243 1 6 92))
(set-state-ptr! s 7)
(loop s
(loop s (decrement-byte s))
(decrement-ptr s))
(check-equal? 0 (state-ptr s))
(check-equal? (vector 0 0 0 0 0 0 0 0) (state-data s)))