;;; C311
;;; Homework 1
;;; Basic Scheme


;; member-twice? returns #t if a particular atom appears more than once
;; in a list of atoms.

; with helper procedure
(define member-twice?
  (lambda (a ls)
    (cond
      [(null? ls) #f]
      [(eq? a (car ls)) (member? a (cdr ls))]
      [else (member-twice? a (cdr ls))])
)
)


(define member?
  (lambda (a ls)
    (cond
      [(null? ls) #f]
      [(eq? a (car ls)) #t]
      [else (member? a (cdr ls))])
)
)


; same function, with encapsulation
(define member-twice?
  (letrec
    ([member?
       (lambda (a ls)
     (cond
       [(null? ls) #f]
       [(eq? a (car ls)) #t]
       [else (member? a (cdr ls))])
)
]
)

    (lambda (a ls)
      (cond
    [(null? ls) #f]
    [(eq? a (car ls)) (member? a (cdr ls))]
    [else (member-twice? a (cdr ls))])
)
)
)




;; list index takes an atom "a" and a list "ls" and returns the
;; zero-based index of a in ls.  If the atom does not occur in ls,
;; it returns -1.

; with a weird helper function
(define list-index
  (lambda (a ls)
    (cond
      [(null? ls) -1]
      [(eq? (car ls) a) 0]
      [else (maybe-add1 (list-index a (cdr ls)))])
)
)


(define maybe-add1
  (lambda (n)
    (if (= n -1)
    -1
    (add1 n))
)
)


; same approach, with encapsulation
(define list-index
  (let ([maybe-add1
      (lambda (n)
        (if (= n -1)
        -1
        (add1 n))
)
]
)

    (lambda (a ls)
      (cond
    [(null? ls)  -1]
    [(eq? (car ls) a) 0]
    [else (maybe-add1 (list-index a (cdr ls)))])
)
)
)


; same approach, but without helper function:
(define list-index
  (lambda (a ls)
    (cond
      [(null? ls) -1]
      [(eq? (car ls) a) 0]
      [else
    (let ([answ (list-index a (cdr ls))])
      (if (= answ -1)
          -1
          (add1 answ))
)
]
)
)
)


; iterative approach
(define list-index
  (lambda (a ls)
    (letrec ([list-index-hlp
           (lambda (ls n)
         (cond
           [(null? ls) -1]
           [(eq? (car ls) a) n]
           [else (list-index-hlp
               (cdr ls)
               (add1 n))
]
)
)
]
)

      (list-index-hlp ls 0))
)
)




;; duplicate takes a number n and an atom a and returns a list
;; cointaining n occurrences of a.

; straighforward approach
(define duplicate
  (lambda (n a)
    (if (zero? n)
    '()
    (cons a (duplicate (sub1 n) a)))
)
)


; iterative
(define duplicate
  (lambda (n a)
    (letrec ([dup-hlp
           (lambda (n ans)
         (if (zero? n)
             ans
             (dup-hlp (sub1 n) (cons a ans)))
)
]
)

      (dup-hlp n '()))
)
)




;; assq takes an atom and an association list and returns the
;; first list in als whoe car is in a
(define assq
  (lambda (a als)
    (cond
      [(null? als) #f]
      [(eq? (caar als) a) (car als)]
      [else (assq a (cdr als))])
)
)




;; list-ref takes a number and a list and returns the n-th element
;; of the list
(define list-ref
  (lambda (n ls)
    (if (zero? n)
    (car ls)
    (list-ref (sub1 n) (cdr ls)))
)
)




;; snoc takes a list ls and a datum i and returns a new list where
;; i has been added to the end of the list

; a valid solution
(define snoc
  (lambda (ls i)
    (if (null? ls)
    (list i)
    (cons (car ls) (snoc (cdr ls) i)))
)
)


; a not so valid solution
(define snoc
  (lambda (ls i)
    (append ls (list i)))
)


; another kind of invalid solution
(define snoc
  (lambda (ls i)
    (reverse (cons i (reverse ls))))
)




;; intersection takes two sets and returns the intersection of
;; them

; simple solution
(define intersection
  (lambda (s0 s1)
    (cond
      [(null? s0) '()]
      [(memq (car s0) s1)  (cons (car s0)
                 (intersection (cdr s0) s1))
]

      [else (intersection (cdr s0) s1)])
)
)


; iterative solution
(define intersection
  (lambda (s0 s1)
    (letrec ([inter-hlp
           (lambda (s0 answ)
         (cond
           [(null? s0) answ]
           [(memq (car s0) s1)
            (inter-hlp (cdr s0) (cons (car s0) answ))]

           [else (inter-hlp (cdr s0) answ)])
)
]
)

      (inter-hlp s0 '()))
)
)




;; count-parens takes a deep list and returns the number of parentheses
;; in the printed representation of the list.
(define count-parens
  (lambda (ls)
    (cond
      [(null? ls) 2]
      [(pair? ls) (+ (count-parens (car ls)) (count-parens (cdr ls)))]
      [else 0])
)
)


; another solution
(define count-parens
  (lambda (ls)
    (cond
      [(null? ls) 2]
      [(list? (car ls)) (+ (count-parens (car ls)) (count-parens (cdr ls)))]
      [else (count-parens (cdr ls))])
)
)




;; sum-of-squares takes a list of numbers and returns the sum of
;; the squares of the numbers
(define sum-of-squares
  (lambda (tup)
    (if (null? tup)
    0
    (+ (^2 (car tup)) (sum-of-squares (cdr tup))))
)
)


; iterative version
(define sum-of-squares
  (letrec ([sum-hlp
         (lambda (tup sum)
           (if (null? tup)
           sum
           (sum-hlp (cdr tup)
             (+ sum (^2 (car tup))))
)
)
]
)

    (lambda (tup)
      (sum-hlp tup 0))
)
)

            
; helper procedure
(define ^2
  (lambda (n)
    (* n n))
)




;; union takes two lists of symbols without duplicates and returns
;; the list with all the symbols in the lists, without duplicates.
(define union
  (lambda (s0 s1)
    (cond
      [(null? s0) s1]
      [(memq (car s0) s1)  (union (cdr s0) s1)]
      [else (cons (car s0) (union (cdr s0) s1))])
)
)

 
; iterative solution
(define union
  (lambda (s0 s1)
    (letrec ([union-hlp
           (lambda (s0 answ)
         (cond
           [(null? s0) answ]
           [(memq (car s0) s1)
            (union-hlp (cdr s0) answ)]

           [else
             (union-hlp (cdr s0) (cons (car s0) answ))]
)
)
]
)

      (union-hlp s0 s1))
)
)




;; set? takes a list of symbols and returns #t if the list does not
;; contains duplicates
(define set?
  (lambda (ls)
    (cond
      [(null? ls) #t]
      [(memq (car ls) (cdr ls)) #f]
      [else (set? (cdr ls))])
)
)



;; depth takes a list ls and returns the depth of ls.
(define depth
  (lambda (ls)
    (cond
      [(null? ls) 1]
      [(pair? ls) (max (add1 (depth (car ls)))
            (depth (cdr ls)))
]

      [else 0])
)
)


; other solution
(define depth
  (lambda (ls)
    (cond
      [(null? ls) 1]
      [(list? (car ls)) (max (add1 (depth (car ls)))
              (depth (cdr ls)))
]

      [else (depth (cdr ls))])
)
)




;; div performs integer division doing only sums.
;;
(define div
  (lambda (n m)
    (if ( n m)
    0
    (add1 (div (- n m) m)))
)
)


; iterative solution
(define div
  (lambda (n m)
    (letrec ([div-hlp
           (lambda (n quotient)
         (if ( n m)
             quotient
             (div-hlp (- n m) (add1 quotient)))
)
]
)

      (div-hlp n 0))
)
)




;; prefixes takes a list and returns the prefixes of the list
;;
(define prefixes
  (letrec ([prefix-hlp
         (lambda (ls current-prefix answ)
           (if (null? ls)
           answ
           (let ([new-prefix (snoc current-prefix (car ls))])
             (prefix-hlp
               (cdr ls)
               new-prefix
               (cons new-prefix answ))
)
)
)
]
)

    (lambda (ls)
      (prefix-hlp ls '() '(())))
)
)




;; vector-index takes an atom and a vector and returns the zero-based
;; index of the first occurrence of the atom in the vector.
;; returns -1 if the atom is not in there.
(define vector-index
  (lambda (a v)
    (let ([vlen (vector-length v)])
      (letrec ([vindex-hlp
         (lambda (n)
           (cond
             [(= vlen n)    -1]
             [(eq? (vector-ref v n) a)    n]
             [else (vindex-hlp (add1 n))])
)
]
)

    (vindex-hlp 0))
)
)
)




;; flatten takes a deep list removes all the inner parentheses from
;; its argument
(define flatten
  (lambda (ls)
    (cond
      [(null? ls) '()]
      [(list? (car ls)) (append (flatten (car ls)) (flatten (cdr ls)))]
      [else (cons (car ls) (flatten (cdr ls)))])
)
)


; other solution
(define flatten
  (lambda (ls)
    (cond
      [(null? ls) '()]
      [(pair? ls)  (append (flatten (car ls)) (flatten (cdr ls)))]
      [else        (list ls)])
)
)