1 ;; Support for Maxima sets.
2 ;; Author: Barton Willis
3 ;; Send bug reports to willisb@unk.edu
5 ;; This code is in the public domain. It has no warranty. Use this
6 ;; code at your own risk.
10 ;; Use the predicate canonlt to order the elements of a set. The
11 ;; default is $charsets_unorderedp. The predicate $charsets_unorderedp always
12 ;; returns true; when canonlt is its default value, sets are
13 ;; never sorted. Other choices for $charsets_canonlt include $ordergreatp
16 (defun $charsets_unorderedp
(a b
) t
)
17 (defmvar $charsets_canonlt
'$charsets_unorderedp
)
19 ;; The set package doesn't distinguish between sets and lists. We're
20 ;; in trouble if we need to work simultaneously with a set of
21 ;; lists and a set of sets. The commercial Macsyma seems to treat
22 ;; all set elements as lists; thus setify([[1,2],[2,1]) returns
23 ;; [[1,2],[2,1]] because [1,2] and [2,1] are treated as lists
24 ;; (and consequently they are not equal). In this package, the
25 ;; user may decide if set elements that are lists are treated as
26 ;; lists or as sets. When $charsets_set_elements_can_be_sets is true
27 ;; (the default), set elements that are lists are treated
28 ;; as sets; otherwise, when $charsets_set_elements_can_be_sets is
29 ;; false, set elements that are lists are treated as lists.
31 (defmvar $charsets_set_elements_can_be_sets t
)
33 ;; For non-lists x and y, equalp(x,y) returns is(ratsimp(x-y)=0).
34 ;; Signal an error if either x or y is a list. Since equalp uses
35 ;; ratsimp, equalp(x/x,1) is true and equalp(x^(a*b),(x^a)^b)
38 (defun $charsets_equalp
(x y
)
39 (cond ((or ($listp x
) ($listp y
))
40 (merror "Both arguments to `equalp' must be non-lists."))
41 (t ($charsets_xequalp x y
))))
43 ;; If you are certain that x and y are not lists, you might call
44 ;; (at Maxima level) ?xequalp instead of equalp.
46 (defun $charsets_xequalp
(x y
)
47 (like 0 ($ratsimp
(add* x
(*mminus y
)))))
49 ;; If x and y are not lists, $charsets_elem_equalp(x,y) returns
50 ;; equalp(x,y). If x and y are both lists, return
51 ;; setequality(x,y) if set_elements_can_be_sets; otherwise
52 ;; return equalp(x[1],y[1]) and equalp(x[2],y[2]) and ....
53 ;; Finally, if exactly one of x or y is a list, return false.
55 (defun $charsets_elem_equalp
(x y
)
56 (cond ((and ($listp x
) ($listp y
))
57 (cond ($charsets_set_elements_can_be_sets
58 ($charsets_setequality x y
))
59 ((and ($charsets_emptyp x
) ($charsets_emptyp y
)) t
)
62 (= ($length x
) ($length y
))
63 ($charsets_elem_equalp
($first x
) ($first y
))
64 ($charsets_elem_equalp
($rest x
) ($rest y
))))))
65 ((or ($listp x
) ($listp y
)) nil
)
66 (t ($charsets_xequalp x y
))))
68 ;; Adjoin x to the Maxima list a; use equalp for the equality test.
69 ;; When a isn't a list, signal an error.
70 ;; Name this function charsets_adjoin2 to distinguish from 3-arg function
71 ;; of same name in charsets.mac.
73 (defun $charsets_adjoin2
(x a
)
75 (cons '(mlist) (adjoin x
(margs a
) :test
#'$charsets_elem_equalp
)))
76 (t (merror "The second argument to `charsets_adjoin2' must be a list"))))
78 ;; Setify removes duplicates from a Maxima list and sorts the
79 ;; list using the partial ordering function canonlt. To remove the
80 ;; duplicates from the list, we use element_equalp to test for equality.
81 ;; When the argument isn't a list, signal an error.
83 (defun $charsets_setify
(a)
85 (charsets_mysort (cons '(mlist) (remove-duplicates (margs a
) :test
#'$charsets_elem_equalp
))))
86 (t (merror "The argument to `setify' must be a list."))))
88 ;; When $charsets_canonlt is $charsets_unorderedp, don't sort; when $charsets_canonlt isn't
89 ;; $charsets_unorderedp, sort the list using the predicate $charsets_canonlt.
91 (defun charsets_mysort (a)
92 (cond ((eq $charsets_canonlt
'$charsets_unorderedp
) a
)
93 (t ($sort a $charsets_canonlt
))))
95 ;; The maxima function call union(a1,a2,...an) forms the union of the
98 (defmfun $charsets_union
( &rest a
)
99 ; (setq a (margs a)) this is buggy
100 (cond ((member nil
(mapcar #'$listp a
))
101 (merror "Each argument to `union' must be a list."))
103 (cons '(mlist) (remove-duplicates (apply 'append
(map 'list
'rest a
)) :test
#'$charsets_elem_equalp
)))))
105 ;; Remove elements of b from a. Signal an error if a or b aren't lists.
106 ;; Use element_equalp for the equality test.
108 (defun $charsets_setdifference
(a b
)
109 (cond ((and ($listp a
) ($listp b
))
110 (cons '(mlist) (set-difference (margs a
) (margs b
) :test
#'$charsets_elem_equalp
)))
111 (t (merror "Both arguments to `setdifference' must be lists."))))
113 ;; Return the intersection of lists a and b. Use element_equalp for the
114 ;; equality test. Signal an error if a or b aren't lists.
116 (defmfun $charsets_intersection
( &rest a
)
118 (cond ((member nil
(mapcar #'$listp a
))
119 (merror "Each argument to `intersection' must be a list."))
121 (setq a
(mapcar #'margs a
))
123 (reduce #'(lambda (x y
)
124 (intersection x y
:test
#'$charsets_elem_equalp
))
127 ;; Return true iff a is a subset of b. Signal an error if
128 ;; a or b aren't Maxima lists.
130 (defun $charsets_subsetp
(a b
)
131 (cond ((and ($listp a
) ($listp b
))
132 (charsets_xsubsetp (margs a
) b
))
133 (t (merror "Both arguments to `subsetp' must be lists."))))
135 ;; charsets_xsubsetp returns true if and only if each element of the Lisp
136 ;; list a is a member of the Maxima list b. This function isn't
137 ;; intended to be a user function; it doesn't check whether b is a
138 ;; Maxima list. Notice that the empty set is a subset of every
141 (defun charsets_xsubsetp (a b
)
144 (and ($charsets_elementp
(car a
) b
) (charsets_xsubsetp (cdr a
) b
)))))
146 ;; Return true iff a is a subset of b and b is a subset of a; return
147 ;; false if a or b are not lists.
149 (defun $charsets_setequality
(a b
)
150 (cond ((and ($listp a
) ($listp b
))
151 (if (and ($charsets_subsetp a b
) ($charsets_subsetp b a
)) t nil
))
155 ;; Return true iff x as an element of the list a; use $charsets_elem_equalp
156 ;; to test for equality if x isn't a list and use $charsets_setequality to
157 ;; test for equality if x is a list. Return false if a isn't a list.
159 (defun $charsets_elementp
(x a
)
162 (cond ($charsets_set_elements_can_be_sets
163 (if (member x
(margs a
) :test
#'$charsets_setequality
) t nil
))
165 (if (member x
(margs a
) :test
#'$charsets_elem_equalp
) t nil
))))
167 (if (member x
(margs a
) :test
#'$charsets_elem_equalp
) t nil
))))
170 ;; Return true if e is an empty Maxima list; otherwise, signal an
173 (defun $charsets_emptyp
(e)
176 (t (merror "Argument to `emptyp' must be a list."))))
178 ;; Return an n element Maxima list [e,e,e,...e]. When n < 0 or
179 ;; n isn't an integer, signal an error.
181 (defun $charsets_dupe
(e n
)
182 (cond ((and (integerp n
) (> n -
1))
183 (cons '(mlist) (make-list n
:initial-element e
)))
184 (t (merror "Second argument to `dupe' must be a nonnegative integer."))))
186 ;; Return true if and only if the lists a and b are disjoint;
187 ;; signal an error if a or b aren't lists.
189 (defun $charsets_disjointp
(a b
)
190 (cond ((and ($listp a
) ($listp b
))
191 (not (intersection (margs a
) (margs b
) :test
#'$charsets_elem_equalp
)))
192 (t (merror "Both arguments to `disjointp' must be lists."))))
194 ;; Return those elements of a for which the predicate f evaluates
195 ;; to true; signal an error if a isn't a list.
197 ;; Return the union of a - b and b - a; signal an error if a or b
200 (defun $charsets_symmdifference
(a b
)
201 (cond ((and ($listp a
) ($listp b
))
202 (mfuncall '$charsets_union
($charsets_setdifference a b
) ($charsets_setdifference b a
)))
203 (t (merror "Both arguments to `symmdifference' must be lists."))))
205 ;; Return a list of the elements in b that are not in a.
207 (defun $charsets_complement
(a b
)
208 (cond ((and ($listp a
) ($listp b
))
209 ($charsets_setdifference b a
))
210 (t (merror "Both arguments to `complement' must be lists."))))
212 ;; Return true if and only if the argument is a Maxima list and the
213 ;; list does not have duplicate elements. charsets_setp doesn't check that
214 ;; the list is ordered according to canonlt.
216 (defun $charsets_setp
(a)
217 (and ($listp a
) (charsets_setp (margs a
))))
219 (defun charsets_setp (a)
221 (t (and (charsets_setp (cdr a
)) (not (member (car a
) (cdr a
) :test
#'$charsets_elem_equalp
))))))
223 ;; Return the set of all subsets of a. If a has n elements, charsets_powerset(a) has
224 ;; 2^n elements. Signal an error if the argument isn't a Maxima list.
226 (defun $charsets_powerset
(a)
228 (setq a
($charsets_setify a
))
229 (cons '(mlist) (mapcar #'(lambda (x) (cons '(mlist) x
))
230 (charsets_powerset (margs a
)))))
231 (t (merror "Argument to `charsets_powerset' must be a list."))))
233 (defun charsets_powerset (a)
234 (cond ((null a
) (list nil
))
237 (b (charsets_powerset (cdr a
))))
238 (append b
(mapcar #'(lambda (u) (cons x u
)) b
))))))
240 ;; Return the set of all subsets of a that have exactly n elements.
241 ;; Signal an error if the first argument isn't a Maxima list or if
242 ;; the second argument isn't a nonnegative integer.
244 (defun $charsets_subpowerset
(a n
)
246 (setq a
($charsets_setify a
))
247 (cond ((and (integerp n
) (> n -
1))
248 (cons '(mlist) (mapcar #'(lambda (x) (cons '(mlist) x
))
249 (charsets_subpowerset (margs a
) n
))))
251 (merror "Second argument to SUBPOWERSET must
252 be a nonnegative integer."))))
253 (t (merror "First argument to `charsets_subpowerset' must be a list."))))
255 (defun charsets_subpowerset (a n
)
256 (cond ((or (< n
1) (null a
))
258 ((= n
1) (mapcar #'list a
))
260 (b (charsets_subpowerset (cdr a
) (- n
1))))
261 (append (charsets_subpowerset (cdr a
) n
)
262 (mapcar #'(lambda (u) (cons x u
)) b
))))))