Updated testsuite with an expected GCL error in to_poly_share
[maxima.git] / src / nset.lisp
blob061568138c452d27c8cafb0268b64157e50327b5
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
14 (in-package :maxima)
16 (macsyma-module nset)
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)
36 (def-lbp |$}| 5.)
38 (setf (get '|${| 'nud) 'parse-matchfix)
39 (def-match |${| |$}|)
40 (def-lbp |${| 200.)
41 ;No RBP
42 (def-mheader |${| ($set))
43 (def-pos |${| $any)
44 ;No LPOS
45 ;No RPOS
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*.
54 (putopr "{" '$set)
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.
84 (defmfun $unique (x)
85 (if ($listp x)
86 `((mlist) ,@(sorted-remove-duplicates (sort (copy-list (cdr x)) '$orderlessp)))
87 x))
89 ;; When a is a list, setify(a) is equivalent to apply(set, a). When a
90 ;; isn't a list, signal an error.
92 (defmfun $setify (a)
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)
99 (cond (($listp a)
100 `(($set) ,@(mapcar '$fullsetify (cdr a))))
101 (t a)))
103 ;; If a is a set, convert the top-level set to a list; when a isn't a
104 ;; list, return 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)
119 ;; Simplify a set.
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
125 `(($set simp) ,@a))
127 ;; Return true iff a is an empty set or list
129 (defmfun $emptyp (a)
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.
134 (defmfun $setp (a)
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
153 ;; or sets.
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))))
167 `(($set simp) ,@a)))
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))
182 (i len (decf i))
183 (l l (cdr l)))
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)
193 (let ((acc nil))
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)))
209 (cond ((consp b)
210 (dolist (bi b)
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)))
235 (catch 'disjoint
236 (dolist (ai a)
237 (if (b-search-expr ai b 0 n) (throw 'disjoint nil)))
238 t)))
241 (defmfun $disjointp (a b)
242 (setq a (require-set a '$disjointp))
243 (setq b (require-set b '$disjointp))
244 (set-disjointp a b))
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
249 ;; unknown.
251 (defmfun $subset (a f)
252 (setq a (require-set a '$subset))
253 (let ((acc nil) (b))
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)
281 (let ((acc nil))
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))
291 (dolist (lk l)
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"))
303 (cond ((null k)
304 (cons `($set simp)
305 (mapcar #'(lambda (s)
306 (cons '($set simp) s)) (power-set a))))
307 ((integerp k)
308 (powerset-subset a k (length a)))
309 (t (merror (intl:gettext "The second argument to powerset must be an integer; found ~:M") k))))
312 (defun power-set (a)
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)
319 (let ((s) (b) (acc))
320 (cond ((= k 0)
321 (setq acc (cons `(($set simp)) acc)))
322 ((<= k n)
323 (dotimes (i k)
324 (setq s (cons i s)))
325 (setq s (nreverse s))
326 (while (not (null s))
327 (setq b nil)
328 (dotimes (i k)
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
336 ;; Stinson.
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)))
342 (decf i))
343 (cond ((< i 0)
344 nil)
346 (setq j i)
347 (setq si (+ 1 (- (nth i s) i)))
348 (while (< j k)
349 (setf (nth j u) (+ si j))
350 (incf j))
351 u))))
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);
355 ;; the
357 (defmfun $permutations (a)
358 (cond (($listp 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))
366 (i) (acc) (q)
367 (need-to-simp (not (= (length a)
368 (length (sorted-remove-duplicates (copy-list a)))))))
370 (dotimes (i (+ 1 n))
371 (setf (aref p i) i))
372 (dotimes (i n)
373 (setf (aref b (+ i 1)) (nth i a)))
375 (cond ((not (null a))
376 (while (not (null p))
377 (setq i 1)
378 (setq q nil)
379 (while (<= i n)
380 (setq q (cons (aref b (aref p i)) q))
381 (incf i))
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
392 ;; Stinson.
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))
401 (setf (aref p 0) 0)
402 (while (< (aref p (+ i 1)) (aref p i))
403 (decf i))
404 (cond ((= i 0)
405 nil)
407 (while (< (aref p j) (aref p i))
408 (decf j))
409 (setq tm (aref p j))
410 (setf (aref p j) (aref p i))
411 (setf (aref p i) tm)
412 (setq j (+ i 1))
413 (while (<= j n)
414 (setf (aref r j) (aref p j))
415 (incf j))
416 (setq j (+ i 1))
417 (setq m (+ n i 1))
418 (while (<= j n)
419 (setf (aref p j) (aref r (- m j)))
420 (incf j))
421 p))))
423 (defmfun $random_permutation (a)
424 (if ($listp a)
425 (setq a (copy-list (cdr a)))
426 (setq a (copy-list (require-set a '$random_permutation))))
428 (let ((n (length a)))
429 (dotimes (i n)
430 (let
431 ((j (+ i ($random (- n i))))
432 (tmp (nth i a)))
433 (setf (nth i a) (nth j a))
434 (setf (nth j a) tmp))))
436 `((mlist) ,@a))
440 ;;; Returns 3 values
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)
448 (let ((mid) (midl))
449 (while (> len 1)
450 (if ($orderlessp x (car (setq midl (nthcdr (setq mid (floor len 2)) l))))
451 (setq len mid)
452 (setq l midl
453 lo (+ lo mid)
454 len (- len mid))))
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)))))))
460 ;;; Returns 3 values
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)
468 (progn
469 ;; uses great directly instead of $orderlessp; only specrepcheck x once
470 (setq x (specrepcheck x))
471 (let ((mid) (midl) (midel))
472 (while (> len 1)
473 (cond
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
478 ((alike1
480 (setq midel
481 (specrepcheck (car (setq midl (nthcdr (setq mid (floor
482 len 2)) l))))))
483 (setq l midl
484 lo (+ lo mid)
485 len -1))
487 ((great midel x)
488 (setq len mid))
489 (t (setq l midl
490 lo (+ lo mid)
491 len (- len mid)))))
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);
505 ;; (D3) DONE
506 ;; (C4) ev(d2);
507 ;; (D4) f(g(f(x)))
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)))
523 (list e))
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)
533 (prog1 l
534 (while (cdr l)
535 (while (and (cdr l) (like (car l) (cadr l))
536 (rplacd l (cddr l))))
537 (setq l (cdr l)))))
539 (defun set-intersect (l1 l2)
540 ;; Only works for lists of sorted by $orderlessp.
541 (with-collector collect
542 (do-merge-symm
543 l1 l2
544 #'like
545 #'$orderlessp
546 #'collect
547 nil)))
549 (defun set-union (l1 l2)
550 ;; Only works for lists of sorted by $orderlessp.
551 (with-collector collect
552 (do-merge-symm
553 l1 l2
554 #'like
555 #'$orderlessp
556 #'collect
557 #'collect)))
559 (defun sset-difference (l1 l2)
560 ;; Only works for lists of sorted by $orderlessp.
561 (with-collector collect
562 (do-merge-asym
563 l1 l2
564 #'like
565 #'$orderlessp
567 #'collect
568 nil)))
570 (defun set-subsetp (l1 l2)
571 ;; Is l1 a subset of l2
572 (catch 'subset
573 (do-merge-asym
574 l1 l2
575 #'like
576 #'$orderlessp
578 #'(lambda (xx) (declare (ignore xx)) (throw 'subset nil))
579 nil)
582 (defun set-symmetric-difference (l1 l2) ; i.e. xor
583 (with-collector collect
584 (do-merge-symm
585 l1 l2
586 #'like
587 #'$orderlessp
589 #'collect)))
591 (defun set-disjointp (l1 l2)
592 (catch 'disjoint
593 (do-merge-symm
594 l1 l2
595 #'like
596 #'$orderlessp
597 #'(lambda (xx) (declare (ignore xx)) (throw 'disjoint nil))
598 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))
607 (cond ((null a)
608 `(($set simp)))
610 (cond ((eq s '$min)
611 (setq s -1))
612 ((eq s '$max)
613 (setq s 1))
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)))
618 (x))
619 (setq max-subset `(,max-subset))
620 (setq a (cdr a))
621 (dolist (ai a)
622 (setq x (mul s (mfuncall f ai)))
623 (cond ((mevalp_tr (mgrp x mx) t)
624 (setq mx x
625 max-subset `(,ai)))
626 ((like x mx)
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)
634 ; bool)))
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))
650 (do ((l l (cdr l))
651 (acc)
652 (tail)
653 (x))
654 ((null l) (simplify (cons '($set) (mapcar #'(lambda (x) (cons '($set) x)) acc))))
655 (setq x (car l))
656 (setq tail (member-if #'(lambda (z) (bool-checked-mfuncall f x (car z))) acc))
657 (cond ((null tail)
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)
670 (if (null b)
671 '(($set) ((mlist)))
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)
686 (if (null b)
687 '((mlist) ((mlist)))
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)
696 (setq b (reverse b))
697 (let
698 ((a)
699 (acc (mapcar #'list (car b))))
700 (setq b (cdr b))
701 (dolist (bi b)
702 (setq a nil)
703 (dolist (bij bi (setq acc a))
704 (setq a (append a (mapcar #'(lambda (x) (cons bij x)) acc)))))
705 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
709 ;; all partitions.
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)))
721 ((null n-sub)
722 (setq n-sub (length a))
723 (let ((acc (set-partitions a 0)) (k 1))
724 (while (<= k n-sub)
725 (setq acc (append acc (set-partitions a k)))
726 (incf k))
727 `(($set) ,@acc)))
729 (merror (intl:gettext "set_partitions: second argument must be a positive integer; found: ~:M") n-sub))))
731 (defun set-partitions (a n)
732 (cond ((= n 0)
733 (cond ((null a)
734 (list `(($set simp))))
736 nil)))
737 ((null a)
738 nil)
740 (let ((p) (x) (acc) (w) (s) (z))
741 (setq x (car a))
742 (setq p (set-partitions (cdr a) n))
743 (dolist (pj p)
744 (setq w nil)
745 (setq s (cdr pj))
746 (while (not (null s))
747 (setq z (pop 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)))
753 (dolist (pj p acc)
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)
761 (let ((acc))
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))))
766 (if (not acc)
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))
779 (setf (nth 0 p) k)
780 (setq d (- n (reduce #'+ p)))
781 (setq j k)
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))
785 (setq d r)
786 (decf k)))
787 (setq acc (cons (copy-list (reverse p)) acc))
788 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))
795 (setq len (- len 1))
796 (setq i (- n (min n b)))
797 (setq 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))
801 (decf n)
802 (incf i))))
803 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)))
814 (setf (aref p 0) 1)
815 (loop for i from 1 to n
816 do (loop with j = 0
817 for k from 1
818 if (oddp k) do
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)))
825 else do
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)
833 (let ((acc))
834 (incf n)
835 (dotimes (i n)
836 (push (aref p i) acc))
837 (setq acc (nreverse acc))
838 (push '(mlist simp) (cdr acc))))
840 (aref p n)))))
841 (t (if lst `(($num_partitions simp) ,n ,lst)
842 `(($num_partitions simp) ,n)))))
844 (defmfun $num_distinct_partitions (n &optional lst)
845 (cond ((eql n 0) 1)
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)))
850 (sum) (i) (j))
851 (setf (aref p 0) 1)
852 (setf (aref p 1) 1)
854 (setq i 0)
855 (while (<= i n)
856 (setf (aref s i) (mfuncall '$divsum i 1))
857 (incf i))
858 (setq i 0)
859 (while (<= i n)
860 (if (oddp i)
861 (setf (aref u i) (aref s i))
862 (setf (aref u i) (- (aref s i) (* 2 (aref s (/ i 2))))))
863 (incf i))
864 (setq i 2)
865 (while (<= i n)
866 (setq sum 0)
867 (setq j 1)
868 (while (<= j i)
869 (setq sum (+ sum (* (aref u j) (aref p (- i j)))))
870 (incf j))
871 (setf (aref p i) (/ sum i))
872 (incf i))
874 (cond ((eq lst '$list)
875 (let ((acc))
876 (incf n)
877 (dotimes (i n)
878 (push (aref p i) acc))
879 (setq acc (nreverse acc))
880 (push '(mlist simp) (cdr acc))))
882 (aref p n)))))
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)
893 ;; is an identity.
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
919 (dolist (lk l)
920 (setq lk (simpcheck lk z))
921 (cond ((some #'(lambda (s) (eq t (mnqp s lk))) acc) ;; lk # some member of acc, return zero.
922 (throw 'done t))
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)))
959 (if l (wna-err fn))
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)
982 (cond ((>= m n)
983 (let ((s (make-array `(,(+ m 1) ,(+ m 1)) :initial-element 0))
984 (i) (j) (k) (im1))
985 (setf (aref s 0 0) 1)
986 (setq i 1)
987 (while (<= i m)
988 (setq k (min i n))
989 (setq j 1)
990 (setq im1 (- i 1))
991 (while (<= j k)
992 (setf (aref s i j) (- (aref s im1 (- j 1))
993 (* im1 (aref s im1 j))))
994 (incf j))
995 (incf i))
996 (aref s m n)))
997 (t 0)))
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))))
1021 (if l (wna-err fn))
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))
1050 (while (<= i m)
1051 (setq z (* b (expt i n)))
1052 (setq f (* f i))
1053 (setq b (/ (* (- m i) b) (+ i 1)))
1054 (if (oddp i) (setq z (- z)))
1055 (setq s (+ s z))
1056 (incf i))
1057 (setq s (/ s f))
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,
1069 ;; and equalities.
1071 (defun simp-belln (n y z)
1072 (oneargcheck n)
1073 (setq y (caar n))
1074 (setq n (simpcheck (cadr n) z))
1075 (cond ((and (integerp n) (> n -1))
1076 (integer-belln n))
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))
1083 (while (<= i n)
1084 (setq s (+ s (integer-stirling2 n i)))
1085 (incf 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)
1100 (let ((n 0) (d 1))
1101 (dolist (ai a)
1102 (setq n (add n ai))
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))
1123 ((null s)
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)))
1135 (defun xappend (s)
1136 #+(or cmu scl)
1137 (cons '(mlist) (apply 'append (mapcar #'(lambda (x)
1138 (require-list x '$append)) s)))
1139 #-(or cmu scl)
1140 (let ((acc))
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)))
1164 (cond (opfn
1165 (setq s (require-list-or-set s '$xreduce))
1166 (if (not (equal init 'no-init))
1167 (setq s (cons init s)))
1169 (if (null s)
1170 (cadr op-props) ; is this clause really needed?
1172 (funcall opfn s)))
1174 (op-props
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))
1188 (if (null a)
1189 (merror (intl:gettext "tree_reduce: either a nonempty set or initial value must be given.")))
1191 (let ((acc) (x) (doit nil))
1192 (while (consp a)
1193 (setq x (pop a))
1194 (while (consp a)
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))
1199 (setq acc nil))
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).
1215 ;; Three cases:
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.
1235 ;; Notes:
1236 ;; (a) 'some' and 'every' automatically apply 'maybe'; thus the following
1237 ;; work correctly
1239 ;; (C1) some("<",[a,b,5],[1,2,8]);
1240 ;; (D1) TRUE
1241 ;; (C2) some("=",[2,3],[2,7]);
1242 ;; (D2) TRUE
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]);
1252 ;; (%o1) true
1253 ;; (%i2) every("<",[i,1],[3,12]);
1254 ;; (%o2) false
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)
1297 (let ((errcatch t))
1298 (errset (mfuncall f x))))
1300 (defmfun $every (f &rest x)
1301 (cond ((or (null x) (and (null (cdr x)) ($emptyp (first x)))) t)
1303 ((or ($listp (first x)) (and ($setp (first x)) (null (cdr x))))
1304 (setq x (margs (simplify (apply #'map1 (cons f x)))))
1305 (checked-and (mapcar #'car (mapcar #'(lambda (s) (ignore-errors-mfuncall '$maybe s)) x))))
1307 ((every '$matrixp x)
1308 (let ((fmaplvl 2))
1309 (setq x (margs (simplify (apply #'fmapl1 (cons f x)))))
1310 (checked-and (mapcar #'(lambda (s) ($every '$identity s)) x))))
1313 ;; NOT CLEAR FROM PRECEDING CODE WHAT IS "INVALID" HERE
1314 (merror (intl:gettext "every: invalid arguments.")))))
1316 (defmfun $some (f &rest x)
1317 (cond ((or (null x) (and (null (cdr x)) ($emptyp (first x)))) nil)
1319 ((or ($listp (first x)) (and ($setp (first x)) (null (cdr x))))
1320 (setq x (margs (simplify (apply #'map1 (cons f x)))))
1321 (checked-or (mapcar #'car (mapcar #'(lambda (s) (ignore-errors-mfuncall '$maybe s)) x))))
1323 ((every '$matrixp x)
1324 (let ((fmaplvl 2))
1325 (setq x (margs (simplify (apply #'fmapl1 (cons f x)))))
1326 (checked-or (mapcar #'(lambda (s) ($some '$identity s)) x))))
1329 ;; NOT CLEAR FROM PRECEDING CODE WHAT IS "INVALID" HERE
1330 (merror (intl:gettext "some: invalid arguments.")))))
1332 (defmspec $makeset (l)
1333 (let* ((fn (car (pop l)))
1334 (f (if l (pop l) (wna-err fn)))
1335 (v (if l (pop l) (wna-err fn)))
1336 (s (if l (pop l) (wna-err fn))))
1337 (if l (wna-err fn))
1338 (if (or (not ($listp v)) (not (every #'(lambda (x) (or ($symbolp x) ($subvarp x))) (cdr v))))
1339 (merror (intl:gettext "makeset: second argument must be a list of symbols; found: ~:M") v))
1340 (setq s (require-list-or-set (meval s) '$makeset))
1341 (setq f (list (list 'lambda) v f))
1342 (setq v (margs v))
1343 (dolist (sk v) (setq f (subst (gensym) sk f :test #'alike1)))
1344 (simplifya (cons '($set) (mapcar #'(lambda (x) (mfuncall '$apply f x)) s)) t)))
1346 ;; Thread fn over l and apply op to the resulting list.
1348 (defun thread (fn l op)
1349 (simplify (cons `(,op) (mapcar #'(lambda (x) (simplify `((,fn) ,x))) l))))
1351 ;; Return a set of the divisors of n. If n isn't a positive integer,
1352 ;; return a noun form. We consider both 1 and n to be divisors of n.
1353 ;; The divisors of a negative number are the divisors of its absolute
1354 ;; value; divisors(0) simplifies to itself. We thread divisors over
1355 ;; lists, sets, matrices, and equalities.
1357 (defprop $divisors simp-divisors operators)
1359 (defun simp-divisors (n y z)
1360 (oneargcheck n)
1361 (setq y (caar n))
1362 (setq n (simpcheck (cadr n) z))
1363 (cond ((or ($listp n) ($setp n) ($matrixp n) (mequalp n))
1364 (thread y (cdr n) (caar n)))
1365 ((and (integerp n) (not (zerop n)))
1366 (let (($intfaclim nil)
1367 (n (abs n)))
1368 `(($set simp) ,@(sort (mapcar #'car (divisors (cfactorw n))) #'<))))
1369 (t `(($divisors simp) ,n))))
1371 ;; The Moebius function; it threads over lists, sets, matrices, and equalities.
1373 (defprop $moebius simp-moebius operators)
1375 (defun simp-moebius (n y z)
1376 (oneargcheck n)
1377 (setq y (caar n))
1378 (setq n (simpcheck (cadr n) z))
1379 (cond ((posint n)
1380 (if (= n 1)
1382 (let (($intfaclim nil)
1383 (pfl (get-factor-list n))) ; pfl is a list of (prime exponent) pairs
1384 (if (every #'(lambda (x) (= 1 (second x))) pfl) ; if n is not
1385 ; squarefree return 0
1386 (if (evenp (length pfl)) 1 -1) ; else (-1)^(number of prime factors)
1387 0))))
1388 ((or ($listp n) ($setp n) ($matrixp n) (mequalp n))
1389 (thread y (cdr n) (caar n)))
1390 (t `(($moebius simp) ,n))))
1392 ; Find indices of elements which satisfy a predicate.
1393 ; Thanks to Bill Wood (william.wood3@comcast.net) for his help.
1394 ; Released under terms of GNU GPL v2 with Bill's approval.
1396 (defmfun $sublist_indices (items pred)
1397 (let ((items (require-list items '$sublist_indices)))
1398 (do ((i 0 (1+ i))
1399 (xs items (cdr xs))
1400 (acc '() (if (definitely-so (mfuncall pred (car xs))) (cons (1+ i) acc) acc)))
1401 ((endp xs) `((mlist) ,@(nreverse acc))))))