;;AUTODOC: sets "functions on sets"
;;Sets are implemented as hashtables for efficiency. Implementing them as 
;;normal lists as in common lisp is too inefficient
;;The following functions are defined on sets:
;; (set:member? set element)  is element member of set?
;; (set:adjoin set element)   add an element to a set
;; (set:remove set element)   remove an element to a set
;; (set:list set)             coerces a set into a list of elements (copy)
;; (set:make list)            coerces a list of elements into a set (copy)
;; (Set elements...)          makes a new set out of elements
;; (set:empty? set)           is the set empty? 
;; (set:subset? set1 set2)    return t if set2 is included in set1
;; (set:dolist (var set) forms...) executes form for all elements of set
;; (set:union sets...)        returns a new set, union of all sets
;; (set:union! sets...)       modify first set to be the union of all sets
;; (set:intersection sets...) returns a new set, intersection of all sets
;; (set:intersection! sets...) modify first set to be the intersection
;; (set:difference set1 set2) returns a new set, set1 less the elements of set2
;; (set:difference! set1 set2) modify set1 by removing all elements of set2

;;AUTOLOAD: set:member?
(defvar set:member? getn)

;;AUTOLOAD: set:adjoin
(defun set:adjoin (set elt)
  (put set elt t)
)

;;AUTOLOAD: set:remove
(defun set:remove (set elt)
  (delete set elt)
)

;;AUTOLOAD: set:list
(defun set:list (set &aux (l (list)))
  (dohash (k v set) (put l -1 k))
  l
)

;;AUTOLOAD: set:make
(defun set:make (l &aux (set (Hashtable ())))
  (dolist (elt l) (put set elt t))
  set
)

;;AUTOLOAD: Set
(defun Set (&rest l &aux (set (Hashtable ())))
  (dolist (elt l) (put set elt t))
  set
))

;;AUTOLOAD: set:empty?
(defun set:empty? (set)
  (/= (length set 0))
)

;:AUTOLOAD: set:subset?
(defun set:subset? (set subset)
  (catch 'no
    (dohash (elt v subset)
      (if (getn set elt)
	()
	(throw 'no ())
    ))
    t
))

(setq set:dolist-error "Bad set:dolist syntax")

;;AUTOLOAD: set:dolist
(defmacro set:dolist (vardecls &rest forms)
  `(dohash (,(get vardecls 0 '(error 'set:dolist-error))
      set:#dummy#
      ,(get vardecls 1 '(error 'set:dolist-error)))
    ,@forms
))

;;AUTOLOAD: set:union
(defun set:union (&rest sets &aux (set (Hashtable ())))
  (dolist (curset sets)
    (dohash (elt v curset)
      (put set elt t)
  ))
  set
)

;;AUTOLOAD: set:union!
(defun set:union! (set &rest sets)
  (dolist (curset sets)
    (dohash (elt v curset)
      (put set elt t)
  ))
  set
)
  
;;AUTOLOAD: set:intersection
(defun set:intersection (&rest sets &aux (set (Hashtable ()))
    (n (length (getn sets 0)))
    (min-set-index 0)
    sets-to-check
  )
  ;; find smallest set for performance
  (dotimes (i (length sets))
    (if (< (length (get sets i)) n) (progn
	(setq n (length (get sets i)))
	(setq min-set-index i)
  )))
  (setq sets-to-check (delete (copy sets) min-set-index))
  (dohash (elt v (get sets min-set-index))
    (catch 'no
      (dolist (curset sets-to-check)
	(if (getn curset elt)
	  ()
	  (throw 'no)
      ))
      (put set elt t)
  ))
  set
)

;;AUTOLOAD: set:intersection!
(defun set:intersection! (set &rest sets)
  (dohash (elt v set)
    (catch 'no
      (dolist (curset sets)
	(if (getn curset elt)
	  ()
	  (progn
	    (delete set elt)
	    (throw 'no)
	  )
  ))))
  set
)
    
;;AUTOLOAD: set:difference
(defun set:difference (set1 set2 &aux (set (Hashtable ())))
  (dohash (elt v set1)
    (if (getn set2 elt)
      ()
      (put set elt t)
  ))
  set
)

;;AUTOLOAD: set:difference!
(defun set:difference! (set set2)
  (dohash (elt v set)
    (if (getn set2 elt)
      ()
      (delete set elt t)
  ))
  set
)
