expand.cpp 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609
  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
  11. #include "stdafx.h"
  12. #include "defs.h"
  13. void
  14. eval_expand(void)
  15. {
  16. // 1st arg
  17. push(cadr(p1));
  18. eval();
  19. // 2nd arg
  20. push(caddr(p1));
  21. eval();
  22. p2 = pop();
  23. if (p2 == symbol(NIL))
  24. guess();
  25. else
  26. push(p2);
  27. expand();
  28. }
  29. #define A p2
  30. #define B p3
  31. #define C p4
  32. #define F p5
  33. #define P p6
  34. #define Q p7
  35. #define T p8
  36. #define X p9
  37. void
  38. expand(void)
  39. {
  40. save();
  41. X = pop();
  42. F = pop();
  43. if (istensor(F)) {
  44. expand_tensor();
  45. restore();
  46. return;
  47. }
  48. // if sum of terms then sum over the expansion of each term
  49. if (car(F) == symbol(ADD)) {
  50. push_integer(0);
  51. p1 = cdr(F);
  52. while (iscons(p1)) {
  53. push(car(p1));
  54. push(X);
  55. expand();
  56. add();
  57. p1 = cdr(p1);
  58. }
  59. restore();
  60. return;
  61. }
  62. // B = numerator
  63. push(F);
  64. numerator();
  65. B = pop();
  66. // A = denominator
  67. push(F);
  68. denominator();
  69. A = pop();
  70. remove_negative_exponents();
  71. // Q = quotient
  72. push(B);
  73. push(A);
  74. push(X);
  75. divpoly();
  76. Q = pop();
  77. // remainder B = B - A * Q
  78. push(B);
  79. push(A);
  80. push(Q);
  81. multiply();
  82. subtract();
  83. B = pop();
  84. // if the remainder is zero then we're done
  85. if (iszero(B)) {
  86. push(Q);
  87. restore();
  88. return;
  89. }
  90. // A = factor(A)
  91. push(A);
  92. push(X);
  93. factorpoly();
  94. A = pop();
  95. expand_get_C();
  96. expand_get_B();
  97. expand_get_A();
  98. if (istensor(C)) {
  99. push(C);
  100. inv();
  101. push(B);
  102. inner();
  103. push(A);
  104. inner();
  105. } else {
  106. push(B);
  107. push(C);
  108. divide();
  109. push(A);
  110. multiply();
  111. }
  112. push(Q);
  113. add();
  114. restore();
  115. }
  116. void
  117. expand_tensor(void)
  118. {
  119. int i;
  120. push(F);
  121. copy_tensor();
  122. F = pop();
  123. for (i = 0; i < F->u.tensor->nelem; i++) {
  124. push(F->u.tensor->elem[i]);
  125. push(X);
  126. expand();
  127. F->u.tensor->elem[i] = pop();
  128. }
  129. push(F);
  130. }
  131. void
  132. remove_negative_exponents(void)
  133. {
  134. int h, i, j, k, n;
  135. h = tos;
  136. factors(A);
  137. factors(B);
  138. n = tos - h;
  139. // find the smallest exponent
  140. j = 0;
  141. for (i = 0; i < n; i++) {
  142. p1 = stack[h + i];
  143. if (car(p1) != symbol(POWER))
  144. continue;
  145. if (cadr(p1) != X)
  146. continue;
  147. push(caddr(p1));
  148. k = pop_integer();
  149. if (k == (int) 0x80000000)
  150. continue;
  151. if (k < j)
  152. j = k;
  153. }
  154. tos = h;
  155. if (j == 0)
  156. return;
  157. // A = A / X^j
  158. push(A);
  159. push(X);
  160. push_integer(-j);
  161. power();
  162. multiply();
  163. A = pop();
  164. // B = B / X^j
  165. push(B);
  166. push(X);
  167. push_integer(-j);
  168. power();
  169. multiply();
  170. B = pop();
  171. }
  172. // Returns the expansion coefficient matrix C.
  173. //
  174. // Example:
  175. //
  176. // B 1
  177. // --- = -----------
  178. // A 2
  179. // x (x + 1)
  180. //
  181. // We have
  182. //
  183. // B Y1 Y2 Y3
  184. // --- = ---- + ---- + -------
  185. // A 2 x x + 1
  186. // x
  187. //
  188. // Our task is to solve for the unknowns Y1, Y2, and Y3.
  189. //
  190. // Multiplying both sides by A yields
  191. //
  192. // AY1 AY2 AY3
  193. // B = ----- + ----- + -------
  194. // 2 x x + 1
  195. // x
  196. //
  197. // Let
  198. //
  199. // A A A
  200. // W1 = ---- W2 = --- W3 = -------
  201. // 2 x x + 1
  202. // x
  203. //
  204. // Then the coefficient matrix C is
  205. //
  206. // coeff(W1,x,0) coeff(W2,x,0) coeff(W3,x,0)
  207. //
  208. // C = coeff(W1,x,1) coeff(W2,x,1) coeff(W3,x,1)
  209. //
  210. // coeff(W1,x,2) coeff(W2,x,2) coeff(W3,x,2)
  211. //
  212. // It follows that
  213. //
  214. // coeff(B,x,0) Y1
  215. //
  216. // coeff(B,x,1) = C Y2
  217. //
  218. // coeff(B,x,2) = Y3
  219. //
  220. // Hence
  221. //
  222. // Y1 coeff(B,x,0)
  223. // -1
  224. // Y2 = C coeff(B,x,1)
  225. //
  226. // Y3 coeff(B,x,2)
  227. void
  228. expand_get_C(void)
  229. {
  230. int h, i, j, n;
  231. U **a;
  232. h = tos;
  233. if (car(A) == symbol(MULTIPLY)) {
  234. p1 = cdr(A);
  235. while (iscons(p1)) {
  236. F = car(p1);
  237. expand_get_CF();
  238. p1 = cdr(p1);
  239. }
  240. } else {
  241. F = A;
  242. expand_get_CF();
  243. }
  244. n = tos - h;
  245. if (n == 1) {
  246. C = pop();
  247. return;
  248. }
  249. C = alloc_tensor(n * n);
  250. C->u.tensor->ndim = 2;
  251. C->u.tensor->dim[0] = n;
  252. C->u.tensor->dim[1] = n;
  253. a = stack + h;
  254. for (i = 0; i < n; i++) {
  255. for (j = 0; j < n; j++) {
  256. push(a[j]);
  257. push(X);
  258. push_integer(i);
  259. power();
  260. divide();
  261. push(X);
  262. filter();
  263. C->u.tensor->elem[n * i + j] = pop();
  264. }
  265. }
  266. tos -= n;
  267. }
  268. // The following table shows the push order for simple roots, repeated roots,
  269. // and inrreducible factors.
  270. //
  271. // Factor F Push 1st Push 2nd Push 3rd Push 4th
  272. //
  273. //
  274. // A
  275. // x ---
  276. // x
  277. //
  278. //
  279. // 2 A A
  280. // x ---- ---
  281. // 2 x
  282. // x
  283. //
  284. //
  285. // A
  286. // x + 1 -------
  287. // x + 1
  288. //
  289. //
  290. // 2 A A
  291. // (x + 1) ---------- -------
  292. // 2 x + 1
  293. // (x + 1)
  294. //
  295. //
  296. // 2 A Ax
  297. // x + x + 1 ------------ ------------
  298. // 2 2
  299. // x + x + 1 x + x + 1
  300. //
  301. //
  302. // 2 2 A Ax A Ax
  303. // (x + x + 1) --------------- --------------- ------------ ------------
  304. // 2 2 2 2 2 2
  305. // (x + x + 1) (x + x + 1) x + x + 1 x + x + 1
  306. //
  307. //
  308. // For T = A/F and F = P^N we have
  309. //
  310. //
  311. // Factor F Push 1st Push 2nd Push 3rd Push 4th
  312. //
  313. // x T
  314. //
  315. // 2
  316. // x T TP
  317. //
  318. //
  319. // x + 1 T
  320. //
  321. // 2
  322. // (x + 1) T TP
  323. //
  324. // 2
  325. // x + x + 1 T TX
  326. //
  327. // 2 2
  328. // (x + x + 1) T TX TP TPX
  329. //
  330. //
  331. // Hence we want to push in the order
  332. //
  333. // T * (P ^ i) * (X ^ j)
  334. //
  335. // for all i, j such that
  336. //
  337. // i = 0, 1, ..., N - 1
  338. //
  339. // j = 0, 1, ..., deg(P) - 1
  340. //
  341. // where index j runs first.
  342. void
  343. expand_get_CF(void)
  344. { int d, i, j, n;
  345. if (!find(F, X))
  346. return;
  347. trivial_divide();
  348. if (car(F) == symbol(POWER)) {
  349. push(caddr(F));
  350. n = pop_integer();
  351. P = cadr(F);
  352. } else {
  353. n = 1;
  354. P = F;
  355. }
  356. push(P);
  357. push(X);
  358. degree();
  359. d = pop_integer();
  360. for (i = 0; i < n; i++) {
  361. for (j = 0; j < d; j++) {
  362. push(T);
  363. push(P);
  364. push_integer(i);
  365. power();
  366. multiply();
  367. push(X);
  368. push_integer(j);
  369. power();
  370. multiply();
  371. }
  372. }
  373. }
  374. // Returns T = A/F where F is a factor of A.
  375. void
  376. trivial_divide(void)
  377. {
  378. int h;
  379. if (car(A) == symbol(MULTIPLY)) {
  380. h = tos;
  381. p0 = cdr(A);
  382. while (iscons(p0)) {
  383. if (!equal(car(p0), F)) {
  384. push(car(p0));
  385. eval(); // force expansion of (x+1)^2, f.e.
  386. }
  387. p0 = cdr(p0);
  388. }
  389. multiply_all(tos - h);
  390. } else
  391. push_integer(1);
  392. T = pop();
  393. }
  394. // Returns the expansion coefficient vector B.
  395. void
  396. expand_get_B(void)
  397. {
  398. int i, n;
  399. if (!istensor(C))
  400. return;
  401. n = C->u.tensor->dim[0];
  402. T = alloc_tensor(n);
  403. T->u.tensor->ndim = 1;
  404. T->u.tensor->dim[0] = n;
  405. for (i = 0; i < n; i++) {
  406. push(B);
  407. push(X);
  408. push_integer(i);
  409. power();
  410. divide();
  411. push(X);
  412. filter();
  413. T->u.tensor->elem[i] = pop();
  414. }
  415. B = T;
  416. }
  417. // Returns the expansion fractions in A.
  418. void
  419. expand_get_A(void)
  420. {
  421. int h, i, n;
  422. if (!istensor(C)) {
  423. push(A);
  424. reciprocate();
  425. A = pop();
  426. return;
  427. }
  428. h = tos;
  429. if (car(A) == symbol(MULTIPLY)) {
  430. T = cdr(A);
  431. while (iscons(T)) {
  432. F = car(T);
  433. expand_get_AF();
  434. T = cdr(T);
  435. }
  436. } else {
  437. F = A;
  438. expand_get_AF();
  439. }
  440. n = tos - h;
  441. T = alloc_tensor(n);
  442. T->u.tensor->ndim = 1;
  443. T->u.tensor->dim[0] = n;
  444. for (i = 0; i < n; i++)
  445. T->u.tensor->elem[i] = stack[h + i];
  446. tos = h;
  447. A = T;
  448. }
  449. void
  450. expand_get_AF(void)
  451. { int d, i, j, n = 1;
  452. if (!find(F, X))
  453. return;
  454. if (car(F) == symbol(POWER)) {
  455. push(caddr(F));
  456. n = pop_integer();
  457. F = cadr(F);
  458. }
  459. push(F);
  460. push(X);
  461. degree();
  462. d = pop_integer();
  463. for (i = n; i > 0; i--) {
  464. for (j = 0; j < d; j++) {
  465. push(F);
  466. push_integer(i);
  467. power();
  468. reciprocate();
  469. push(X);
  470. push_integer(j);
  471. power();
  472. multiply();
  473. }
  474. }
  475. }
  476. #if SELFTEST
  477. static char *s[] = {
  478. // general cases
  479. "expand(1/(x+1)/(x+2))",
  480. "1/(x+1)-1/(x+2)",
  481. "expand((2x^3-x+2)/(x^2-2x+1))",
  482. "4+2*x+5/(x-1)+3/(x^2-2*x+1)",
  483. "expand(1/x^2/(x-1))",
  484. "-1/(x^2)-1/x+1/(x-1)",
  485. "p=5s+2",
  486. "",
  487. "q=(s+1)(s+2)^2",
  488. "",
  489. "expand(p/q)",
  490. "-3/(s+1)+3/(s+2)+8/(s^2+4*s+4)",
  491. // ensure denominators are expanded (result seems preferable that way)
  492. "q=(x-1)(x-2)^3",
  493. "",
  494. "expand(1/q)",
  495. "1/(x^3-6*x^2+12*x-8)+1/(x-2)-1/(x-1)-1/(x^2-4*x+4)",
  496. // fractional poles
  497. "expand(1/(x+1/2)/(x+1/3))",
  498. "-12/(2*x+1)+18/(3*x+1)",
  499. // expand tensor
  500. "f=1/(x+1)/(x+2)",
  501. "",
  502. "g=1/(x+1)-1/(x+2)",
  503. "",
  504. "expand(((f,f),(f,f)))-((g,g),(g,g))",
  505. "((0,0),(0,0))",
  506. // denominator normalized?
  507. "expand(1/(1+1/x))",
  508. "1-1/(x+1)",
  509. // poles at zero
  510. "expand(1/x/(x+1))",
  511. "1/x-1/(x+1)",
  512. "expand(1/x^2/(x+1))",
  513. "x^(-2)-1/x+1/(x+1)",
  514. // other corner cases
  515. "expand(1/x)",
  516. "1/x",
  517. "expand(1/x^2)",
  518. "x^(-2)",
  519. "expand(1/(x^2-4x+4))",
  520. "1/(x^2-4*x+4)",
  521. };
  522. void
  523. test_expand(void)
  524. {
  525. test(__FILE__, s, sizeof s / sizeof (char *));
  526. }
  527. #endif