1 ;;;; This software is part of the SBCL system. See the README file for
4 ;;;; This software is derived from the CMU CL system, which was
5 ;;;; written at Carnegie Mellon University and released into the
6 ;;;; public domain. The software is in the public domain and is
7 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
8 ;;;; files for more information.
12 ;;;; A somewhat efficient set implementation that can store arbitrary
13 ;;;; objects. For small sets the data is stored in a list, but when
14 ;;;; the amount of elements grows beyond +XSET-LIST-SIZE-LIMIT+, we
15 ;;;; switch to a hash-table instead.
17 ;;;; ALLOC-XSET allocates an empty XSET. ADD-TO-XSET adds an element
18 ;;;; to an XSET: it should be used only on freshly allocated XSETs.
20 ;;;; XSET-EMPTY-P, XSET-INTERSECTION, XSET-SUBSET-P, and XSET-MEMBER-P
21 ;;;; do the obvious things. MAP-XSET maps over the element, but
22 ;;;; requires a function as the first argument -- not a function
25 ;;;; XSET-LIST-SIZE is true only for XSETs whose data is stored into a
26 ;;;; list -- XSET-COUNT returns the real value.
28 (in-package "SB!KERNEL")
30 (defstruct (xset (:constructor alloc-xset
) (:copier nil
) (:predicate nil
))
31 (list-size 0 :type index
)
32 (data nil
:type
(or list hash-table
)))
34 (defun xset-count (xset)
35 (let ((data (xset-data xset
)))
38 (hash-table-count data
))))
40 (defun map-xset (function xset
)
41 (declare (function function
))
42 (let ((data (xset-data xset
)))
45 (funcall function elt
))
46 (maphash (lambda (k v
)
52 (defconstant +xset-list-size-limit
+ 24)
54 ;;; Checks that the element is not in the set yet.
55 (defun add-to-xset (elt xset
)
56 (let ((data (xset-data xset
))
57 (size (xset-list-size xset
)))
59 (if (< size
+xset-list-size-limit
+)
60 (unless (member elt data
:test
#'eql
)
61 (setf (xset-list-size xset
) (1+ size
)
62 (xset-data xset
) (cons elt data
)))
63 (let ((table (make-hash-table :size
(* 2 size
) :test
#'eql
)))
64 (setf (gethash elt table
) t
)
66 (setf (gethash x table
) t
))
67 (setf (xset-data xset
) table
)))
68 (setf (gethash elt data
) t
))))
70 ;; items must be canonical - no duplicates - and few in number.
71 (defun xset-from-list (items)
72 (let ((n (length items
)))
73 (aver (<= n
+xset-list-size-limit
+))
74 (let ((xset (alloc-xset)))
75 (setf (xset-list-size xset
) n
(xset-data xset
) items
)
78 (defun xset-union (a b
)
79 (let ((xset (alloc-xset)))
88 (defun xset-member-p (elt xset
)
89 (let ((data (xset-data xset
)))
91 (member elt data
:test
#'eql
)
94 (defun xset-members (xset)
95 (let ((data (xset-data xset
)))
99 (maphash (lambda (k v
)
105 (defun xset-intersection (a b
)
106 (let ((intersection (alloc-xset)))
107 (multiple-value-bind (source lookup
)
108 (if (< (xset-list-size a
) (xset-list-size b
))
111 (let ((data (xset-data lookup
)))
112 (map-xset (if (listp data
)
114 (when (member elt data
:test
#'eql
)
115 (add-to-xset elt intersection
)))
117 (when (gethash elt data
)
118 (add-to-xset elt intersection
))))
122 (defun xset-subset-p (xset1 xset2
)
123 (when (<= (xset-count xset1
) (xset-count xset2
))
124 (let ((data (xset-data xset2
)))
128 (unless (member elt data
:test
#'eql
)
129 (return-from xset-subset-p nil
)))
131 (unless (gethash elt data
)
132 (return-from xset-subset-p nil
))))
136 #!-sb-fluid
(declaim (inline xset-empty-p
))
137 (defun xset-empty-p (xset)
138 (not (xset-data xset
)))