123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724 |
- // Symbolic multiplication
- #include "stdafx.h"
- #include "defs.h"
- extern void append(void);
- static void parse_p1(void);
- static void parse_p2(void);
- static void __normalize_radical_factors(int);
- void
- multiply(void)
- {
- if (esc_flag)
- stop("escape key stop");
- if (isnum(stack[tos - 2]) && isnum(stack[tos - 1]))
- multiply_numbers();
- else {
- save();
- yymultiply();
- restore();
- }
- }
- void
- yymultiply(void)
- {
- int h, i, n;
- // pop operands
- p2 = pop();
- p1 = pop();
- h = tos;
- // is either operand zero?
- if (iszero(p1) || iszero(p2)) {
- push(zero);
- return;
- }
- // is either operand a sum?
- if (expanding && isadd(p1)) {
- p1 = cdr(p1);
- push(zero);
- while (iscons(p1)) {
- push(car(p1));
- push(p2);
- multiply();
- add();
- p1 = cdr(p1);
- }
- return;
- }
- if (expanding && isadd(p2)) {
- p2 = cdr(p2);
- push(zero);
- while (iscons(p2)) {
- push(p1);
- push(car(p2));
- multiply();
- add();
- p2 = cdr(p2);
- }
- return;
- }
- // scalar times tensor?
- if (!istensor(p1) && istensor(p2)) {
- push(p1);
- push(p2);
- scalar_times_tensor();
- return;
- }
- // tensor times scalar?
- if (istensor(p1) && !istensor(p2)) {
- push(p1);
- push(p2);
- tensor_times_scalar();
- return;
- }
- // adjust operands
- if (car(p1) == symbol(MULTIPLY))
- p1 = cdr(p1);
- else {
- push(p1);
- list(1);
- p1 = pop();
- }
- if (car(p2) == symbol(MULTIPLY))
- p2 = cdr(p2);
- else {
- push(p2);
- list(1);
- p2 = pop();
- }
- // handle numerical coefficients
- if (isnum(car(p1)) && isnum(car(p2))) {
- push(car(p1));
- push(car(p2));
- multiply_numbers();
- p1 = cdr(p1);
- p2 = cdr(p2);
- } else if (isnum(car(p1))) {
- push(car(p1));
- p1 = cdr(p1);
- } else if (isnum(car(p2))) {
- push(car(p2));
- p2 = cdr(p2);
- } else
- push(one);
- parse_p1();
- parse_p2();
- while (iscons(p1) && iscons(p2)) {
- // if (car(p1)->gamma && car(p2)->gamma) {
- // combine_gammas(h);
- // p1 = cdr(p1);
- // p2 = cdr(p2);
- // parse_p1();
- // parse_p2();
- // continue;
- // }
- if (caar(p1) == symbol(OPERATOR) && caar(p2) == symbol(OPERATOR)) {
- push_symbol(OPERATOR);
- push(cdar(p1));
- push(cdar(p2));
- append();
- cons();
- p1 = cdr(p1);
- p2 = cdr(p2);
- parse_p1();
- parse_p2();
- continue;
- }
- switch (cmp_expr(p3, p4)) {
- case -1:
- push(car(p1));
- p1 = cdr(p1);
- parse_p1();
- break;
- case 1:
- push(car(p2));
- p2 = cdr(p2);
- parse_p2();
- break;
- case 0:
- combine_factors(h);
- p1 = cdr(p1);
- p2 = cdr(p2);
- parse_p1();
- parse_p2();
- break;
- default:
- stop("internal error 2");
- break;
- }
- }
- // push remaining factors, if any
- while (iscons(p1)) {
- push(car(p1));
- p1 = cdr(p1);
- }
- while (iscons(p2)) {
- push(car(p2));
- p2 = cdr(p2);
- }
- // normalize radical factors
- // example: 2*2(-1/2) -> 2^(1/2)
- // must be done after merge because merge may produce radical
- // example: 2^(1/2-a)*2^a -> 2^(1/2)
- __normalize_radical_factors(h);
- // this hack should not be necessary, unless power returns a multiply
- //for (i = h; i < tos; i++) {
- // if (car(stack[i]) == symbol(MULTIPLY)) {
- // multiply_all(tos - h);
- // return;
- // }
- //}
- if (expanding) {
- for (i = h; i < tos; i++) {
- if (isadd(stack[i])) {
- multiply_all(tos - h);
- return;
- }
- }
- }
- // n is the number of result factors on the stack
- n = tos - h;
- if (n == 1)
- return;
- // discard integer 1
- if (isrational(stack[h]) && equaln(stack[h], 1)) {
- if (n == 2) {
- p7 = pop();
- pop();
- push(p7);
- } else {
- stack[h] = symbol(MULTIPLY);
- list(n);
- }
- return;
- }
- list(n);
- p7 = pop();
- push_symbol(MULTIPLY);
- push(p7);
- cons();
- }
- // Decompose a factor into base and power.
- //
- // input: car(p1) factor
- //
- // output: p3 factor's base
- //
- // p5 factor's power (possibly 1)
- static void
- parse_p1(void)
- {
- p3 = car(p1);
- p5 = one;
- if (car(p3) == symbol(POWER)) {
- p5 = caddr(p3);
- p3 = cadr(p3);
- }
- }
- // Decompose a factor into base and power.
- //
- // input: car(p2) factor
- //
- // output: p4 factor's base
- //
- // p6 factor's power (possibly 1)
- static void
- parse_p2(void)
- {
- p4 = car(p2);
- p6 = one;
- if (car(p4) == symbol(POWER)) {
- p6 = caddr(p4);
- p4 = cadr(p4);
- }
- }
- void
- combine_factors(int h)
- {
- push(p4);
- push(p5);
- push(p6);
- add();
- power();
- p7 = pop();
- if (isnum(p7)) {
- push(stack[h]);
- push(p7);
- multiply_numbers();
- stack[h] = pop();
- } else if (car(p7) == symbol(MULTIPLY)) {
- // power can return number * factor (i.e. -1 * i)
- if (isnum(cadr(p7)) && cdddr(p7) == symbol(NIL)) {
- push(stack[h]);
- push(cadr(p7));
- multiply_numbers();
- stack[h] = pop();
- push(caddr(p7));
- } else
- push(p7);
- } else
- push(p7);
- }
- int gp[17][17] = {
- {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
- {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
- {0,0,1,-6,-7,-8,-3,-4,-5,13,14,15,-16,9,10,11,-12},
- {0,0,6,-1,-11,10,-2,-15,14,12,-5,4,-9,16,-8,7,-13},
- {0,0,7,11,-1,-9,15,-2,-13,5,12,-3,-10,8,16,-6,-14},
- {0,0,8,-10,9,-1,-14,13,-2,-4,3,12,-11,-7,6,16,-15},
- {0,0,3,2,15,-14,1,11,-10,16,-8,7,13,12,-5,4,9},
- {0,0,4,-15,2,13,-11,1,9,8,16,-6,14,5,12,-3,10},
- {0,0,5,14,-13,2,10,-9,1,-7,6,16,15,-4,3,12,11},
- {0,0,13,12,-5,4,16,-8,7,-1,-11,10,-3,-2,-15,14,-6},
- {0,0,14,5,12,-3,8,16,-6,11,-1,-9,-4,15,-2,-13,-7},
- {0,0,15,-4,3,12,-7,6,16,-10,9,-1,-5,-14,13,-2,-8},
- {0,0,16,-9,-10,-11,-13,-14,-15,-3,-4,-5,1,-6,-7,-8,2},
- {0,0,9,-16,8,-7,-12,5,-4,-2,-15,14,6,-1,-11,10,3},
- {0,0,10,-8,-16,6,-5,-12,3,15,-2,-13,7,11,-1,-9,4},
- {0,0,11,7,-6,-16,4,-3,-12,-14,13,-2,8,-10,9,-1,5},
- {0,0,12,13,14,15,9,10,11,-6,-7,-8,-2,-3,-4,-5,-1}
- };
- #if 0
- static void
- combine_gammas(int h)
- {
- int n;
- n = gp[(int) p1->gamma][(int) p2->gamma];
- if (n < 0) {
- n = -n;
- push(stack[h]);
- negate();
- stack[h] = pop();
- }
- if (n > 1)
- push(_gamma[n]);
- }
- #endif
- void
- multiply_noexpand(void)
- {
- int x;
- x = expanding;
- expanding = 0;
- multiply();
- expanding = x;
- }
- // multiply n factors on stack
- void
- multiply_all(int n)
- {
- int h, i;
- if (n == 1)
- return;
- if (n == 0) {
- push(one);
- return;
- }
- h = tos - n;
- push(stack[h]);
- for (i = 1; i < n; i++) {
- push(stack[h + i]);
- multiply();
- }
- stack[h] = pop();
- tos = h + 1;
- }
- void
- multiply_all_noexpand(int n)
- {
- int x;
- x = expanding;
- expanding = 0;
- multiply_all(n);
- expanding = x;
- }
- //-----------------------------------------------------------------------------
- //
- // Symbolic division
- //
- // Input: Dividend and divisor on stack
- //
- // Output: Quotient on stack
- //
- //-----------------------------------------------------------------------------
- void
- divide(void)
- {
- if (isnum(stack[tos - 2]) && isnum(stack[tos - 1]))
- divide_numbers();
- else {
- inverse();
- multiply();
- }
- }
- void
- inverse(void)
- {
- if (isnum(stack[tos - 1]))
- invert_number();
- else {
- push_integer(-1);
- power();
- }
- }
- void
- reciprocate(void)
- {
- if (isnum(stack[tos - 1]))
- invert_number();
- else {
- push_integer(-1);
- power();
- }
- }
- void
- negate(void)
- {
- if (isnum(stack[tos - 1]))
- negate_number();
- else {
- push_integer(-1);
- multiply();
- }
- }
- void
- negate_expand(void)
- {
- int x;
- x = expanding;
- expanding = 1;
- negate();
- expanding = x;
- }
- void
- negate_noexpand(void)
- {
- int x;
- x = expanding;
- expanding = 0;
- negate();
- expanding = x;
- }
- //-----------------------------------------------------------------------------
- //
- // Normalize radical factors
- //
- // Input: stack[h] Coefficient factor, possibly 1
- //
- // stack[h + 1] Second factor
- //
- // stack[tos - 1] Last factor
- //
- // Output: Reduced coefficent and normalized radicals (maybe)
- //
- // Example: 2*2^(-1/2) -> 2^(1/2)
- //
- // (power number number) is guaranteed to have the following properties:
- //
- // 1. Base is an integer
- //
- // 2. Absolute value of exponent < 1
- //
- // These properties are assured by the power function.
- //
- //-----------------------------------------------------------------------------
- #define A p1
- #define B p2
- #define BASE p3
- #define EXPO p4
- #define TMP p5
- static int __is_radical_number(U *);
- static void
- __normalize_radical_factors(int h)
- {
- int i;
- // if coeff is 1 or floating then don't bother
- if (isplusone(stack[h]) || isminusone(stack[h]) || isdouble(stack[h]))
- return;
- // if no radicals then don't bother
- for (i = h + 1; i < tos; i++)
- if (__is_radical_number(stack[i]))
- break;
- if (i == tos)
- return;
- // ok, try to simplify
- save();
- // numerator
- push(stack[h]);
- mp_numerator();
- A = pop();
- for (i = h + 1; i < tos; i++) {
- if (isplusone(A) || isminusone(A))
- break;
- if (!__is_radical_number(stack[i]))
- continue;
- BASE = cadr(stack[i]);
- EXPO = caddr(stack[i]);
- // exponent must be negative
- if (!isnegativenumber(EXPO))
- continue;
- // numerator divisible by BASE?
- push(A);
- push(BASE);
- divide();
- TMP = pop();
- if (!isinteger(TMP))
- continue;
- // reduce numerator
- A = TMP;
- // invert radical
- push_symbol(POWER);
- push(BASE);
- push(one);
- push(EXPO);
- add();
- list(3);
- stack[i] = pop();
- }
- // denominator
- push(stack[h]);
- mp_denominator();
- B = pop();
- for (i = h + 1; i < tos; i++) {
- if (isplusone(B))
- break;
- if (!__is_radical_number(stack[i]))
- continue;
- BASE = cadr(stack[i]);
- EXPO = caddr(stack[i]);
- // exponent must be positive
- if (isnegativenumber(EXPO))
- continue;
- // denominator divisible by BASE?
- push(B);
- push(BASE);
- divide();
- TMP = pop();
- if (!isinteger(TMP))
- continue;
- // reduce denominator
- B = TMP;
- // invert radical
- push_symbol(POWER);
- push(BASE);
- push(EXPO);
- push(one);
- subtract();
- list(3);
- stack[i] = pop();
- }
- // reconstitute the coefficient
- push(A);
- push(B);
- divide();
- stack[h] = pop();
- restore();
- }
- // don't include i
- static int
- __is_radical_number(U *p)
- {
- // don't use i
- if (car(p) == symbol(POWER) && isnum(cadr(p)) && isnum(caddr(p)) && !isminusone(cadr(p)))
- return 1;
- else
- return 0;
- }
- //-----------------------------------------------------------------------------
- //
- // > a*hilbert(2)
- // ((a,1/2*a),(1/2*a,1/3*a))
- //
- // Note that "a" is presumed to be a scalar. Is this correct?
- //
- // Yes, because "*" has no meaning if "a" is a tensor.
- // To multiply tensors, "dot" or "outer" should be used.
- //
- // > dot(a,hilbert(2))
- // dot(a,((1,1/2),(1/2,1/3)))
- //
- // In this case "a" could be a scalar or tensor so the result is not
- // expanded.
- //
- //-----------------------------------------------------------------------------
- #if SELFTEST
- static char *s[] = {
- "0*a",
- "0",
- "a*0",
- "0",
- "1*a",
- "a",
- "a*1",
- "a",
- "a*a",
- "a^2",
- "a^2*a",
- "a^3",
- "a*a^2",
- "a^3",
- "a^2*a^2",
- "a^4",
- "2^a*2^(3-a)", // symbolic exponents cancel
- "8",
- "sqrt(2)/2",
- "2^(-1/2)",
- "2/sqrt(2)",
- "2^(1/2)",
- "-sqrt(2)/2",
- "-1/(2^(1/2))",
- "2^(1/2-a)*2^a/10",
- "1/(5*2^(1/2))",
- "i/4",
- "1/4*i",
- "1/(4 i)",
- "-1/4*i",
- // ensure 1.0 is not discarded
- "1.0 pi 1/2",
- "0.5*pi",
- "1.0 1/2 pi",
- "0.5*pi",
- };
- void
- test_multiply(void)
- {
- test(__FILE__, s, sizeof s / sizeof (char *));
- }
- #endif
|