Add translation for plog (using TRANSLATE-WITH-FLONUM-OP)
[maxima.git] / src / csimp.lisp
blob37457bc033951fe84aeb4abce9bfba38c5b21567
1 ;;; -*- Mode: Lisp; Package: Maxima; Syntax: Common-Lisp; Base: 10 -*- ;;;;
2 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3 ;;; The data in this file contains enhancements. ;;;;;
4 ;;; ;;;;;
5 ;;; Copyright (c) 1984,1987 by William Schelter,University of Texas ;;;;;
6 ;;; All rights reserved ;;;;;
7 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
8 ;;; (c) Copyright 1980 Massachusetts Institute of Technology ;;;
9 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
11 (in-package :maxima)
13 (macsyma-module csimp)
15 (declare-top (special $exponentialize
16 var genvar $ratprint
17 errorsw))
19 (load-macsyma-macros rzmac)
21 (loop for (a b) on
22 '(%sin %asin %cos %acos %tan %atan
23 %cot %acot %sec %asec %csc %acsc
24 %sinh %asinh %cosh %acosh %tanh %atanh
25 %coth %acoth %sech %asech %csch %acsch)
26 by #'cddr
27 do (putprop a b '$inverse) (putprop b a '$inverse))
29 (defmfun $demoivre (exp)
30 (let ($exponentialize nexp)
31 (cond ((atom exp) exp)
32 ((and (eq (caar exp) 'mexpt) (eq (cadr exp) '$%e)
33 (setq nexp (demoivre (caddr exp))))
34 nexp)
35 (t (recur-apply #'$demoivre exp)))))
37 (defun demoivre (l)
38 (cond ($exponentialize
39 (merror (intl:gettext "demoivre: 'demoivre' and 'exponentialize' cannot both be true.")))
40 (t (setq l (islinear l '$%i))
41 (and l (not (equal (car l) 0))
42 (m* (m^ '$%e (cdr l))
43 (m+ (list '(%cos) (car l))
44 (m* '$%i (list '(%sin) (car l)))))))))
46 ;; If expr is of the form a*var1+b where a is freeof var1
47 ;; then (a . b) is returned else nil.
48 (defun islinear (expr var1)
49 (let ((a (let ((*islinp* t))
50 (sdiff expr var1))))
51 (if (freeof var1 a)
52 (cons a (maxima-substitute 0 var1 expr)))))
54 (defmfun $partition (e var1)
55 (let ((e (mratcheck e))
56 (var1 (getopr var1)))
57 (cond
58 (($listp e)
59 (do ((l (cdr e) (cdr l)) (l1) (l2) (x))
60 ((null l) (list '(mlist simp)
61 (cons '(mlist simp) (nreverse l1))
62 (cons '(mlist simp) (nreverse l2))))
63 (setq x (mratcheck (car l)))
64 (cond ((free x var1) (setq l1 (cons x l1)))
65 (t (setq l2 (cons x l2))))))
67 ((mplusp e)
68 (destructuring-bind (constant . variable) (partition e var1 0)
69 (list '(mlist simp) constant variable)))
71 ((mtimesp e)
72 (destructuring-bind (constant . variable) (partition e var1 1)
73 (list '(mlist simp) constant variable)))
76 (merror
77 (intl:gettext
78 "partition: first argument must be a list or '+' or '*' expression; found ~M") e)))))
80 ;; Apply PREDICATE to each element in the sequence SEQ and return
82 ;; (VALUES YES NO),
84 ;; where YES and NO are lists consisting of elements for which PREDICATE is true
85 ;; or false, respectively.
86 (defun partition-by (predicate seq)
87 (let ((yes) (no))
88 (map nil
89 (lambda (x)
90 (if (funcall predicate x)
91 (push x yes)
92 (push x no)))
93 seq)
94 (values yes no)))
96 ;; Partition an expression, EXP, into terms that contain VAR1 and terms that
97 ;; don't contain VAR1. EXP is either considered as a product or a sum (for which
98 ;; you should pass K = 1 or 0, respectively).
100 (defun partition (exp var1 k)
101 (let ((op (if (= k 0) 'mplus 'mtimes))
102 (exp-op (unless (atom exp) (caar exp))))
103 (cond
104 ;; Exit immediately if exp = var1
105 ((alike1 exp var1) (cons k exp))
107 ;; If we have the wrong operation then the expression is either entirely
108 ;; constant or entirely variable.
109 ((not (eq exp-op op))
110 (if (freeof var1 exp)
111 (cons exp k)
112 (cons k exp)))
114 ;; Otherwise, we want to partition the arguments into constant and
115 ;; variable.
117 (multiple-value-bind (constant variable)
118 (partition-by (lambda (x) (freeof var1 x)) (cdr exp))
119 (cons (cond ((null constant) k)
120 ((null (cdr constant)) (car constant))
121 (t (simplifya
122 (cons (list op) (nreverse constant)) t)))
123 (cond ((null variable) k)
124 ((null (cdr variable)) (car variable))
125 (t (simplifya
126 (cons (list op) (nreverse variable)) t)))))))))
128 ;;To use this INTEGERINFO and *ASK* need to be special.
129 ;;(defun integerpw (x)
130 ;; ((lambda (*ask*)
131 ;; (integerp10 (ssimplifya (sublis '((z** . 0) (*z* . 0)) x))))
132 ;; t))
134 ;;(defun integerp10 (x)
135 ;; ((lambda (d)
136 ;; (cond ((or (null x) (not (free x '$%i))) nil)
137 ;; ((mnump x) (integerp x))
138 ;; ((setq d (assolike x integerinfo)) (eq d 'yes))
139 ;; (*ask* (setq d (cond ((integerp x) 'yes) (t (needinfo x))))
140 ;; (setq integerinfo (cons (list x d) integerinfo))
141 ;; (eq d 'yes))))
142 ;; nil))
144 (setq var (make-symbol "foo"))
146 (defun numden (e)
147 (prog (varlist)
148 (setq varlist (list var))
149 (newvar (setq e (fmt e)))
150 (setq e (cdr (ratrep* e)))
151 (setq dn*
152 (simplifya (pdis (ratdenominator e))
153 nil))
154 (setq nn*
155 (simplifya (pdis (ratnumerator e))
156 nil)))
157 (values nn* dn*))
159 ;; Like NUMDEN but dependency on VAR is explicit. Use this instead of
160 ;; NUMDEN if possible. Also, we do not set the special variables DN*
161 ;; and NN*. The callers are responsible for doing that, now.
162 (defun numden-var (e var1)
163 (let ((varlist (list var1))
164 dn nn)
165 (newvar (setq e (fmt e)))
166 (setq e (cdr (ratrep* e)))
167 (setq dn
168 (simplifya (pdis (ratdenominator e))
169 nil))
170 (setq nn
171 (simplifya (pdis (ratnumerator e))
172 nil))
173 (values nn dn)))
175 (defun fmt (exp)
176 (let (nn*)
177 (cond ((atom exp) exp)
178 ((mnump exp) exp)
179 ((eq (caar exp) 'mexpt)
180 (cond ((and (mnump (caddr exp))
181 (eq ($sign (caddr exp)) '$neg))
182 (list '(mquotient)
184 (cond ((equal (caddr exp) -1)
185 (fmt (cadr exp)))
186 (t (list (list (caar exp))
187 (fmt (cadr exp))
188 (timesk -1 (caddr exp)))))))
189 ((atom (caddr exp))
190 (list (list (caar exp))
191 (fmt (cadr exp))
192 (caddr exp)))
193 ((and (mtimesp (setq nn* (sratsimp (caddr exp))))
194 (mnump (cadr nn*))
195 (equal ($sign (cadr nn*)) '$neg))
196 (list '(mquotient)
198 (list (list (caar exp))
199 (fmt (cadr exp))
200 (cond ((equal (cadr nn*) -1)
201 (cons '(mtimes)
202 (cddr nn*)))
203 (t (neg nn*))))))
204 ((eq (caar nn*) 'mplus)
205 (fmt (spexp (cdr nn*) (cadr exp))))
206 (t (cons (ncons (caar exp))
207 (mapcar #'fmt (cdr exp))))))
208 (t (cons (delsimp (car exp)) (mapcar #'fmt (cdr exp)))))))
210 (defun spexp (expl dn*)
211 (cons '(mtimes) (mapcar #'(lambda (e) (list '(mexpt) dn* e)) expl)))
213 (defun subin (y x)
214 (cond ((not (among var x)) x)
215 (t (maxima-substitute y var x))))
217 ;; Like SUBIN but dependency on VAR is explicit. Use this instead
218 ;; when possible.
219 (defun subin-var (y x ivar)
220 (cond ((not (among ivar x)) x)
221 (t (maxima-substitute y ivar x))))
223 ;; Right-hand side (rhs) and left-hand side (lhs) of binary infix expressions.
224 ;; These are unambiguous for relational operators, some other built-in infix operators,
225 ;; and user-defined infix operators (declared by the infix function).
227 ;; a - b and a / b are somewhat problematic, since subtraction and division are not
228 ;; ordinarily represented as such (rather a - b = a + (-1)*b and a / b = a * b^(-1)).
229 ;; Also, - can be unary. So let's not worry about - and / .
231 ;; Other problematic cases: The symbols $< $<= $= $# $>= $> have a LED property,
232 ;; but these symbols never appear in expressions returned by the Maxima parser;
233 ;; MLESSP, MLEQP, MEQUAL etc are substituted. So ignore those symbols here.
234 (let
235 ((relational-ops
236 ;; < <= = # equal notequal >= >
237 '(mlessp mleqp mequal mnotequal $equal $notequal mgeqp mgreaterp
238 %mlessp %mleqp %mequal %mnotequal %equal %notequal %mgeqp %mgreaterp))
240 (other-infix-ops
241 ;; := ::= : :: ->
242 '(mdefine mdefmacro msetq mset marrow
243 %mdefine %mdefmacro %msetq %mset %marrow)))
245 (defmfun $rhs (rel)
246 (if (atom rel)
248 (if (or (member (caar rel) (append relational-ops other-infix-ops) :test #'eq)
249 ;; This test catches user-defined infix operators.
250 (eq (get (caar rel) 'led) 'parse-infix))
251 (caddr rel)
252 0)))
254 (defmfun $lhs (rel)
255 (if (atom rel)
257 (if (or (member (caar rel) (append relational-ops other-infix-ops) :test #'eq)
258 ;; This test catches user-defined infix operators.
259 (eq (get (caar rel) 'led) 'parse-infix))
260 (cadr rel)
261 rel))))
263 (defun ratgreaterp (x y)
264 (cond ((and (ratnump x) (ratnump y))
265 (great x y))
266 ((equal ($asksign (m- x y)) '$pos))))
268 ;; Simplify the exponential function of the type exp(p/q*%i*%pi+x) using the
269 ;; periodicity of the exponential function and special values for rational
270 ;; numbers with a denominator q = 2, 3, 4, or 6. e is the argument of the
271 ;; exponential function. For float and bigfloat numbers in the argument e only
272 ;; simplify for an integer representation or a half integral value.
273 ;; The result is an exponential function with a simplified argument.
274 (defun %especial (e)
275 (prog (varlist y k kk j ans $%emode $ratprint genvar)
276 (let (($keepfloat nil) ($float nil))
277 (unless (setq y (pip ($ratcoef e '$%i))) (return nil))
278 ;; Subtract the term y*%i*%pi from the expression e.
279 (setq k ($expand (add e (mul -1 '$%pi '$%i y)) 1))
280 ;; This is a workaround to get the type (integer, float, or bigfloat)
281 ;; of the expression. kk must evaluate to 1, 1.0, or 1.0b0.
282 ;; Furthermore, if e is nonlinear, kk does not simplify to a number ONE.
283 ;; Because of this we do not simplify something like exp((2+x)^2*%i*%pi)
284 (setq kk (div (sub ($expand e) k) (mul '$%i '$%pi y)))
285 ;; Return if kk is not an integer or kk is ONE, but y not an integer
286 ;; or a half integral value.
287 (if (not (or (integerp kk)
288 (and (onep1 kk)
289 (integerp (add y y)))))
290 (return nil))
291 (setq j (trigred y))
292 (setq ans (spang1 j t)))
293 (cond ((among '%sin ans)
294 (cond ((equal y j) (return nil))
295 ((zerop1 k)
296 ;; To preverse the type we add k into the result.
297 (return (power '$%e (mul '$%pi '$%i (add k j)))))
299 ;; To preserve the type we multiply kk into the result.
300 (return
301 (power '$%e (add (mul kk k) (mul kk '$%pi '$%i j))))))))
302 (setq y (spang1 j nil))
303 ;; To preserve the type we multiply kk into the result.
304 (return (mul (power '$%e (mul kk k)) (add y (mul '$%i ans))))))
306 (defun trigred (r)
307 (prog (m n eo flag)
308 (cond ((numberp r) (return (cond ((even r) 0) (t 1)))))
309 (setq m (cadr r))
310 (cond ((minusp m) (setq m (- m)) (setq flag t)))
311 (setq n (caddr r))
312 loop (cond ((> m n)
313 (setq m (- m n))
314 (setq eo (not eo))
315 (go loop)))
316 (setq m (list '(rat)
317 (cond (flag (- m)) (t m))
319 (return (cond (eo (addk m (cond (flag 1) (t -1))))
320 (t m)))))
322 (defun polyinx (exp x ind)
323 (prog (genvar varlist var $ratfac)
324 (setq var x)
325 (cond ((numberp exp)(return t))
326 ((polyp exp)
327 (cond (ind (go on))
328 (t (return t))))
329 (t (return nil)))
330 on (setq genvar nil)
331 (setq varlist (list x))
332 (newvar exp)
333 (setq exp (cdr (ratrep* exp)))
334 (cond
335 ((or (numberp (cdr exp))
336 (not (eq (car (last genvar)) (cadr exp))))
337 (setq x (pdis (cdr exp)))
338 (return (cond ((eq ind 'leadcoef)
339 (div* (pdis (caddr (car exp))) x))
340 (t (setq exp (car exp))
341 (div* (cond ((atom exp) exp)
343 (pdis (list (car exp)
344 (cadr exp)
345 (caddr exp)))))
347 ))))))
349 (defun polyp (a)
350 (cond ((atom a) t)
351 ((member (caar a) '(mplus mtimes) :test #'eq)
352 (every #'polyp (cdr a)))
353 ((eq (caar a) 'mexpt)
354 (cond ((free (cadr a) var)
355 (free (caddr a) var))
356 (t (and (integerp (caddr a))
357 (> (caddr a) 0)
358 (polyp (cadr a))))))
359 (t (andmapcar #'(lambda (subexp)
360 (free subexp var))
361 (cdr a)))))
363 ;; Like polyp but takes an extra arg for the variable.
364 (defun polyp-var (a var2)
365 (cond ((atom a) t)
366 ((member (caar a) '(mplus mtimes) :test #'eq)
367 (every #'(lambda (p)
368 (polyp-var p var2))
369 (cdr a)))
370 ((eq (caar a) 'mexpt)
371 (cond ((free (cadr a) var2)
372 (free (caddr a) var2))
373 (t (and (integerp (caddr a))
374 (> (caddr a) 0)
375 (polyp-var (cadr a) var2)))))
376 (t (andmapcar #'(lambda (subexp)
377 (free subexp var2))
378 (cdr a)))))
380 (defun pip (e)
381 (prog (varlist d c)
382 (newvar e)
383 (cond ((not (member '$%pi varlist :test #'eq)) (return nil)))
384 (setq varlist '($%pi))
385 (newvar e)
386 (let (($ratfac nil))
387 ;; non-nil $ratfac changes form of CRE
388 (setq e (cdr (ratrep* e))))
389 (setq d (cdr e))
390 (cond ((not (atom d)) (return nil))
391 ((equal e '(0 . 1))
392 (setq c 0)
393 (go loop)))
394 (setq c (ptterm (cdar e) 1))
395 loop (cond ((atom c)
396 (cond ((equal c 0) (return nil))
397 ((equal 1 d) (return c))
398 (t (return (list '(rat) c d))))))
399 (setq c (ptterm (cdr c) 0))
400 (go loop)))
402 (defun spang1 (j ind)
403 (prog (ang ep $exponentialize $float $keepfloat)
404 (cond ((floatp j) (setq j (maxima-rationalize j))
405 (setq j (list '(rat simp) (car j) (cdr j)))))
406 (setq ang j)
407 (cond
408 (ind nil)
409 ((numberp j)
410 (cond ((zerop j) (return 1)) (t (return -1))))
411 (t (setq j
412 (trigred (add2* '((rat simp) 1 2)
413 (list (car j)
414 (- (cadr j))
415 (caddr j)))))))
416 (cond ((numberp j) (return 0))
417 ((mnump j) (setq j (cdr j))))
418 (return
419 (cond ((equal j '(1 2)) 1)
420 ((equal j '(-1 2)) -1)
421 ((or (equal j '(1 3))
422 (equal j '(2 3)))
423 (div ($sqrt 3) 2))
424 ((or (equal j '(-1 3))
425 (equal j '(-2 3)))
426 (div ($sqrt 3) -2))
427 ((or (equal j '(1 6))
428 (equal j '(5 6)))
429 '((rat simp) 1 2))
430 ((or (equal j '(-1 6))
431 (equal j '(-5 6)))
432 '((rat simp) -1 2))
433 ((or (equal j '(1 4))
434 (equal j '(3 4)))
435 (div 1 ($sqrt 2)))
436 ((or (equal j '(-1 4))
437 (equal j '(-3 4)))
438 (div -1 ($sqrt 2)))
439 (t (cond ((mnegp ang)
440 (setq ang (timesk -1 ang) ep t)))
441 (setq ang (list '(mtimes simp)
443 '$%pi))
444 (cond (ind (cond (ep (list '(mtimes simp)
446 (list '(%sin simp) ang)))
447 (t (list '(%sin simp) ang))))
448 (t (list '(%cos simp) ang))))))))
450 (defun archk (a b v)
451 (simplify
452 (cond ((and (equal a 1) (equal b 1)) v)
453 ((and (equal b -1) (equal 1 a))
454 (list '(mtimes) -1 v))
455 ((equal 1 b)
456 (list '(mplus) '$%pi (list '(mtimes) -1 v)))
457 (t (list '(mplus) v (list '(mtimes) -1 '$%pi))))))
459 (defun genfind (h v)
460 ;;; finds gensym coresponding to v h
461 (do ((varl (caddr h) (cdr varl))
462 (genl (cadddr h) (cdr genl)))
463 ;;;is car of rat form
464 ((eq (car varl) v) (car genl))))