mirror of
https://github.com/mfillpot/mathomatic.git
synced 2026-01-08 04:29:39 +00:00
1173 lines
30 KiB
C
1173 lines
30 KiB
C
/*
|
|
* Mathomatic symbolic factorizing routines, not polynomial factoring.
|
|
*
|
|
* 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"
|
|
|
|
#define ALWAYS_FACTOR_POWER 1
|
|
|
|
static int fplus_recurse(token_type *equation, int *np, int loc, int level, long v, double d, int whole_flag, int div_only);
|
|
static int fplus_sub(token_type *equation, int *np, int loc, int i1, int n1, int i2, int n2, int level, long v, double d, int whole_flag, int div_only);
|
|
static int big_fplus(token_type *equation, int level, int diff_sign, int sop1, int op1, int op2, int i1, int i2, int b1, int b2, int ai, int aj, int i, int j, int e1, int e2);
|
|
static int ftimes_recurse(token_type *equation, int *np, int loc, int level);
|
|
static int ftimes_sub(token_type *equation, int *np, int loc, int i1, int n1, int i2, int n2, int level);
|
|
static int fpower_recurse(token_type *equation, int *np, int loc, int level);
|
|
static int fpower_sub(token_type *equation, int *np, int loc, int i1, int n1, int i2, int n2, int level);
|
|
|
|
/*
|
|
* Factor divides only: (a/c + b/c) -> ((a+b)/c).
|
|
*
|
|
* Return true if equation side was modified.
|
|
*/
|
|
int
|
|
factor_divide(equation, np, v, d)
|
|
token_type *equation;
|
|
int *np;
|
|
long v;
|
|
double d;
|
|
{
|
|
return fplus_recurse(equation, np, 0, 1, v, d, false, true);
|
|
}
|
|
|
|
/*
|
|
* Take care of subtraction and addition of the same expression
|
|
* multiplied by constants: (2*a + 3*a - a) -> (4*a).
|
|
*
|
|
* Return true if equation side was modified.
|
|
*/
|
|
int
|
|
subtract_itself(equation, np)
|
|
token_type *equation;
|
|
int *np;
|
|
{
|
|
return fplus_recurse(equation, np, 0, 1, 0L, 0.0, true, false);
|
|
}
|
|
|
|
/*
|
|
* Factor equation side: (a*c + b*c) -> (c*(a + b)).
|
|
*
|
|
* If "v" and "d" equal 0, factor anything,
|
|
* including identical bases raised to powers (Horner factoring): (x^2 + x) -> (x*(x + 1)).
|
|
* If "d" equals 1.0, only factor identical bases raised to the power of a constant.
|
|
*
|
|
* If "v" is a variable, or MATCH_ANY, only factor expressions containing that variable,
|
|
* or any variable, respectively, with no Horner factoring.
|
|
* IF "v" is not MATCH_ANY, and "d" is not equal to 0.0 or 1.0,
|
|
* factor only expressions containing "v" raised to the power of "d".
|
|
*
|
|
* In order to factor down to the smallest possible expression,
|
|
* factoring the most common and largest sub-expressions needs to be done first.
|
|
* Mathomatic does not have the ability to do that.
|
|
* However, simpb_side() factors expressions with the most used variables first,
|
|
* which is helpful.
|
|
*
|
|
* Return true if equation side was modified.
|
|
*/
|
|
int
|
|
factor_plus(equation, np, v, d)
|
|
token_type *equation; /* pointer to beginning of equation side */
|
|
int *np; /* pointer to length of equation side */
|
|
long v; /* Mathomatic variable */
|
|
double d; /* control exponent */
|
|
{
|
|
return fplus_recurse(equation, np, 0, 1, v, d, false, false);
|
|
}
|
|
|
|
/*
|
|
* Recursively factor at "level" of parentheses and deeper,
|
|
* beginning at index "loc".
|
|
*
|
|
* Return true if equation side was modified.
|
|
*/
|
|
static int
|
|
fplus_recurse(equation, np, loc, level, v, d, whole_flag, div_only)
|
|
token_type *equation;
|
|
int *np, loc, level;
|
|
long v;
|
|
double d;
|
|
int whole_flag; /* factor only whole expressions multiplied by a constant */
|
|
int div_only; /* factor only divides */
|
|
{
|
|
int modified = false;
|
|
int i, j, k;
|
|
int op = 0;
|
|
int len1, len2;
|
|
|
|
for (i = loc + 1; i < *np && equation[i].level >= level; i += 2) {
|
|
if (equation[i].level == level) {
|
|
op = equation[i].token.operatr;
|
|
break;
|
|
}
|
|
}
|
|
switch (op) {
|
|
case PLUS:
|
|
case MINUS:
|
|
for (i = loc;;) {
|
|
f_again:
|
|
for (k = i + 1;; k += 2) {
|
|
if (k >= *np || equation[k].level <= level)
|
|
break;
|
|
}
|
|
len1 = k - i;
|
|
for (j = i + len1 + 1;; j += len2 + 1) {
|
|
if (j >= *np || equation[j-1].level < level)
|
|
break;
|
|
for (k = j + 1;; k += 2) {
|
|
if (k >= *np || equation[k].level <= level)
|
|
break;
|
|
}
|
|
len2 = k - j;
|
|
if (fplus_sub(equation, np, loc, i, len1, j, len2, level + 1, v, d, whole_flag, div_only)) {
|
|
modified = true;
|
|
goto f_again;
|
|
}
|
|
}
|
|
i += len1 + 1;
|
|
if (i >= *np || equation[i-1].level < level)
|
|
break;
|
|
}
|
|
}
|
|
if (modified)
|
|
return true;
|
|
for (i = loc; i < *np && equation[i].level >= level;) {
|
|
if (equation[i].level > level) {
|
|
modified |= fplus_recurse(equation, np, i, level + 1, v, d, whole_flag, div_only);
|
|
i++;
|
|
for (; i < *np && equation[i].level > level; i += 2)
|
|
;
|
|
continue;
|
|
}
|
|
i++;
|
|
}
|
|
return modified;
|
|
}
|
|
|
|
/*
|
|
* Do the factoring of two sub-expressions added together.
|
|
*
|
|
* Return true if a transformation was made.
|
|
*/
|
|
static int
|
|
fplus_sub(equation, np, loc, i1, n1, i2, n2, level, v, d, whole_flag, div_only)
|
|
token_type *equation; /* the entire expression */
|
|
int *np; /* pointer to length of the entire expression */
|
|
int loc; /* index into the beginning of this additive sub-expression */
|
|
int i1, n1, i2, n2;
|
|
int level;
|
|
long v;
|
|
double d;
|
|
int whole_flag; /* factor only whole expressions multiplied by a constant */
|
|
int div_only; /* factor only divides */
|
|
{
|
|
int e1, e2;
|
|
int op1, op2;
|
|
int i, j, k, l, m;
|
|
int b1, b2;
|
|
int len;
|
|
int sop1;
|
|
int diff_sign;
|
|
int ai, aj;
|
|
int flag1, flag2;
|
|
int same_flag;
|
|
double save_k1, save_k2;
|
|
double save_d1, save_d2;
|
|
double power; /* for constant power horner factoring */
|
|
double d1, d2;
|
|
|
|
e1 = i1 + n1;
|
|
e2 = i2 + n2;
|
|
op2 = equation[i2-1].token.operatr;
|
|
if (i1 <= loc) {
|
|
op1 = PLUS;
|
|
} else {
|
|
op1 = equation[i1-1].token.operatr;
|
|
}
|
|
i = i1 - 1;
|
|
f_outer:
|
|
b1 = i + 1;
|
|
if (b1 >= e1)
|
|
return false;
|
|
if (whole_flag) {
|
|
i = e1;
|
|
if (n1 > 1 && equation[b1].kind == CONSTANT
|
|
&& equation[b1+1].level == level
|
|
&& (equation[b1+1].token.operatr == TIMES
|
|
|| equation[b1+1].token.operatr == DIVIDE)) {
|
|
b1 += 2;
|
|
}
|
|
} else {
|
|
i = b1 + 1;
|
|
for (; i < e1; i += 2) {
|
|
if (equation[i].level == level
|
|
&& (equation[i].token.operatr == TIMES
|
|
|| equation[i].token.operatr == DIVIDE)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (b1 <= i1)
|
|
sop1 = TIMES;
|
|
else
|
|
sop1 = equation[b1-1].token.operatr;
|
|
if ((div_only && sop1 != DIVIDE)
|
|
|| (i - b1 == 1 && equation[b1].kind == CONSTANT && fabs(equation[b1].token.constant) == 1.0)) {
|
|
goto f_outer;
|
|
}
|
|
if (!whole_flag && (v != MATCH_ANY)) {
|
|
if (d == 0.0 || d == 1.0) {
|
|
if (v) {
|
|
for (k = b1;; k += 2) {
|
|
if (k >= i)
|
|
goto f_outer;
|
|
if (equation[k].kind == VARIABLE && equation[k].token.variable == v) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
for (k = b1 + 1;; k += 2) {
|
|
if (k >= i)
|
|
goto f_outer;
|
|
if (equation[k].token.operatr == POWER
|
|
&& equation[k].level == equation[k+1].level
|
|
&& equation[k+1].kind == CONSTANT
|
|
&& equation[k+1].token.constant == d) {
|
|
if (v == 0)
|
|
goto factor_this;
|
|
for (l = k - 1; l >= 0; l--) {
|
|
if (equation[l].level < equation[k].level)
|
|
break;
|
|
if (equation[l].kind == VARIABLE
|
|
&& equation[l].token.variable == v)
|
|
goto factor_this;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
factor_this:
|
|
j = i2 - 1;
|
|
f_inner:
|
|
b2 = j + 1;
|
|
if (b2 >= e2)
|
|
goto f_outer;
|
|
if (whole_flag) {
|
|
j = e2;
|
|
if (n2 > 1 && equation[b2].kind == CONSTANT
|
|
&& equation[b2+1].level == level
|
|
&& (equation[b2+1].token.operatr == TIMES
|
|
|| equation[b2+1].token.operatr == DIVIDE)) {
|
|
b2 += 2;
|
|
}
|
|
} else {
|
|
j = b2 + 1;
|
|
for (; j < e2; j += 2) {
|
|
if (equation[j].level == level
|
|
&& (equation[j].token.operatr == TIMES
|
|
|| equation[j].token.operatr == DIVIDE)) {
|
|
break;
|
|
}
|
|
}
|
|
if (b2 <= i2) {
|
|
if (sop1 == DIVIDE)
|
|
goto f_inner;
|
|
} else {
|
|
if (equation[b2-1].token.operatr != sop1)
|
|
goto f_inner;
|
|
}
|
|
}
|
|
if (j - b2 == 1 && equation[b2].kind == CONSTANT && fabs(equation[b2].token.constant) == 1.0) {
|
|
goto f_inner;
|
|
}
|
|
ai = i;
|
|
aj = j;
|
|
save_k1 = 0.0;
|
|
save_k2 = 0.0;
|
|
flag1 = (whole_flag && b1 > i1);
|
|
if (flag1) {
|
|
b1 = i1;
|
|
save_k1 = equation[b1].token.constant;
|
|
equation[b1].token.constant = 1.0;
|
|
}
|
|
flag2 = (whole_flag && b2 > i2);
|
|
if (flag2) {
|
|
b2 = i2;
|
|
save_k2 = equation[b2].token.constant;
|
|
equation[b2].token.constant = 1.0;
|
|
}
|
|
same_flag = se_compare(&equation[b1], i - b1, &equation[b2], j - b2, &diff_sign);
|
|
if (flag1) {
|
|
equation[i1].token.constant = save_k1;
|
|
b1 += 2;
|
|
}
|
|
if (flag2) {
|
|
equation[i2].token.constant = save_k2;
|
|
b2 += 2;
|
|
}
|
|
if (same_flag) {
|
|
/* do the factor transformation */
|
|
power = 1.0; /* not doing Horner factoring */
|
|
horner_factor:
|
|
if (sop1 == DIVIDE) {
|
|
scratch[0].level = level;
|
|
scratch[0].kind = CONSTANT;
|
|
scratch[0].token.constant = 1.0;
|
|
scratch[1].level = level;
|
|
scratch[1].kind = OPERATOR;
|
|
scratch[1].token.operatr = DIVIDE;
|
|
len = 2;
|
|
} else {
|
|
len = 0;
|
|
}
|
|
k = len;
|
|
blt(&scratch[len], &equation[b1], (ai - b1) * sizeof(token_type));
|
|
len += ai - b1;
|
|
if (power != 1.0) {
|
|
for (; k < len; k++)
|
|
scratch[k].level += 2;
|
|
scratch[len].level = level + 1;
|
|
scratch[len].kind = OPERATOR;
|
|
scratch[len].token.operatr = POWER;
|
|
len++;
|
|
scratch[len].level = level + 1;
|
|
scratch[len].kind = CONSTANT;
|
|
scratch[len].token.constant = power;
|
|
len++;
|
|
if (always_positive(power))
|
|
diff_sign = false;
|
|
} else if (b1 == i1 && ai == e1) {
|
|
for (; k < len; k++)
|
|
scratch[k].level++;
|
|
}
|
|
scratch[len].level = level;
|
|
scratch[len].kind = OPERATOR;
|
|
scratch[len].token.operatr = TIMES;
|
|
len++;
|
|
k = len;
|
|
blt(&scratch[len], &equation[i1], (b1 - i1) * sizeof(token_type));
|
|
len += b1 - i1;
|
|
if (ai != i) {
|
|
l = len;
|
|
m = len + ai - b1;
|
|
blt(&scratch[len], &equation[b1], (i - b1) * sizeof(token_type));
|
|
len += i - b1;
|
|
if (b1 == i1 && i == e1) {
|
|
for (; l < len; l++)
|
|
scratch[l].level++;
|
|
}
|
|
l = m;
|
|
m++;
|
|
for (; m < len; m++)
|
|
scratch[m].level++;
|
|
scratch[len].level = scratch[l].level + 1;
|
|
scratch[len].kind = OPERATOR;
|
|
scratch[len].token.operatr = MINUS;
|
|
len++;
|
|
scratch[len].level = scratch[l].level + 1;
|
|
scratch[len].kind = CONSTANT;
|
|
scratch[len].token.constant = power;
|
|
len++;
|
|
scratch[len].level = level;
|
|
scratch[len].kind = OPERATOR;
|
|
scratch[len].token.operatr = TIMES;
|
|
len++;
|
|
}
|
|
scratch[len].level = level;
|
|
scratch[len].kind = CONSTANT;
|
|
if (op1 == MINUS) {
|
|
scratch[len].token.constant = -1.0;
|
|
} else {
|
|
scratch[len].token.constant = 1.0;
|
|
}
|
|
len++;
|
|
blt(&scratch[len], &equation[i], (e1 - i) * sizeof(token_type));
|
|
len += e1 - i;
|
|
for (; k < len; k++)
|
|
scratch[k].level += 2;
|
|
scratch[len].level = level + 1;
|
|
scratch[len].kind = OPERATOR;
|
|
diff_sign ^= (op2 == MINUS);
|
|
if (diff_sign) {
|
|
scratch[len].token.operatr = MINUS;
|
|
} else {
|
|
scratch[len].token.operatr = PLUS;
|
|
}
|
|
len++;
|
|
k = len;
|
|
if (aj != j) {
|
|
if (len + n2 + 2 > n_tokens) {
|
|
error_huge();
|
|
}
|
|
} else {
|
|
if (len + (b2 - i2) + (e2 - j) + 1 > n_tokens) {
|
|
error_huge();
|
|
}
|
|
}
|
|
blt(&scratch[len], &equation[i2], (b2 - i2) * sizeof(token_type));
|
|
len += b2 - i2;
|
|
if (aj != j) {
|
|
m = len + aj - b2;
|
|
blt(&scratch[len], &equation[b2], (j - b2) * sizeof(token_type));
|
|
len += j - b2;
|
|
l = m;
|
|
m++;
|
|
for (; m < len; m++)
|
|
scratch[m].level++;
|
|
scratch[len].level = scratch[l].level + 1;
|
|
scratch[len].kind = OPERATOR;
|
|
scratch[len].token.operatr = MINUS;
|
|
len++;
|
|
scratch[len].level = scratch[l].level + 1;
|
|
scratch[len].kind = CONSTANT;
|
|
scratch[len].token.constant = power;
|
|
len++;
|
|
} else {
|
|
scratch[len].level = level;
|
|
scratch[len].kind = CONSTANT;
|
|
scratch[len].token.constant = 1.0;
|
|
len++;
|
|
}
|
|
blt(&scratch[len], &equation[j], (e2 - j) * sizeof(token_type));
|
|
len += e2 - j;
|
|
for (; k < len; k++)
|
|
scratch[k].level += 2;
|
|
end_mess:
|
|
if (*np + len - n1 - (n2 + 1) > n_tokens) {
|
|
error_huge();
|
|
}
|
|
if (op1 == MINUS) {
|
|
equation[i1-1].token.operatr = PLUS;
|
|
}
|
|
blt(&equation[i2-1], &equation[e2], (*np - e2) * sizeof(token_type));
|
|
*np -= n2 + 1;
|
|
blt(&equation[i1+len], &equation[e1], (*np - e1) * sizeof(token_type));
|
|
*np += len - n1;
|
|
blt(&equation[i1], scratch, len * sizeof(token_type));
|
|
return true;
|
|
}
|
|
if (whole_flag)
|
|
return false;
|
|
if (v || div_only)
|
|
goto f_inner; /* don't do Horner factoring */
|
|
if (b1 == i1 && i == e1)
|
|
k = level;
|
|
else
|
|
k = level + 1;
|
|
save_d1 = 1.0;
|
|
for (l = b1 + 1;; l += 2) {
|
|
if (l >= i) {
|
|
break;
|
|
}
|
|
if (equation[l].level == k && equation[l].token.operatr == POWER) {
|
|
if (equation[l+1].level == k && equation[l+1].kind == CONSTANT) {
|
|
save_d1 = equation[l+1].token.constant;
|
|
if (save_d1 <= 0.0)
|
|
goto f_inner;
|
|
} else {
|
|
save_d1 = -1.0;
|
|
}
|
|
ai = l;
|
|
break;
|
|
}
|
|
}
|
|
if (b2 == i2 && j == e2)
|
|
k = level;
|
|
else
|
|
k = level + 1;
|
|
save_d2 = 1.0;
|
|
for (l = b2 + 1;; l += 2) {
|
|
if (l >= j) {
|
|
break;
|
|
}
|
|
if (equation[l].level == k && equation[l].token.operatr == POWER) {
|
|
if (equation[l+1].level == k && equation[l+1].kind == CONSTANT) {
|
|
save_d2 = equation[l+1].token.constant;
|
|
if (save_d2 <= 0.0)
|
|
goto f_inner;
|
|
} else {
|
|
save_d2 = -1.0;
|
|
}
|
|
aj = l;
|
|
break;
|
|
}
|
|
}
|
|
if (ai == i && aj == j)
|
|
goto f_inner;
|
|
if (ai - b1 == 1 && equation[b1].kind == CONSTANT)
|
|
goto f_inner;
|
|
if (d == 1.0 && (save_d1 < 0.0 || save_d2 < 0.0))
|
|
goto f_inner;
|
|
if (se_compare(&equation[b1], ai - b1, &equation[b2], aj - b2, &diff_sign)) {
|
|
if (save_d1 > 0.0 || save_d2 > 0.0) {
|
|
if (save_d1 < 0.0) {
|
|
power = save_d2;
|
|
} else if (save_d2 < 0.0) {
|
|
power = save_d1;
|
|
} else {
|
|
power = min(save_d1, save_d2);
|
|
if (!diff_sign && (fmod(power, 1.0) != 0.0)) {
|
|
if (fmod(max(save_d1, save_d2) - power, 1.0) == 0.0) {
|
|
goto horner_factor;
|
|
}
|
|
}
|
|
}
|
|
if (power < 1.0)
|
|
goto f_inner;
|
|
modf(power, &power); /* same as power = trunc(power); ? */
|
|
goto horner_factor;
|
|
}
|
|
d1 = i - ai;
|
|
d2 = j - aj;
|
|
if (d1 == d2) {
|
|
d1 = 1.0;
|
|
d2 = 1.0;
|
|
if ((ai + 2) < i) {
|
|
k = equation[ai].level;
|
|
if (equation[ai+1].level == (k + 1)
|
|
&& equation[ai+2].level == (k + 1)
|
|
&& equation[ai+1].kind == CONSTANT
|
|
&& (equation[ai+2].token.operatr == TIMES
|
|
|| equation[ai+2].token.operatr == DIVIDE)) {
|
|
d1 = fabs(equation[ai+1].token.constant);
|
|
}
|
|
}
|
|
if ((aj + 2) < j) {
|
|
k = equation[aj].level;
|
|
if (equation[aj+1].level == (k + 1)
|
|
&& equation[aj+2].level == (k + 1)
|
|
&& equation[aj+1].kind == CONSTANT
|
|
&& (equation[aj+2].token.operatr == TIMES
|
|
|| equation[aj+2].token.operatr == DIVIDE)) {
|
|
d2 = fabs(equation[aj+1].token.constant);
|
|
}
|
|
}
|
|
}
|
|
if (d1 <= d2) {
|
|
len = big_fplus(equation, level, diff_sign, sop1,
|
|
op1, op2, i1, i2, b1, b2, ai, aj, i, j, e1, e2);
|
|
} else {
|
|
len = big_fplus(equation, level, diff_sign, sop1,
|
|
op2, op1, i2, i1, b2, b1, aj, ai, j, i, e2, e1);
|
|
}
|
|
goto end_mess;
|
|
}
|
|
goto f_inner;
|
|
}
|
|
|
|
/*
|
|
* Factor transformation for a more general pair of sub-expressions added together
|
|
* with a common base and any exponent.
|
|
*/
|
|
static int
|
|
big_fplus(equation, level, diff_sign, sop1, op1, op2, i1, i2, b1, b2, ai, aj, i, j, e1, e2)
|
|
token_type *equation;
|
|
int level;
|
|
int diff_sign;
|
|
int sop1;
|
|
int op1, op2;
|
|
int i1, i2;
|
|
int b1, b2;
|
|
int ai, aj;
|
|
int i, j;
|
|
int e1, e2;
|
|
{
|
|
int k, l, m, n, o;
|
|
int len;
|
|
|
|
if (sop1 == DIVIDE) {
|
|
scratch[0].level = level;
|
|
scratch[0].kind = CONSTANT;
|
|
scratch[0].token.constant = 1.0;
|
|
scratch[1].level = level;
|
|
scratch[1].kind = OPERATOR;
|
|
scratch[1].token.operatr = DIVIDE;
|
|
len = 2;
|
|
} else {
|
|
len = 0;
|
|
}
|
|
k = len;
|
|
o = len + ai - b1;
|
|
blt(&scratch[len], &equation[b1], (i - b1) * sizeof(token_type));
|
|
len += i - b1;
|
|
if (b1 == i1 && i == e1) {
|
|
for (; k < len; k++)
|
|
scratch[k].level++;
|
|
}
|
|
scratch[len].level = level;
|
|
scratch[len].kind = OPERATOR;
|
|
scratch[len].token.operatr = TIMES;
|
|
len++;
|
|
k = len;
|
|
blt(&scratch[len], &equation[i1], (b1 - i1) * sizeof(token_type));
|
|
len += b1 - i1;
|
|
scratch[len].level = level;
|
|
scratch[len].kind = CONSTANT;
|
|
if (op1 == MINUS) {
|
|
scratch[len].token.constant = -1.0;
|
|
} else {
|
|
scratch[len].token.constant = 1.0;
|
|
}
|
|
len++;
|
|
blt(&scratch[len], &equation[i], (e1 - i) * sizeof(token_type));
|
|
len += e1 - i;
|
|
for (; k < len; k++)
|
|
scratch[k].level += 2;
|
|
scratch[len].level = level + 1;
|
|
scratch[len].kind = OPERATOR;
|
|
scratch[len].token.operatr = op2;
|
|
len++;
|
|
k = len;
|
|
blt(&scratch[len], &equation[i2], (b2 - i2) * sizeof(token_type));
|
|
len += b2 - i2;
|
|
if (len + (e2 - b2) + 2 * (i - ai) + 2 > n_tokens) {
|
|
error_huge();
|
|
}
|
|
n = len;
|
|
m = len + aj - b2;
|
|
blt(&scratch[len], &equation[b2], (j - b2) * sizeof(token_type));
|
|
len += j - b2;
|
|
l = m;
|
|
m++;
|
|
for (; m < len; m++)
|
|
scratch[m].level++;
|
|
if (diff_sign && b2 == i2 && j == e2) {
|
|
for (; n < len; n++)
|
|
scratch[n].level++;
|
|
}
|
|
scratch[len].level = scratch[l].level + 1;
|
|
scratch[len].kind = OPERATOR;
|
|
scratch[len].token.operatr = MINUS;
|
|
len++;
|
|
m = len;
|
|
blt(&scratch[len], &equation[ai+1], (i - (ai + 1)) * sizeof(token_type));
|
|
len += i - (ai + 1);
|
|
n = min_level(&equation[ai+1], i - (ai + 1));
|
|
n = scratch[l].level + 2 - n;
|
|
for (; m < len; m++)
|
|
scratch[m].level += n;
|
|
if (diff_sign) {
|
|
scratch[len].level = level;
|
|
scratch[len].kind = OPERATOR;
|
|
if (sop1 == DIVIDE)
|
|
scratch[len].token.operatr = TIMES;
|
|
else
|
|
scratch[len].token.operatr = DIVIDE;
|
|
len++;
|
|
scratch[len].level = level + 1;
|
|
scratch[len].kind = CONSTANT;
|
|
scratch[len].token.constant = -1.0;
|
|
len++;
|
|
blt(&scratch[len], &scratch[o], (i - ai) * sizeof(token_type));
|
|
len += i - ai;
|
|
}
|
|
blt(&scratch[len], &equation[j], (e2 - j) * sizeof(token_type));
|
|
len += e2 - j;
|
|
for (; k < len; k++)
|
|
scratch[k].level += 2;
|
|
return len;
|
|
}
|
|
|
|
/*
|
|
* Factor equation side.
|
|
* a^b * a^c -> a^(b + c).
|
|
* Return true if equation side was modified.
|
|
*/
|
|
int
|
|
factor_times(equation, np)
|
|
token_type *equation;
|
|
int *np;
|
|
{
|
|
return ftimes_recurse(equation, np, 0, 1);
|
|
}
|
|
|
|
static int
|
|
ftimes_recurse(equation, np, loc, level)
|
|
token_type *equation;
|
|
int *np, loc, level;
|
|
{
|
|
int modified = false;
|
|
int i, j, k;
|
|
int op = 0;
|
|
int len1, len2;
|
|
|
|
for (i = loc + 1; i < *np && equation[i].level >= level; i += 2) {
|
|
if (equation[i].level == level) {
|
|
op = equation[i].token.operatr;
|
|
break;
|
|
}
|
|
}
|
|
switch (op) {
|
|
case TIMES:
|
|
case DIVIDE:
|
|
for (i = loc;;) {
|
|
f_again:
|
|
for (k = i + 1;; k += 2) {
|
|
if (k >= *np || equation[k].level <= level)
|
|
break;
|
|
}
|
|
len1 = k - i;
|
|
for (j = i + len1 + 1;; j += len2 + 1) {
|
|
if (j >= *np || equation[j-1].level < level)
|
|
break;
|
|
for (k = j + 1;; k += 2) {
|
|
if (k >= *np || equation[k].level <= level)
|
|
break;
|
|
}
|
|
len2 = k - j;
|
|
if (ftimes_sub(equation, np, loc, i, len1, j, len2, level + 1)) {
|
|
modified = true;
|
|
goto f_again;
|
|
}
|
|
}
|
|
i += len1 + 1;
|
|
if (i >= *np || equation[i-1].level < level)
|
|
break;
|
|
}
|
|
}
|
|
if (modified)
|
|
return true;
|
|
for (i = loc; i < *np && equation[i].level >= level;) {
|
|
if (equation[i].level > level) {
|
|
modified |= ftimes_recurse(equation, np, i, level + 1);
|
|
i++;
|
|
for (; i < *np && equation[i].level > level; i += 2)
|
|
;
|
|
continue;
|
|
}
|
|
i++;
|
|
}
|
|
return modified;
|
|
}
|
|
|
|
static int
|
|
ftimes_sub(equation, np, loc, i1, n1, i2, n2, level)
|
|
token_type *equation;
|
|
int *np, loc, i1, n1, i2, n2, level;
|
|
{
|
|
int e1, e2;
|
|
int op1, op2;
|
|
int i, j, k;
|
|
int len, rlen1, len2;
|
|
int diff_sign;
|
|
int both_divide;
|
|
|
|
e1 = i1 + n1;
|
|
e2 = i2 + n2;
|
|
op2 = equation[i2-1].token.operatr;
|
|
if (i1 <= loc) {
|
|
op1 = TIMES;
|
|
} else {
|
|
op1 = equation[i1-1].token.operatr;
|
|
}
|
|
#if 1
|
|
if ((n1 == 1 && equation[i1].kind == CONSTANT) && (n2 == 1 && equation[i2].kind == CONSTANT)) {
|
|
return false;
|
|
}
|
|
#else
|
|
if ((n1 == 1 && equation[i1].kind == CONSTANT) || (n2 == 1 && equation[i2].kind == CONSTANT)) {
|
|
return false;
|
|
}
|
|
#endif
|
|
both_divide = (op1 == DIVIDE && op2 == DIVIDE);
|
|
if (se_compare(&equation[i1], n1, &equation[i2], n2, &diff_sign)) {
|
|
i = e1;
|
|
j = e2;
|
|
goto common_base;
|
|
}
|
|
for (i = i1 + 1; i < e1; i += 2) {
|
|
if (equation[i].level == level && equation[i].token.operatr == POWER) {
|
|
break;
|
|
}
|
|
}
|
|
for (j = i2 + 1; j < e2; j += 2) {
|
|
if (equation[j].level == level && equation[j].token.operatr == POWER) {
|
|
break;
|
|
}
|
|
}
|
|
if (i >= e1 && j >= e2) {
|
|
return false;
|
|
}
|
|
if (se_compare(&equation[i1], i - i1, &equation[i2], j - i2, &diff_sign)) {
|
|
goto common_base;
|
|
}
|
|
if (i < e1 && j < e2) {
|
|
if (se_compare(&equation[i1], n1, &equation[i2], j - i2, &diff_sign)) {
|
|
i = e1;
|
|
goto common_base;
|
|
}
|
|
if (se_compare(&equation[i1], i - i1, &equation[i2], n2, &diff_sign)) {
|
|
j = e2;
|
|
goto common_base;
|
|
}
|
|
}
|
|
return false;
|
|
|
|
common_base:
|
|
rlen1 = ((i == e1) ? 1 : (e1 - i - 1));
|
|
len = (i - i1) + 1 + ((op1 == DIVIDE && !both_divide) ? 2 : 0) + rlen1 + 1
|
|
+ ((j == e2) ? 1 : (e2 - j - 1));
|
|
len -= n1;
|
|
if (j - i2 == 1 && equation[i2].kind == CONSTANT && equation[i2].token.constant == -1.0)
|
|
return false;
|
|
if (diff_sign) {
|
|
if (j - i2 == 1 && equation[i2].kind == CONSTANT)
|
|
return false;
|
|
len2 = 2 + e2 - j;
|
|
if (*np + len2 + len > n_tokens) {
|
|
error_huge();
|
|
}
|
|
blt(&equation[e2+len2], &equation[e2], (*np - e2) * sizeof(token_type));
|
|
*np += len2;
|
|
equation[e2].level = level - 1;
|
|
equation[e2].kind = OPERATOR;
|
|
equation[e2].token.operatr = op2;
|
|
equation[e2+1].level = level;
|
|
equation[e2+1].kind = CONSTANT;
|
|
equation[e2+1].token.constant = -1.0;
|
|
blt(&equation[e2+2], &equation[j], (e2 - j) * sizeof(token_type));
|
|
}
|
|
if (*np + len > n_tokens) {
|
|
error_huge();
|
|
}
|
|
blt(&equation[e1+len], &equation[e1], (*np - e1) * sizeof(token_type));
|
|
*np += len;
|
|
if (i == e1) {
|
|
for (k = i1; k < e1; k++)
|
|
equation[k].level++;
|
|
equation[i].level = level;
|
|
equation[i].kind = OPERATOR;
|
|
equation[i].token.operatr = POWER;
|
|
equation[i+1].level = level;
|
|
equation[i+1].kind = CONSTANT;
|
|
equation[i+1].token.constant = 1.0;
|
|
}
|
|
if (op1 == DIVIDE && !both_divide) {
|
|
equation[i1-1].token.operatr = TIMES;
|
|
blt(&equation[i+3], &equation[i+1], rlen1 * sizeof(token_type));
|
|
i++;
|
|
equation[i].level = level;
|
|
equation[i].kind = CONSTANT;
|
|
equation[i].token.constant = -1.0;
|
|
i++;
|
|
equation[i].level = level;
|
|
equation[i].kind = OPERATOR;
|
|
equation[i].token.operatr = TIMES;
|
|
binary_parenthesize(equation, i + 1 + rlen1, i);
|
|
}
|
|
i += rlen1 + 1;
|
|
equation[i].level = level;
|
|
equation[i].kind = OPERATOR;
|
|
if (op2 == DIVIDE && !both_divide)
|
|
equation[i].token.operatr = MINUS;
|
|
else
|
|
equation[i].token.operatr = PLUS;
|
|
if (j == e2) {
|
|
equation[i+1].level = level;
|
|
equation[i+1].kind = CONSTANT;
|
|
equation[i+1].token.constant = 1.0;
|
|
binary_parenthesize(equation, i + 2, i);
|
|
} else {
|
|
blt(&equation[i+1], &equation[j+len+1], (e2 - j - 1) * sizeof(token_type));
|
|
binary_parenthesize(equation, i + e2 - j, i);
|
|
}
|
|
blt(&equation[i2+len-1], &equation[e2+len], (*np - (e2 + len)) * sizeof(token_type));
|
|
*np -= n2 + 1;
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Factor equation side.
|
|
* a^c * b^c -> (a * b)^c.
|
|
* Return true if equation side was modified.
|
|
*/
|
|
int
|
|
factor_power(equation, np)
|
|
token_type *equation;
|
|
int *np;
|
|
{
|
|
return fpower_recurse(equation, np, 0, 1);
|
|
}
|
|
|
|
static int
|
|
fpower_recurse(equation, np, loc, level)
|
|
token_type *equation;
|
|
int *np, loc, level;
|
|
{
|
|
int modified = false;
|
|
int i, j, k;
|
|
int op = 0;
|
|
int len1, len2;
|
|
|
|
for (i = loc + 1; i < *np && equation[i].level >= level; i += 2) {
|
|
if (equation[i].level == level) {
|
|
op = equation[i].token.operatr;
|
|
break;
|
|
}
|
|
}
|
|
switch (op) {
|
|
case TIMES:
|
|
case DIVIDE:
|
|
for (i = loc;;) {
|
|
f_again:
|
|
for (k = i + 1;; k += 2) {
|
|
if (k >= *np || equation[k].level <= level)
|
|
break;
|
|
}
|
|
len1 = k - i;
|
|
for (j = i + len1 + 1;; j += len2 + 1) {
|
|
if (j >= *np || equation[j-1].level < level)
|
|
break;
|
|
for (k = j + 1;; k += 2) {
|
|
if (k >= *np || equation[k].level <= level)
|
|
break;
|
|
}
|
|
len2 = k - j;
|
|
if (fpower_sub(equation, np, loc, i, len1, j, len2, level + 1)) {
|
|
modified = true;
|
|
goto f_again;
|
|
}
|
|
}
|
|
i += len1 + 1;
|
|
if (i >= *np || equation[i-1].level < level)
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (i = loc; i < *np && equation[i].level >= level;) {
|
|
if (equation[i].level > level) {
|
|
modified |= fpower_recurse(equation, np, i, level + 1);
|
|
i++;
|
|
for (; i < *np && equation[i].level > level; i += 2)
|
|
;
|
|
continue;
|
|
}
|
|
i++;
|
|
}
|
|
return modified;
|
|
}
|
|
|
|
static int
|
|
fpower_sub(equation, np, loc, i1, n1, i2, n2, level)
|
|
token_type *equation;
|
|
int *np, loc, i1, n1, i2, n2, level;
|
|
{
|
|
int e1, e2;
|
|
int op1, op2;
|
|
int pop1 = TIMES;
|
|
int start2;
|
|
int i, j, k;
|
|
int b1, b2;
|
|
int len;
|
|
int diff_sign;
|
|
int all_divide;
|
|
#if !ALWAYS_FACTOR_POWER
|
|
int abs1_flag = false, abs2_flag = false;
|
|
#endif
|
|
|
|
e1 = i1 + n1;
|
|
e2 = i2 + n2;
|
|
op2 = equation[i2-1].token.operatr;
|
|
if (i1 <= loc) {
|
|
op1 = TIMES;
|
|
} else {
|
|
op1 = equation[i1-1].token.operatr;
|
|
}
|
|
for (i = i1 + 1;; i += 2) {
|
|
if (i >= e1)
|
|
return false;
|
|
if (equation[i].level == level && equation[i].token.operatr == POWER) {
|
|
#if !ALWAYS_FACTOR_POWER
|
|
/* avoid absolute values: */
|
|
if (i > (i1 + 2)
|
|
&& ((equation[i+1].level == level && (i + 2) == e1)
|
|
|| (equation[i+1].level == (level + 1)
|
|
&& (i + 4) == e1
|
|
&& equation[i+2].level == (level + 1)
|
|
&& equation[i+2].token.operatr == DIVIDE
|
|
&& equation[i+3].level == (level + 1)
|
|
&& equation[i+3].kind == CONSTANT))
|
|
&& equation[i-1].level == (level + 1)
|
|
&& equation[i+1].kind == CONSTANT
|
|
&& equation[i-1].kind == CONSTANT
|
|
&& equation[i-2].level == (level + 1) && equation[i-2].token.operatr == POWER) {
|
|
abs1_flag = true;
|
|
/* return false; */
|
|
}
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
for (j = i2 + 1;; j += 2) {
|
|
if (j >= e2)
|
|
return false;
|
|
if (equation[j].level == level && equation[j].token.operatr == POWER) {
|
|
#if !ALWAYS_FACTOR_POWER
|
|
/* avoid absolute values: */
|
|
if (j > (i2 + 2)
|
|
&& ((equation[j+1].level == level && (j + 2) == e2)
|
|
|| (equation[j+1].level == (level + 1)
|
|
&& (j + 4) == e2
|
|
&& equation[j+2].level == (level + 1)
|
|
&& equation[j+2].token.operatr == DIVIDE
|
|
&& equation[j+3].level == (level + 1)
|
|
&& equation[j+3].kind == CONSTANT))
|
|
&& equation[j-1].level == (level + 1)
|
|
&& equation[j+1].kind == CONSTANT
|
|
&& equation[j-1].kind == CONSTANT
|
|
&& equation[j-2].level == (level + 1) && equation[j-2].token.operatr == POWER) {
|
|
abs2_flag = true;
|
|
/* return false; */
|
|
}
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
#if !ALWAYS_FACTOR_POWER
|
|
if (abs1_flag && abs2_flag)
|
|
return false;
|
|
#endif
|
|
start2 = j;
|
|
#if 1
|
|
if (se_compare(&equation[i+1], e1 - (i + 1), &one_token, 1, &diff_sign)) {
|
|
return false;
|
|
}
|
|
if (se_compare(&equation[i+1], e1 - (i + 1), &equation[j+1], e2 - (j + 1), &diff_sign)) {
|
|
b1 = i + 1;
|
|
b2 = j + 1;
|
|
i = e1;
|
|
j = e2;
|
|
goto common_exponent;
|
|
}
|
|
#endif
|
|
|
|
fp_outer:
|
|
pop1 = equation[i].token.operatr;
|
|
if (pop1 == POWER)
|
|
pop1 = TIMES;
|
|
b1 = i + 1;
|
|
if (b1 >= e1)
|
|
return false;
|
|
i = b1 + 1;
|
|
for (; i < e1; i += 2) {
|
|
if (equation[i].level == (level + 1)
|
|
&& (equation[i].token.operatr == TIMES
|
|
|| equation[i].token.operatr == DIVIDE)) {
|
|
break;
|
|
}
|
|
}
|
|
if (se_compare(&equation[b1], i - b1, &one_token, 1, &diff_sign)) {
|
|
goto fp_outer;
|
|
}
|
|
j = start2;
|
|
fp_inner:
|
|
b2 = j + 1;
|
|
if (b2 >= e2)
|
|
goto fp_outer;
|
|
j = b2 + 1;
|
|
for (; j < e2; j += 2) {
|
|
if (equation[j].level == (level + 1)
|
|
&& (equation[j].token.operatr == TIMES
|
|
|| equation[j].token.operatr == DIVIDE)) {
|
|
break;
|
|
}
|
|
}
|
|
if (equation[b2-1].token.operatr == POWER) {
|
|
if (pop1 != TIMES)
|
|
goto fp_inner;
|
|
} else if (equation[b2-1].token.operatr != pop1) {
|
|
goto fp_inner;
|
|
}
|
|
if (se_compare(&equation[b1], i - b1, &equation[b2], j - b2, &diff_sign)) {
|
|
common_exponent:
|
|
if (op2 == DIVIDE)
|
|
diff_sign = !diff_sign;
|
|
all_divide = (op1 == DIVIDE && diff_sign);
|
|
len = 0;
|
|
blt(&scratch[len], &equation[i1], (b1 - i1) * sizeof(token_type));
|
|
len += b1 - i1;
|
|
scratch[len].level = level + 1;
|
|
scratch[len].kind = CONSTANT;
|
|
if (!all_divide && op1 == DIVIDE) {
|
|
scratch[len].token.constant = -1.0;
|
|
} else {
|
|
scratch[len].token.constant = 1.0;
|
|
}
|
|
len++;
|
|
blt(&scratch[len], &equation[i], (e1 - i) * sizeof(token_type));
|
|
len += e1 - i;
|
|
for (k = 0; k < len; k++)
|
|
scratch[k].level += 2;
|
|
scratch[len].level = level + 1;
|
|
scratch[len].kind = OPERATOR;
|
|
scratch[len].token.operatr = TIMES;
|
|
len++;
|
|
k = len;
|
|
blt(&scratch[len], &equation[i2], (b2 - i2) * sizeof(token_type));
|
|
len += b2 - i2;
|
|
scratch[len].level = level + 1;
|
|
scratch[len].kind = CONSTANT;
|
|
if (!all_divide && diff_sign) {
|
|
scratch[len].token.constant = -1.0;
|
|
} else {
|
|
scratch[len].token.constant = 1.0;
|
|
}
|
|
len++;
|
|
blt(&scratch[len], &equation[j], (e2 - j) * sizeof(token_type));
|
|
len += e2 - j;
|
|
for (; k < len; k++)
|
|
scratch[k].level += 2;
|
|
scratch[len].level = level;
|
|
scratch[len].kind = OPERATOR;
|
|
scratch[len].token.operatr = POWER;
|
|
len++;
|
|
if (pop1 == DIVIDE) {
|
|
scratch[len].level = level + 1;
|
|
scratch[len].kind = CONSTANT;
|
|
scratch[len].token.constant = 1.0;
|
|
len++;
|
|
scratch[len].level = level + 1;
|
|
scratch[len].kind = OPERATOR;
|
|
scratch[len].token.operatr = DIVIDE;
|
|
len++;
|
|
}
|
|
k = len;
|
|
blt(&scratch[len], &equation[b1], (i - b1) * sizeof(token_type));
|
|
len += i - b1;
|
|
for (; k < len; k++)
|
|
scratch[k].level++;
|
|
if (*np + len - n1 - (n2 + 1) > n_tokens) {
|
|
error_huge();
|
|
}
|
|
if (!all_divide && op1 == DIVIDE) {
|
|
equation[i1-1].token.operatr = TIMES;
|
|
}
|
|
blt(&equation[i2-1], &equation[e2], (*np - e2) * sizeof(token_type));
|
|
*np -= n2 + 1;
|
|
blt(&equation[i1+len], &equation[e1], (*np - e1) * sizeof(token_type));
|
|
*np += len - n1;
|
|
blt(&equation[i1], scratch, len * sizeof(token_type));
|
|
return true;
|
|
}
|
|
goto fp_inner;
|
|
}
|