UNPKG

13.1 kBtext/coffeescriptView Raw
1### Power function
2
3 Input: push Base
4
5 push Exponent
6
7 Output: Result on stack
8###
9
10DEBUG_POWER = false
11
12Eval_power = ->
13 if DEBUG_POWER then debugger
14 push(cadr(p1))
15 Eval()
16 push(caddr(p1))
17 Eval()
18 power()
19
20power = ->
21 save()
22 yypower()
23 restore()
24
25yypower = ->
26 if DEBUG_POWER then debugger
27 n = 0
28
29 p2 = pop() # exponent
30 p1 = pop() # base
31
32 inputExp = p2
33 inputBase = p1
34 #debugger
35
36 if DEBUG_POWER
37 console.log("POWER: " + p1 + " ^ " + p2)
38
39 # first, some very basic simplifications right away
40
41 # 1 ^ a -> 1
42 # a ^ 0 -> 1
43 if (equal(p1, one) || iszero(p2))
44 if evaluatingAsFloats then push_double(1.0) else push(one)
45 if DEBUG_POWER then console.log " power of " + inputBase + " ^ " + inputExp + ": " + stack[tos-1]
46 return
47
48 # a ^ 1 -> a
49 if (equal(p2, one))
50 push(p1)
51 if DEBUG_POWER then console.log " power of " + inputBase + " ^ " + inputExp + ": " + stack[tos-1]
52 return
53
54 # -1 ^ -1 -> -1
55 if (isminusone(p1) and isminusone(p2))
56 if evaluatingAsFloats then push_double(1.0) else push(one)
57 negate()
58 if DEBUG_POWER then console.log " power of " + inputBase + " ^ " + inputExp + ": " + stack[tos-1]
59 return
60
61 # -1 ^ 1/2 -> i
62 if (isminusone(p1) and (isoneovertwo(p2)))
63 push(imaginaryunit)
64 if DEBUG_POWER then console.log " power of " + inputBase + " ^ " + inputExp + ": " + stack[tos-1]
65 return
66
67 # -1 ^ -1/2 -> -i
68 if (isminusone(p1) and isminusoneovertwo(p2))
69 push(imaginaryunit)
70 negate()
71 if DEBUG_POWER then console.log " power of " + inputBase + " ^ " + inputExp + ": " + stack[tos-1]
72 return
73
74 # -1 ^ rational
75 if (isminusone(p1) and !isdouble(p1) and isrational(p2) and !isinteger(p2) and ispositivenumber(p2) and !evaluatingAsFloats)
76 if DEBUG_POWER then console.log(" power: -1 ^ rational")
77 if DEBUG_POWER then console.log " trick: p2.q.a , p2.q.b " + p2.q.a + " , " + p2.q.b
78 if p2.q.a < p2.q.b
79 push_symbol(POWER)
80 push(p1)
81 push(p2)
82 list(3)
83 else
84 push_symbol(MULTIPLY)
85
86 push(p1)
87
88 push_symbol(POWER)
89 push(p1)
90 push_rational(p2.q.a.mod(p2.q.b), p2.q.b)
91 list(3)
92
93 list(3)
94 if DEBUG_POWER then console.log " trick applied : " + stack[tos-1]
95
96 # evaluates clock form into
97 # rectangular form. This seems to give
98 # slightly better form to some test results.
99 rect()
100 if DEBUG_POWER then console.log " power of " + inputBase + " ^ " + inputExp + ": " + stack[tos-1]
101 return
102
103
104 # both base and exponent are rational numbers?
105
106 if (isrational(p1) && isrational(p2))
107 if DEBUG_POWER then console.log(" power: isrational(p1) && isrational(p2)")
108 push(p1)
109 push(p2)
110 qpow()
111 if DEBUG_POWER then console.log " power of " + inputBase + " ^ " + inputExp + ": " + stack[tos-1]
112 return
113
114 # both base and exponent are either rational or double?
115 if (isnum(p1) && isnum(p2))
116 if DEBUG_POWER then console.log " power: both base and exponent are either rational or double "
117 if DEBUG_POWER then console.log("POWER - isnum(p1) && isnum(p2)")
118 push(p1)
119 push(p2)
120 dpow()
121 if DEBUG_POWER then console.log " power of " + inputBase + " ^ " + inputExp + ": " + stack[tos-1]
122 return
123
124 if (istensor(p1))
125 if DEBUG_POWER then console.log " power: istensor(p1) "
126 power_tensor()
127 if DEBUG_POWER then console.log " power of " + inputBase + " ^ " + inputExp + ": " + stack[tos-1]
128 return
129
130 # if we only assume variables to be real, then |a|^2 = a^2
131 # (if x is complex this doesn't hold e.g. i, which makes 1 and -1
132 if (car(p1) == symbol(ABS) && iseveninteger(p2) and !iszero(get_binding(symbol(ASSUME_REAL_VARIABLES))))
133 if DEBUG_POWER then console.log " power: even power of absolute of real value "
134 push(cadr(p1))
135 push(p2)
136 power()
137 if DEBUG_POWER then console.log " power of " + inputBase + " ^ " + inputExp + ": " + stack[tos-1]
138 return
139
140 # e^log(...)
141 if (p1 == symbol(E) && car(p2) == symbol(LOG))
142 push(cadr(p2))
143 if DEBUG_POWER then console.log " power of " + inputBase + " ^ " + inputExp + ": " + stack[tos-1]
144 return
145
146 # e^some_float
147 if (p1 == symbol(E) && isdouble(p2))
148 if DEBUG_POWER then console.log " power: p1 == symbol(E) && isdouble(p2) "
149 push_double(Math.exp(p2.d))
150 if DEBUG_POWER then console.log " power of " + inputBase + " ^ " + inputExp + ": " + stack[tos-1]
151 return
152
153 # complex number in exponential form, get it to rectangular
154 # but only if we are not in the process of calculating a polar form,
155 # otherwise we'd just undo the work we want to do
156 if (p1 == symbol(E) && Find(p2, imaginaryunit) != 0 and Find(p2, symbol(PI))!= 0 and !evaluatingPolar)
157 push_symbol(POWER)
158 push(p1)
159 push(p2)
160 list(3)
161 if DEBUG_POWER then console.log " power: turning complex exponential to rect: " + stack[tos-1]
162 rect()
163
164 hopefullySimplified = pop(); # put new (hopefully simplified expr) in p2
165 if Find(hopefullySimplified, symbol(PI)) == 0
166 if DEBUG_POWER then console.log " power: turned complex exponential to rect: " + hopefullySimplified
167 push hopefullySimplified
168 return
169
170
171 # (a * b) ^ c -> (a ^ c) * (b ^ c)
172 # note that we can't in general do this, for example
173 # sqrt(x*y) != x^(1/2) y^(1/2) (counterexample" x = -1 and y = -1)
174 # BUT we can carve-out here some cases where this
175 # transformation is correct
176
177 if (car(p1) == symbol(MULTIPLY) && isinteger(p2))
178 if DEBUG_POWER then console.log " power: (a * b) ^ c -> (a ^ c) * (b ^ c) "
179 p1 = cdr(p1)
180 push(car(p1))
181 push(p2)
182 power()
183 p1 = cdr(p1)
184 while (iscons(p1))
185 push(car(p1))
186 push(p2)
187 power()
188 multiply()
189 p1 = cdr(p1)
190 if DEBUG_POWER then console.log " power of " + inputBase + " ^ " + inputExp + ": " + stack[tos-1]
191 return
192
193 # (a ^ b) ^ c -> a ^ (b * c)
194 # note that we can't in general do this, for example
195 # sqrt(x^y) != x^(1/2 y) (counterexample x = -1)
196 # BUT we can carve-out here some cases where this
197 # transformation is correct
198
199
200 # simple numeric check to see if a is a number > 0
201 is_a_moreThanZero = false
202 if isnum(cadr(p1))
203 is_a_moreThanZero = sign(compare_numbers(cadr(p1), zero))
204
205 if (car(p1) == symbol(POWER) && (
206 isinteger(p2) or # when c is an integer
207 is_a_moreThanZero # when a is >= 0
208 ))
209 push(cadr(p1))
210 push(caddr(p1))
211 push(p2)
212 multiply()
213 power()
214 if DEBUG_POWER then console.log " power of " + inputBase + " ^ " + inputExp + ": " + stack[tos-1]
215 return
216
217 b_isEven_and_c_isItsInverse = false
218 if iseveninteger(caddr(p1))
219 push caddr(p1)
220 push p2
221 multiply()
222
223 isThisOne = pop()
224 if isone(isThisOne)
225 b_isEven_and_c_isItsInverse = true
226
227 if (car(p1) == symbol(POWER) && b_isEven_and_c_isItsInverse)
228 if DEBUG_POWER then console.log " power: car(p1) == symbol(POWER) && b_isEven_and_c_isItsInverse "
229 push(cadr(p1))
230 abs()
231 if DEBUG_POWER then console.log " power of " + inputBase + " ^ " + inputExp + ": " + stack[tos-1]
232 return
233
234
235 # when expanding,
236 # (a + b) ^ n -> (a + b) * (a + b) ...
237
238 if (expanding && isadd(p1) && isnum(p2))
239 push(p2)
240 n = pop_integer()
241 if (n > 1 && !isNaN(n))
242 if DEBUG_POWER then console.log " power: expanding && isadd(p1) && isnum(p2) "
243 power_sum(n)
244 if DEBUG_POWER then console.log " power of " + inputBase + " ^ " + inputExp + ": " + stack[tos-1]
245 return
246
247 # sin(x) ^ 2n -> (1 - cos(x) ^ 2) ^ n
248
249 if (trigmode == 1 && car(p1) == symbol(SIN) && iseveninteger(p2))
250 if DEBUG_POWER then console.log " power: trigmode == 1 && car(p1) == symbol(SIN) && iseveninteger(p2) "
251 push_integer(1)
252 push(cadr(p1))
253 cosine()
254 push_integer(2)
255 power()
256 subtract()
257 push(p2)
258 push_rational(1, 2)
259 multiply()
260 power()
261 if DEBUG_POWER then console.log " power of " + inputBase + " ^ " + inputExp + ": " + stack[tos-1]
262 return
263
264 # cos(x) ^ 2n -> (1 - sin(x) ^ 2) ^ n
265
266 if (trigmode == 2 && car(p1) == symbol(COS) && iseveninteger(p2))
267 if DEBUG_POWER then console.log " power: trigmode == 2 && car(p1) == symbol(COS) && iseveninteger(p2) "
268 push_integer(1)
269 push(cadr(p1))
270 sine()
271 push_integer(2)
272 power()
273 subtract()
274 push(p2)
275 push_rational(1, 2)
276 multiply()
277 power()
278 if DEBUG_POWER then console.log " power of " + inputBase + " ^ " + inputExp + ": " + stack[tos-1]
279 return
280
281
282 # complex number? (just number, not expression)
283
284
285 if (iscomplexnumber(p1))
286
287 if DEBUG_POWER then console.log " power - handling the case (a + ib) ^ n"
288 # integer power?
289
290 # n will be negative here, positive n already handled
291
292 if (isinteger(p2))
293
294 # / \ n
295 # -n | a - ib |
296 # (a + ib) = | -------- |
297 # | 2 2 |
298 # \ a + b /
299
300 push(p1)
301 conjugate()
302 p3 = pop()
303 push(p3)
304
305 # gets the denominator
306 push(p3)
307 push(p1)
308 multiply()
309
310 divide()
311
312 if !isone(p2)
313 push(p2)
314 negate()
315 power()
316
317 if DEBUG_POWER then console.log " power of " + inputBase + " ^ " + inputExp + ": " + stack[tos-1]
318 return
319
320 # noninteger or floating power?
321
322 if (isnum(p2))
323 push(p1)
324 abs()
325 push(p2)
326 power()
327 push_integer(-1)
328 push(p1)
329 arg()
330 push(p2)
331 multiply()
332 if evaluatingAsFloats or (iscomplexnumberdouble(p1) and isdouble(p2))
333 # remember that the "double" type is
334 # toxic, i.e. it propagates, so we do
335 # need to evaluate PI to its actual double
336 # value
337 push_double(Math.PI)
338 else
339 #console.log("power pushing PI when p1 is: " + p1 + " and p2 is:" + p2)
340 push(symbol(PI))
341 divide()
342 power()
343 multiply()
344
345 # if we calculate the power making use of arctan:
346 # * it prevents nested radicals from being simplified
347 # * results become really hard to manipulate afterwards
348 # * we can't go back to other forms.
349 # so leave the power as it is.
350 if avoidCalculatingPowersIntoArctans
351 if Find(stack[tos-1], symbol(ARCTAN))
352 pop()
353 push_symbol(POWER)
354 push(p1)
355 push(p2)
356 list(3)
357
358 if DEBUG_POWER then console.log " power of " + inputBase + " ^ " + inputExp + ": " + stack[tos-1]
359 return
360
361 #
362 #push(p1)
363 #abs()
364 #push(p2)
365 #power()
366 #push(symbol(E))
367 #push(p1)
368 #arg()
369 #push(p2)
370 #multiply()
371 #push(imaginaryunit)
372 #multiply()
373 #power()
374 #multiply()
375 #
376
377 if (simplify_polar())
378 if DEBUG_POWER then console.log " power: using simplify_polar"
379 return
380
381
382
383 if DEBUG_POWER then console.log " power: nothing can be done "
384 push_symbol(POWER)
385 push(p1)
386 push(p2)
387 list(3)
388 if DEBUG_POWER then console.log " power of " + inputBase + " ^ " + inputExp + ": " + stack[tos-1]
389
390#-----------------------------------------------------------------------------
391#
392# Compute the power of a sum
393#
394# Input: p1 sum
395#
396# n exponent
397#
398# Output: Result on stack
399#
400# Note:
401#
402# Uses the multinomial series (see Math World)
403#
404# n n! n1 n2 nk
405# (a1 + a2 + ... + ak) = sum (--------------- a1 a2 ... ak )
406# n1! n2! ... nk!
407#
408# The sum is over all n1 ... nk such that n1 + n2 + ... + nk = n.
409#
410#-----------------------------------------------------------------------------
411
412# first index is the term number 0..k-1, second index is the exponent 0..n
413
414#define A(i, j) frame[(i) * (n + 1) + (j)]
415
416power_sum = (n) ->
417 a = []
418 i = 0
419 j = 0
420 k = 0
421
422 # number of terms in the sum
423
424 k = length(p1) - 1
425
426 # local frame
427
428 push_frame(k * (n + 1))
429
430 # array of powers
431
432 p1 = cdr(p1)
433 for i in [0...k]
434 for j in [0..n]
435 push(car(p1))
436 push_integer(j)
437 power()
438 stack[frame + (i) * (n + 1) + (j)] = pop()
439 p1 = cdr(p1)
440
441 push_integer(n)
442 factorial()
443 p1 = pop()
444
445 for i in [0...k]
446 a[i] = 0
447
448 push(zero)
449
450 multinomial_sum(k, n, a, 0, n)
451
452 pop_frame(k * (n + 1))
453
454#-----------------------------------------------------------------------------
455#
456# Compute multinomial sum
457#
458# Input: k number of factors
459#
460# n overall exponent
461#
462# a partition array
463#
464# i partition array index
465#
466# m partition remainder
467#
468# p1 n!
469#
470# A factor array
471#
472# Output: Result on stack
473#
474# Note:
475#
476# Uses recursive descent to fill the partition array.
477#
478#-----------------------------------------------------------------------------
479
480#int k, int n, int *a, int i, int m
481multinomial_sum = (k,n,a,i,m) ->
482 j = 0
483
484 if (i < k - 1)
485 for j in [0..m]
486 a[i] = j
487 multinomial_sum(k, n, a, i + 1, m - j)
488 return
489
490 a[i] = m
491
492 # coefficient
493
494 push(p1)
495
496 for j in [0...k]
497 push_integer(a[j])
498 factorial()
499 divide()
500
501 # factors
502
503 for j in [0...k]
504 push(stack[frame + (j) * (n + 1) + a[j]])
505 multiply()
506
507 add()
508
509# exp(n/2 i pi) ?
510
511# p2 is the exponent expression
512
513# clobbers p3
514
515simplify_polar = ->
516 n = 0
517
518 n = isquarterturn(p2)
519
520 switch(n)
521 when 0
522 doNothing = 1
523 when 1
524 push_integer(1)
525 return 1
526 when 2
527 push_integer(-1)
528 return 1
529 when 3
530 push(imaginaryunit)
531 return 1
532 when 4
533 push(imaginaryunit)
534 negate()
535 return 1
536
537 if (car(p2) == symbol(ADD))
538 p3 = cdr(p2)
539 while (iscons(p3))
540 n = isquarterturn(car(p3))
541 if (n)
542 break
543 p3 = cdr(p3)
544 switch (n)
545 when 0
546 return 0
547 when 1
548 push_integer(1)
549 when 2
550 push_integer(-1)
551 when 3
552 push(imaginaryunit)
553 when 4
554 push(imaginaryunit)
555 negate()
556 push(p2)
557 push(car(p3))
558 subtract()
559 exponential()
560 multiply()
561 return 1
562
563 return 0
564
565
566