;;; Eksempelkode fra PVVs Lisp-kurs 11.03.2010.

;;; Skrevet av eirikald og oysteini.

;;; Se http://wiki.pvv.ntnu.no/pvv/Kurs/Lisp_1

;;; Denne filen inneholder alle funksjonsdefinisjonene fra kurset.  Se
;;; presentasjonen for beskrivelser og eksempler på anvendelse av
;;; funksjonene.

;;; I SLIME kan du laste inn hele filen med C-c C-l eller
;;; enkeltdefinisjoner med C-M-x.  En advarsel om at variabelen _ er
;;; ubrukt i funksjonen LEN* er forventet.  For øvrig bør filen kunne
;;; lastes inn uten problemer.




;;; Del 1.

(defun square (x) (* x x))

(defun hypothenuse (side1 side2)
  (sqrt (+ (square side1) (square side2))))

(defun describe-number (n)
  (if (evenp n)
      "It is even!"
      "It is odd!"))

(defun ! (n)
  (if (= n 0)
      1
      (* n
         (! (- n 1)))))

(defun fib (n)
  (if (< n 2)
      1
      (+ (fib (- n 1))
         (fib (- n 2)))))

(defun elem (n list)
  (if (= n 0)               ; hvis N er 0
      (first list)          ; vil vi ha første element,
      (elem (- n 1)         ; ellers vil vi ha (N-1)-te
            (rest list))))  ; fra resten av listen

(defun len (list)
  (if (null list)         ; Hvis listen er tom
      0                   ; er lengden 0,
      (+ 1                ; ellers er den 1 pluss
         (len             ; lengden til
          (rest list))))) ; resten av listen.

(defun sum (list)
  (if (null list)          ; Hvis listen er tom
      0                    ; er dens sum 0,
      (+ (first list)      ; ellers første tall pluss
         (sum              ; summen av
          (rest list)))))  ; resten av listen.

(defun iota (a b)
  (if (> a b)
      nil
      (cons a
            (iota (+ a 1) b))))

(defun cat (list1 list2) ; meow
  (if (null list1)
      list2
      (cons (first list1)
            (cat (rest list1) list2))))

(defun insert (elem i list)
  (if (= i 0)
      (cons elem list)
      (cons (first list)
            (insert elem
                    (- i 1)
                    (rest list)))))

(defun collatz (n)
  (if (evenp n)
      (/ n 2)
      (1+ (* n 3))))

(defun collatz-length (n)
  (if (= n 1)
      0
      (+ 1 (collatz-length (collatz n)))))




;;; Del 2.

(defun compose (f g)
  (lambda (x)
    (funcall f (funcall g x))))

(defun apply-over (fun list)
  (if (null list)
      nil
      (cons (funcall fun (first list))
            (apply-over fun (rest list)))))

(defun foldr (fun initial list)
  (if (null list)
      initial
      (funcall fun
               (first list)
               (foldr fun initial (rest list)))))

(defun !* (n)
  (foldr #'* 1 (iota 1 n)))

(defun len* (list)
  (foldr (lambda (_ l) (1+ l))
         0
         list))

(defun drop (n list)
  (if (= n 0)
      list
      (drop (- n 1) (rest list))))

(defun fact-tailrec (n acc)
  (if (= n 0)
      acc
      (fact-tailrec (- n 1) (* n acc))))

(defun fact (n)
  (fact-tailrec n 1))

(defun fib-rec (n i fibI fibI-1)
  (if (= i n)
      fibI
      (fib-rec n (1+ i)
               (+ fibI fibI-1) fibI)))

(defun fib* (n)
  (if (< n 2)
      1
      (fib-rec n 1 1 1)))

(defun fn-key (&key name (age 20))
  (list name age))

(defun fn-optional (&optional name (age 20))
  (list name age))

(defun fn-rest (mom &rest children)
  (list mom children))

(defun flip-append (list tail)
  (if (null list)
      tail
      (flip-append (rest list)
                   (cons (first list) tail))))

(defun flip (list)
  (flip-append list nil))

(defun repeat (fun initial end &key (test #'eql))
  (if (funcall test initial end)
      (list end)
      (cons initial
            (repeat fun (funcall fun initial) end
                    :test test))))

(defun collatz-list (n)
  (repeat #'collatz n 1))




;;; Del 3.

(defun braid (list1 list2) ; merge
  (if (null list1)
      list2
      (if (null list2)
          list1
          (if (<= (first list1) (first list2))
              (cons (first list1)
                    (braid (rest list1) list2))
              (cons (first list2)
                    (braid list1 (rest list2)))))))

(defun braid* (list1 list2)
  (cond
    ((null list1) list2)
    ((null list2) list1)
    ((<= (first list1) (first list2))
     (cons (first list1)
           (braid (rest list1) list2)))
    (t
     (cons (first list2)
           (braid list1 (rest list2))))))

(defun assoc-value (key alist &key (default nil))
  (let ((pair (assoc key alist)))
    (if pair
        (cdr pair)
        default)))

(defun prefix-to-infix (expr)
  (let ((func (first expr))
        (arg1 (second expr))
        (arg2 (third expr)))
    (list arg1 func arg2)))