; blockchain
 
(define (fold-left op base xs)
  (if (null? xs)
      base
      (fold-left op (op base (car xs)) (cdr xs))))
 
(define (range . args)
  (case (length args)
    ((1) (range 0 (car args) (if (negative? (car args)) -1 1)))
    ((2) (range (car args) (cadr args) (if (< (car args) (cadr args)) 1 -1)))
    ((3) (let ((le? (if (negative? (caddr args)) >= <=)))
           (let loop ((x(car args)) (xs '()))
             (if (le? (cadr args) x)
                 (reverse xs)
                 (loop (+ x (caddr args)) (cons x xs))))))
    (else (error 'range "unrecognized arguments"))))
 
(define seed 20180525)
(define (random) (set! seed (modulo (* 16807 seed) 2147483647)) seed)
(define (randint n) (floor (* n (random) (/ 2147483647))))
 
(define (shuffle x)
  (do ((v (list->vector x)) (n (length x) (- n 1)))
      ((zero? n) (vector->list v))
    (let* ((r (randint n)) (t (vector-ref v r)))
      (vector-set! v r (vector-ref v (- n 1)))
      (vector-set! v (- n 1) t))))
 
(define t (list->vector (shuffle (range 256))))
 
(define (pearson8 str)
  (fold-left (lambda (n h) (vector-ref t (modulo (+ n h) 256)))
             0 (map char->integer (string->list str))))

(define (index block) (vector-ref block 0))
(define (datum block) (vector-ref block 1))
(define (phash block) (vector-ref block 2))
(define (chash block) (vector-ref block 3))

(define (hash index datum phash)
  (pearson8 (string-append (number->string index) datum (number->string phash))))

(define genesis (vector 0 "Genesis Block" 0 (hash 0 "Genesis Block" 0)))

(define (adjoin chain datum)
  (let ((index (+ (index (car chain)) 1)) (phash (chash (car chain))))
    (cons (vector index datum phash (hash index datum phash)) chain)))

(define (validate? chain)
  (define (valid? curr prev)
    (and (= (index curr) (+ (index prev) 1))
         (= (phash curr) (chash prev))
         (= (hash (index curr) (datum curr) (phash curr)) (chash curr))))
  (if (null? (cdr chain)) (equal? (car chain) genesis)
    (and (valid? (car chain) (cadr chain)) (validate? (cdr chain)))))
   
(define b (list genesis))
(set! b (adjoin b "Pearson Hashing"))
(set! b (adjoin b "Floyd's Triangle"))
(set! b (adjoin b "Billing Period"))
(set! b (adjoin b "Sum Embedded Numbers"))
(set! b (adjoin b "Help Wanted: Report Generator"))

(display b) (newline)
(display (validate? b)) (newline)