;; Library of  useful base functions for functional programming.
;; Version: 2007 January 29
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Selecting members of a list -- correspond to functions 1, 2, ... in Backus
;; less characters so longer expressions are easier to write.


(defun 1st (list) (first list))
(defun 2nd (list) (second list))
(defun 3rd (list) (third list))
(defun 4th (list) (fourth list))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Swap a pair of arguments

(defun swap (pair) (list (2nd pair) (1st pair)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Inverse of a number
;; Pre-condition: n /= 0

(defun inverse(n) (/ 1 n))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Identity
;; Example use  sum 1..n == (sigma 1 n 'I)

(defun I(x) x)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Return first of two arguments
;; Example use  length(list) == (reduce '+ (mapcar (bu 'K 1) list))

(defun K(x y) x)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; One argument add and multiply functions.
;; Require: List of numbers of at least length 2
;; Ensure: Result is + or * the first two numbers in the list

(defun +pair (a-pair) (+ (first a-pair) (second a-pair)))
(defun *pair (a-pair) (* (first a-pair) (second a-pair)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Factorial
;; Pre-condition: n >=0

(defun factorial (n) (reduce-pl '* (range 1 n) 1))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Raise a term to a given exponent value
;; term power exponent    For example, 2 power 4 = 16

(defun power (term exponent)
 (cond ((< exponent 0) (inverse (power term (- exponent))))
       ((= exponent 0) 1)
       (t (* term (power term (1- exponent))))
))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Partition is a general utility function useful to a wide range of
;; problems. Below is an example of what partition does
;;
;;    (partition 4 '(a b c d e f g h i j k))
;;        returns   ( (a b c d) (e f g h) (i j k) )
;;
;; Notice that the last partition is often shorter than the others because the
;; original input list is not a multiple of the partition size.
;;
;; Input:  "aList" and the "size" of a partition
;; Return: a list of sublists each of length "size" except for the last
;;         sublist
;; Side effects: none
;;
;; The function makes use of one auxilary function "splitAt" which returns
;; two values.

    (defun partition (size list)
     (cond ((null list) nil)
           (t (multiple-value-bind (firstPart butfirst)
                 (splitAt size list)
              (cons firstPart (partition size butfirst))))
    ))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Split a list into two sublists given the length of the first sublist.
;;
;; Input:  a "list" and a "count"
;; Return: two values -- the first is the first count items from the list
;;         and the second is the rest of the list.
;;         e.g. (splitAt 3 '(a b c d e f g)) ==> (a b c) (d e f g)
;;              (splitAt 5 '(a b c d e f g)) ==> (a b c d e)  (f g)
;;
;; Side effects: none
;;

    (defun splitAt(count list)
     (cond ((null list) (values nil nil))
           ((= count 0) (values nil list))
           (t (multiple-value-bind (firstItem butfirst)
                                   (splitAt (1- count) (cdr list))
               (values (cons (car list) firstItem) butfirst)))
    ))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; repeat a list n times

(defun repeat (list n)
 (mapcar #'(lambda(i) (I list)) (range 1 n)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Prefix, Suffix and Sublist by position

;; Require: bound to be an integer, list to be a  list
;; Ensure: Result = list[1 .. min(bound, #list)]

(defun prefix (bound list)
 (cond ((null list) nil)
       ((> bound 0) (cons (car list) (prefix (1- bound) (cdr list))))
))

;; Require: bound to be an integer, list to be a  list
;; Ensure: Result = list[max(1,bound) .. #list]

(defun suffix (bound list)
 (cond ((null list) nil)
       ((> bound 1) (suffix (1- bound) (cdr list)))
       (t list)
))

;; Require: bound to be an integer, list to be a  list
;; Ensure: Result = list[max(1,lowerBound) .. min(upperBound, #list)]

(defun sublist (lowerBound upperBound list)
 (cond ((null list) nil)
       ((> lowerBound 1) (sublist (1- lowerBound) (1- upperBound) (cdr list)))
       ((> upperBound 0) (cons (car list)
                               (sublist lowerBound (1- upperBound) (cdr list))))
))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; Random numbers
;; Generate a random number from  0 < seed < 1.0

(defun random-1 (seed)
 (let ((r (* seed 147.0))) (- r (float (floor r))) ))

;; Generate a sequence of length n of random numbers beginning with
;; a seed value; eg. sin(0.1)

(defun randlist (n seed) (genlist seed 'random-1 n))

;; Generate an integer in the range i..j from random number 0 < r < 1.0

(defun randint (i j r) (1+ (floor (* r (1+ (- j i))))))


