;; (unary-map car '((1 2 3) (4 5 6))) => '(1 4)
;; (unary-map cdr '((1 2 3) (4 5 6))) => '((2 3) (5 6))
(define unary-map
  (lambda (proc ls)
    (if (null? ls)
        ()
        (cons (proc (car ls))
              (unary-map proc (cdr ls))))))

;; (ormap null? '((1 2) () (3 4))) => #t
;; (ormap null? '((1 2) (5) (3 4))) => #f
(define ormap
  (lambda (pred ls)
    (if (null? ls)
        #f
        (or (pred (car ls))
            (ormap pred (cdr ls))))))

;; ok
(define map
  (lambda args
    (let ((proc (car args)) (args (cdr args)))
      (if (ormap null? args)
          ()
          (cons (apply proc (unary-map car args))
                (apply map (cons proc (unary-map cdr args))))))))

;; better
(define map
  (lambda (proc . args)
    (if (ormap null? args)
        ()
        (cons (apply proc (unary-map car args))
              (apply map proc (unary-map cdr args))))))

(define explode
  (lambda (s)
    (map string->symbol
	 (map string 
	      (string->list (symbol->string s))))))

(define implode
  (lambda (ls)
    (string->symbol
     (apply string-append 
	    (map symbol->string ls)))))

(define compose
  (lambda (f g)
    (lambda (x) (f (g x)))))

(define iterate
  (lambda (op n)
    (if (= n 0)
	(lambda (x) x)
	(compose op (iterate op (sub1 n))))))

(define list-ref
  (lambda (n ls)
    (car ((iterate cdr n) ls))))

;; (u + n/u)/2 -> u
(define square-root
  (lambda (n)
    ((iterate (lambda (u) (/ (+ u (/ n u)) 2.0)) 10) 1)))

(define rot
  (lambda (ls)
    (append (cdr ls)
	    (list (car ls)))))

(define select
  (lambda (pred ls1 ls2)
    (if (null? ls1)
	()
	(if (pred (car ls1))
	    (cons (car ls2)
		  (select pred (cdr ls1) (cdr ls2)))
	    (select pred (cdr ls1) (cdr ls2))))))

(define alphabet '(a b c d e f g h i j k l m n o p q r s t u v w x y z))

(define encipher-letter
  (lambda (letter n)
    (car (select (lambda (x) (eq? x letter))
		 alphabet
		 ((iterate rot n) alphabet)))))

(define encipher-symbol
  (lambda (s n)
    (implode (map (lambda (x) (encipher-letter x n))
		  (explode s)))))

(define encipher-message
  (lambda (message n)
    (map (lambda (x) (encipher-symbol x n)) 
	 message)))

(define decipher-message
  (lambda (message n)
    (encipher-message message (- 26 n))))

(define break-code
  (lambda (message)
    (map (lambda (n) (decipher-message message n))
	 (iota 26))))