1 ;;; -*- Mode: Lisp; Package: Maxima; Syntax: Common-Lisp; Base: 10 -*- ;;;;
2 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3 ;;; The data in this file contains enhancments. ;;;;;
5 ;;; Copyright (c) 1984,1987 by William Schelter,University of Texas ;;;;;
6 ;;; All rights reserved ;;;;;
7 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
10 ;; ** (c) Copyright 1982 Massachusetts Institute of Technology **
12 (macsyma-module comm2
)
16 (declare-top (special $props $dotdistrib
))
20 (cond ((null (cdddr e
))
21 (cond ((alike1 x
(caddr e
)) (cadr e
))
22 ((and (not (atom (caddr e
))) (atom x
) (not (free (caddr e
) x
)))
23 (mul2 (cadr e
) (sdiff (caddr e
) x
)))
24 ((or ($constantp
(setq a
(sdiff (cadr e
) x
)))
25 (and (atom (caddr e
)) (free a
(caddr e
))))
27 (t (simplifya (list '(%integrate
) a
(caddr e
)) t
))))
28 ((alike1 x
(caddr e
)) (addn (diffint1 (cdr e
) x x
) t
))
29 (t (addn (cons (if (equal (setq a
(sdiff (cadr e
) x
)) 0)
31 (simplifya (list '(%integrate
) a
(caddr e
)
32 (cadddr e
) (car (cddddr e
)))
34 (diffint1 (cdr e
) x
(caddr e
)))
37 (defun diffint1 (e x y
)
38 (let ((u (sdiff (cadddr e
) x
)) (v (sdiff (caddr e
) x
)))
39 (list (if (pzerop u
) 0 (mul2 u
(maxima-substitute (cadddr e
) y
(car e
))))
40 (if (pzerop v
) 0 (mul3 v
(maxima-substitute (caddr e
) y
(car e
)) -
1)))))
42 (defun diffsumprod (e x
)
43 (cond ((or (not ($mapatom x
)) (not (free (cadddr e
) x
)) (not (free (car (cddddr e
)) x
)))
44 (diff%deriv
(list e x
1)))
46 (t (let ((u (sdiff (cadr e
) x
)))
47 (setq u
(simplifya (list '(%sum
)
48 (if (eq (caar e
) '%sum
) u
(div u
(cadr e
)))
49 (caddr e
) (cadddr e
) (car (cddddr e
)))
51 (if (eq (caar e
) '%sum
) u
(mul2 e u
))))))
53 (defun difflaplace (e x
)
54 (cond ((or (not (atom x
)) (eq (cadddr e
) x
)) (diff%deriv
(list e x
1)))
56 (t ($laplace
(sdiff (cadr e
) x
) (caddr e
) (cadddr e
)))))
59 (cond ((freeof x e
) 0)
60 ((not (freeofl x
(hand-side (caddr e
) 'r
))) (diff%deriv
(list e x
1)))
61 (t ($at
(sdiff (cadr e
) x
) (caddr e
)))))
63 (defun diffncexpt (e x
)
64 (let ((base* (cadr e
))
66 (cond ((and (mnump pow
) (or (not (fixnump pow
)) (< pow
0))) ; POW cannot be 0
67 (diff%deriv
(list e x
1)))
68 ((and (atom base
*) (eq base
* x
) (free pow base
*))
69 (mul2* pow
(list '(mncexpt) base
* (add2 pow -
1))))
71 (let ((deriv (sdiff base
* x
))
73 (do ((i 0 (1+ i
))) ((= i pow
))
74 (push (list '(mnctimes) (list '(mncexpt) base
* i
)
75 (list '(mnctimes) deriv
76 (list '(mncexpt) base
* (- pow
1 i
))))
79 ((and (not (depends pow x
)) (or (atom pow
) (and (atom base
*) (free pow base
*))))
80 (let ((deriv (sdiff base
* x
))
81 (index (gensumindex)))
84 (list '(mnctimes) (list '(mncexpt) base
* index
)
85 (list '(mnctimes) deriv
86 (list '(mncexpt) base
*
87 (list '(mplus) pow -
1 (list '(mtimes) -
1 index
)))))
88 index
0 (list '(mplus) pow -
1)) nil
)))
89 (t (diff%deriv
(list e x
1))))))
92 (cond ((or (mnump e
) (constant e
)) 0)
93 ((or (atom e
) (member 'array
(cdar e
) :test
#'eq
))
94 (let ((w (mget (if (atom e
) e
(caar e
)) 'depends
)))
97 (list '(mtimes) (chainrule e x
) (list '(%del
) x
)))
100 ((specrepp e
) (stotaldiff (specdisrep e
)))
101 ((eq (caar e
) 'mnctimes
)
102 (let (($dotdistrib t
))
103 (add2 (ncmuln (cons (stotaldiff (cadr e
)) (cddr e
)) t
)
104 (ncmul2 (cadr e
) (stotaldiff (ncmuln (cddr e
) t
))))))
105 ((eq (caar e
) 'mncexpt
)
106 (if (and (fixnump (caddr e
)) (> (caddr e
) 0))
107 (stotaldiff (list '(mnctimes) (cadr e
)
108 (ncpower (cadr e
) (1- (caddr e
)))))
109 (list '(%derivative
) e
)))
110 (t (addn (cons 0 (mapcar #'(lambda (x)
111 (mul2 (sdiff e x
) (list '(%del simp
) x
)))
112 (extractvars (margs e
))))
115 (defun extractvars (e &aux vars
)
118 (cond ((not (maxima-constantp (car e
)))
119 (cond ((setq vars
(mget (car e
) 'depends
))
120 ;; The symbol has dependencies. Put the dependencies on
121 ;; the list of extracted vars.
122 (union* vars
(extractvars (cdr e
))))
124 ;; Put the symbol on the list of extracted vars.
125 (union* (ncons (car e
)) (extractvars (cdr e
))))))
126 (t (extractvars (cdr e
)))))
127 ((member 'array
(cdaar e
) :test
#'eq
)
128 (union* (ncons (car e
)) (extractvars (cdr e
))))
129 (t (union* (extractvars (cdar e
)) (extractvars (cdr e
))))))
133 ;;dummy-variable-operators is defined in COMM, which uses it inside of SUBST1.
134 (declare-top (special atvars
*atp
* munbound dummy-variable-operators
))
136 (defmfun $atvalue
(exp eqs val
)
138 (cond ((notloreq eqs
) (improper-arg-err eqs
'$atvalue
))
139 ((or (atom exp
) (and (eq (caar exp
) '%derivative
) (atom (cadr exp
))))
140 (improper-arg-err exp
'$atvalue
)))
141 (cond ((not (eq (caar exp
) '%derivative
))
144 dl
(make-list (length vl
) :initial-element
0)))
145 (t (setq fun
(caaadr exp
) vl
(cdadr exp
))
147 (setq dl
(nconc dl
(ncons (or (getf (cddr exp
) v
) 0)))))))
148 (if (or (mopp fun
) (eq fun
'mqapply
)) (improper-arg-err exp
'$atvalue
))
150 (do ((vl1 vl
(cdr vl1
)) (l atvars
(cdr l
))) ((null vl1
))
151 (if (and (symbolp (car vl1
)) (not (kindp (car vl1
) '$constant
)))
152 (setq val
(maxima-substitute (car l
) (car vl1
) val
))
153 (improper-arg-err (cons '(mlist) vl
) '$atvalue
)))
154 (setq eqs
(if (eq (caar eqs
) 'mequal
) (list eqs
) (cdr eqs
)))
155 (setq eqs
(do ((eqs eqs
(cdr eqs
)) (l)) ((null eqs
) l
)
156 (if (not (member (cadar eqs
) vl
:test
#'eq
))
157 (improper-arg-err (car eqs
) '$atvalue
))
158 (setq l
(nconc l
(ncons (cons (cadar eqs
) (caddar eqs
)))))))
159 (setq vl
(do ((vl vl
(cdr vl
)) (l)) ((null vl
) l
)
160 (setq l
(nconc l
(ncons (cdr (or (assoc (car vl
) eqs
:test
#'eq
)
161 (cons nil munbound
))))))))
162 (do ((atvalues (mget fun
'atvalues
) (cdr atvalues
)))
164 (mputprop fun
(cons (list dl vl val
) (mget fun
'atvalues
)) 'atvalues
))
165 (when (and (equal (caar atvalues
) dl
) (equal (cadar atvalues
) vl
))
166 (rplaca (cddar atvalues
) val
) (return nil
)))
170 (defprop %at simp-%at operators
)
172 (defun simp-%at
(expr ignored simp-flag
)
173 (declare (ignore ignored
))
175 (let* ((arg (simpcheck (cadr expr
) simp-flag
))
176 (e (resimplify (caddr expr
)))
178 (if (= ($length e
) 1) ($first e
) (cons '(mlist simp
) (cdr ($sort e
))))
180 (cond (($constantp arg
) arg
)
181 ((alike1 eqn
'((mlist))) arg
)
182 ((at-not-dependent eqn arg
))
183 (t (eqtest (list '(%at
) arg eqn
) expr
)))))
185 ;; Remove any variable from EQN if ARG is not dependent on it.
186 (defun at-not-dependent (eqn arg
)
187 (if (eq (caar eqn
) 'mequal
)
188 (setq eqn
(list '(mlist) eqn
)))
189 (multiple-value-bind (e0 e1
) (at-not-dependent-find-vars eqn arg
)
193 ((e1 (mapcar #'(lambda (x) (list '(mequal) x
($assoc x eqn
))) e1
))
194 (eqn1 (if (= (length e1
) 1) (first e1
) (cons '(mlist) e1
))))
195 (list '(%at
) arg eqn1
))
198 ;; Test dependence via derivative to account for declared dependencies.
199 (defun at-not-dependent-find-vars (eqn arg
)
200 (let ((e (mapcar #'second
(rest eqn
))))
201 (partition-by #'(lambda (x) (at-not-dependent-find-vars-1 x arg
)) e
)))
203 (defun at-not-dependent-find-vars-1 (x arg
)
205 (eql (mfuncall '$diff arg x
) 0)
206 ;; We might be called with something like -1*x as the variable.
207 ;; (That might or might not be a bug in itself, but let it go for the moment.)
208 ;; Try to extract a variable and test for dependence on that.
209 ;; If there are 2 or more variables, return NIL (i.e., not at-not-dependent).
210 (let ((v ($listofvars x
)))
211 (if (eql ($length v
) 1)
212 (at-not-dependent-find-vars-1 ($first v
) arg
)))))
214 (defmfun $at
(expr ateqs
)
215 (if (notloreq ateqs
) (improper-arg-err ateqs
'$at
))
216 (atscan (let ((*atp
* t
)) ($psubstitute ateqs expr
)) ateqs
))
218 (defun atscan (expr ateqs
)
219 (cond ((or (atom expr
)
220 (eq (caar expr
) 'mrat
)
221 (like ateqs
'((mlist))))
223 ((eq (caar expr
) '%derivative
)
224 (or (and (not (atom (cadr expr
)))
225 (let ((vl (cdadr expr
)) dl
)
227 (setq dl
(nconc dl
(ncons (or (getf (cddr expr
) v
) 0)))))
228 (atfind (caaadr expr
)
229 (cdr ($psubstitute ateqs
(cons '(mlist) vl
)))
231 (list '(%at
) expr ateqs
)))
232 ((member (caar expr
) dummy-variable-operators
:test
#'eq
)
233 (list '(%at
) expr ateqs
))
235 (t (recur-apply #'(lambda (x) (atscan x ateqs
)) expr
))))
238 (atfind (caar expr
) (cdr expr
) (make-list (length (cdr expr
)) :initial-element
0)))
240 (defun atfind (fun vl dl
)
241 (do ((atvalues (mget fun
'atvalues
) (cdr atvalues
)))
243 (and (equal (caar atvalues
) dl
)
244 (do ((l (cadar atvalues
) (cdr l
)) (vl vl
(cdr vl
)))
246 (if (and (not (equal (car l
) (car vl
)))
247 (not (eq (car l
) munbound
)))
251 (substitutel vl atvars
(caddar atvalues
)))))))
253 (declare-top (special $ratfac genvar varlist $keepfloat
))
255 (defmvar $logconcoeffp nil
)
257 (defmfun $logcontract
(e)
258 (lgcreciprocal (logcon e
))) ; E is assumed to be simplified.
262 ((member (caar e
) '(mplus mtimes
) :test
#'eq
)
263 (if (not (lgcsimplep e
)) (setq e
(lgcsort e
)))
264 (cond ((mplusp e
) (lgcplus e
))
265 ((mtimesp e
) (lgctimes e
))
267 (t (recur-apply #'logcon e
))))
269 ;; The logcontract algorithm for a sum.
271 ;; The function accumulates the arguments of things like log(a)+log(b) into a
272 ;; list called LOG. It calls out to lgctimes to deal with things like
273 ;; a*log(b). When all the arguments have been processed, it simplifies all the
274 ;; logarithmic arguments using sratsimp.
276 (let ((log) (notlogs))
277 (dolist (arg (cdr e
))
279 ((atom arg
) (push arg notlogs
))
280 ;; Only gather up log(x), not log[x]. It's not particularly obvious
281 ;; whether log(x)+log[y] should become log(x*y) or log[x*y], so we just
282 ;; ignore the fact that log[x] is a logarithm.
283 ((and (eq (caar arg
) '%log
)
284 (not (member 'array
(car arg
))))
285 (push (logcon (second arg
)) log
))
286 ((eq (caar arg
) 'mtimes
)
287 (let ((y (lgctimes arg
)))
288 (if (or (atom y
) (not (eq (caar y
) '%log
)))
290 (push (cadr y
) log
))))
292 (push (logcon arg
) notlogs
))))
295 (subst0 (cons '(mplus) (nreverse notlogs
)) e
))
297 (let ((simplified-log (lgcsimp
299 (sratsimp (muln log t
))))))
300 (addn (cons simplified-log notlogs
) t
))))))
302 ;; The logcontract algorithm for a product
304 ;; The main transformation this does is of the form 3*log(x) => log(x^3). To
305 ;; make this work, we find the first %log term and insert any coefficients we
306 ;; find into that. Coefficients are identified by LOGCONCOEFFP, which checks the
307 ;; $LOGCONCOEFFP user variable.
309 ;; Apply logcontract to the arguments. It's possible that the subsequent
310 ;; simplification means that the result isn't a product any more. In that
311 ;; case, just return it.
312 (setq e
(subst0 (cons '(mtimes) (mapcar 'logcon
(cdr e
))) e
))
313 (if (not (mtimesp e
))
315 (let ((log) (notlogs) (decints))
316 (dolist (arg (cdr e
))
317 (cond ((and (null log
) (not (atom arg
))
318 (eq (caar arg
) '%log
) (not (equal (cadr arg
) -
1)))
319 (setq log
(cadr arg
)))
320 ((logconcoeffp arg
) (push arg decints
))
321 (t (setq notlogs
(push arg notlogs
)))))
323 ((or (null log
) (null decints
)) e
)
324 (t (muln (cons (lgcsimp (power log
(muln decints t
)))
330 ;; e.g. log(1) -> 0, or log(%e) -> 1
331 (simplify (list '(%log
) e
)))
332 ((and (mexptp e
) (eq (cadr e
) '$%e
))
333 ;; log(%e^expr) -> expr
334 (simplify (list '(%log
) e
)))
336 (list '(%log simp
) e
))))
338 ;; Tests that its argument is a sum of terms that are "simple".
340 ;; A "simple" term is either completely free of logarithms, is a logarithm
341 ;; itself, or is a number times a logarithm.
343 ;; This function assumes that its argument is not an atom.
344 (defun lgcsimplep (e)
345 (flet ((lgc-nonsimple-arg-p (arg)
347 (eq (caar arg
) '%log
)
348 (not (isinop arg
'%log
))
349 ;; Product of a number with a logarithm e.g. 3*log(x)
350 (and (eq (caar arg
) 'mtimes
)
353 (not (atom (caddr arg
)))
354 (eq (caar (caddr arg
)) '%log
))))))
355 (and (eq (caar e
) 'mplus
)
356 (not (find-if #'lgc-nonsimple-arg-p
(cdr e
))))))
358 ;; Sort the argument so that coefficients come before logarithms and logarithms
359 ;; come before everything else.
361 (let ((logs) (notlogs) (decints) (varlist))
362 ;; Split the variables in E into logs, notlogs and coefficients. The list of
363 ;; variables is calculated by NEWVAR (and stored in the special variable
364 ;; VARLIST, which is why we have to bind it above).
365 (dolist (var (newvar e
))
367 ((and (not (atom var
)) (eq (caar var
) '%log
)) (push var logs
))
368 ((logconcoeffp var
) (push var decints
))
369 (t (push var notlogs
))))
370 (let* ((vl (nreconc decints
(nconc (sort logs
#'great
)
371 (nreverse notlogs
))))
372 (e1 (ratdisrep (ratrep e vl
))))
373 (if (alike1 e e1
) e e1
))))
375 ;; lgcreciprocal performs the transformation log(1/x) => -log(x)
376 (defun lgcreciprocal (e)
380 ((and (eq (caar e
) '%log
)
381 (setq num
(member ($num
(cadr e
)) '(1 -
1) :test
#'equal
))
382 (not (equal (setq denom
($denom
(cadr e
))) 1)))
383 (list '(mtimes simp
) -
1
384 (list '(%log simp
) (if (= (car num
) 1) denom
(neg denom
)))))
385 (t (recur-apply #'lgcreciprocal e
)))))
387 (defun logconcoeffp (e)
389 (is `(($logconcoeffp
) ,e
))
390 (maxima-integerp e
)))
394 (declare-top (special $radexpand $domain
))
396 (defmvar $rootsconmode t
)
398 (defmfun $rootscontract
(e) ; E is assumed to be simplified
399 (let ((radpe (and $radexpand
(not (eq $radexpand
'$all
)) (eq $domain
'$real
)))
403 (defun rtcon (e radpe
)
405 ((eq (caar e
) 'mtimes
)
406 (do ((x (cdr e
) (cdr x
)) (roots) (notroots) (y))
408 (cond ((null roots
) (subst0 (cons '(mtimes) (nreverse notroots
)) e
))
410 (multiple-value-bind (min gcd lcm
)
412 (cond ((and (= min gcd
) (not (= gcd
1))
414 (not (eq $rootsconmode
'$all
)))
420 (rtc-divide-by-gcd roots gcd
)
424 ((eq $rootsconmode
'$all
)
426 (rt-separ (simp-roots lcm roots
)
428 (rtc-fixitup roots notroots
))))
429 (cond ((atom (car x
))
430 (cond ((eq (car x
) '$%i
) (setq roots
(rt-separ (list 2 -
1) roots
)))
431 (t (setq notroots
(cons (car x
) notroots
)))))
432 ((and (eq (caaar x
) 'mexpt
) (ratnump (setq y
(caddar x
))))
433 (setq roots
(rt-separ (list (caddr y
)
435 (rtcon (cadar x
) radpe
) (cadr y
)))
438 ((and radpe
(eq (caaar x
) 'mabs
))
439 (setq roots
(rt-separ (list 2 `((mexpt) ,(rtcon (cadar x
) radpe
) 2) 1)
441 (t (setq notroots
(cons (rtcon (car x
) radpe
) notroots
))))))
442 ((and radpe
(eq (caar e
) 'mabs
))
443 (power (power (rtcon (cadr e
) radpe
) 2) '((rat simp
) 1 2)))
444 (t (recur-apply #'(lambda (x) (rtcon x radpe
)) e
))))
446 ;; RT-SEPAR separates like roots into their appropriate "buckets",
447 ;; where a bucket looks like:
448 ;; ((<denom of power> (<term to be raised> <numer of power>)
449 ;; (<term> <numer>)) etc)
451 (defun rt-separ (a roots
)
452 (let ((u (assoc (car a
) roots
:test
#'equal
)))
453 (cond (u (nconc u
(cdr a
))) (t (setq roots
(cons a roots
)))))
456 (defun simp-roots (lcm root-list
)
458 (do ((x root-list
(cdr x
)))
459 ((null x
) (push lcm root1
))
460 (push (list '(mexpt) (muln (cdar x
) nil
) (quotient lcm
(caar x
)))
463 (defun rtc-getinfo (list)
464 (let ((m (caar list
))
467 (dolist (x (cdr list
) (values m g l
))
468 (setq m
(min m
(car x
))
470 l
(lcm l
(car x
))))))
472 (defun rtc-fixitup (roots notroots
)
473 (mapcar #'(lambda (x) (rplacd x
(list (sratsimp (muln (cdr x
) (not $rootsconmode
))))))
475 (muln (nconc (mapcar #'(lambda (x) (power* (cadr x
) `((rat) 1 ,(car x
))))
478 (not $rootsconmode
)))
480 (defun rtc-divide-by-gcd (llist gcd
)
481 (mapcar #'(lambda (x) (rplaca x
(quotient (car x
) gcd
))) llist
)
487 ((eq (caar e
) 'mtimes
)
488 (if (equal -
1 (cadr e
)) (setq e
(cdr e
)))
489 (do ((l (cdr e
) (cdr l
)) (c 1 (* c
($nterms
(car l
)))))
491 ((eq (caar e
) 'mplus
)
492 (do ((l (cdr e
) (cdr l
)) (c 0 (+ c
($nterms
(car l
)))))
494 ((and (eq (caar e
) 'mexpt
) (integerp (caddr e
)) (plusp (caddr e
)))
495 ($binomial
(+ (caddr e
) ($nterms
(cadr e
)) -
1) (caddr e
)))
496 ((specrepp e
) ($nterms
(specdisrep e
)))
501 (declare-top (special $numer $logarc $trigsign
))
503 ;; atan2 distributes over lists, matrices, and equations
504 (defprop $atan2
(mlist $matrix mequal
) distribute_over
)
506 (defun simpatan2 (expr vestigial z
) ; atan2(y,x) ~ atan(y/x)
507 (declare (ignore vestigial
))
509 (let (y x signy signx
)
510 (setq y
(simpcheck (cadr expr
) z
)
511 x
(simpcheck (caddr expr
) z
))
512 (cond ((and (zerop1 y
) (zerop1 x
))
513 (merror (intl:gettext
"atan2: atan2(0,0) is undefined.")))
515 (and (or (numberp x
) (ratnump x
)) ; both numbers
516 (or (numberp y
) (ratnump y
)) ; ... but not bigfloats
517 (or $numer
(floatp x
) (floatp y
))) ; at least one float
518 (atan ($float y
) ($float x
)))
519 ( ;; bfloat contagion
522 (or ($bfloatp x
) ($bfloatp y
))) ; at least one bfloat
525 (*fpatan y
(list x
)))
526 ;; Simplifify infinities
528 (alike1 x
'((mtimes) -
1 $minf
)))
529 ;; Simplify atan2(y,inf) -> 0
532 (alike1 x
'((mtimes) -
1 $inf
)))
533 ;; Simplify atan2(y,minf) -> %pi for realpart(y)>=0 or
534 ;; -%pi for realpart(y)<0. When sign of y unknwon, return noun form.
535 (cond ((member (setq signy
($sign
($realpart x
))) '($pos $pz $zero
))
537 ((eq signy
'$neg
) (mul -
1 '$%pi
))
538 (t (eqtest (list '($atan2
) y x
) expr
))))
540 (alike1 y
'((mtimes) -
1 $minf
)))
541 ;; Simplify atan2(inf,x) -> %pi/2
544 (alike1 y
'((mtimes -
1 $inf
))))
545 ;; Simplify atan2(minf,x) -> -%pi/2
547 ((and (free x
'$%i
) (setq signx
($sign x
))
548 (free y
'$%i
) (setq signy
($sign y
))
550 (cond ((eq signx
'$neg
) '$%pi
)
551 ((member signx
'($pos $pz
)) 0)))
553 (cond ((eq signy
'$neg
) (div '$%pi -
2))
554 ((member signy
'($pos $pz
)) (div '$%pi
2))))
556 (cond ((eq signx
'$neg
) (mul -
3 (div '$%pi
4)))
557 ((member signx
'($pos $pz
)) (div '$%pi
4))))
558 ((alike1 y
(mul -
1 x
))
559 (cond ((eq signx
'$neg
) (mul 3 (div '$%pi
4)))
560 ((member signx
'($pos $pz
)) (div '$%pi -
4)))))))
562 (logarc '%atan2
(list ($logarc y
) ($logarc x
))))
563 ((and $trigsign
(eq t
(mminusp y
)))
564 (neg (take '($atan2
) (neg y
) x
)))
565 ;; atan2(y,x) = atan(y/x) + pi sign(y) (1-sign(x))/2
567 (take '(%atan
) (div y x
)))
568 ((and (eq signx
'$neg
)
569 (member (setq signy
($csign y
)) '($pos $neg
) :test
#'eq
))
570 (add (take '(%atan
) (div y x
))
571 (porm (eq signy
'$pos
) '$%pi
)))
572 ((and (eq signx
'$zero
) (eq signy
'$zero
))
573 ;; Unfortunately, we'll rarely get here. For example,
574 ;; assume(equal(x,0)) atan2(x,x) simplifies via the alike1 case above
575 (merror (intl:gettext
"atan2: atan2(0,0) is undefined.")))
576 (t (eqtest (list '($atan2
) y x
) expr
)))))
580 (defmfun $fibtophi
(e &optional
(lnorecurse nil
))
583 (setq e
(cond (lnorecurse (cadr e
)) (t ($fibtophi
(cadr e
) lnorecurse
))))
584 (let ((phi (meval '$%phi
)))
585 (div (add2 (power phi e
) (neg (power (add2 1 (neg phi
)) e
)))
586 (add2 -
1 (mul2 2 phi
)))))
587 (t (recur-apply #'(lambda (x) ($fibtophi x lnorecurse
)) e
))))
589 (defmspec $numerval
(l) (setq l
(cdr l
))
590 (do ((l l
(cddr l
)) (x (ncons '(mlist simp
)))) ((null l
) x
)
591 (cond ((null (cdr l
)) (merror (intl:gettext
"numerval: expected an even number of arguments.")))
592 ((not (symbolp (car l
)))
593 (merror (intl:gettext
"numerval: expected a symbol; found ~M") (car l
)))
595 (merror (intl:gettext
"numerval: cannot declare a value because ~M is bound.") (car l
))))
596 (mputprop (car l
) (cadr l
) '$numer
)
597 (add2lnc (car l
) $props
)
598 (nconc x
(ncons (car l
)))))
601 (declare (special my-powers
))
603 (defmfun $derivdegree
(e depvar var
)
604 (let (my-powers) (declare (special my-powers
)) (derivdeg1 e depvar var
) (if (null my-powers
) 0 (maximin my-powers
'$max
))))
606 (defun derivdeg1 (e depvar var
)
607 (cond ((or (atom e
) (specrepp e
)))
608 ((eq (caar e
) '%derivative
)
609 (cond ((alike1 (cadr e
) depvar
)
610 (do ((l (cddr e
) (cddr l
))) ((null l
))
611 (cond ((alike1 (car l
) var
)
612 (return (setq my-powers
(cons (cadr l
) my-powers
)))))))))
613 (t (mapc #'(lambda (x) (derivdeg1 x depvar var
)) (cdr e
))))))
617 ;; Set the the property reversealias
618 (defprop mbox $box reversealias
)
619 (defprop mlabox $box reversealias
)
621 (defmfun $dpart
(&rest args
)
622 (mpart args nil t nil
'$dpart
))
624 (defmfun $lpart
(e &rest args
)
625 (mpart args nil
(list e
) nil
'$lpart
))
627 (defmfun $box
(e &optional
(l nil l?
))
629 (list '(mlabox) e
(box-label l
))
635 ($box e
(car label
))))
640 (coerce (mstring x
) 'string
)))
642 (defmfun $rembox
(e &optional
(l nil l?
))
643 (let ((label (if l?
(box-label l
) '(nil))))
646 (defun rembox1 (e label
)
648 ((or (and (eq (caar e
) 'mbox
)
649 (or (equal label
'(nil)) (member label
'($unlabelled $unlabeled
) :test
#'eq
)))
650 (and (eq (caar e
) 'mlabox
)
651 (or (equal label
'(nil)) (equal label
(caddr e
)))))
652 (rembox1 (cadr e
) label
))
653 (t (recur-apply #'(lambda (x) (rembox1 x label
)) e
))))
657 (declare-top (special scanmapp
))
659 (defmspec $scanmap
(l)
661 (resimplify (apply #'scanmap1
(mmapev l
)))))
663 (defun scanmap1 (func e
&optional
(flag nil flag?
))
664 (let ((arg2 (specrepcheck e
)) newarg2
)
665 (cond ((eq func
'$rat
)
666 (merror (intl:gettext
"scanmap: cannot apply 'rat'.")))
668 (unless (eq flag
'$bottomup
)
669 (merror (intl:gettext
"scanmap: third argument must be 'bottomup', if present; found ~M") flag
))
671 (funcer func
(ncons arg2
))
673 (ncons (mcons-op-args (mop arg2
)
674 (mapcar #'(lambda (u)
675 (scanmap1 func u
'$bottomup
))
679 (funcer func
(ncons arg2
)))
681 (setq newarg2
(specrepcheck (funcer func
(ncons arg2
))))
682 (cond ((mapatom newarg2
)
684 ((and (alike1 (cadr newarg2
) arg2
) (null (cddr newarg2
)))
685 (subst0 (cons (ncons (caar newarg2
))
687 (mcons-op-args (mop arg2
)
688 (mapcar #'(lambda (u) (scanmap1 func u
))
693 (subst0 (mcons-op-args (mop newarg2
)
694 (mapcar #'(lambda (u) (scanmap1 func u
))
698 (defun subgen (form) ; This function does mapping of subscripts.
699 (do ((ds (if (eq (caar form
) 'mqapply
) (list (car form
) (cadr form
))
701 (outermap1 #'dsfunc1
(simplify (car sub
)) ds
))
702 (sub (reverse (or (and (eq 'mqapply
(caar form
)) (cddr form
))
707 (defun dsfunc1 (dsn dso
)
708 (cond ((or (atom dso
) (atom (car dso
))) dso
)
709 ((member 'array
(car dso
) :test
#'eq
)
710 (cond ((eq 'mqapply
(caar dso
))
711 (nconc (list (car dso
) (cadr dso
) dsn
) (cddr dso
)))
712 (t (nconc (list (car dso
) dsn
) (cdr dso
)))))
713 (t (mapcar #'(lambda (d) (dsfunc1 dsn d
)) dso
))))
717 (defmfun $genmatrix
(a i2
&optional
(j2 i2
) (i1 1) (j1 i1
))
718 (let ((f) (l (ncons '($matrix
))))
719 (setq f
(if (or (symbolp a
) (hash-table-p a
) (arrayp a
))
720 #'(lambda (i j
) (meval (list (list a
'array
) i j
)))
721 #'(lambda (i j
) (mfuncall a i j
))))
723 (if (notevery #'fixnump
(list i2 j2 i1 j1
))
724 (merror (intl:gettext
"genmatrix: bounds must be integers; found ~M, ~M, ~M, ~M") i2 j2 i1 j1
))
726 (if (or (> i1 i2
) (> j1 j2
))
727 (merror (intl:gettext
"genmatrix: upper bounds must be greater than or equal to lower bounds; found ~M, ~M, ~M, ~M") i2 j2 i1 j1
))
729 (dotimes (i (1+ (- i2 i1
)))
730 (nconc l
(ncons (ncons '(mlist)))))
736 (nconc (car l
) (ncons (funcall f i j
)))))
739 ; Execute deep copy for copymatrix and copylist.
740 ; Resolves SF bug report [ 1224960 ] sideeffect with copylist.
741 ; An optimization would be to call COPY-TREE only on mutable expressions.
743 (defmfun $copymatrix
(x)
745 (merror (intl:gettext
"copymatrix: argument must be a matrix; found ~M") x
))
748 (defmfun $copylist
(x)
750 (merror (intl:gettext
"copylist: argument must be a list; found ~M") x
))
758 (defmfun $addrow
(m &rest rows
)
759 (declare (dynamic-extent rows
))
760 (cond ((not ($matrixp m
))
762 (intl:gettext
"addrow: first argument must be a matrix; found ~M")
766 (let ((m (copy-tree m
)))
768 (setq m
(addrow m r
)))))))
770 (defmfun $addcol
(m &rest cols
)
771 (declare (dynamic-extent cols
))
772 (cond ((not ($matrixp m
)) (merror (intl:gettext
"addcol: first argument must be a matrix; found ~M") m
))
775 (apply '$addcol
(cons (ensure-matrix-column (first cols
)) (rest cols
))))
776 (t (let ((m ($transpose m
)))
777 (dolist (c cols
($transpose m
))
778 (setq m
(addrow m
($transpose c
))))))))
780 (defun ensure-matrix-column (a)
782 ;; otherwise must be a MLIST.
783 `(($matrix
) ,@(mapcar #'(lambda (e) `((mlist) ,e
)) (cdr a
)))))
786 (cond ((not (mxorlistp r
)) (merror (intl:gettext
"addrow or addcol: argument must be a matrix or list; found ~M") r
))
788 (or (and (eq (caar r
) 'mlist
) (not (= (length (cadr m
)) (length r
))))
789 (and (eq (caar r
) '$matrix
)
790 (not (= (length (cadr m
)) (length (cadr r
))))
791 (prog2 (setq r
($transpose r
))
792 (not (= (length (cadr m
)) (length (cadr r
))))))))
793 (merror (intl:gettext
"addrow or addcol: incompatible structure."))))
794 (append m
(if (eq (caar r
) '$matrix
) (cdr r
) (ncons r
))))
798 (defun my-nonatomic-expr-p (e)
799 (and (consp e
) (consp (car e
)) (symbolp (caar e
))))
801 (defun my-lambda-expr-p (e)
802 (and (consp e
) (consp (car e
)) (eq 'lambda
(caar e
))))
804 (defmfun $arraymake
(ary subs
)
806 ;; We go through some gyrations here to allow as wide a range of inputs as possible.
807 ;; Previously $ARRAYMAKE didn't check the first argument at all;
808 ;; this is an attempt at a minimally-restrictive change.
809 ((not (or (symbolp ary
) ($subvarp ary
) (and (my-nonatomic-expr-p ary
) (not (my-lambda-expr-p ary
)))))
810 (merror (intl:gettext
"arraymake: first argument must be a symbol, subscripted symbol, or nonatomic expression (but not a lambda expression); found: ~M") ary
))
811 ((or (not ($listp subs
)) (null (cdr subs
)))
812 (merror (intl:gettext
"arraymake: second argument must be a list of one or more elements; found ~M") subs
))
814 (cons (cons (getopr ary
) '(array)) (cdr subs
)))
815 (t (cons '(mqapply array
) (cons ary
(cdr subs
))))))
817 (defmspec $arrayinfo
(ary)
819 (arrayinfo-aux (car ary
) (getvalue (car ary
))))
821 (defun arrayinfo-aux (sym val
)
826 (or (hash-table-p arra
)
828 (eq (marray-type arra
) '$functional
)))
829 (cond ((hash-table-p arra
)
830 (let ((dim1 (gethash 'dim1 arra
)))
831 (return (list* '(mlist) '$hash_table
(if dim1
1 t
)
832 (loop for u being the hash-keys in arra
837 (cons '(mlist simp
) u
)))))))
839 (return (let ((dims (array-dimensions arra
)))
840 (list '(mlist) '$declared
841 ;; they don't want more info (array-type arra)
843 (cons '(mlist) (mapcar #'1- dims
))))))
844 ((eq (marray-type arra
) '$functional
)
845 (return (arrayinfo-aux sym
(mgenarray-content arra
)))))
846 (let ((gen (safe-mgetl sym
'(hashar array
))) ary1
)
848 (merror (intl:gettext
"arrayinfo: ~M is not an array.") ary
))
849 (setq ary1
(cadr gen
))
850 (cond ((eq (car gen
) 'hashar
)
851 (setq ary1
(symbol-array ary1
))
852 (return (append '((mlist simp
) $hashed
)
854 (do ((i 3 (1+ i
)) (l)
855 (n (cadr (arraydims ary1
))))
856 ((= i n
) (sort l
#'(lambda (x y
) (great y x
))))
857 (do ((l1 (aref ary1 i
) (cdr l1
)))
859 (push (cons '(mlist simp
) (caar l1
)) l
)))))))
860 (t (setq ary1
(arraydims ary1
))
861 (return (list '(mlist simp
)
862 (cond ((safe-get ary
'array
)
863 (cdr (assoc (car ary1
)
864 '((t . $complete
) (fixnum . $integer
)
865 (flonum . $float
)) :test
#'eq
)))
868 (cons '(mlist simp
) (mapcar #'1-
(cdr ary1
)))))))))))
872 (declare-top (special greatorder lessorder
))
874 (defmspec $ordergreat
(l)
875 (if greatorder
(merror (intl:gettext
"ordergreat: reordering is not allowed.")))
876 (makorder (setq greatorder
(reverse (cdr l
))) '_
))
878 (defmspec $orderless
(l)
879 (if lessorder
(merror (intl:gettext
"orderless: reordering is not allowed.")))
880 (makorder (setq lessorder
(cdr l
)) '|
#|
))
882 (defun makorder (l char
)
887 (implode (nconc (ncons char
) (mexploden n
)
888 (exploden (stripdollar (car l
))))))))
893 (nconc (mapcar #'(lambda (x) (remalias (getalias x
))) lessorder
)
894 (mapcar #'(lambda (x) (remalias (getalias x
))) greatorder
)))
896 (setq lessorder nil greatorder nil
)
901 (defmfun $concat
(&rest l
)
902 "Concatenates its arguments.
903 The arguments must evaluate to atoms. The return value is a symbol if
904 the first argument is a symbol and a string otherwise."
906 (merror (intl:gettext
"concat: there must be at least one argument.")))
907 (let ((result-is-a-string (or (numberp (car l
)) (stringp (car l
)))))
908 (setq l
(mapcan #'(lambda (x) (unless (atom x
) (merror (intl:gettext
"concat: argument must be an atom; found ~M") x
)) (string* x
)) l
))
909 (if result-is-a-string
911 (getalias (implode (cons '#\$ l
))))))