mathomatic/unfactor.c

753 lines
17 KiB
C

/*
* Mathomatic unfactorizing (expanding) routines.
*
* Copyright (C) 1987-2012 George Gesslein II.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
The chief copyright holder can be contacted at gesslein@mathomatic.org, or
George Gesslein II, P.O. Box 224, Lansing, NY 14882-0224 USA.
*/
#include "includes.h"
static int unf_sub(token_type *equation, int *np, int b1, int loc, int e1, int level, int ii);
/*
* Unfactor times and divide only (products of sums) and simplify.
*
* Return true if equation side was unfactored.
*/
int
uf_tsimp(equation, np)
token_type *equation;
int *np;
{
int rv;
rv = uf_times(equation, np);
simp_loop(equation, np);
while (uf_times(equation, np)) {
rv = true;
simp_loop(equation, np);
}
return rv;
}
/*
* Unfactor power only.
* (a * b)^c -> a^c * b^c
*
* Return true if equation side was unfactored.
*/
int
uf_power(equation, np)
token_type *equation;
int *np;
{
int count = -1;
do {
organize(equation, np);
if (++count > 0)
break;
} while (sub_ufactor(equation, np, 2));
return count;
}
/*
* Unfactor power only.
* a^(b + c) -> a^b * a^c
*
* Return true if equation side was unfactored.
*/
int
uf_pplus(equation, np)
token_type *equation;
int *np;
{
int count = -1;
do {
organize(equation, np);
if (++count > 0)
break;
} while (sub_ufactor(equation, np, 4));
return count;
}
/*
* Unfactor all power operators.
* Same as a call to uf_pplus() and uf_power(), only faster.
*/
void
uf_allpower(equation, np)
token_type *equation;
int *np;
{
do {
organize(equation, np);
} while (sub_ufactor(equation, np, 0));
}
/*
* Unfactor power only if it will help with expansion and exponent is <= 100.
* (a + 1)^2 -> (a + 1)*(a + 1)
* Afterwards we also sneak in simplifying division by irrational constants,
* because it works best here.
*
* uf_times() is usually called after this to complete the expansion.
*/
void
uf_repeat(equation, np)
token_type *equation;
int *np;
{
int count = -1;
do {
organize(equation, np);
if (++count > 0)
break;
} while (sub_ufactor(equation, np, 6));
patch_root_div(equation, np);
}
/*
* Unfactor power only.
* a^2 -> a*a
* Useful for removing all integer powers.
*/
void
uf_repeat_always(equation, np)
token_type *equation;
int *np;
{
int count = -1;
do {
organize(equation, np);
if (++count > 0)
break;
} while (sub_ufactor(equation, np, 8));
}
/*
* Totally unfactor equation side and simplify.
*/
void
uf_simp(equation, np)
token_type *equation; /* pointer to beginning of equation side */
int *np; /* pointer to length of equation side */
{
uf_tsimp(equation, np);
uf_power(equation, np);
uf_repeat(equation, np);
uf_tsimp(equation, np);
}
/*
* Unfactor equation side and simplify.
* Don't call uf_repeat().
*/
void
uf_simp_no_repeat(equation, np)
token_type *equation;
int *np;
{
uf_power(equation, np);
uf_tsimp(equation, np);
}
/*
* Totally unfactor equation side with no simplification.
*/
int
ufactor(equation, np)
token_type *equation;
int *np;
{
int rv;
uf_repeat(equation, np);
rv = uf_times(equation, np);
uf_allpower(equation, np);
return rv;
}
/*
* Increase the level of numerators by 2, so that the divide operator is not unfactored.
*/
static void
no_divide(equation, np)
token_type *equation;
int *np;
{
int i, j;
int level;
for (i = 1; i < *np; i += 2) {
if (equation[i].token.operatr == DIVIDE) {
level = equation[i].level;
for (j = i - 1; j >= 0; j--) {
if (equation[j].level < level)
break;
equation[j].level += 2;
}
}
}
}
/*
* Unfactor times and divide only (products of sums like (a+b)*(c+d)).
* (a + b)*c -> a*c + b*c
* If partial_flag is true, don't expand (a+b)/(c+d) nor (a+b)/c.
*
* Return true if equation side was unfactored.
*/
int
uf_times(equation, np)
token_type *equation;
int *np;
{
int i;
int rv = false;
do {
organize(equation, np);
if (reorder(equation, np)) {
organize(equation, np);
}
group_proc(equation, np);
if (partial_flag) {
no_divide(equation, np);
}
i = sub_ufactor(equation, np, 1);
rv |= i;
} while (i);
organize(equation, np);
return rv;
}
/*
* General equation side algebraic expansion routine.
* Expands everything.
* The type of expansion to be done is indicated by the code in "ii".
*
* Return true if equation side was modified.
*/
int
sub_ufactor(equation, np, ii)
token_type *equation;
int *np;
int ii;
{
int modified = false;
int i;
int b1, e1;
int level;
for (i = 1; i < *np; i += 2) {
switch (equation[i].token.operatr) {
case TIMES:
case DIVIDE:
if (ii == 1)
break;
else
continue;
case POWER:
if ((ii & 1) == 0) /* even number codes only */
break;
default:
continue;
}
level = equation[i].level;
for (b1 = i - 2; b1 >= 0; b1 -= 2)
if (equation[b1].level < level)
break;
b1++;
for (e1 = i + 2; e1 < *np; e1 += 2) {
if (equation[e1].level < level)
break;
}
if (unf_sub(equation, np, b1, i, e1, level, ii)) {
modified = true;
i = b1 - 1;
continue;
}
}
return modified;
}
static int
unf_sub(equation, np, b1, loc, e1, level, ii)
token_type *equation;
int *np;
int b1, loc, e1, level;
int ii;
{
int i, j, k;
int b2, eb1, be1;
int len;
double d1, d2;
switch (equation[loc].token.operatr) {
case TIMES:
case DIVIDE:
if (ii != 1)
break;
for (i = b1 + 1; i < e1; i += 2) {
if (equation[i].level == level + 1) {
switch (equation[i].token.operatr) {
case PLUS:
case MINUS:
break;
default:
continue;
}
for (b2 = i - 2; b2 >= b1; b2 -= 2)
if (equation[b2].level <= level)
break;
b2++;
eb1 = b2;
for (be1 = i + 2; be1 < e1; be1 += 2)
if (equation[be1].level <= level)
break;
if (eb1 > b1 && equation[eb1-1].token.operatr == DIVIDE) {
i = be1 - 2;
continue;
}
len = 0;
u_again:
if (len + (eb1 - b1) + (i - b2) + (e1 - be1) + 1 > n_tokens) {
error_huge();
}
blt(&scratch[len], &equation[b1], (eb1 - b1) * sizeof(token_type));
j = len;
len += (eb1 - b1);
for (; j < len; j++)
scratch[j].level++;
blt(&scratch[len], &equation[b2], (i - b2) * sizeof(token_type));
len += (i - b2);
blt(&scratch[len], &equation[be1], (e1 - be1) * sizeof(token_type));
j = len;
len += (e1 - be1);
for (; j < len; j++)
scratch[j].level++;
if (i < be1) {
scratch[len] = equation[i];
scratch[len].level--;
len++;
b2 = i + 1;
for (i += 2; i < be1; i += 2) {
if (equation[i].level == (level + 1))
break;
}
goto u_again;
} else {
if (*np - (e1 - b1) + len > n_tokens) {
error_huge();
}
blt(&equation[b1+len], &equation[e1], (*np - e1) * sizeof(token_type));
*np += len - (e1 - b1);
blt(&equation[b1], scratch, len * sizeof(token_type));
return true;
}
}
}
break;
case POWER:
#if !ALWAYS_UNFACTOR_POWER
/* avoid absolute values: */
if ((loc + 3) < *np && equation[loc+1].level == level && equation[loc+1].kind == CONSTANT
&& equation[loc+2].level == (level - 1) && equation[loc+2].token.operatr == POWER
&& equation[loc+3].kind == CONSTANT && ((equation[loc+3].level == (level - 1))
|| ((loc + 5) < *np && equation[loc+3].level == level
&& equation[loc+4].level == level && equation[loc+4].token.operatr == DIVIDE
&& equation[loc+5].level == level && equation[loc+5].kind == CONSTANT
&& ((loc + 6) >= *np || equation[loc+6].level < level)))) {
return false;
}
#endif
if (ii == 2 || ii == 0) {
for (i = b1 + 1; i < loc; i += 2) {
if (equation[i].level != level + 1)
continue;
switch (equation[i].token.operatr) {
case TIMES:
case DIVIDE:
break;
default:
goto do_pplus;
}
b2 = b1;
len = 0;
u1_again:
if (len + (i - b2) + (e1 - loc) + 1 > n_tokens) {
error_huge();
}
blt(&scratch[len], &equation[b2], (i - b2) * sizeof(token_type));
len += (i - b2);
blt(&scratch[len], &equation[loc], (e1 - loc) * sizeof(token_type));
j = len;
len += (e1 - loc);
for (; j < len; j++)
scratch[j].level++;
if (i < loc) {
scratch[len] = equation[i];
scratch[len].level--;
len++;
b2 = i + 1;
for (i += 2; i < loc; i += 2) {
if (equation[i].level == (level + 1))
break;
}
goto u1_again;
} else {
if (*np - (e1 - b1) + len > n_tokens) {
error_huge();
}
blt(&equation[b1+len], &equation[e1], (*np - e1) * sizeof(token_type));
*np += len - (e1 - b1);
blt(&equation[b1], scratch, len * sizeof(token_type));
return true;
}
}
}
do_pplus:
if (ii == 4 || ii == 0) {
for (i = loc + 2; i < e1; i += 2) {
if (equation[i].level != level + 1)
continue;
switch (equation[i].token.operatr) {
case PLUS:
case MINUS:
break;
default:
goto do_repeat;
}
b2 = loc + 1;
len = 0;
u2_again:
if (len + (loc - b1) + (i - b2) + 2 > n_tokens) {
error_huge();
}
j = len;
blt(&scratch[len], &equation[b1], (loc + 1 - b1) * sizeof(token_type));
len += (loc + 1 - b1);
for (; j < len; j++)
scratch[j].level++;
blt(&scratch[len], &equation[b2], (i - b2) * sizeof(token_type));
len += (i - b2);
if (i < e1) {
scratch[len].level = level;
scratch[len].kind = OPERATOR;
if (equation[i].token.operatr == PLUS)
scratch[len].token.operatr = TIMES;
else
scratch[len].token.operatr = DIVIDE;
len++;
b2 = i + 1;
for (i += 2; i < e1; i += 2) {
if (equation[i].level == (level + 1))
break;
}
goto u2_again;
} else {
if (*np - (e1 - b1) + len > n_tokens) {
error_huge();
}
blt(&equation[b1+len], &equation[e1], (*np - e1) * sizeof(token_type));
*np += len - (e1 - b1);
blt(&equation[b1], scratch, len * sizeof(token_type));
return true;
}
}
}
do_repeat:
/* unfactor power: (a + 1)^2 -> (a + 1)*(a + 1) */
if (ii != 6 && ii != 8)
break;
i = loc;
if (equation[i+1].level != level || equation[i+1].kind != CONSTANT)
break;
d1 = equation[i+1].token.constant;
if (!isfinite(d1) || d1 <= 1.0)
break;
if (ii != 8) { /* if true, only do useful expansions */
if (d1 > 100.0) /* ignore exponents over 100 */
break;
if ((i - b1) == 1 && equation[b1].kind != CONSTANT)
break;
if ((i - b1) > 1 && d1 > 2.0 && fmod(d1, 1.0) != 0.0)
break;
}
d1 = ceil(d1) - 1.0;
d2 = d1 * ((i - b1) + 1.0);
if ((*np + d2) > (n_tokens - 10)) {
break; /* too big to expand, do nothing */
}
j = d1;
k = d2;
blt(&equation[e1+k], &equation[e1], (*np - e1) * sizeof(token_type));
*np += k;
equation[i+1].token.constant -= d1;
k = e1;
while (j-- > 0) {
equation[k].level = level;
equation[k].kind = OPERATOR;
equation[k].token.operatr = TIMES;
blt(&equation[k+1], &equation[b1], (i - b1) * sizeof(token_type));
k += (i - b1) + 1;
}
if (equation[i+1].token.constant == 1.0) {
blt(&equation[i], &equation[e1], (*np - e1) * sizeof(token_type));
*np -= (e1 - i);
} else {
for (j = b1; j < e1; j++)
equation[j].level++;
}
return true;
}
return false;
}
static int
usp_sub(equation, np, i)
token_type *equation;
int *np, i;
{
int level;
int j;
level = equation[i].level;
for (j = i - 2;; j -= 2) {
if (j < 0)
return false;
if (equation[j].level < level) {
if (equation[j].level == (level - 1) && equation[j].token.operatr == DIVIDE) {
break;
} else
return false;
}
}
if ((*np + 2) > n_tokens) {
error_huge();
}
equation[j].token.operatr = TIMES;
for (j = i + 1;; j++) {
if (j >= *np || equation[j].level < level)
break;
equation[j].level++;
}
i++;
blt(&equation[i+2], &equation[i], (*np - i) * sizeof(token_type));
*np += 2;
equation[i].level = level + 1;
equation[i].kind = CONSTANT;
equation[i].token.constant = -1.0;
i++;
equation[i].level = level + 1;
equation[i].kind = OPERATOR;
equation[i].token.operatr = TIMES;
return true;
}
/*
* Convert a/(x^y) to a*x^(-1*y).
* If y is a constant, don't do.
*
* Return true if equation side is modified.
*/
int
unsimp_power(equation, np)
token_type *equation;
int *np;
{
int modified = false;
int i;
for (i = 1; i < *np; i += 2) {
if (equation[i].token.operatr == POWER) {
if (equation[i].level == equation[i+1].level
&& equation[i+1].kind == CONSTANT)
continue;
modified |= usp_sub(equation, np, i);
}
}
return modified;
}
#if 0 /* the following is not currently used */
/*
* Convert a/(x^y) to a*(1/x)^y.
* If y is a constant, don't do.
*
* Return true if equation side is modified.
*/
int
unsimp2_power(equation, np)
token_type *equation;
int *np;
{
int modified = false;
int i;
for (i = 1; i < *np; i += 2) {
if (equation[i].token.operatr == POWER) {
modified |= usp2_sub(equation, np, i);
}
}
return modified;
}
int
usp2_sub(equation, np, i)
token_type *equation;
int *np, i;
{
int level;
int j, k;
level = equation[i].level;
if (equation[i+1].level == level && equation[i+1].kind == CONSTANT) {
return false;
}
for (j = i - 2;; j -= 2) {
if (j < 0)
return false;
if (equation[j].level < level) {
if (equation[j].level == (level - 1) && equation[j].token.operatr == DIVIDE) {
break;
} else
return false;
}
}
if ((*np + 2) > n_tokens) {
error_huge();
}
equation[j].token.operatr = TIMES;
j++;
for (k = j; k < i; k++) {
equation[k].level++;
}
blt(&equation[j+2], &equation[j], (*np - j) * sizeof(token_type));
*np += 2;
equation[j].level = level + 1;
equation[j].kind = CONSTANT;
equation[j].token.constant = 1.0;
j++;
equation[j].level = level + 1;
equation[j].kind = OPERATOR;
equation[j].token.operatr = DIVIDE;
return true;
}
#endif
/*
* Convert anything times a negative constant to a positive constant
* divided by -1.
* When uf_times() is done after this, the additive denominators will be
* attempted to be negated, possibly getting rid of unneeded times -1.
*/
void
uf_neg_help(equation, np)
token_type *equation;
int *np;
{
int i;
int level;
for (i = 0; i < *np - 1; i += 2) {
if (equation[i].kind == CONSTANT && equation[i].token.constant < 0.0) {
level = equation[i].level;
if (equation[i+1].level == level) {
switch (equation[i+1].token.operatr) {
case TIMES:
case DIVIDE:
if ((*np + 2) > n_tokens) {
error_huge();
}
blt(&equation[i+3], &equation[i+1], (*np - (i + 1)) * sizeof(token_type));
*np += 2;
equation[i].token.constant = -equation[i].token.constant;
i++;
equation[i].level = level;
equation[i].kind = OPERATOR;
equation[i].token.operatr = DIVIDE;
i++;
equation[i].level = level;
equation[i].kind = CONSTANT;
equation[i].token.constant = -1.0;
break;
}
}
}
}
}
/*
* Simplify division by irrational constants (roots like 2^.5) by
* converting 1/(k1^k2) to 1/(k1^(k2-1))/k1 if k1 is integer,
* otherwise convert 1/(k1^k2) to 1*((1/k1)^k2).
*
* Return true if equation side was modified.
*/
int
patch_root_div(equation, np)
token_type *equation;
int *np;
{
int i;
int level;
int modified = false;
for (i = 1; i < *np - 2; i += 2) {
if (equation[i].token.operatr == DIVIDE) {
level = equation[i].level + 1;
if (equation[i+2].token.operatr == POWER
&& equation[i+2].level == level
&& equation[i+1].kind == CONSTANT
&& equation[i+3].level == level
&& equation[i+3].kind == CONSTANT) {
if (fmod(equation[i+1].token.constant, 1.0) == 0.0) {
if (!rationalize_denominators
|| !isfinite(equation[i+3].token.constant)
|| equation[i+3].token.constant <= 0.0
|| equation[i+3].token.constant >= 1.0) {
continue;
}
if (*np + 2 > n_tokens)
error_huge();
equation[i+3].token.constant -= 1.0;
blt(&equation[i+2], &equation[i], (*np - i) * sizeof(token_type));
*np += 2;
i++;
equation[i].level = level - 1;
equation[i].kind = CONSTANT;
equation[i].token.constant = equation[i+2].token.constant;
i++;
} else {
equation[i].token.operatr = TIMES;
equation[i+1].token.constant = 1.0 / equation[i+1].token.constant;
}
modified = true;
}
}
}
return modified;
}