1 ;;; -*- Mode: Lisp; Package: Maxima; Syntax: Common-Lisp; Base: 10 -*- ;;;;
2 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3 ;;; The data in this file contains enhancements. ;;;;;
5 ;;; Copyright (c) 1984,1987 by William Schelter,University of Texas ;;;;;
6 ;;; All rights reserved ;;;;;
7 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
8 ;;; (c) Copyright 1981 Massachusetts Institute of Technology ;;;
9 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
13 (macsyma-module trgred
)
15 (declare-top (special var
*schatc-ans
* *trigred
*noexpand
*lin
))
17 (load-macsyma-macros rzmac
)
19 (defvar *trans-list-plus
*
20 '((((mplus) ((coeffpt) (c true
) ((mexpt) ((%tan
) (x true
)) 2))
22 ((mtimes) c
((mexpt) ((%sec
) x
) 2)))
23 (((mplus) ((coeffpt) (c true
) ((mexpt) ((%cot
) (x true
)) 2))
25 ((mtimes) c
((mexpt) ((%csc
) x
) 2)))
26 (((mplus) ((coeffpt) (c true
) ((mexpt) ((%tanh
) (x true
)) 2))
27 ((mtimes) -
1 (var* (uvar) c
)))
28 ((mtimes) -
1 c
((mexpt) ((%sech
) x
) 2)))
29 (((mplus) ((coeffpt) (c true
) ((mexpt) ((%coth
) (x true
)) 2))
30 ((mtimes) -
1 (var* (uvar) c
)))
31 ((mtimes) c
((mexpt) ((%csch
) x
) 2)))))
34 '(* %sin
(* %cot %cos %sec %tan
) %cos
(* %tan %sin %csc %cot
)
35 %tan
(* %cos %sin %csc %sec
) %cot
(* %sin %cos %sec %csc
)
36 %sec
(* %sin %tan %cot %csc
) %csc
(* %cos %cot %tan %sec
)))
39 '(* %sinh
(* %coth %cosh %sech %tanh
) %cosh
(* %tanh %sinh %csch %coth
)
40 %tanh
(* %cosh %sinh %csch %sech
) %coth
(* %sinh %cosh %sech %csch
)
41 %sech
(* %sinh %tanh %coth %csch
) %csch
(* %cosh %coth %tanh %sech
)))
43 (defvar *sc^ndisp
* '((%sin . sin^n
) (%cos . cos^n
) (%sinh . sinh^n
) (%cosh . cosh^n
)))
46 (defvar *trigbuckets
*)
47 (defvar *hyperbuckets
*)
49 ;;The Trigreduce file contains a group of routines which can be used to
50 ;;make trigonometric simplifications of expressions. The bulk of the
51 ;;routines here involve the reductions of products of sin's and cos's.
53 ;; *TRIGRED indicates that the special simplifications for
54 ;; $TRIGREDUCE are to be used.
55 ;; *NOEXPAND indicates that trig functions of sums of
56 ;; angles are not to be used.
58 ;; Without the check for the value of $simp, trigreduce can enter an
59 ;; endless loop--for example trigreduce((-1)*x). There is at least one
60 ;; test in the testsuite that sets simp to false and then needs to compute
61 ;; a limit. But the limit code calls trigreduce and the test fails. The
62 ;; top level check for simp seems like the best way to prevent infinite
63 ;; loops when simp is false.
64 (defmfun ($trigreduce
:properties
((evfun t
))) (exp &optional
(var '*novar
))
67 $trigexpand $verbose $ratprint
)
68 (declare (special $trigexpand
*trigred
))
69 (if $simp
(gcdred (sp1 exp
)) exp
)))
71 ;; The first pass in power series expansion (used by $powerseries in
72 ;; series.lisp), but also used by $trigreduce. Expands / reduces the expression
73 ;; E as a function VAR, controlled by the *TRIGRED and *NOEXPAND flags.
78 ;; We recognise some special patterns that are expressed as sums, such as
79 ;; 1+tan(x)^2 => sec(x)^2. Rewrite using the first matching pattern (and
80 ;; recurse to try to simplify further). If no pattern matches, expand each
83 (or (dolist (pair *trans-list-plus
*)
84 (let ((match (m2 e
(first pair
))))
86 (return (sp1 (sch-replace match
(second pair
)))))))
87 (m+l
(mapcar #'sp1
(cdr e
)))))
89 ((eq (caar e
) 'mtimes
)
93 (sp1expt (sp1 (cadr e
)) (sp1 (caddr e
))))
96 (sp1log (sp1 (cadr e
))))
98 ((member (caar e
) '(%cos %sin %tan %cot %sec %csc
99 %cosh %sinh %tanh %coth %sech %csch
) :test
#'eq
)
100 (sp1trig (list (car e
) (let* ((*noexpand t
)) (sp1 (cadr e
))))))
102 ((member (caar e
) '(%asin %acos %atan %acot %asec %acsc
103 %asinh %acosh %atanh %acoth %asech %acsch
) :test
#'eq
)
104 (sp1atrig (caar e
) (let* ((*noexpand t
)) (sp1 (cadr e
)))))
106 ((eq (caar e
) 'mrat
) (sp1 (ratdisrep e
)))
109 (cons (list (caar e
)) (mapcar #'(lambda (u)
112 ((eq (caar e
) '%integrate
)
113 (list* '(%integrate
) (sp1 (cadr e
)) (cddr e
)))
115 (t (recur-apply #'sp1 e
))))
118 (or (and (not (atom e
)) (trigp (caar e
))) (equal e
1)))
122 ((eq (caar e
) 'mplus
) (m+l
(mapcar #'gcdred
(cdr e
))))
123 ((eq (caar e
) 'mtimes
)
127 (do ((e (cdr e
) (cdr e
)))
129 (setq nn
(m*l nn
) nd
(m*l nd
)))
130 (cond ((and (mexptp (car e
))
131 (or (signp l
(caddar e
))
132 (and (mtimesp (caddar e
))
133 (signp l
(cadr (caddar e
))))))
134 (setq nd
(cons (m^
(cadar e
) (m- (caddar e
))) nd
)))
136 (setq nn
(cons (cadar e
) nn
)
137 nd
(cons (caddar e
) nd
)))
138 ((setq nn
(cons (car e
) nn
)))))
139 (cond ((equal nd
1) nn
)
140 ((equal (setq gcd
($gcd nn nd
)) 1) e
)
141 ((div* (cadr ($divide nn gcd
))
142 (cadr ($divide nd gcd
)))))))
143 (t (recur-apply #'gcdred e
))))
153 (dolist (factor (cdr e
))
154 (cond ((or (mnump factor
)
155 (and (not (eq var
'*novar
)) (free factor var
)))
157 ((atom factor
) (push factor g
))
159 (and (eq (caar factor
) 'mexpt
)
160 (trigfp (cadr factor
))))
164 (setq g
(mapcar #'sp1 g
))
166 (mapc #'(lambda (q) (sp1sincos q t
)) *trigbuckets
*)
167 (mapc #'(lambda (q) (sp1sincos q nil
)) *hyperbuckets
*)
168 (setq fr
(cons (m^
1//2 (m+l
*lin
)) fr
)
170 (setq tr
(cons '* (mapcan #'sp1untrep
*trigbuckets
*)))
171 (setq g
(nconc (sp1tlin tr t
) (sp1tplus *lin t
) g
)
173 (setq hyp
(cons '* (mapcan #'sp1untrep
*hyperbuckets
*)))
174 (setq g
(nconc (sp1tlin hyp nil
) (sp1tplus *lin nil
) g
))
175 (setq g
($expand
(let* (($keepfloat t
))
176 (declare (special $keepfloat
))
177 ($ratsimp
(cons '(mtimes) g
)))))
179 (setq g
(mapcar #'sp1
(cdr g
)))
180 (setq g
(list (sp1 g
))))
181 (m*l
(cons 1 (nconc g fr
(cdr tr
) (cdr hyp
))))))
183 (defun sp1tlin (l trig
)
184 (cond ((null (cdr l
)) nil
)
185 ((and (eq (caaadr l
) 'mexpt
)
186 (integerp (caddr (cadr l
)))
187 (member (caaadr (cadr l
))
188 (if trig
'(%sin %cos
) '(%sinh %cosh
)) :test
#'eq
))
189 (cons (funcall (cdr (assoc (caaadr (cadr l
)) *sc^ndisp
* :test
#'eq
))
190 (caddr (cadr l
)) (cadadr (cadr l
)))
191 (sp1tlin (rplacd l
(cddr l
)) trig
)))
192 ((member (caaadr l
) (if trig
'(%sin %cos
) '(%sinh %cosh
)) :test
#'eq
)
194 (sp1tlin (rplacd l
(cddr l
)) trig
))
195 ((sp1tlin (cdr l
) trig
))))
197 ;; Rewrite a product of sines and cosines as a sum
199 ;; L is a list of sines and cosines. For example, consider the list
201 ;; sin(x), sin(2*x), sin(3*x)
203 ;; This represents the product sin(x)*sin(2*x)*sin(3*x).
205 ;; ANS starts as sin(x). Then for each term in the rest of the list, we multiply
206 ;; the answer that we have found so far by that term. The result will be a sum
207 ;; of sines. In this example, sin(x)*sin(2*x) gives us
209 ;; 1/2 * (cos(x) - cos(3*x))
211 ;; In fact we don't calculate the 1/2 coefficient in sp1sintcos: you always get
212 ;; a factor of 2^(k-1), where k is the length of the list, so this is calculated
213 ;; at the bottom of sp1tplus. Anyway, next we calculate cos(x)*sin(3*x) and
214 ;; -cos(3*x)*sin(3*x) and sum the answers. Note that -cos(3*x) will crop up
215 ;; represented as ((mtimes) -1 ((%cos) ((mtimes) 3 $x))). See note in the let
216 ;; form for info on what form ANS must take.
217 (defun sp1tplus (l trig
)
218 (if (or (null l
) (null (cdr l
)))
220 ;; ANS is a list containing the terms in a sum for the expanded
221 ;; expression. Each element in this list is either of the form sc(x),
222 ;; where sc is sin or cos, or of the form ((mtimes) coeff ((sc) $x)),
223 ;; where coeff is some coefficient.
225 ;; multiply-sc-terms rewrites a*sc as a sum of sines and cosines. The
226 ;; result is a list containing the terms in a sum which is
227 ;; mathematically equal to a*sc. Assuming that term is of one of the
228 ;; forms described for ANS below and that SC is of the form sc(x), the
229 ;; elements of the resulting list will all be of suitable form for
230 ;; inclusion into ANS.
231 (flet ((multiply-sc-terms (term sc
)
232 (let* ((coefficient (when (mtimesp term
) (cadr term
)))
233 (term-sc (if (mtimesp term
) (caddr term
) term
))
234 (expanded (sp1sintcos term-sc sc trig
)))
235 ;; expanded will now either be sin(foo) or cos(foo) OR it
236 ;; will be a sum of such terms.
238 ((not coefficient
) (list expanded
))
240 (member (caar expanded
) '(%sin %cos %sinh %cosh
)
242 (list (m* coefficient expanded
)))
244 (mapcar (lambda (summand) (m* coefficient summand
))
247 ;; SP1SINTCOS can also return numbers and constant expressions.
248 ;; Assume that's the case here.
249 (list (m* coefficient expanded
))))))
250 ;; Treat EXPR as a sum and return a list of its terms
252 (if (mplusp expr
) (cdr expr
) (ncons expr
))))
254 (let ((ans (list (first l
))))
255 (dolist (sc (rest l
))
256 (setq ans
(terms-of-sum
257 (m+l
(mapcan (lambda (q)
258 (multiply-sc-terms q sc
)) ans
)))))
259 (list (list '(rat) 1 (expt 2 (1- (length l
))))
262 ;; The core of trigreduce. Performs transformations like sin(x)*cos(x) =>
265 ;; This function only does something non-trivial if both a and b have one of
266 ;; sin, cos, sinh and cosh as top-level operators. (Note the first term in the
267 ;; cond: we assume that if a,b are non-atomic and not both of them are
268 ;; hyperbolic/trigonometric then we can just multiply the two terms)
269 (defun sp1sintcos (a b trig
)
272 (cond ((or (atom a
) (atom b
)
273 (not (member (caar a
) '(%sin %cos %sinh %cosh
) :test
#'eq
))
274 (not (member (caar b
) '(%sin %cos %sinh %cosh
) :test
#'eq
)))
276 ((prog2 (setq x
(m+ (cadr a
) (cadr b
)) y
(m- (cadr a
) (cadr b
)))
277 (null (eq (caar a
) (caar b
))))
278 (setq b
(if trig
'(%sin
) '(%sinh
)))
279 (or (eq (caar a
) '%sin
) (eq (caar a
) '%sinh
)
281 (m+ (list b x
) (list b y
)))
282 ((member (caar a
) '(%cos %cosh
) :test
#'eq
)
283 (m+ (list (list (caar a
)) x
)
284 (list (list (caar a
)) y
)))
286 (m- (list '(%cos
) y
) (list '(%cos
) x
)))
287 ((m- (list '(%cosh
) x
) (list '(%cosh
) y
))))))
289 ;; For COS(X)^2, TRIGBUCKET is (X (1 (COS . 2))) or, more generally,
290 ;; (arg (numfactor-of-arg (operator . exponent)))
293 (let* ((n (cond ((eq (caar e
) 'mexpt
)
294 (cond ((= (signum1 (caddr e
)) -
1)
295 (prog1 (m- (caddr e
))
296 (setq e
(cons (list (zl-get (caaadr e
) 'recip
)) (cdadr e
)))))
297 ((prog1 (caddr e
) (setq e
(cadr e
))))))
299 (arg (sp1kget (cadr e
)))
301 (*laws
* *hyperlaws
*))
302 (cond ((member (caar e
) '(%sin %cos %tan %cot %sec %csc
) :test
#'eq
)
303 (cond ((setq buc
(assoc (cdr arg
) *trigbuckets
* :test
#'equal
))
304 (setq *laws
* *triglaws
*)
305 (sp1addbuc (caar e
) (car arg
) n buc
))
307 (cons (list (cdr arg
) (list (car arg
) (cons (caar e
) n
)))
309 ((setq buc
(assoc (cdr arg
) *hyperbuckets
* :test
#'equal
))
310 (sp1addbuc (caar e
) (car arg
) n buc
))
311 ((setq *hyperbuckets
*
312 (cons (list (cdr arg
) (list (car arg
) (cons (caar e
) n
)))
315 (defun sp1addbuc (f arg n b
) ;FUNCTION, ARGUMENT, EXPONENT, BUCKET LIST
316 (cond ((and (cdr b
) (alike1 arg
(caadr b
))) ;GOES IN THIS BUCKET
317 (sp1putbuc f n
(cadr b
)))
318 ((or (null (cdr b
)) (great (caadr b
) arg
))
319 (rplacd b
(cons (list arg
(cons f n
)) (cdr b
))))
320 ((sp1addbuc f arg n
(cdr b
)))))
322 (defun sp1putbuc (f n
*buc
) ;PUT IT IN THERE
323 (do ((buc *buc
(cdr buc
)))
325 (rplacd buc
(list (cons f n
))))
326 (cond ((eq f
(caadr buc
)) ;SAME FUNCTION
328 (rplacd (cadr buc
) (m+ n
(cdadr buc
))))) ;SO BOOST EXPONENT
329 ((eq (caadr buc
) (zl-get f
'recip
)) ;RECIPROCAL FUNCTIONS
330 (setq n
(m- (cdadr buc
) n
))
332 (cond ((signp e n
) (rplacd buc
(cddr buc
)))
334 (rplaca (cadr buc
) f
)
335 (rplacd (cadr buc
) (neg n
)))
336 (t (rplacd (cadr buc
) n
)))))
337 (t (let* ((nf (zl-get (zl-get *laws
* (caadr buc
)) f
))
339 (cond ((null nf
)) ;NO SIMPLIFICATIONS HERE
340 ((equal n
(cdadr buc
)) ;EXPONENTS MATCH
341 (rplacd buc
(cddr buc
))
343 (sp1putbuc1 nf n
*buc
))) ;TO MAKE SURE IT DOESN'T OCCUR TWICE
344 ((eq (setq m
(sp1great n
(cdadr buc
))) 'nomatch
))
345 (m (setq m
(cdadr buc
))
346 (rplacd buc
(cddr buc
))
347 (sp1putbuc1 nf m
*buc
)
348 (sp1putbuc1 f
(m- n m
) *buc
)
350 (t (rplacd (cadr buc
) (m- (cdadr buc
) n
))
351 (return (sp1putbuc1 nf n
*buc
)))))))))
353 (defun sp1putbuc1 (f n buc
)
354 (cond ((null (cdr buc
))
355 (rplacd buc
(list (cons f n
))))
357 (rplacd (cadr buc
) (m+ n
(cdadr buc
))))
358 ((sp1putbuc1 f n
(cdr buc
)))))
360 (defun sp1great (x y
)
364 (cond ((mnump y
) (great x y
)) (t 'nomatch
)))
365 ((or (atom x
) (atom y
)) 'nomatch
)
366 ((and (eq (caar x
) (caar y
))
367 (alike (cond ((mnump (cadr x
))
368 (setq a
(cadr x
)) (cddr x
))
369 (t (setq a
1) (cdr x
)))
370 (cond ((mnump (cadr y
))
371 (setq b
(cadr y
)) (cddr y
))
372 (t (setq b
1) (cdr y
)))))
379 (mapcar #'(lambda (term)
380 (let* ((bas (simplifya (list (list (car term
))
381 (m* (car b
) (car buc
)))
383 (cond ((equal (cdr term
) 1) bas
)
384 ((m^ bas
(cdr term
))))))
388 (defun sp1kget (e) ;FINDS NUMERIC COEFFICIENTS
389 (or (and (mtimesp e
) (numberp (cadr e
))
390 (cons (cadr e
) (m*l
(cddr e
))))
393 (defun sp1sincos (l trig
)
394 (mapcar #'(lambda (q) (sp1sincos2 (m* (car l
) (car q
)) q trig
)) (cdr l
)))
396 (defun sp1sincos2 (arg l trig
)
398 (cond ((null (cdr l
)))
400 (setq a
(member (caadr l
)
402 '(%sinh %cosh %sinh %csch %sech %csch
)
403 '(%sin %cos %sin %csc %sec %csc
)) :test
#'eq
))
404 (cddr l
)) ;THERE MUST BE SOMETHING TO MATCH TO.
405 (sp1sincos1 (cadr a
) l arg trig
))
406 ((sp1sincos2 arg
(cdr l
) trig
)))))
408 (defun sp1sincos1 (s l arg trig
)
411 (do ((ll (cdr l
) (cdr ll
)))
413 (cond ((eq s
(caadr ll
))
414 (setq arg
(m* 2 arg
))
416 (cond ((member s
'(%sin %cos
) :test
#'eq
)
418 ((setq s
'%csc e -
1))))
420 (cond ((member s
'(%sinh %cosh
) :test
#'eq
)
422 ((setq s
'%csch e -
1)))))
423 (cond ((alike1 (cdadr ll
) (cdadr l
))
424 (sp1addto s arg
(cdadr l
))
425 (setq *lin
(cons (m* e
(cdadr l
)) *lin
))
426 (rplacd ll
(cddr ll
)) ;;;MUST BE IN THIS ORDER!!
429 ((eq (setq g
(sp1great (cdadr l
) (cdadr ll
))) 'nomatch
))
431 (rplacd (cadr ll
) (m- (cdadr ll
) (cdadr l
)))
432 (sp1addto s arg
(cdadr l
))
433 (setq *lin
(cons (m* e
(cdadr l
)) *lin
))
437 (rplacd (cadr l
) (m- (cdadr l
) (cdadr ll
)))
438 (sp1addto s arg
(cdadr ll
))
439 (push (m* e
(cdadr ll
)) *lin
)
440 (rplacd ll
(cddr ll
))
443 (defun sp1addto (fn arg exp
)
444 (setq arg
(list (list fn
) arg
))
445 (sp1add (if (equal exp
1) arg
(m^ arg exp
))))
449 (power (sp1 b
) (sp1 e
)))
450 ((and (null (trigfp b
)) (free e var
))
454 ((and (null (eq var
'*novar
)) (free b var
))
455 (sp1expt2 (m* (list '(%log
) b
) e
)))
456 ((and (consp b
) (consp (car b
)) (member (caar b
) '(%sin %cos %tan %cot %sec %csc
457 %sinh %cosh %tanh %coth %sech %csch
) :test
#'eq
))
458 (cond ((= (signum1 e
) -
1)
459 (sp1expt (list (list (zl-get (caar b
) 'recip
)) (cadr b
))
462 (member (caar b
) '(%sin %cos %sinh %cosh
) :test
#'eq
))
463 (funcall (cdr (assoc (caar b
) *sc^ndisp
* :test
#'eq
)) e
(cadr b
)))
468 (let* ((*schatc-ans
* (m2 e
'((mplus) ((coeffpp) (fr freevar
)) ((coeffpp) (exp true
)))))
469 (fr (cdr (assoc 'fr
*schatc-ans
* :test
#'eq
)))
470 (exp (cdr (assoc 'exp
*schatc-ans
* :test
#'eq
))))
473 ((m* (m^
'$%e fr
) (m^
'$%e exp
))))))
475 ;; Split TERMS into (VALUES NON-NEG OTHER) where NON-NEG and OTHER are a
476 ;; partition of the elements of TERMS. Expressions that are known not to be
477 ;; negative are placed in NON-NEG and all others end up in OTHER.
479 ;; This function is used to safely split products when expanding logarithms to
480 ;; avoid accidentally ending up with something like
482 ;; log(1 - x) => log(-1) + log(x-1).
484 ;; Note that we don't check a term is strictly positive: if it was actually
485 ;; zero, the logarithm was bogus in the first place.
486 (defun non-negative-split (terms)
487 (let ((non-neg) (other))
489 (if (memq ($sign term
) '($pos $pz $zero
))
492 (values non-neg other
)))
494 ;; Try to expand a logarithm for use in a power series in VAR by splitting up
496 (defun sp1log (e &optional no-recurse
)
498 ;; If E is free of VAR, is an atom, or we're supposed to be reducing rather
499 ;; than expanding, then just return E.
500 ((or *trigred
(atom e
) (free e var
))
503 ;; The logarithm of a sum doesn't simplify very nicely, but call $factor to
504 ;; see if we can pull out one or more terms and then recurse (setting
505 ;; NO-RECURSE to make sure we don't end up in a loop)
506 ((eq (caar e
) 'mplus
)
507 (let* ((exp (m1- e
)) *a
*n
)
508 (declare (special *n
*a
))
513 (sp1log ($factor e
) t
))
516 ;; A product is much more promising. Do the transformation log(ab) =>
517 ;; log(a)+log(b) and pass it to SP1 for further simplification.
519 ;; We need to be a little careful here because eg. factor(1-x) gives
520 ;; -(x-1). We don't want to end up with a log(-1) term! So check the sign of
521 ;; terms and only pull out the terms we know to be non-negative. If the
522 ;; argument was a negative real in the first place then we'd already got
523 ;; rubbish, but otherwise we won't pull out anything we don't want.
524 ((eq (caar e
) 'mtimes
)
525 (multiple-value-bind (non-neg other
) (non-negative-split (cdr e
))
527 ((null non-neg
) (sp1log2 e
))
529 (sp1 (m+l
(mapcar #'sp1log
(append other non-neg
))))))))
531 ;; Similarly, transform log(a^b) => b log(a) and pass back to SP1.
532 ((eq (caar e
) 'mexpt
)
533 (sp1 (m* (caddr e
) (list '(%log
) (cadr e
)))))
535 ;; If we can't find any other expansions, pass the result to SP1LOG2, which
536 ;; tries again after expressing E as integrate(diff(e)/e).
539 ;; We didn't manage to expand the expression, so make use of the fact that
540 ;; diff(log(f(x)), x) = f'(x)/f(x) and return integrate(f'(x)/f(x), x), hoping
541 ;; that a later stage will be able to do something useful with it.
543 ;; We have to be a little bit careful because an indefinite integral might have
544 ;; the wrong constant term. Instead, rewrite as
546 ;; log(f(x0+h)) = log(f(x0+h)) - log(f(x0)) + log(f(x0))
547 ;; = integrate(diff(log(f(x0+k)), k), k, 0, h) + log(f(x0))
548 ;; = integrate(diff(f(x0+k))/f(x0+k), k, 0, h) + log(f(x0))
550 ;; The "x0" about which we expand is always zero (see the code in $powerseries)
553 (mtell (intl:gettext
"trigreduce: failed to expand.~%~%"))
554 (show-exp (list '(%log
) e
))
555 (mtell (intl:gettext
"trigreduce: try again after applying rule:~2%~M~%~%")
561 ((mquotient) ((%derivative
) ,e
,var
1) ,e
) ,var
))))))
562 (let* ((dummy-sym ($gensym
)))
563 (m+ (list '(%log
) ($limit e var
0))
565 (maxima-substitute dummy-sym var
566 (sp1 (m// (sdiff e var
) e
)))
570 (cond ((atom (cadr e
)) (simplify e
))
571 ((eq (caaadr e
) (zl-get (caar e
) '$inverse
)) (sp1 (cadadr e
)))
572 ((eq (caaadr e
) (zl-get (zl-get (caar e
) 'recip
) '$inverse
))
573 (sp1 (m// (cadadr e
))))
574 ((and (null *trigred
) (null *noexpand
) (eq (caaadr e
) 'mplus
))
578 ;; Return the expansion of ((trigfun) ((mplus) a b)). For example sin(a+b) =
579 ;; sin(a)cos(b) + cos(a)sin(b).
580 (defun expand-trig-of-sum (trigfun a b
)
581 (flet ((expand-it (op f1 f2 f3 f4
)
583 (m* (sp1trig (list f1 a
)) (sp1trig (list f2 b
)))
584 (m* (sp1trig (list f3 a
)) (sp1trig (list f4 b
))))))
586 (%sin
(expand-it #'add2
* '(%sin
) '(%cos
) '(%cos
) '(%sin
)))
587 (%cos
(expand-it #'sub
* '(%cos
) '(%cos
) '(%sin
) '(%sin
)))
588 (%sinh
(expand-it #'add2
* '(%sinh
) '(%cosh
) '(%cosh
) '(%sinh
)))
589 (%cosh
(expand-it #'sub
* '(%cosh
) '(%cosh
) '(%sinh
) '(%sinh
))))))
591 ;; Try to expand f(a+b) where f is sin, cos, sinh or cosh.
594 ;; Ideally, we'd like to split the argument of the trig function into terms
595 ;; that involve VAR and those that are free of it.
596 ((m2 (cadr e
) '((mplus) ((coeffpp) (a freevar
)) ((coeffpp) (b true
))))
599 ;; Make sure that if B is zero then so is A (to simplify the cond)
600 (when (signp e b
) (rotatef a b
))
602 ;; Assuming we didn't just swap them, A will be free of VAR and B will
603 ;; contain any other terms. If A is zero (because the argument of trig
604 ;; function is a sum of terms, all of which involve VAR), then fall back on
605 ;; a different splitting, by terms of taking the first term of B.
609 (eq (caar b
) 'mplus
))
610 (expand-trig-of-sum (caar e
)
613 (cons (car b
) (cddr b
))
616 ;; For some weird reason, B isn't a sum. Give up.
619 ;; Do the splitting we intended in the first place.
621 (expand-trig-of-sum (caar e
) a b
))))
623 ;; E doesn't match f(a+b). Return it unmodified.
626 (defun sp1atrig (fn exp
)
629 ((eq fn
(zl-get (caar exp
) '$inverse
))
631 (t (sp1atrig2 fn exp
))))
633 (defun sp1atrig2 (fn exp
)
634 (cond ((member fn
'(%cot %sec %csc %coth %sech %csch
) :test
#'eq
)
635 (setq exp
(sp1 (m// exp
))
636 fn
(cdr (assoc fn
'((%acot . %atan
) (%asec . %acos
) (%acsc . %asin
)
637 (%acoth . %atanh
) (%asech . %acosh
) (%acsch . %asinh
)) :test
#'eq
)))))
638 (cond ((and (null *trigred
)
639 (member fn
'(%acos %acosh
) :test
#'eq
))
641 (list (cdr (assoc fn
'((%acos . %asin
) (%acosh . %asinh
)) :test
#'eq
)))
643 ((list (list fn
) exp
))))
646 (sc^n %n v
(if (oddp %n
) '(%sin
) '(%cos
)) (not (oddp %n
))
647 (m^ -
1 (m+ (ash %n -
1) 'k
))))
651 (sc^n %n v
'(%sinh
) nil
(m^ -
1 'k
))
652 (if (zerop (mod %n
4))
653 (sc^n %n v
'(%cosh
) t
(m^ -
1 'k
))
654 (m- (sc^n %n v
'(%cosh
) t
(m- (m^ -
1 'k
)))))))
657 (sc^n %n v
'(%cos
) (not (oddp %n
)) 1))
660 (sc^n %n v
'(%cosh
) (not (oddp %n
)) 1))
662 (defun sc^n
(%n v fn fl coef
)
664 (merror "trigreduce: internal error; %N must be nonnegative, found: ~M") %n
)
665 (m* (list '(rat) 1 (expt 2 %n
))
667 (list '(%binomial
) %n
(ash %n -
1))
669 (maxima-substitute v
'trig-var
671 (list '(%binomial
) %n
'k
)
673 (list fn
(m* 'trig-var
674 (m+ %n
(m* -
2 'k
))))))
675 'k
0 (ash (1- %n
) -
1) t
)))))