1 ;; Copyright 2002-2003 by
2 ;; Stavros Macrakis (macrakis@alum.mit.edu) and
3 ;; Barton Willis (willisb@unk.edu)
5 ;; Maxima nset is free software; you can redistribute it and/or
6 ;; modify it under the terms of the GNU General Public License,
7 ;; http://www.gnu.org/copyleft/gpl.html.
9 ;; Maxima nset has NO WARRANTY, not even the implied warranty of
10 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 ;; A Maxima set package
18 ($put
'$nset
1.21 '$version
)
19 ;; Let's remove built-in symbols from list for user-defined properties.
20 (setq $props
(remove '$nset $props
))
22 ;; Display sets as { .. }.
24 (defprop $set msize-matchfix grind
)
26 (setf (get '$set
'dissym
) '((#\
{ ) #\
} ))
27 (setf (get '$set
'dimension
) 'dimension-match
)
29 ;; Parse {a, b, c} into set(a, b, c).
31 (setf (get '$set
'op
) "{")
33 (setf (get '|$
}|
'nud
) 'delim-err
)
34 (setf (get '|$
}|
'led
) 'erb-err
)
38 (setf (get '|$
{|
'nud
) 'parse-matchfix
)
42 (def-mheader |$
{|
($set
))
47 (def-operator "{" '$any nil
'$any nil nil nil nil
'(nud . parse-matchfix
) 'msize-matchfix
'dimension-match
"}")
48 ;; Let's remove built-in operators from list for user-defined properties.
49 (setq $props
(remove "{" $props
:test
#'equal
))
50 (setq $props
(remove "}" $props
:test
#'equal
))
52 ;; DEF-OPERATOR makes "{" map to ${, but it needs to map to $SET.
53 ;; Just clobber whatever DEF-OPERATOR put into *OPR-TABLE*.
56 ;; Support for TeXing sets. If your mactex doesn't TeX the empty set
57 ;; correctly, get the latest mactex.lisp.
59 (defprop $set tex-matchfix tex
)
60 (defprop $set
(("\\left \\{" ) " \\right \\}") texsym
)
62 (defun require-set (x context-string
)
63 (if ($setp x
) (cdr x
) (merror (intl:gettext
"~:M: argument must be a set; found: ~:M") context-string x
)))
65 ;; If x is a Maxima list, return a Lisp list of its members; otherwise,
66 ;; signal an error. Unlike require-set, the function require-list does not
67 ;; coerce the result to a set.
69 (defun require-list (x context-string
)
70 (if ($listp x
) (cdr x
)
71 (merror (intl:gettext
"~:M: argument must be a list; found: ~:M") context-string x
)))
73 ;; If x is a Maxima list or set, return a Lisp list of its members; otherwise,
74 ;; signal an error. Unlike require-set, the function require-list-or-set
75 ;; does not coerce the result to a set.
77 (defun require-list-or-set (x context-string
)
78 (if (or ($listp x
) ($setp x
)) (cdr x
)
79 (merror (intl:gettext
"~:M: argument must be a list or a set; found: ~:M") context-string x
)))
81 ;; When a is a list, return a list of the unique elements of a.
82 ;; Otherwise just return a.
86 `((mlist) ,@(sorted-remove-duplicates (sort (copy-list (cdr x
)) '$orderlessp
)))
89 ;; When a is a list, setify(a) is equivalent to apply(set, a). When a
90 ;; isn't a list, signal an error.
93 (simplifya `(($set
) ,@(require-list a
'$setify
)) nil
))
95 ;; When a is a list, convert a and all of its elements that are lists
96 ;; into sets. When a isn't a list, return a.
98 (defmfun $fullsetify
(a)
100 `(($set
) ,@(mapcar '$fullsetify
(cdr a
))))
103 ;; If a is a set, convert the top-level set to a list; when a isn't a
106 (defmfun $listify
(a)
107 (if ($setp a
) `((mlist simp
) ,@(cdr a
)) a
))
109 ;; full_listify(a) converts all sets in a into lists.
111 (defmfun $full_listify
(a)
112 (setq a
($ratdisrep a
))
113 (cond (($mapatom a
) a
)
114 (($setp a
) (simplify (cons (list 'mlist
) (mapcar #'$full_listify
(cdr a
)))))
115 (t (simplify (cons (car a
) (mapcar #'$full_listify
(cdr a
)))))))
117 (defprop $set simp-set operators
)
121 (defun simp-set (a yy z
)
122 (declare (ignore yy
))
123 (setq a
(mapcar #'(lambda (x) (simplifya x z
)) (cdr a
)))
124 (setq a
(sorted-remove-duplicates (stable-sort a
'$orderlessp
)));FIXME consider a total order function with #'sort
127 ;; Return true iff a is an empty set or list
130 (or (like a
`(($set
))) (like a
`((mlist))) (and ($matrixp a
) (every '$emptyp
(margs a
)))))
132 ;; Return true iff the operator of a is set.
135 (and (consp a
) (consp (car a
)) (eq (caar a
) '$set
)))
137 ;; Return the cardinality of a set. This function works even when $simp is false.
139 (defmfun $cardinality
(a)
140 (if $simp
(length (require-set a
'$cardinality
))
141 (let (($simp t
)) ($cardinality
(simplify a
)))))
143 ;; Return true iff a is a subset of b. If either argument is a list, first
144 ;; convert it to a set. Signal an error if a or b aren't lists or sets.
146 (defmfun $subsetp
(a b
)
147 (setq a
(require-set a
'$subsetp
))
148 (setq b
(require-set b
'$subsetp
))
149 (and (<= (length a
) (length b
)) (set-subsetp a b
)))
151 ;; Return true iff sets a and b are equal; If either argument is a list, first
152 ;; convert convert it to a set. Signal an error if either a or b aren't lists
155 (defmfun $setequalp
(a b
)
156 (setq a
(require-set a
'$setequalp
))
157 (setq b
(require-set b
'$setequalp
))
158 (and (= (length a
) (length b
)) (every #'like a b
)))
161 ;; Adjoin x to the list or set a and return a set.
163 (defmfun $adjoin
(x a
)
164 (setq a
(require-set a
'$adjoin
))
165 (multiple-value-bind (f i b
) (b-search-expr x a
0 (length a
))
166 (if (not f
) (setq a
(prefixconc a i
(cons x b
))))
169 ;; If x is a member of the set or list a, delete x from setify(a); otherwise, return
170 ;; setify(a). For a set a, disjoin(x,a) == delete(x,a) == setdifference(a,set(x));
171 ;; however, disjoin should be the fastest way to delete a member from a set.
173 (defmfun $disjoin
(x a
)
174 (setq a
(require-set a
'$disjoin
))
175 (multiple-value-bind (f i b
) (b-search-expr x a
0 (length a
))
176 `(($set simp
) ,@(if f
(prefixconc a i b
) a
))))
178 ;; (previxconc l len rest) is equivalent to (nconc (subseq l len) rest)
180 (defun prefixconc (l len rest
)
181 (do ((res nil
(cons (car l
) res
))
184 ((= i
0) (nreconc res rest
))
185 (declare (fixnum i
))))
187 ;; union(a1,a2,...an) returns the union of the sets a1,a2,...,an.
188 ;; If any argument is a list, convert it to a set. Signal an error
189 ;; if one of the arguments isn't a list or a set. When union receives
190 ;; no arguments, it returns the empty set.
192 (defmfun $union
(&rest a
)
194 (dolist (ai a
`(($set simp
) ,@acc
))
195 (setq acc
(set-union acc
(require-set ai
'$union
))))))
197 ;; Remove elements of b from a. Works on lists or sets.
199 (defmfun $setdifference
(a b
)
200 `(($set simp
) ,@(sset-difference (require-set a
'$setdifference
)
201 (require-set b
'$setdifference
))))
203 ;; intersection(a1,a2,...an) returns the intersection of the sets
204 ;; a1,a2,...,an. Signal an error if one of the arguments isn't a
205 ;; list or a set. intersection must receive at least one argument.
207 (defmfun $intersection
(a &rest b
)
208 (let ((acc (require-set a
'$intersection
)))
211 (setq acc
(set-intersect acc
(require-set bi
'$intersection
))))))
212 `(($set simp
) ,@acc
)))
214 ;; intersect is an alias for intersection.
216 (defmfun $intersect
(a &rest b
)
217 (apply '$intersection
(cons a b
)))
219 ;; Return true iff x as an element of the set or list a. Use like
220 ;; to test for equality. Signal an error if a isn't a set or list.
222 (defmfun $elementp
(x a
)
223 (setq a
(require-set a
'$elementp
))
224 (b-search-expr x a
0 (length a
)))
226 ;; Return true if and only if the lists or sets a and b are disjoint;
227 ;; signal an error if a or b aren't lists or sets.
230 (defmfun $disjointp-binary-search-version
(a b
)
231 (setq a
(require-set a
'$disjointp
))
232 (setq b
(require-set b
'$disjointp
))
233 (if (> (length a
) (length b
)) (rotatef a b
))
234 (let ((n (length b
)))
237 (if (b-search-expr ai b
0 n
) (throw 'disjoint nil
)))
241 (defmfun $disjointp
(a b
)
242 (setq a
(require-set a
'$disjointp
))
243 (setq b
(require-set b
'$disjointp
))
246 ;; Return the set of elements of the list or set a for which the predicate
247 ;; f evaluates to true; signal an error if a isn't a list or a set. Also,
248 ;; signal an error if the function f doesn't evaluate to true, false, or
251 (defmfun $subset
(a f
)
252 (setq a
(require-set a
'$subset
))
254 (dolist (x a
`(($set simp
) ,@(nreverse acc
)))
255 (setq b
(mfuncall f x
))
256 (cond ((eq t b
) (push x acc
))
257 ((not (or (eq b nil
) (eq b
'$unknown
)))
258 (merror (intl:gettext
"subset: ~:M(~:M) evaluates to a non-boolean.") f x
))))))
260 ;; Return a list of three sets. The first set is the subset of a for which
261 ;; the predicate f evaluates to true, the second is the subset of a
262 ;; for which f evaluates to false, and the third is the subset of a
263 ;; for which f evaluates to unknown.
265 (defmfun $partition_set
(a f
)
266 (setq a
(require-set a
'$partition_set
))
267 (let ((t-acc) (f-acc) (b))
268 (dolist (x a
`((mlist simp
)
269 (($set simp
) ,@(nreverse f-acc
))
270 (($set simp
) ,@(nreverse t-acc
))))
271 (setq b
(mfuncall f x
))
272 (cond ((eq t b
) (push x t-acc
))
273 ((or (eq b nil
) (eq b
'$unknown
)) (push x f-acc
))
275 (merror (intl:gettext
"partition_set: ~:M(~:M) evaluates to a non-boolean.") f x
))))))
277 ;; The symmetric difference of sets, that is (A-B) union (B - A), is associative.
278 ;; Thus the symmetric difference extends unambiguously to n-arguments.
280 (defmfun $symmdifference
(&rest l
)
282 (dolist (lk l
(cons '($set simp
) acc
))
283 (setq acc
(set-symmetric-difference acc
(require-set lk
'$symmdifference
))))))
285 ;; Return {x | x in exactly one set l1, l2, ...}
287 (defmfun $in_exactly_one
(&rest l
)
288 ;; u = union of l1, l2,...
289 ;; r = members that are in two or more l1, l2, ...
290 (let ((u nil
) (r nil
))
292 (setq lk
(require-set lk
'$in_exactly_one
))
293 (setq r
(set-union r
(set-intersect u lk
)))
294 (setq u
(set-union u lk
)))
295 (cons '($set simp
) (sset-difference u r
))))
297 ;; When k is an integer, return the set of all subsets of the set a
298 ;; that have exactly k elements; when k is nil, return the power set
299 ;; of a. Signal an error if the first argument isn't a list or a set.
301 (defmfun $powerset
(a &optional k
)
302 (setq a
(require-set a
"powerset"))
305 (mapcar #'(lambda (s)
306 (cons '($set simp
) s
)) (power-set a
))))
308 (powerset-subset a k
(length a
)))
309 (t (merror (intl:gettext
"The second argument to powerset must be an integer; found ~:M") k
))))
313 (cond ((null a
) `(()))
315 (let ((x (car a
)) (b (power-set (cdr a
))))
316 (append `(()) (mapcar #'(lambda (u) (cons x u
)) b
) (cdr b
))))))
318 (defun powerset-subset (a k n
)
321 (setq acc
(cons `(($set simp
)) acc
)))
325 (setq s
(nreverse s
))
326 (while (not (null s
))
329 (setq b
(cons (nth (nth i s
) a
) b
)))
330 (setq acc
(cons (cons `($set simp
) (nreverse b
)) acc
))
331 (setq s
(ksubset-lex-successor s k n
)))))
332 (cons `($set simp
) (nreverse acc
))))
334 ;; This code is based on Algorithm 2.6 "Combinatorial Algorithms Generation,
335 ;; Enumeration, and Search," CRC Press, 1999 by Donald Kreher and Douglas
338 (defun ksubset-lex-successor (s k n
)
339 (let ((u (copy-list s
))
340 (i (- k
1)) (j) (si (- n k
)))
341 (while (and (>= i
0) (= (nth i s
) (+ si i
)))
347 (setq si
(+ 1 (- (nth i s
) i
)))
349 (setf (nth j u
) (+ si j
))
353 ;; When the list a is redundant, need-to-simp is set to true; this flag
354 ;; determines if acc needs to be simplified. Initially, p = (0,1,2,..,n);
357 (defmfun $permutations
(a)
359 (setq a
(sort (copy-list (cdr a
)) '$orderlessp
)))
361 (setq a
(require-set a
'$permutations
))))
363 (let* ((n (length a
)) (p (make-array (+ n
1) :element-type
'fixnum
))
364 (r (make-array (+ n
1) :initial-element
0 :element-type
'fixnum
))
365 (b (make-array (+ n
1) :initial-element
0))
367 (need-to-simp (not (= (length a
)
368 (length (sorted-remove-duplicates (copy-list a
)))))))
373 (setf (aref b
(+ i
1)) (nth i a
)))
375 (cond ((not (null a
))
376 (while (not (null p
))
380 (setq q
(cons (aref b
(aref p i
)) q
))
382 (setq acc
(cons (cons '(mlist simp
) (nreverse q
)) acc
))
383 (setq p
(permutation-lex-successor n p r
))))
385 (setq acc
`(((mlist simp
))))))
386 (setq acc
(nreverse acc
))
387 (if need-to-simp
`(($set
) ,@acc
)
388 `(($set simp
) ,@acc
))))
390 ;; This code is based on Algorithm 2.14 "Combinatorial Algorithms Generation,
391 ;; Enumeration, and Search," CRC Press, 1999 by Donald Kreher and Douglas
394 ;; The array elements p(1) thru p(n) specify the permutation; the array
395 ;; r gets used for swapping elements of p. Initially p = (0,1,2,..,n).
397 (defun permutation-lex-successor (n p r
)
398 (declare (type (simple-array fixnum
*) p r
))
399 (declare (type fixnum n
))
400 (let ((i (- n
1)) (j n
) (m) (tm))
402 (while (< (aref p
(+ i
1)) (aref p i
))
407 (while (< (aref p j
) (aref p i
))
410 (setf (aref p j
) (aref p i
))
414 (setf (aref r j
) (aref p j
))
419 (setf (aref p j
) (aref r
(- m j
)))
423 (defmfun $random_permutation
(a)
425 (setq a
(copy-list (cdr a
)))
426 (setq a
(copy-list (require-set a
'$random_permutation
))))
428 (let ((n (length a
)))
431 ((j (+ i
($random
(- n i
))))
433 (setf (nth i a
) (nth j a
))
434 (setf (nth j a
) tmp
))))
441 ;;; FOUND -- is X in L
442 ;;; POSITION -- where is X in L; if not in L, position it is before
443 ;;; REST -- everything after X in L
445 (defun old-b-search-expr (x l lo len
)
446 (declare (fixnum lo len
))
447 (if (= len
0) (values nil lo l
)
450 (if ($orderlessp x
(car (setq midl
(nthcdr (setq mid
(floor len
2)) l
))))
455 (cond (($orderlessp x
(nth 0 l
)) (values nil lo l
))
456 ((like x
(nth 0 l
)) (values t lo
(cdr l
)))
457 (t (values nil
(1+ lo
) (cdr l
)))))))
461 ;;; FOUND -- is X in L
462 ;;; POSITION -- where is X in L; if not in L, position it is before
463 ;;; REST -- everything after X in L
465 (defun b-search-expr (x l lo len
)
466 (declare (fixnum lo len
))
467 (if (= len
0) (values nil lo l
)
469 ;; uses great directly instead of $orderlessp; only specrepcheck x once
470 (setq x
(specrepcheck x
))
471 (let ((mid) (midl) (midel))
474 ;; Previously, it could hit x and continue searching
475 ;; Since great doesn't guarantee inequality, we need the check for
476 ;; alike1 anyway (hidden inside $orderlessp), so we might as well
477 ;; take advantage of it
481 (specrepcheck (car (setq midl
(nthcdr (setq mid
(floor
493 (cond ((= len -
1) (values t lo
(cdr l
)))
494 ((alike1 x
(specrepcheck (nth 0 l
))) (values t lo
(cdr l
)))
495 ((great (specrepcheck (nth 0 l
)) x
) (values nil lo l
))
496 (t (values nil
(1+ lo
) (cdr l
))))))))
498 ;; Flatten is somewhat difficult to define -- essentially it evaluates an
499 ;; expression as if its main operator had been declared nary; however, there
500 ;; is a difference. We have
502 ;; (C2) flatten(f(g(f(f(x)))));
503 ;; (D2) f(g(f(f(x))))
504 ;; (C3) declare(f,nary);
509 ;; Unlike declaring the main operator of an expression to be nary, flatten
510 ;; doesn't recurse into other function arguments.
512 ;; To successfully flatten an expression, the main operator must be
513 ;; defined for zero or more arguments; if this isn't the case,
514 ;; Maxima can halt with an error. So be it.
516 (defmfun $flatten
(e)
517 (cond ((or (specrepp e
) (mapatom e
)) e
)
518 (t (mcons-op-args (mop e
) (flattenl-op (margs e
) (mop e
))))))
520 (defun flattenl-op (e op
)
521 (mapcan #'(lambda (e)
522 (cond ((or (mapatom e
) (not (alike1 (mop e
) op
)))
524 (t (flattenl-op (margs e
) op
))))
527 ; doesn't work on f[1](f[1](x)).
528 ;(defmfun $flatten (e)
529 ; (if (or (specrepp e) (mapatom e)) e
530 ; (cons `(,(mop e)) (total-nary e))))
532 (defun sorted-remove-duplicates (l)
535 (while (and (cdr l
) (like (car l
) (cadr l
))
536 (rplacd l
(cddr l
))))
539 (defun set-intersect (l1 l2
)
540 ;; Only works for lists of sorted by $orderlessp.
541 (with-collector collect
549 (defun set-union (l1 l2
)
550 ;; Only works for lists of sorted by $orderlessp.
551 (with-collector collect
559 (defun sset-difference (l1 l2
)
560 ;; Only works for lists of sorted by $orderlessp.
561 (with-collector collect
570 (defun set-subsetp (l1 l2
)
571 ;; Is l1 a subset of l2
578 #'(lambda (xx) (declare (ignore xx
)) (throw 'subset nil
))
582 (defun set-symmetric-difference (l1 l2
) ; i.e. xor
583 (with-collector collect
591 (defun set-disjointp (l1 l2
)
597 #'(lambda (xx) (declare (ignore xx
)) (throw 'disjoint nil
))
601 ;; When s = $max, return { x in a | f(x) = maximum of f on a} and
602 ;; when s = $min, return { x in a | f(x) = minimum of f on a}.
603 ;; Signal an error when s isn't $max or $min.
605 (defmfun $extremal_subset
(a f s
)
606 (setq a
(require-set a
'$extremal_subset
))
615 (merror (intl:gettext
"extremal_subset: third argument must be 'max or 'min; found: ~:M") s
)))
616 (let* ((max-subset (nth 0 a
))
617 (mx (mul s
(mfuncall f max-subset
)))
619 (setq max-subset
`(,max-subset
))
622 (setq x
(mul s
(mfuncall f ai
)))
623 (cond ((mevalp_tr (mgrp x mx
) t
)
627 (setq max-subset
(cons ai max-subset
)))))
628 `(($set simp
) ,@(nreverse max-subset
))))))
630 (defun bool-checked-mfuncall (f x y
)
631 ; (let ((bool (is-boole-check (mfuncall f x y))))
632 ; (if (not (or (eq bool 't) (eq bool nil)))
633 ; (merror "~:M(~:M,~:M) doesn't evaluate to a boolean" f x y)
635 (let (($prederror nil
) (b))
636 (setq b
(mevalp (mfuncall f x y
)))
637 (if (or (eq b t
) (eq b nil
)) b
638 (merror (intl:gettext
"equiv_classes: ~:M(~:M, ~:M) evaluates to a non-boolean.") f x y
))))
641 ;; Return the set of equivalence classes of f on the set l. The
642 ;; function f must be an boolean-valued function defined on the
643 ;; cartesian product of l with l; additionally, f should be an
644 ;; equivalence relation.
646 ;; The lists acc and tail share structure.
648 (defmfun $equiv_classes
(l f
)
649 (setq l
(require-set l
'$equiv_classes
))
654 ((null l
) (simplify (cons '($set
) (mapcar #'(lambda (x) (cons '($set
) x
)) acc
))))
656 (setq tail
(member-if #'(lambda (z) (bool-checked-mfuncall f x
(car z
))) acc
))
658 (setq acc
(cons `(,x
) acc
)))
660 (setf (car tail
) (cons x
(car tail
)))))))
662 ;; cartesian_product(a,b1,b2,...,bn), where a, b1, ..., bn are all sets,
663 ;; returns the set with members of the form [x0,x1, ..., xn],
664 ;; where x0 in a, x1 in b1, ... , and xn in bn.
665 ;; With just one argument cartesian_product(a) returns the
666 ;; set with members [a1],[a2], ... [an], where a1, ..., an are the members of a.
667 ;; With no arguments, cartesian_product() returns {[]}.
669 (defmfun $cartesian_product
(&rest b
)
672 (if (every #'$setp b
)
673 (let ((l (apply #'cartesian-product
(mapcar #'cdr b
))))
674 (cons '($set
) (mapcar #'(lambda (e) (cons '(mlist) e
)) l
)))
675 ;; MAYBE JUST PRINT THE LIST OF TYPES OR OPERATORS INSTEAD OF B IN ITS ENTIRETY !!
676 (merror (intl:gettext
"cartesian_product: all arguments must be sets; found: ~M") (cons '(mlist) b
)))))
678 ;; cartesian_product_list(a,b1,b2,...,bn), where a, b1, ..., bn are all lists,
679 ;; returns the list with elements of the form [x0,x1, ..., xn],
680 ;; where x0 in a, x1 in b1, ... , and xn in bn.
681 ;; With just one argument cartesian_product_list(a) returns the
682 ;; list with elements [a1],[a2], ... [an], where a1, ..., an are the elements of a.
683 ;; With no arguments, cartesian_product_list() returns [[]].
685 (defmfun $cartesian_product_list
(&rest b
)
688 (if (every #'$listp b
)
689 (let ((l (apply #'cartesian-product
(mapcar #'cdr b
))))
690 (cons '(mlist) (mapcar #'(lambda (e) (cons '(mlist) e
)) l
)))
691 ;; MAYBE JUST PRINT THE LIST OF TYPES OR OPERATORS INSTEAD OF B IN ITS ENTIRETY !!
692 (merror (intl:gettext
"cartesian_product_list: all arguments must be lists; found: ~M") (cons '(mlist) b
)))))
694 ;; Assume here that B is nonempty; caller has already handled case B = NIL.
695 (defun cartesian-product (&rest b
)
699 (acc (mapcar #'list
(car b
))))
703 (dolist (bij bi
(setq acc a
))
704 (setq a
(append a
(mapcar #'(lambda (x) (cons bij x
)) acc
)))))
707 ;; When n is defined, return a set of partitions of the set or list a
708 ;; into n disjoint subsets. When n isn't defined, return the set of
711 ;; Let S be a set. We say a set P is a partition of S provided
712 ;; (1) p in P implies p is a set,
713 ;; (2) p1, p2 in P and p1 # p2 implies p1 and p2 are disjoint,
714 ;; (3) union(x | x in P) = S.
715 ;; Thus set() is a partition of set().
717 (defmfun $set_partitions
(a &optional n-sub
)
718 (setq a
(require-set a
'$set_partitions
))
719 (cond ((and (integerp n-sub
) (> n-sub -
1))
720 `(($set
) ,@(set-partitions a n-sub
)))
722 (setq n-sub
(length a
))
723 (let ((acc (set-partitions a
0)) (k 1))
725 (setq acc
(append acc
(set-partitions a k
)))
729 (merror (intl:gettext
"set_partitions: second argument must be a positive integer; found: ~:M") n-sub
))))
731 (defun set-partitions (a n
)
734 (list `(($set simp
))))
740 (let ((p) (x) (acc) (w) (s) (z))
742 (setq p
(set-partitions (cdr a
) n
))
746 (while (not (null s
))
748 (setq acc
(cons (simplifya `(($set
) ,@w
,($adjoin x z
) ,@s
) t
) acc
))
749 (setq w
(cons z w
))))
751 (setq x
`(($set simp
) ,x
))
752 (setq p
(set-partitions (cdr a
) (- n
1)))
754 (setq acc
(cons ($adjoin x pj
) acc
)))))))
756 ;; Generate the integer partitions in dictionary order. When the optional
757 ;; argument len is defined, only generate the partitions with exactly len
758 ;; members, including 0.
760 (defmfun $integer_partitions
(n &optional len
)
762 (cond ((and (integerp n
) (>= n
0))
763 (setq acc
(cond ((= n
0) nil
)
764 ((integerp len
) (fixed-length-partitions n n len
))
765 (t (integer-partitions n
))))
767 (setq acc
`(((mlist simp
))))
768 (setq acc
(mapcar #'(lambda (x) (simplify (cons '(mlist) x
))) acc
)))
769 `(($set simp
) ,@acc
))
771 (if len
`(($integer_partitions simp
) ,n
,len
) `(($integer_partitions simp
) ,n
))))))
773 (defun integer-partitions (n)
774 (let ((p `(,n
)) (acc nil
) (d) (k) (j) (r))
775 (while (> (car (last p
)) 1)
776 (setq acc
(cons (copy-list (reverse p
)) acc
))
777 (setq p
(member t p
:key
#'(lambda (x) (> x
1))))
778 (setq k
(- (nth 0 p
) 1))
780 (setq d
(- n
(reduce #'+ p
)))
782 (while (and (> k
0) (> d
0))
783 (multiple-value-setq (d r
) (floor d k
))
784 (setq p
(append (make-list d
:initial-element k
) p
))
787 (setq acc
(cons (copy-list (reverse p
)) acc
))
790 (defun fixed-length-partitions (n b len
)
791 (let ((p t
) (acc) (i))
792 (cond ((> n
(* b len
)) nil
)
793 ((= len
1) (if (<= n b
) (setq acc
`((,n
))) nil
))
796 (setq i
(- n
(min n b
)))
798 (while (not (null p
))
799 (setq p
(mapcar #'(lambda (x) (cons n x
)) (fixed-length-partitions i
(min i n
) len
)))
800 (setq acc
(append p acc
))
805 ;; When n is a nonnegative integer, return the number of partitions of n.
806 ;; If the optional parameter lst has the value "list", return a list of
807 ;; the number of partitions of 1,2,3, ... , n. If n isn't a nonnegative
808 ;; integer, return a noun form.
810 (defmfun $num_partitions
(n &optional lst
)
811 (cond ((equal n
0) 1)
812 ((and (integerp n
) (> n -
1))
813 (let ((p (make-array (+ n
1) :initial-element
0)))
815 (loop for i from
1 to n
819 (setf j
(floor (* k
(1- (* 3 k
))) 2))
820 (when (> j i
) (return))
821 (incf (aref p i
) (aref p
(- i j
)))
822 (setf j
(floor (* k
(1+ (* 3 k
))) 2))
823 (when (> j i
) (return))
824 (incf (aref p i
) (aref p
(- i j
)))
826 (setf j
(floor (* k
(1- (* 3 k
))) 2))
827 (when (> j i
) (return))
828 (decf (aref p i
) (aref p
(- i j
)))
829 (setf j
(floor (* k
(1+ (* 3 k
))) 2))
830 (when (> j i
) (return))
831 (decf (aref p i
) (aref p
(- i j
)))))
832 (cond ((eq lst
'$list
)
836 (push (aref p i
) acc
))
837 (setq acc
(nreverse acc
))
838 (push '(mlist simp
) (cdr acc
))))
841 (t (if lst
`(($num_partitions simp
) ,n
,lst
)
842 `(($num_partitions simp
) ,n
)))))
844 (defmfun $num_distinct_partitions
(n &optional lst
)
846 ((and (integerp n
) (> n -
1))
847 (let ((p (make-array (+ n
1)))
848 (s (make-array (+ n
1)))
849 (u (make-array (+ n
1)))
856 (setf (aref s i
) (mfuncall '$divsum i
1))
861 (setf (aref u i
) (aref s i
))
862 (setf (aref u i
) (- (aref s i
) (* 2 (aref s
(/ i
2))))))
869 (setq sum
(+ sum
(* (aref u j
) (aref p
(- i j
)))))
871 (setf (aref p i
) (/ sum i
))
874 (cond ((eq lst
'$list
)
878 (push (aref p i
) acc
))
879 (setq acc
(nreverse acc
))
880 (push '(mlist simp
) (cdr acc
))))
883 (t (if lst
`(($num_distinct_partitions simp
) ,n
,lst
)
884 `(($num_distinct_partitions simp
) ,n
)))))
886 ;; A n-ary Kronecker delta function: kron_delta(n0,n1, ..., nk) simplifies to 1 if
887 ;; (meqp ni nj) is true for *all* pairs ni, nj in (n0,n1, ..., nk); it simplifies to 0 if
888 ;; (mnqp ni nj) is true for *some* pair ni, nj in (n0,n1, ..., nk). Further kron_delta() --> 1
889 ;; and kron_delta(xxx) --> wrong number of arguments error. Thus
891 ;; kron_delta(x0,...,xn) * kron_delta(y0,..., ym) = kron_delta(x0, ..., xn, y0, ..., ym)
895 (defprop %kron_delta simp-kron-delta operators
)
896 (setf (get '$kron_delta
'verb
) '%kron_delta
)
897 (setf (get '%kron_delta
'noun
) '$kron_delta
)
898 (setf (get '$kron_delta
'alias
) '%kron_delta
)
899 (setf (get '%kron_delta
'reversealias
) '$kron_delta
)
900 (defmfun $kron_delta
(&rest x
) (simplifya `((%kron_delta
) ,@x
) t
))
901 (setf (get '%kron_delta
'real-valued
) t
) ;; conjugate(kron_delta(xxx)) --> kron_delta(xxx)
902 (setf (get '%kron_delta
'integer-valued
) t
) ;; featurep(kron_delta(xxx), integer) --> true
903 (mputprop '%kron_delta t
'$scalar
) ;; same effect as declare(kron_delta, scalar)
905 (putprop '%kron_delta
#'(lambda (yy) (declare (ignore yy
)) (setq sign
'$pz
)) 'sign-function
)
907 (defun simp-kron-delta (l yy z
)
908 (declare (ignore yy
))
910 (setq l
(cdr l
)) ;; remove (($kron_delta simp)
911 (if (and l
(null (cdr l
))) (wna-err '$kron_delta
)) ;; wrong number of arguments error for exactly one argument
913 ;; Checking both mnqp and meqp is convenient, but unnecessary. This code misses simplifications that
914 ;; involve three or more arguments. Example: kron_delta(a,b,a+b+1,a-b+5) could (but doesn't) simplify
915 ;; to 0 (the solution set (a = b, a = a+b+1, a=a-b+5) is empty.
917 (let ((acc nil
) (return-zero nil
))
918 (setq return-zero
(catch 'done
920 (setq lk
(simpcheck lk z
))
921 (cond ((some #'(lambda (s) (eq t
(mnqp s lk
))) acc
) ;; lk # some member of acc, return zero.
923 ((some #'(lambda (s) (eq t
(meqp s lk
))) acc
)) ;; lk = some member of acc, do nothing
924 (t (push lk acc
))));; push lk onto acc
925 nil
)) ;; set return-zero to nil
926 (cond (return-zero 0)
927 ((or (null acc
) (null (cdr acc
))) 1)
928 (t ;; reflection: kron_delta(-a,-b,...) == kron_delta(a,b,...).
929 (let ((neg-acc (sort (mapcar #'neg acc
) '$orderlessp
)))
930 (setq acc
(sort acc
'$orderlessp
))
931 `((%kron_delta simp
) ,@(if (great (cons '(mlist) neg-acc
) (cons '(mlist) acc
)) neg-acc acc
)))))))
933 (defprop %kron_delta tex-kron-delta tex
)
935 (defun tex-kron-delta (x l r
)
936 (append l
`("\\delta_{" ,@(tex-list (cdr x
) nil
(list "} ") ", ")) r
))
938 ;; Stirling numbers of the first kind.
940 (defprop $stirling1 simp-stirling1 operators
)
941 ;; Stirling numbers of the first kind.
943 (defprop $stirling1 simp-stirling1 operators
)
945 ;; Stirling1 simplifications; for n,k in Z (Z = set of integers)
946 ;; (1) stirling1(1,k) = kron_delta(1,k), k >= 0, (http://dlmf.nist.gov/26.8.E2)
947 ;; (2) stirling1(n,n) = 1, n >= 0 (http://dlmf.nist.gov/26.8.E1)
948 ;; (3) stirling1(n,n-1) = -binomial(n,2), n >= 1, (http://dlmf.nist.gov/26.8.E16)
949 ;; (4) stirling1(n,0) = kron_delta(n,0), n >=0 (http://dlmf.nist.gov/26.8.E14 and http://dlmf.nist.gov/26.8.E1)
950 ;; (5) stirling1(n,1) =(-1)^(n-1) (n-1)!, n >= 1 (http://dlmf.nist.gov/26.8.E14)
951 ;; (6) stirling1(n,k) = 0, n >= 0 and k > n.
953 (defun simp-stirling1 (l yy z
)
954 (declare (ignore yy
))
955 (let* ((fn (car (pop l
)))
956 (n (if l
(simplifya (pop l
) z
) (wna-err fn
)))
957 (k (if l
(simplifya (pop l
) z
) (wna-err fn
)))
958 (n-is-nonnegative-int (nonnegative-integerp n
)))
960 (cond ((and (integerp n
) (integerp k
) (> n -
1) (> k -
1))
961 (integer-stirling1 n k
))
962 ((and (eql n
1) ($featurep k
'$integer
) (eq t
(mgrp k -
1)))
963 (take (list '%kron_delta
) 1 k
))
964 ((and n-is-nonnegative-int
(like n k
))
966 ((and (nonnegative-integerp (sub n
1)) (like n
(add k
1)))
967 (mul -
1 (take (list '%binomial
) n
2)))
968 ((and n-is-nonnegative-int
(eql k
0))
969 (take (list '%kron_delta
) n
0))
970 ((and n-is-nonnegative-int
(eql k
1) (eq t
(mgqp n
1)))
971 (mul (power -
1 (sub n
1)) (take (list 'mfactorial
) (sub n
1))))
972 ((and n-is-nonnegative-int
($featurep k
'$integer
) (eq t
(mgrp k n
)))
974 (t (list (list fn
'simp
) n k
)))))
976 ;; This code is based on Algorithm 3.17 "Combinatorial Algorithms Generation,
977 ;; Enumeration, and Search," CRC Press, 1999 by Donald Kreher and Douglas
978 ;; Stinson. There is a typographical error in Algorithm 3.17; replace i - j
979 ;; with i - 1. See Theorem 3.14.
981 (defun integer-stirling1 (m n
)
983 (let ((s (make-array `(,(+ m
1) ,(+ m
1)) :initial-element
0))
985 (setf (aref s
0 0) 1)
992 (setf (aref s i j
) (- (aref s im1
(- j
1))
993 (* im1
(aref s im1 j
))))
1000 ;; Stirling1 simplifications; for n,k in Z (Z = set of integers)
1001 ;; (1) stirling2(n,0) = 1, n >= 1 (http://dlmf.nist.gov/26.8.E17 and stirling2(0,0) = 1)
1002 ;; (2) stirling2(n,n) = 1, n >= 0, (http://dlmf.nist.gov/26.8.E4)
1003 ;; (3) stirling2(n,1) = 1, n >= 1, (http://dlmf.nist.gov/26.8.E17 and stirling2(0,1) = 0)
1004 ;; (4) stirling2(n,2) = 2^(n-1) -1 , n >= 1, (http://dlmf.nist.gov/26.8.E17)
1005 ;; (5) stirling2(n,n-1) = binomial(n,2), n>= 1 (http://dlmf.nist.gov/26.8.E16)
1006 ;; (6) stirling2(n,k) = 0, n >= 0 and k > n.
1008 (defun nonnegative-integerp (e)
1009 (and ($featurep e
'$integer
)
1010 (member ($sign
(specrepcheck e
)) `($pos $zero $pz
) :test
#'eq
)))
1012 (defprop $stirling2 simp-stirling2 operators
)
1014 (defun simp-stirling2 (l yy z
)
1015 (declare (ignore yy
))
1016 (let* ((fn (car (pop l
)))
1017 (n (if l
(simplifya (pop l
) z
) (wna-err fn
)))
1018 (k (if l
(simplifya (pop l
) z
) (wna-err fn
)))
1019 (n-is-nonnegative-int (nonnegative-integerp n
))
1020 (n-is-positive-int (nonnegative-integerp (sub n
1))))
1022 (cond ((and (integerp n
) (integerp k
) (> n -
1) (> k -
1))
1023 (integer-stirling2 n k
))
1025 ((and n-is-positive-int
(eql k
0))
1028 ((and n-is-positive-int
(eql k
1))
1031 ((and n-is-positive-int
(eql k
2))
1032 (add (power 2 (sub n
1)) -
1))
1034 ((and n-is-nonnegative-int
(like n k
))
1037 ((and n-is-positive-int
(like n
(add k
1)))
1038 (take (list '%binomial
) n
2))
1040 ((and n-is-nonnegative-int
($featurep k
'$integer
) (eq t
(mgrp k n
)))
1043 (t (list (list fn
'simp
) n k
)))))
1045 ;; Stirling2(n,m) = sum((-1)^(m - k) binomial(m k) k^n,i,1,m) / m!.
1046 ;; See A & S 24.1.4, page 824.
1048 (defun integer-stirling2 (n m
)
1049 (let ((s (if (= n
0) 1 0)) (i 1) (z) (f 1) (b m
))
1051 (setq z
(* b
(expt i n
)))
1053 (setq b
(/ (* (- m i
) b
) (+ i
1)))
1054 (if (oddp i
) (setq z
(- z
)))
1058 (if (oddp m
) (- s
) s
)))
1060 ;; Return the Bell number of n; specifically, belln(n) is the
1061 ;; cardinality of the set of partitions of a set with n elements.
1063 (defprop $belln simp-belln operators
)
1065 ;; Simplify the Bell function. Other than evaluation for nonnegative
1066 ;; integer arguments, there isn't much that can be done. I don't know
1067 ;; a reasonable extension of the Bell function to non-integers or of
1068 ;; any simplifications -- we do thread belln over lists, sets, matrices,
1071 (defun simp-belln (n y z
)
1074 (setq n
(simpcheck (cadr n
) z
))
1075 (cond ((and (integerp n
) (> n -
1))
1077 ((or ($listp n
) ($setp n
) ($matrixp n
) (mequalp n
))
1078 (thread y
(cdr n
) (caar n
)))
1079 (t `(($belln simp
) ,n
))))
1081 (defun integer-belln (n)
1082 (let ((s (if (= n
0) 1 0)) (i 1))
1084 (setq s
(+ s
(integer-stirling2 n i
)))
1088 ;; The multinomial coefficient; explicitly multinomial_coeff(a1,a2, ... an) =
1089 ;; (a1 + a2 + ... + an)! / (a1! a2! ... an!). The multinomial coefficient
1090 ;; gives the number of ways of placing a1 + a2 + ... + an distinct objects
1091 ;; into n boxes with ak elements in the k-th box.
1093 ;; multinomial_coeff is symmetric; thus when at least one of its arguments
1094 ;; is symbolic, we sort them. Additionally any zero element of the
1095 ;; argument list can be removed without changing the value of
1096 ;; multinomial_coeff; we make this simplification as well. If
1097 ;; b is nil following (remove 0 b), something has gone wrong.
1099 (defmfun $multinomial_coeff
(&rest a
)
1103 (setq d
(mult d
(simplify `((mfactorial) ,ai
)))))
1104 (div (simplify `((mfactorial) ,n
)) d
)))
1106 ;; Extend a function f : S x S -> S to n arguments using right associativity.
1107 ;; Thus rreduce(f,[0,1,2]) -> f(0,f(1,2)). The second argument must be a list.
1109 (defmfun $rreduce
(f s
&optional
(init 'no-init
))
1110 (rl-reduce f s t init
'$rreduce
))
1112 ;; Extend a function f : S x S -> S to n arguments using left associativity.
1113 ;; Thus lreduce(f,[0,1,2]) -> f(f(0,1),2). The second argument must be a list.
1115 (defmfun $lreduce
(f s
&optional
(init 'no-init
))
1116 (rl-reduce f s nil init
'$lreduce
))
1118 (defun rl-reduce (f s left init fn
)
1119 (setq s
(require-list s fn
))
1120 (cond ((not (equal init
'no-init
))
1121 (reduce #'(lambda (x y
) (mfuncall f x y
)) s
:from-end left
1122 :initial-value init
))
1124 (merror (intl:gettext
"~a: either a nonempty set or initial value must be given.") fn
))
1126 (reduce #'(lambda (x y
) (mfuncall f x y
)) s
:from-end left
))))
1128 ;; Define an operator (signature S x S -> S, for some set S) to be nary and
1129 ;; define a function for its n-argument reduction. There isn't a user-level
1130 ;; interface to this mechanism.
1132 (defmacro def-nary
(fn arg f-body id
)
1133 `(setf (get ,fn
'$nary
) (list #'(lambda ,arg
,f-body
) ,id
)))
1137 (cons '(mlist) (apply 'append
(mapcar #'(lambda (x)
1138 (require-list x
'$append
)) s
)))
1141 (dolist (si (reverse s
) (cons '(mlist) acc
))
1142 (setq acc
(append (require-list si
'$append
) acc
)))))
1144 (def-nary 'mand
(s) (mevalp (cons '(mand) s
)) t
)
1145 (def-nary 'mor
(s) (mevalp (cons '(mor) s
)) nil
)
1146 (def-nary 'mplus
(s) (simplify (cons '(mplus) s
)) 0)
1147 (def-nary 'mtimes
(s) (simplify (cons '(mtimes) s
)) 1)
1148 (def-nary '$max
(s) (if (null s
) '$minf
(maximin s
'$max
)) '$minf
)
1149 (def-nary '$min
(s) (if (null s
) '$inf
(maximin s
'$min
)) '$inf
)
1150 (def-nary '$append
(s) (xappend s
) '((mlist)))
1151 (def-nary '$union
(s) ($apply
'$union
(cons '(mlist) s
)) '(($set
)))
1153 ;; Extend a function f : S x S -> S to n arguments. When we
1154 ;; recognize f as a nary function (associative), if possible we call a Maxima
1155 ;; function that does the work efficiently -- examples are "+", "min", and "max".
1156 ;; When there isn't a Maxima function we can call (actually when (get op '$nary)
1157 ;; returns nil) we give up and use rl-reduce with left-associativity.
1160 (defmfun $xreduce
(f s
&optional
(init 'no-init
))
1161 (let* ((op-props (get (if (atom f
) ($verbify f
) nil
) '$nary
))
1162 (opfn (if (consp op-props
) (car op-props
) nil
)))
1165 (setq s
(require-list-or-set s
'$xreduce
))
1166 (if (not (equal init
'no-init
))
1167 (setq s
(cons init s
)))
1170 (cadr op-props
) ; is this clause really needed?
1175 ($apply f
($listify s
)))
1178 (rl-reduce f
($listify s
) nil init
'$xreduce
)))))
1181 ;; Extend a function f : S x S -> S to n arguments using a minimum depth tree.
1182 ;; The function f should be nary (associative); otherwise, the result is somewhat
1183 ;; difficult to describe -- for an odd number of arguments, we favor the left side of the tree.
1185 (defmfun $tree_reduce
(f a
&optional
(init 'no-init
))
1186 (setq a
(require-list-or-set a
'$tree_reduce
))
1187 (if (not (equal init
'no-init
)) (push init a
))
1189 (merror (intl:gettext
"tree_reduce: either a nonempty set or initial value must be given.")))
1191 (let ((acc) (x) (doit nil
))
1195 (push (mfuncall f x
(pop a
)) acc
)
1196 (if (setq doit
(consp a
)) (setq x
(pop a
))))
1197 (if doit
(push x acc
))
1198 (setq a
(nreverse acc
))
1204 ;; An identity function -- may see some use in things like
1205 ;; every(identity, [true, true, false, ..]).
1207 (defmfun $identity
(x) x
)
1209 ;; Maxima 'some' and 'every' functions. The first argument should be
1210 ;; a predicate (a function that evaluates to true, false, or unknown).
1211 ;; The functions 'some' and 'every' locally bind $prederror to false.
1212 ;; Thus within 'some' or 'every,' is(a < b) evaluates to unknown instead
1213 ;; of signaling an error (as it would when $prederror is true).
1217 ;; (1) some(f, set(a1,...,an)) If any f(ai) evaluates to true,
1218 ;; 'some' returns true. 'Some' may or may not evaluate all the
1219 ;; f(ai)'s. Since sets are unordered, 'some' is free to evaluate
1220 ;; f(ai) in any order. To use 'some' on multiple set arguments,
1221 ;; they should first be converted to an ordered sequence so that
1222 ;; their relative alignment becomes well-defined.
1224 ;; (2) some(f,[a11,...,a1n],[a21,...],...) If any f(ai1,ai2,...)
1225 ;; evaluates to true, 'some' returns true. 'Some' may or may not
1226 ;; evaluate all the f(ai)'s. Since sequences are ordered, 'some'
1227 ;; evaluates in the order of increasing 'i'.
1229 ;; (3) some(f, matrix([a111,...],[a121,...],[a1n1...]), matrix(...)).
1230 ;; If any f(a1ij, a2ij, ...) evaluates to true, return true. 'Some'
1231 ;; may or may not evaluate all the predicates. Since there is no
1232 ;; natural order for the entries of a matrix, 'some' is free to
1233 ;; evaluate the predicates in any order.
1236 ;; (a) 'some' and 'every' automatically apply 'maybe'; thus the following
1239 ;; (C1) some("<",[a,b,5],[1,2,8]);
1241 ;; (C2) some("=",[2,3],[2,7]);
1244 ;; (b) Since 'some' is free to choose the order of evaluation, and
1245 ;; possibly stop as soon as any one instance returns true, the
1246 ;; predicate f should not normally have side-effects or signal
1247 ;; errors. Similarly, 'every' may halt after one instance returns false;
1248 ;; however, the function 'maybe' is wrapped inside 'errset' This allows
1249 ;; some things to work that would otherwise signal an error:
1251 ;; (%i1) some("<",[i,1],[3,12]);
1253 ;; (%i2) every("<",[i,1],[3,12]);
1255 ;; (%i3) maybe(%i < 3);
1256 ;; `sign' called on an imaginary argument:
1259 ;; (c) The functions 'some' and 'every' effectively use the functions
1260 ;; 'map' and 'matrixmap' to map the predicate over the arguments. The
1261 ;; option variable 'maperror' modifies the behavior of 'map' and
1262 ;; 'matrixmap;' similarly, the value of 'maperror' modifies the behavior
1263 ;; of 'some' and 'every.'
1265 ;; (d) 'every' behaves similarly to 'some' except that 'every' returns
1266 ;; true iff every f evaluates to true for all its inputs.
1268 ;; (e) If emptyp(e) is true, then some(f,e) --> false and every(f,e) --> true.
1269 ;; Thus (provided an error doesn't get signaled), we have the identities:
1271 ;; some(f,s1) or some(f,s2) == some(f, union(s1,s2)),
1272 ;; every(f,s1) and every(f,s2) == every(f, union(s1,s2)).
1273 ;; Similarly, some(f) --> false and every(f) --> true.
1275 (defun checked-and (x)
1276 (setq x
(mfuncall '$maybe
`((mand) ,@x
)))
1277 (cond ((or (eq x t
) (eq x nil
) (not $prederror
)) x
)
1278 ((eq x
'$unknown
) nil
)
1280 ;; FOLLOWING MESSAGE IS UNREACHABLE FROM WHAT I CAN TELL
1281 ;; SINCE MAYBE RETURNS T, NIL, OR '$UNKNOWN
1282 (merror "Predicate isn't true/false valued; maybe you want to set 'prederror' to false"))))
1284 (defun checked-or (x)
1285 (setq x
(mfuncall '$maybe
`((mor) ,@x
)))
1286 (cond ((or (eq x t
) (eq x nil
) (not $prederror
)) x
)
1287 ((eq x
'$unknown
) nil
)
1289 ;; FOLLOWING MESSAGE IS UNREACHABLE FROM WHAT I CAN TELL
1290 ;; SINCE MAYBE RETURNS T, NIL, OR '$UNKNOWN
1291 (merror "Predicate isn't true/false valued; maybe you want to set 'prederror' to false"))))
1293 ;; Apply the Maxima function f to x. If an error is signaled, return nil; otherwise
1294 ;; return (list (mfuncall f x)).
1296 (defun ignore-errors-mfuncall (f x
)
1298 (declare (special errcatch
))
1299 (errset (mfuncall f x
))))
1301 (defmfun $every
(f &rest x
)
1302 (cond ((or (null x
) (and (null (cdr x
)) ($emptyp
(first x
)))) t
)
1304 ((or ($listp
(first x
)) (and ($setp
(first x
)) (null (cdr x
))))
1305 (setq x
(margs (simplify (apply #'map1
(cons f x
)))))
1306 (checked-and (mapcar #'car
(mapcar #'(lambda (s) (ignore-errors-mfuncall '$maybe s
)) x
))))
1308 ((every '$matrixp x
)
1310 (setq x
(margs (simplify (apply #'fmapl1
(cons f x
)))))
1311 (checked-and (mapcar #'(lambda (s) ($every
'$identity s
)) x
))))
1314 ;; NOT CLEAR FROM PRECEDING CODE WHAT IS "INVALID" HERE
1315 (merror (intl:gettext
"every: invalid arguments.")))))
1317 (defmfun $some
(f &rest x
)
1318 (cond ((or (null x
) (and (null (cdr x
)) ($emptyp
(first x
)))) nil
)
1320 ((or ($listp
(first x
)) (and ($setp
(first x
)) (null (cdr x
))))
1321 (setq x
(margs (simplify (apply #'map1
(cons f x
)))))
1322 (checked-or (mapcar #'car
(mapcar #'(lambda (s) (ignore-errors-mfuncall '$maybe s
)) x
))))
1324 ((every '$matrixp x
)
1326 (setq x
(margs (simplify (apply #'fmapl1
(cons f x
)))))
1327 (checked-or (mapcar #'(lambda (s) ($some
'$identity s
)) x
))))
1330 ;; NOT CLEAR FROM PRECEDING CODE WHAT IS "INVALID" HERE
1331 (merror (intl:gettext
"some: invalid arguments.")))))
1333 (defmspec $makeset
(l)
1334 (let* ((fn (car (pop l
)))
1335 (f (if l
(pop l
) (wna-err fn
)))
1336 (v (if l
(pop l
) (wna-err fn
)))
1337 (s (if l
(pop l
) (wna-err fn
))))
1339 (if (or (not ($listp v
)) (not (every #'(lambda (x) (or ($symbolp x
) ($subvarp x
))) (cdr v
))))
1340 (merror (intl:gettext
"makeset: second argument must be a list of symbols; found: ~:M") v
))
1341 (setq s
(require-list-or-set (meval s
) '$makeset
))
1342 (setq f
(list (list 'lambda
) v f
))
1344 (dolist (sk v
) (setq f
(subst (gensym) sk f
:test
#'alike1
)))
1345 (simplifya (cons '($set
) (mapcar #'(lambda (x) (mfuncall '$apply f x
)) s
)) t
)))
1347 ;; Thread fn over l and apply op to the resulting list.
1349 (defun thread (fn l op
)
1350 (simplify (cons `(,op
) (mapcar #'(lambda (x) (simplify `((,fn
) ,x
))) l
))))
1352 ;; Return a set of the divisors of n. If n isn't a positive integer,
1353 ;; return a noun form. We consider both 1 and n to be divisors of n.
1354 ;; The divisors of a negative number are the divisors of its absolute
1355 ;; value; divisors(0) simplifies to itself. We thread divisors over
1356 ;; lists, sets, matrices, and equalities.
1358 (defprop $divisors simp-divisors operators
)
1360 (defun simp-divisors (n y z
)
1363 (setq n
(simpcheck (cadr n
) z
))
1364 (cond ((or ($listp n
) ($setp n
) ($matrixp n
) (mequalp n
))
1365 (thread y
(cdr n
) (caar n
)))
1366 ((and (integerp n
) (not (zerop n
)))
1367 (let (($intfaclim nil
)
1369 `(($set simp
) ,@(sort (mapcar #'car
(divisors (cfactorw n
))) #'<))))
1370 (t `(($divisors simp
) ,n
))))
1372 ;; The Moebius function; it threads over lists, sets, matrices, and equalities.
1374 (defprop $moebius simp-moebius operators
)
1376 (defun simp-moebius (n y z
)
1379 (setq n
(simpcheck (cadr n
) z
))
1383 (let (($intfaclim nil
)
1384 (pfl (get-factor-list n
))) ; pfl is a list of (prime exponent) pairs
1385 (if (every #'(lambda (x) (= 1 (second x
))) pfl
) ; if n is not
1386 ; squarefree return 0
1387 (if (evenp (length pfl
)) 1 -
1) ; else (-1)^(number of prime factors)
1389 ((or ($listp n
) ($setp n
) ($matrixp n
) (mequalp n
))
1390 (thread y
(cdr n
) (caar n
)))
1391 (t `(($moebius simp
) ,n
))))
1393 ; Find indices of elements which satisfy a predicate.
1394 ; Thanks to Bill Wood (william.wood3@comcast.net) for his help.
1395 ; Released under terms of GNU GPL v2 with Bill's approval.
1397 (defmfun $sublist_indices
(items pred
)
1398 (let ((items (require-list items
'$sublist_indices
)))
1401 (acc '() (if (definitely-so (mfuncall pred
(car xs
))) (cons (1+ i
) acc
) acc
)))
1402 ((endp xs
) `((mlist) ,@(nreverse acc
))))))