; next to last item in a list

(define-syntax assert
  (syntax-rules ()
    ((assert expr result)
      (if (not (equal? expr result))
          (for-each display `(
            #\newline "failed assertion:" #\newline
            expr #\newline "expected: " ,result
            #\newline "returned: " ,expr #\newline))))))

(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 (next-to-last xs)
  (cond ((or (null? xs) (null? (cdr xs))) #f)
        ((null? (cddr xs)) (car xs))
        (else (next-to-last (cdr xs)))))

(define (test-next-to-last)
  (assert (next-to-last '()) #f)
  (assert (next-to-last '(1)) #f)
  (assert (next-to-last '(1 2)) 1)
  (assert (next-to-last '(1 2 3)) 2)
  (assert (next-to-last '(1 2 3 4)) 3)
  (assert (next-to-last '(1 2 3 4 5)) 4))

(test-next-to-last)

(define (nth-to-last n xs)
  (if (not (positive? n)) #f
    (let loop ((n n) (leading xs))
      (if (null? leading) #f
        (if (< 1 n) (loop (- n 1) (cdr leading))
          (let loop ((trailing xs) (leading (cdr leading)))
            (if (null? leading) (car trailing)
              (loop (cdr trailing) (cdr leading)))))))))

(define (test-nth-to-last)
  (assert (nth-to-last 0 '()) #f)
  (assert (nth-to-last 0 '(1)) #f)
  (do ((n 1 (+ n 1))) ((= n 7))
    (do ((x 1 (+ x 1))) ((= x 7))
      (let ((r (if (< x n) #f (- x n -1))))
        (assert (nth-to-last n (range 1 (+ x 1))) r)))))

(test-nth-to-last)