;;;Exercises from chapter 13 of 'Common Lisp Interactive Approach'

;;;Set up package for this file and its functions.
(eval-when (:compile-toplevel :load-toplevel :execute)
  (if (not (find-package "UTIL"))
      ;;Symbol names MUST be upper-case to work properly
      (defpackage "UTIL"
	  (:use "COMMON-LISP")
	;;(shadow "symbol")
	(:export "ELEMENTP" "ELEMENT" "BAGP" "BAG" ))
      (export '(ELEMENTP ELEMENT BAGP BAG) :util) ))

(in-package "UTIL")

;;;Determine if argument meets the criteria of an 'element' as given in '13.12'
(defun elementp (e)
  (cond ((symbolp e) t)
	((characterp e) t)
	((numberp e) t)
	((packagep e) t)
	))

;;;Added definition from ch17.11
(deftype element ()
  "Elements are objects testable by EQL,
namely symbols, characters, numbers, and packages."
  '(satisfies util:elementp))

;;;Helper function to visit list members searching for duplicate elements
;;;This turns out to be unnecessary
(defun list-dup-el-srch (n e l)
  "Returns true if list has duplicate elements, otherwise nil"
  (check-type e element)
  (block b
    (progn
      (if (and (< n 0) (null l))
	  (return-from b nil))
      (loop for i from 0 to (1- n)
	    when (eql e (elt l i))
	      do (return-from b t))
      (list-dup-el-srch (1- n) (first l) (rest l)) )))

;;;Added function from ch17:15
(defun bagp (l)
  "Return true if L is a BAG (a list with at least one duplicate element) otherwise nil"
  ;;Decided not to make a bag have mandatory element duplication
  ;;because then simple lists cannot be sets
  ;;(check-type l list)
  ;;(list-dup-el-srch (1- (length l)) (first l) (rest l))
  (if (listp l) t)  )

;;;Added function from ch17:15
(deftype bag ()
  "Bags are lists with at least one duplicated element"
  '(satisfies bagp))
