Little fix after the last commit (mostly a git fail)
[eigenmath-fx.git] / expand.cpp
blob627040c4f20b9957a5efdbe61a6a5b8d53bfc6ea
1 // Partial fraction expansion
2 //
3 // Example
4 //
5 // expand(1/(x^3+x^2),x)
6 //
7 // 1 1 1
8 // ---- - --- + -------
9 // 2 x x + 1
10 // x
12 #include "stdafx.h"
13 #include "defs.h"
15 void
16 eval_expand(void)
18 // 1st arg
20 push(cadr(p1));
21 eval();
23 // 2nd arg
25 push(caddr(p1));
26 eval();
28 p2 = pop();
29 if (p2 == symbol(NIL))
30 guess();
31 else
32 push(p2);
34 expand();
37 #define A p2
38 #define B p3
39 #define C p4
40 #define F p5
41 #define P p6
42 #define Q p7
43 #define T p8
44 #define X p9
46 void
47 expand(void)
49 save();
51 X = pop();
52 F = pop();
54 if (istensor(F)) {
55 expand_tensor();
56 restore();
57 return;
60 // if sum of terms then sum over the expansion of each term
62 if (car(F) == symbol(ADD)) {
63 push_integer(0);
64 p1 = cdr(F);
65 while (iscons(p1)) {
66 push(car(p1));
67 push(X);
68 expand();
69 add();
70 p1 = cdr(p1);
72 restore();
73 return;
76 // B = numerator
78 push(F);
79 numerator();
80 B = pop();
82 // A = denominator
84 push(F);
85 denominator();
86 A = pop();
88 remove_negative_exponents();
90 // Q = quotient
92 push(B);
93 push(A);
94 push(X);
95 divpoly();
96 Q = pop();
98 // remainder B = B - A * Q
100 push(B);
101 push(A);
102 push(Q);
103 multiply();
104 subtract();
105 B = pop();
107 // if the remainder is zero then we're done
109 if (iszero(B)) {
110 push(Q);
111 restore();
112 return;
115 // A = factor(A)
117 push(A);
118 push(X);
119 factorpoly();
120 A = pop();
122 expand_get_C();
123 expand_get_B();
124 expand_get_A();
126 if (istensor(C)) {
127 push(C);
128 inv();
129 push(B);
130 inner();
131 push(A);
132 inner();
133 } else {
134 push(B);
135 push(C);
136 divide();
137 push(A);
138 multiply();
141 push(Q);
142 add();
144 restore();
147 void
148 expand_tensor(void)
150 int i;
151 push(F);
152 copy_tensor();
153 F = pop();
154 for (i = 0; i < F->u.tensor->nelem; i++) {
155 push(F->u.tensor->elem[i]);
156 push(X);
157 expand();
158 F->u.tensor->elem[i] = pop();
160 push(F);
163 void
164 remove_negative_exponents(void)
166 int h, i, j, k, n;
168 h = tos;
169 factors(A);
170 factors(B);
171 n = tos - h;
173 // find the smallest exponent
175 j = 0;
176 for (i = 0; i < n; i++) {
177 p1 = stack[h + i];
178 if (car(p1) != symbol(POWER))
179 continue;
180 if (cadr(p1) != X)
181 continue;
182 push(caddr(p1));
183 k = pop_integer();
184 if (k == (int) 0x80000000)
185 continue;
186 if (k < j)
187 j = k;
190 tos = h;
192 if (j == 0)
193 return;
195 // A = A / X^j
197 push(A);
198 push(X);
199 push_integer(-j);
200 power();
201 multiply();
202 A = pop();
204 // B = B / X^j
206 push(B);
207 push(X);
208 push_integer(-j);
209 power();
210 multiply();
211 B = pop();
214 // Returns the expansion coefficient matrix C.
216 // Example:
218 // B 1
219 // --- = -----------
220 // A 2
221 // x (x + 1)
223 // We have
225 // B Y1 Y2 Y3
226 // --- = ---- + ---- + -------
227 // A 2 x x + 1
228 // x
230 // Our task is to solve for the unknowns Y1, Y2, and Y3.
232 // Multiplying both sides by A yields
234 // AY1 AY2 AY3
235 // B = ----- + ----- + -------
236 // 2 x x + 1
237 // x
239 // Let
241 // A A A
242 // W1 = ---- W2 = --- W3 = -------
243 // 2 x x + 1
244 // x
246 // Then the coefficient matrix C is
248 // coeff(W1,x,0) coeff(W2,x,0) coeff(W3,x,0)
250 // C = coeff(W1,x,1) coeff(W2,x,1) coeff(W3,x,1)
252 // coeff(W1,x,2) coeff(W2,x,2) coeff(W3,x,2)
254 // It follows that
256 // coeff(B,x,0) Y1
258 // coeff(B,x,1) = C Y2
260 // coeff(B,x,2) = Y3
262 // Hence
264 // Y1 coeff(B,x,0)
265 // -1
266 // Y2 = C coeff(B,x,1)
268 // Y3 coeff(B,x,2)
270 void
271 expand_get_C(void)
273 int h, i, j, n;
274 U **a;
275 h = tos;
276 if (car(A) == symbol(MULTIPLY)) {
277 p1 = cdr(A);
278 while (iscons(p1)) {
279 F = car(p1);
280 expand_get_CF();
281 p1 = cdr(p1);
283 } else {
284 F = A;
285 expand_get_CF();
287 n = tos - h;
288 if (n == 1) {
289 C = pop();
290 return;
292 C = alloc_tensor(n * n);
293 C->u.tensor->ndim = 2;
294 C->u.tensor->dim[0] = n;
295 C->u.tensor->dim[1] = n;
296 a = stack + h;
297 for (i = 0; i < n; i++) {
298 for (j = 0; j < n; j++) {
299 push(a[j]);
300 push(X);
301 push_integer(i);
302 power();
303 divide();
304 push(X);
305 filter();
306 C->u.tensor->elem[n * i + j] = pop();
309 tos -= n;
312 // The following table shows the push order for simple roots, repeated roots,
313 // and inrreducible factors.
315 // Factor F Push 1st Push 2nd Push 3rd Push 4th
318 // A
319 // x ---
320 // x
323 // 2 A A
324 // x ---- ---
325 // 2 x
326 // x
329 // A
330 // x + 1 -------
331 // x + 1
334 // 2 A A
335 // (x + 1) ---------- -------
336 // 2 x + 1
337 // (x + 1)
340 // 2 A Ax
341 // x + x + 1 ------------ ------------
342 // 2 2
343 // x + x + 1 x + x + 1
346 // 2 2 A Ax A Ax
347 // (x + x + 1) --------------- --------------- ------------ ------------
348 // 2 2 2 2 2 2
349 // (x + x + 1) (x + x + 1) x + x + 1 x + x + 1
352 // For T = A/F and F = P^N we have
355 // Factor F Push 1st Push 2nd Push 3rd Push 4th
357 // x T
359 // 2
360 // x T TP
363 // x + 1 T
365 // 2
366 // (x + 1) T TP
368 // 2
369 // x + x + 1 T TX
371 // 2 2
372 // (x + x + 1) T TX TP TPX
375 // Hence we want to push in the order
377 // T * (P ^ i) * (X ^ j)
379 // for all i, j such that
381 // i = 0, 1, ..., N - 1
383 // j = 0, 1, ..., deg(P) - 1
385 // where index j runs first.
387 void
388 expand_get_CF(void)
389 { int d, i, j, n;
390 if (!find(F, X))
391 return;
392 trivial_divide();
393 if (car(F) == symbol(POWER)) {
394 push(caddr(F));
395 n = pop_integer();
396 P = cadr(F);
397 } else {
398 n = 1;
399 P = F;
401 push(P);
402 push(X);
403 degree();
404 d = pop_integer();
405 for (i = 0; i < n; i++) {
406 for (j = 0; j < d; j++) {
407 push(T);
408 push(P);
409 push_integer(i);
410 power();
411 multiply();
412 push(X);
413 push_integer(j);
414 power();
415 multiply();
420 // Returns T = A/F where F is a factor of A.
422 void
423 trivial_divide(void)
425 int h;
426 if (car(A) == symbol(MULTIPLY)) {
427 h = tos;
428 p0 = cdr(A);
429 while (iscons(p0)) {
430 if (!equal(car(p0), F)) {
431 push(car(p0));
432 eval(); // force expansion of (x+1)^2, f.e.
434 p0 = cdr(p0);
436 multiply_all(tos - h);
437 } else
438 push_integer(1);
439 T = pop();
442 // Returns the expansion coefficient vector B.
444 void
445 expand_get_B(void)
447 int i, n;
448 if (!istensor(C))
449 return;
450 n = C->u.tensor->dim[0];
451 T = alloc_tensor(n);
452 T->u.tensor->ndim = 1;
453 T->u.tensor->dim[0] = n;
454 for (i = 0; i < n; i++) {
455 push(B);
456 push(X);
457 push_integer(i);
458 power();
459 divide();
460 push(X);
461 filter();
462 T->u.tensor->elem[i] = pop();
464 B = T;
467 // Returns the expansion fractions in A.
469 void
470 expand_get_A(void)
472 int h, i, n;
473 if (!istensor(C)) {
474 push(A);
475 reciprocate();
476 A = pop();
477 return;
479 h = tos;
480 if (car(A) == symbol(MULTIPLY)) {
481 T = cdr(A);
482 while (iscons(T)) {
483 F = car(T);
484 expand_get_AF();
485 T = cdr(T);
487 } else {
488 F = A;
489 expand_get_AF();
491 n = tos - h;
492 T = alloc_tensor(n);
493 T->u.tensor->ndim = 1;
494 T->u.tensor->dim[0] = n;
495 for (i = 0; i < n; i++)
496 T->u.tensor->elem[i] = stack[h + i];
497 tos = h;
498 A = T;
501 void
502 expand_get_AF(void)
503 { int d, i, j, n = 1;
504 if (!find(F, X))
505 return;
506 if (car(F) == symbol(POWER)) {
507 push(caddr(F));
508 n = pop_integer();
509 F = cadr(F);
511 push(F);
512 push(X);
513 degree();
514 d = pop_integer();
515 for (i = n; i > 0; i--) {
516 for (j = 0; j < d; j++) {
517 push(F);
518 push_integer(i);
519 power();
520 reciprocate();
521 push(X);
522 push_integer(j);
523 power();
524 multiply();
529 #if SELFTEST
531 static char *s[] = {
533 // general cases
535 "expand(1/(x+1)/(x+2))",
536 "1/(x+1)-1/(x+2)",
538 "expand((2x^3-x+2)/(x^2-2x+1))",
539 "4+2*x+5/(x-1)+3/(x^2-2*x+1)",
541 "expand(1/x^2/(x-1))",
542 "-1/(x^2)-1/x+1/(x-1)",
544 "p=5s+2",
547 "q=(s+1)(s+2)^2",
550 "expand(p/q)",
551 "-3/(s+1)+3/(s+2)+8/(s^2+4*s+4)",
553 // ensure denominators are expanded (result seems preferable that way)
555 "q=(x-1)(x-2)^3",
558 "expand(1/q)",
559 "1/(x^3-6*x^2+12*x-8)+1/(x-2)-1/(x-1)-1/(x^2-4*x+4)",
561 // fractional poles
563 "expand(1/(x+1/2)/(x+1/3))",
564 "-12/(2*x+1)+18/(3*x+1)",
566 // expand tensor
568 "f=1/(x+1)/(x+2)",
571 "g=1/(x+1)-1/(x+2)",
574 "expand(((f,f),(f,f)))-((g,g),(g,g))",
575 "((0,0),(0,0))",
577 // denominator normalized?
579 "expand(1/(1+1/x))",
580 "1-1/(x+1)",
582 // poles at zero
584 "expand(1/x/(x+1))",
585 "1/x-1/(x+1)",
587 "expand(1/x^2/(x+1))",
588 "x^(-2)-1/x+1/(x+1)",
590 // other corner cases
592 "expand(1/x)",
593 "1/x",
595 "expand(1/x^2)",
596 "x^(-2)",
598 "expand(1/(x^2-4x+4))",
599 "1/(x^2-4*x+4)",
602 void
603 test_expand(void)
605 test(__FILE__, s, sizeof s / sizeof (char *));
608 #endif