;; ok
(define maxn
  (lambda args
    (letrec
      ((singleton?
         (lambda (ls) 
           (null? (cdr ls))))
       (max2
         (lambda (x y)
           (if (> x y)
             x
             y)))
       (max-list
         (lambda (ls)
           (if (singleton? ls)
             (car ls)
             (max2 (car ls)
                   (max-list (cdr ls)))))))
    (max-list args))))
  
;; better
(define maxn
  (lambda args
    (letrec
      ((singleton?
        (lambda (ls) 
          (null? (cdr ls))))
       (max2
        (lambda (x y)
          (if (> x y)
            x
            y))))
      (if (singleton? args)
        (car args)
        (max2 (car args)
              (apply maxn (cdr args)))))))

;; best
(define maxn
  (lambda (first . rest)
    (letrec
      ((max2
        (lambda (x y)
          (if (> x y)
            x
            y))))
      (if (null? rest)
        first
        (max2 first (apply maxn rest)))))

(define quadratic-formula
  (lambda (a b c)
    (let* ((discriminant (sqrt (- (* b b) (* 4 a c))))
	   (denominator (* 2 a))
           (-b (- b))
           (root (lambda (pm) (/ (pm -b discriminant) denominator))))
      (list (root +) (root -)))))

;; simple map
(define map
  (lambda (proc ls)
    (if (null? ls)
	'()
	(cons (proc (car ls)) (map proc (cdr ls))))))

(define let-args
  (lambda (exp)
    (map car (cadr exp))))

(define let-vals
  (lambda (exp)
    (map cadr (cadr exp))))

(define let-body
  (lambda (exp)
    (caddr exp)))

(define let->lambda
  (lambda (exp)
    (append (list (list 'lambda (let-args exp) (let-body exp)))
	    (let-vals exp))))

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

(define reverse-all
  (lambda (ls)
    (cond ((null? ls) ())
	  ((pair? (car ls))
	   (append (reverse-all (cdr ls))
		   (list (reverse-all (car ls)))))
	  (else
	   (append (reverse-all (cdr ls))
		   (list (car ls)))))))

(define reverse-all
  (lambda (ls)
    (if (pair? ls)
	(reverse (map reverse-all ls))
	ls)))

(define copy-all
  (lambda (ls)
    (if (pair? ls)
	(map copy-all ls)
	ls)))

(define flatten-all
  (lambda (ls)
    (cond ((null? ls) ())
	  ((pair? (car ls))
	   (append (flatten-all (car ls))
		   (flatten-all (cdr ls))))
	  (else
	   (cons (car ls)
		 (flatten-all (cdr ls)))))))

(define flatten
  (lambda (ls)
    (apply append
	   (map (lambda (x) (if (pair? x) x (list x)))
		ls))))

(define flatten-all
  (lambda (ls)
    (if (pair? ls)
	(flatten (map flatten-all ls))
	ls)))

(define filter
  (lambda (pred ls)
    (cond ((null? ls) ())
	  ((pred (car ls))
	   (cons (car ls) (filter pred (cdr ls))))
	  (else
	   (filter pred (cdr ls))))))

(define delete
  (lambda (item ls)
    (filter (lambda (x) (not (eq? x item))) ls)))

(define permutations
  (lambda (s)
    (if (null? s)
	'(())
	(flatten 
	  (map (lambda (x)
	         (map (lambda (p) (cons x p))
		      (permutations (delete x s))))
	       s)))))

;; world's slowest sorting method
(define sort
  (lambda (ls)
    (car (filter (lambda (p) (apply < p)) 
		 (permutations ls)))))

(define iota
  (lambda (n)
    (letrec
      ((loop 
	(lambda (m acc)
	  (if (= m 0)
	      acc
	      (loop (sub1 m) (cons m acc))))))
      (loop n ()))))

(define fact
  (lambda (n)
    (apply * (iota n))))

;; e = 1/0! + 1/1! + 1/2! + 1/3! + 1/4! ...
(define e
  (apply + (map / (map fact (map sub1 (iota 10))))))

(define mini-env
  `((+ ,+) (- ,-) (* ,*) (/ ,/)))

(define mini-eval
  (lambda (exp)
    (if (number? exp)
        exp
        (if (symbol? exp)
            (cadr (assoc exp mini-env))
            (apply (mini-eval (car exp))
                   (map mini-eval (cdr exp)))))))