#lang eopl
(require tests/eopl/private/utils)

;; in-S? : N -> Bool
;; usage: (in-S? n) = #t if n is in S, #f otherwise
;; The set S is defined in Definition 1.1.1 on page 2.
(define in-S?
  (lambda (n)
    (if (zero? n) #t
        (if (>= (- n 3) 0) (in-S? (- n 3))
            #f))))

(check-equal? (in-S? 0) #t)
(check-equal? (in-S? 1) #f)
(check-equal? (in-S? 2) #f)
(check-equal? (in-S? 3) #t)
(check-equal? (in-S? 4) #f)
(check-equal? (in-S? 9) #t)


;; list-length : List -> Int
;; usage: (list-length l) = the length of l
;; Page: 14
(define list-length
  (lambda (lst)
    (if (null? lst)
        0
        (+ 1 (list-length (cdr lst))))))

(check-equal? (list-length '(a (b c) d)) 3)


;; nth-element : List * Int -> SchemeVal
;; usage: (nth-element lst n) = the nth element of lst
;; Page: 15
(define nth-element
  (lambda (lst n)
    (if (null? lst)
        (report-list-too-short n)
        (if (zero? n)
            (car lst)
            (nth-element (cdr lst) (- n 1))))))

(define report-list-too-short
  (lambda (n)
    (eopl:error 'nth-element 
                "List too short by ~s elements.~%" (+ n 1))))

(check-equal? (nth-element '(a b c) 1) 'b)
(check-equal? (nth-element '(a b c) 2) 'c)
(check-error (nth-element '() 5)
             "nth-element: List too short by 6 elements.\n")


;; remove-first : Sym * Listof(Sym) -> Listof(Sym)
;; Page: 18
(define remove-first
  (lambda (s los)
    (if (null? los)
        '()
        (if (eqv? (car los) s)
            (cdr los)
            (cons (car los) (remove-first s (cdr los)))))))

(check-equal? (remove-first 'a '(a b c)) '(b c))
(check-equal? (remove-first 'b '(e f g)) '(e f g))
(check-equal? (remove-first 'a4 '(c1 a4 c1 a4)) '(c1 c1 a4))
(check-equal? (remove-first 'x '()) '())


;; occurs-free? : Sym * Lcexp -> Bool
;; usage:
;;   returns #t if the symbol var occurs free in exp,
;;   otherwise returns #f.
;; Page: 19
(define occurs-free?
  (lambda (var exp)
    (cond
      ((symbol? exp) (eqv? var exp))  
      ((eqv? (car exp) 'lambda)
       (and
        (not (eqv? var (car (cadr exp))))
        (occurs-free? var (caddr exp))))
      (else
       (or
        (occurs-free? var (car exp))
        (occurs-free? var (cadr exp)))))))

(check-equal? (occurs-free? 'x 'x) #t)
(check-equal? (occurs-free? 'x 'y) #f)
(check-equal? (occurs-free? 'x '(lambda (x) (x y))) #f)
(check-equal? (occurs-free? 'x '(lambda (y) (x y))) #t)
(check-equal? (occurs-free? 'x '((lambda (x) x) (x y))) #t)
(check-equal? (occurs-free? 'x '(lambda (y) (lambda (z) (x (y z))))) #t)


;; subst : Sym * Sym * S-list -> S-list
;; Page: 21
(define subst
  (lambda (new old slist)
    (if (null? slist)
        '()
        (cons
         (subst-in-s-exp new old (car slist)) 
         (subst new old (cdr slist))))))

;; subst-in-s-exp : Sym * Sym * S-exp -> S-exp
;; Page: 21
(define subst-in-s-exp
  (lambda (new old sexp)
    (if (symbol? sexp) 
        (if (eqv? sexp old) new sexp)
        (subst new old sexp))))

(check-equal? (subst 'a 'b '((b c) (b () d)))
              '((a c) (a () d)))


;; number-elements-from : Listof(SchemeVal) * Int ->
;;                           Listof(List(Int,SchemeVal))  
;; usage: (number-elements-from '(v0 v1 v2  ...) n) 
;;         = ((n v0 ) (n+1 v1) (n+2 v2) ...)
;; Page: 23
(define number-elements-from
  (lambda (lst n)
    (if (null? lst) '()
        (cons
         (list n (car lst))
         (number-elements-from (cdr lst) (+ n 1))))))

(check-equal? (number-elements-from '(a b c) 5) 
              '((5 a) (6 b) (7 c)))

;; number-elements : List -> Listof(List(Int,SchemeVal))
;; Page: 23.
(define number-elements
  (lambda (lst)
    (number-elements-from lst 0)))

(check-equal? (number-elements '(a b c)) 
              '((0 a) (1 b) (2 c)))

;; list-sum : Listof(Int) -> Int
;; Page: 24
(define list-sum
  (lambda (loi)
    (if (null? loi)
        0
        (+ (car loi) 
           (list-sum (cdr loi))))))

(check-equal? (list-sum (list 1 2 3 4 5)) 15)

;; partial-vector-sum : Vectorof(Int) * Int -> Int
;; usage if 0 <= n < length(v), then
;;            (partial-vector-sum v n) = SUM(v_i from 0 <= i <= n)
;; Page: 25
(define partial-vector-sum
  (lambda (v n)
    (if (zero? n)
        (vector-ref v 0)
        (+ (vector-ref v n)
           (partial-vector-sum v (- n 1))))))

;; vector-sum : Vectorof(Int) -> Int
;; usage (vector-sum v) = SUM(v_i from 0 <= i <= length(v)-1)
;; Page: 25
(define vector-sum
  (lambda (v)
    (let ((n (vector-length v)))
      (if (zero? n)
          0
          (partial-vector-sum v (- n 1))))))

(check-equal? (vector-sum (vector 1 2 3 4 5)) 15)
