mirror of
https://github.com/mfillpot/mathomatic.git
synced 2026-01-08 04:29:39 +00:00
569 lines
13 KiB
C
569 lines
13 KiB
C
/*
|
|
* Mathomatic floating point constant factorizing 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 void try_factor(double arg);
|
|
static int fc_recurse(token_type *equation, int *np, int loc, int level, int level_code);
|
|
|
|
/* The following data is used to factor integers: */
|
|
static double nn, sqrt_value;
|
|
static double skip_multiples[] = { /* Additive array that skips over multiples of 2, 3, 5, and 7. */
|
|
10, 2, 4, 2, 4, 6, 2, 6,
|
|
4, 2, 4, 6, 6, 2, 6, 4,
|
|
2, 6, 4, 6, 8, 4, 2, 4,
|
|
2, 4, 8, 6, 4, 6, 2, 4,
|
|
6, 2, 6, 6, 4, 2, 4, 6,
|
|
2, 6, 4, 2, 4, 2,10, 2
|
|
}; /* sum of all numbers = 210 = (2*3*5*7) */
|
|
|
|
/*
|
|
* Factor the integer in "value".
|
|
* Store the prime factors in the unique[] array.
|
|
*
|
|
* Return true if successful.
|
|
*/
|
|
int
|
|
factor_one(value)
|
|
double value;
|
|
{
|
|
int i;
|
|
double d;
|
|
|
|
uno = 0;
|
|
nn = value;
|
|
if (nn == 0.0 || !isfinite(nn)) {
|
|
/* zero or not finite */
|
|
return false;
|
|
}
|
|
if (fabs(nn) >= MAX_K_INTEGER) {
|
|
/* too large to factor */
|
|
return false;
|
|
}
|
|
if (fmod(nn, 1.0) != 0.0) {
|
|
/* not an integer */
|
|
return false;
|
|
}
|
|
sqrt_value = 1.0 + sqrt(fabs(nn));
|
|
try_factor(2.0);
|
|
try_factor(3.0);
|
|
try_factor(5.0);
|
|
try_factor(7.0);
|
|
d = 1.0;
|
|
while (d <= sqrt_value) {
|
|
for (i = 0; i < ARR_CNT(skip_multiples); i++) {
|
|
d += skip_multiples[i];
|
|
try_factor(d);
|
|
}
|
|
}
|
|
if (nn != 1.0) {
|
|
if (nn < 0 && nn != -1.0) {
|
|
try_factor(fabs(nn));
|
|
}
|
|
try_factor(nn);
|
|
}
|
|
if (uno == 0) {
|
|
try_factor(1.0);
|
|
}
|
|
/* Do some floating point arithmetic self-checking. If the following fails, it is due to a floating point bug. */
|
|
if (nn != 1.0) {
|
|
error_bug("Internal error factoring integers (final nn != 1.0).");
|
|
}
|
|
if (value != multiply_out_unique()) {
|
|
error_bug("Internal error factoring integers (result array value is incorrect).");
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* See if "arg" is one or more factors of "nn".
|
|
* If so, save it and remove it from "nn".
|
|
*/
|
|
static void
|
|
try_factor(arg)
|
|
double arg;
|
|
{
|
|
#if DEBUG
|
|
if (fmod(arg, 1.0) != 0.0) {
|
|
error_bug("Trying factor that is not an integer!");
|
|
}
|
|
#endif
|
|
while (fmod(nn, arg) == 0.0) {
|
|
if (uno > 0 && ucnt[uno-1] > 0 && unique[uno-1] == arg) {
|
|
ucnt[uno-1]++;
|
|
} else {
|
|
while (uno > 0 && ucnt[uno-1] <= 0)
|
|
uno--;
|
|
unique[uno] = arg;
|
|
ucnt[uno++] = 1;
|
|
}
|
|
nn /= arg;
|
|
#if DEBUG
|
|
if (fmod(nn, 1.0) != 0.0) {
|
|
error_bug("nn turned non-integer in try_factor().");
|
|
}
|
|
#endif
|
|
sqrt_value = 1.0 + sqrt(fabs(nn));
|
|
if (fabs(nn) <= 1.5 || fabs(arg) <= 1.5)
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Convert unique[] back into the single integer it represents,
|
|
* which was the value passed in the last call to factor_one(value).
|
|
* Nothing is changed and the value is returned.
|
|
*/
|
|
double
|
|
multiply_out_unique(void)
|
|
{
|
|
int i, j;
|
|
double d;
|
|
|
|
d = 1.0;
|
|
for (i = 0; i < uno; i++) {
|
|
#if DEBUG
|
|
if (ucnt[i] < 0) {
|
|
error_bug("Error in ucnt[] being negative.");
|
|
}
|
|
#endif
|
|
for (j = 0; j < ucnt[i]; j++) {
|
|
d *= unique[i];
|
|
}
|
|
}
|
|
return d;
|
|
}
|
|
|
|
/*
|
|
* Display the integer prime factors in the unique[] array, even if mangled.
|
|
* Must have had a successful call to factor_one(value) previously,
|
|
* to fill out unique[] with the prime factors of value.
|
|
*
|
|
* Return true if successful.
|
|
*/
|
|
int
|
|
display_unique(void)
|
|
{
|
|
int i;
|
|
double value;
|
|
|
|
if (uno <= 0)
|
|
return false;
|
|
value = multiply_out_unique();
|
|
fprintf(gfp, "%.0f = ", value);
|
|
for (i = 0; i < uno;) {
|
|
if (ucnt[i] > 0) {
|
|
fprintf(gfp, "%.0f", unique[i]);
|
|
} else {
|
|
i++;
|
|
continue;
|
|
}
|
|
if (ucnt[i] > 1) {
|
|
fprintf(gfp, "^%d", ucnt[i]);
|
|
}
|
|
do {
|
|
i++;
|
|
} while (i < uno && ucnt[i] <= 0);
|
|
if (i < uno) {
|
|
fprintf(gfp, " * ");
|
|
}
|
|
}
|
|
fprintf(gfp, "\n");
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Determine if the result of factor_one(x) is prime.
|
|
*
|
|
* Return true if x is a prime number.
|
|
*/
|
|
int
|
|
is_prime(void)
|
|
{
|
|
double value;
|
|
|
|
if (uno <= 0) {
|
|
#if DEBUG
|
|
error_bug("uno == 0 in is_prime().");
|
|
#endif
|
|
return false;
|
|
}
|
|
value = multiply_out_unique();
|
|
if (value < 2.0)
|
|
return false;
|
|
if (uno == 1 && ucnt[0] == 1)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Factor integers into their prime factors in an equation side.
|
|
*
|
|
* Return true if the equation side was modified.
|
|
*/
|
|
int
|
|
factor_int(equation, np)
|
|
token_type *equation;
|
|
int *np;
|
|
{
|
|
int i, j;
|
|
int xsize;
|
|
int level;
|
|
int modified = false;
|
|
|
|
for (i = 0; i < *np; i += 2) {
|
|
if (equation[i].kind == CONSTANT && factor_one(equation[i].token.constant) && uno > 0) {
|
|
if (uno == 1 && ucnt[0] <= 1)
|
|
continue; /* prime number */
|
|
level = equation[i].level;
|
|
if (uno > 1 && *np > 1)
|
|
level++;
|
|
xsize = -2;
|
|
for (j = 0; j < uno; j++) {
|
|
if (ucnt[j] > 1)
|
|
xsize += 4;
|
|
else
|
|
xsize += 2;
|
|
}
|
|
if (*np + xsize > n_tokens) {
|
|
error_huge();
|
|
}
|
|
for (j = 0; j < uno; j++) {
|
|
if (ucnt[j] > 1)
|
|
xsize = 4;
|
|
else
|
|
xsize = 2;
|
|
if (j == 0)
|
|
xsize -= 2;
|
|
if (xsize > 0) {
|
|
blt(&equation[i+xsize], &equation[i], (*np - i) * sizeof(token_type));
|
|
*np += xsize;
|
|
if (j > 0) {
|
|
i++;
|
|
equation[i].kind = OPERATOR;
|
|
equation[i].level = level;
|
|
equation[i].token.operatr = TIMES;
|
|
i++;
|
|
}
|
|
}
|
|
equation[i].kind = CONSTANT;
|
|
equation[i].level = level;
|
|
equation[i].token.constant = unique[j];
|
|
if (ucnt[j] > 1) {
|
|
equation[i].level = level + 1;
|
|
i++;
|
|
equation[i].kind = OPERATOR;
|
|
equation[i].level = level + 1;
|
|
equation[i].token.operatr = POWER;
|
|
i++;
|
|
equation[i].level = level + 1;
|
|
equation[i].kind = CONSTANT;
|
|
equation[i].token.constant = ucnt[j];
|
|
}
|
|
}
|
|
modified = true;
|
|
}
|
|
}
|
|
return modified;
|
|
}
|
|
|
|
/*
|
|
* Factor integers in an equation space.
|
|
*
|
|
* Return true if something was factored.
|
|
*/
|
|
int
|
|
factor_int_equation(n)
|
|
int n; /* equation space number */
|
|
{
|
|
int rv = false;
|
|
|
|
if (empty_equation_space(n))
|
|
return rv;
|
|
if (factor_int(lhs[n], &n_lhs[n]))
|
|
rv = true;
|
|
if (factor_int(rhs[n], &n_rhs[n]))
|
|
rv = true;
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* List an equation side with optional integer factoring.
|
|
*/
|
|
int
|
|
list_factor(equation, np, factor_flag)
|
|
token_type *equation;
|
|
int *np;
|
|
int factor_flag;
|
|
{
|
|
if (factor_flag || factor_int_flag) {
|
|
factor_int(equation, np);
|
|
}
|
|
return list_proc(equation, *np, false);
|
|
}
|
|
|
|
/*
|
|
* Neatly factor out coefficients in additive expressions in an equation side.
|
|
* For example: (2*x + 4*y + 6) becomes 2*(x + 2*y + 3).
|
|
*
|
|
* This routine is often necessary because the expression compare (se_compare())
|
|
* does not return a multiplier (except for +/-1.0).
|
|
* Normalization done here is required for simplification of algebraic fractions, etc.
|
|
*
|
|
* If "level_code" is 0, all additive expressions are normalized
|
|
* by making at least one coefficient unity (1) by factoring out
|
|
* the absolute value of the constant coefficient closest to zero.
|
|
* This makes the absolute value of all other coefficients >= 1.
|
|
* If all coefficients are negative, -1 will be factored out, too.
|
|
*
|
|
* If "level_code" is 1, any level 1 additive expression is factored
|
|
* nicely for readability, while all deeper levels are normalized,
|
|
* so that algebraic fractions are simplified.
|
|
*
|
|
* If "level_code" is 2, nothing is normalized unless it increases
|
|
* readability.
|
|
*
|
|
* If "level_code" is 3, nothing is done.
|
|
*
|
|
* Add 4 to "level_code" to always factor out the GCD of rational coefficients
|
|
* to produce all reduced integer coefficients.
|
|
*
|
|
* Return true if equation side was modified.
|
|
*/
|
|
int
|
|
factor_constants(equation, np, level_code)
|
|
token_type *equation; /* pointer to the beginning of equation side */
|
|
int *np; /* pointer to length of equation side */
|
|
int level_code; /* see above */
|
|
{
|
|
if (level_code == 3)
|
|
return false;
|
|
return fc_recurse(equation, np, 0, 1, level_code);
|
|
}
|
|
|
|
static int
|
|
fc_recurse(equation, np, loc, level, level_code)
|
|
token_type *equation;
|
|
int *np, loc, level;
|
|
int level_code;
|
|
{
|
|
int i, j, k, eloc;
|
|
int op;
|
|
double d, minimum = 1.0, cogcd = 1.0;
|
|
int improve_readability, gcd_flag, first = true, neg_flag = true, modified = false;
|
|
int op_count = 0, const_count = 0;
|
|
|
|
for (i = loc; i < *np && equation[i].level >= level;) {
|
|
if (equation[i].level > level) {
|
|
modified |= fc_recurse(equation, np, i, level + 1, level_code);
|
|
i++;
|
|
for (; i < *np && equation[i].level > level; i += 2)
|
|
;
|
|
continue;
|
|
}
|
|
i++;
|
|
}
|
|
if (modified)
|
|
return true;
|
|
improve_readability = ((level_code & 3) > 1 || ((level_code & 3) && (level == 1)));
|
|
gcd_flag = ((improve_readability && factor_out_all_numeric_gcds) || (level_code & 4));
|
|
for (i = loc; i < *np && equation[i].level >= level;) {
|
|
if (equation[i].level == level) {
|
|
switch (equation[i].kind) {
|
|
case CONSTANT:
|
|
const_count++;
|
|
d = equation[i].token.constant;
|
|
break;
|
|
case OPERATOR:
|
|
switch (equation[i].token.operatr) {
|
|
case PLUS:
|
|
neg_flag = false;
|
|
case MINUS:
|
|
op_count++;
|
|
break;
|
|
default:
|
|
return modified;
|
|
}
|
|
i++;
|
|
continue;
|
|
default:
|
|
d = 1.0;
|
|
break;
|
|
}
|
|
if (i == loc && d > 0.0)
|
|
neg_flag = false;
|
|
d = fabs(d);
|
|
if (first) {
|
|
minimum = d;
|
|
cogcd = d;
|
|
first = false;
|
|
} else {
|
|
if (minimum > d)
|
|
minimum = d;
|
|
if (gcd_flag && cogcd != 0.0)
|
|
cogcd = gcd_verified(d, cogcd);
|
|
}
|
|
} else {
|
|
op = 0;
|
|
for (j = i + 1; j < *np && equation[j].level > level; j += 2) {
|
|
#if DEBUG
|
|
if (equation[j].kind != OPERATOR) {
|
|
error_bug("Bug in factor_constants().");
|
|
}
|
|
#endif
|
|
if (equation[j].level == level + 1) {
|
|
op = equation[j].token.operatr;
|
|
}
|
|
}
|
|
if (op == TIMES || op == DIVIDE) {
|
|
for (k = i; k < j; k++) {
|
|
if (equation[k].level == (level + 1) && equation[k].kind == CONSTANT) {
|
|
if (i == j)
|
|
return modified; /* more than one constant */
|
|
if (k > i && equation[k-1].token.operatr != TIMES)
|
|
return modified;
|
|
d = equation[k].token.constant;
|
|
if (i == loc && d > 0.0)
|
|
neg_flag = false;
|
|
d = fabs(d);
|
|
if (first) {
|
|
minimum = d;
|
|
cogcd = d;
|
|
first = false;
|
|
} else {
|
|
if (minimum > d)
|
|
minimum = d;
|
|
if (gcd_flag && cogcd != 0.0)
|
|
cogcd = gcd_verified(d, cogcd);
|
|
}
|
|
i = j;
|
|
}
|
|
}
|
|
if (i == j)
|
|
continue;
|
|
}
|
|
if (i == loc)
|
|
neg_flag = false;
|
|
if (first) {
|
|
minimum = 1.0;
|
|
cogcd = 1.0;
|
|
first = false;
|
|
} else {
|
|
if (minimum > 1.0)
|
|
minimum = 1.0;
|
|
if (gcd_flag && cogcd != 0.0)
|
|
cogcd = gcd_verified(1.0, cogcd);
|
|
}
|
|
i = j;
|
|
continue;
|
|
}
|
|
i++;
|
|
}
|
|
eloc = i;
|
|
if (gcd_flag && cogcd != 0.0 /* && fmod(cogcd, 1.0) == 0.0 */) {
|
|
minimum = cogcd;
|
|
}
|
|
if (first || op_count == 0 || const_count > 1 || (!neg_flag && minimum == 1.0))
|
|
return modified;
|
|
if (minimum == 0.0 || !isfinite(minimum))
|
|
return modified;
|
|
if (improve_readability) {
|
|
for (i = loc; i < eloc;) {
|
|
d = 1.0;
|
|
if (equation[i].kind == CONSTANT) {
|
|
if (equation[i].level == level || ((i + 1) < eloc
|
|
&& equation[i].level == (level + 1)
|
|
&& equation[i+1].level == (level + 1)
|
|
&& (equation[i+1].token.operatr == TIMES
|
|
|| equation[i+1].token.operatr == DIVIDE))) {
|
|
d = equation[i].token.constant;
|
|
}
|
|
}
|
|
#if 0 /* was 1; changed to 0 for optimal results and so 180*(sides-2) simplification works nicely. */
|
|
if (!gcd_flag && minimum >= 1.0) {
|
|
minimum = 1.0;
|
|
break;
|
|
}
|
|
#endif
|
|
#if 1
|
|
if ((minimum < 1.0) && (fmod(d, 1.0) == 0.0)) {
|
|
minimum = 1.0;
|
|
break;
|
|
}
|
|
#endif
|
|
/* Make sure division by the number to factor out results in an integer: */
|
|
if (fmod(d, minimum) != 0.0) {
|
|
minimum = 1.0; /* result not an integer */
|
|
break;
|
|
}
|
|
i++;
|
|
for (; i < *np && equation[i].level > level; i += 2)
|
|
;
|
|
if (i >= *np || equation[i].level < level)
|
|
break;
|
|
i++;
|
|
}
|
|
}
|
|
if (neg_flag)
|
|
minimum = -minimum;
|
|
if (minimum == 1.0)
|
|
return modified;
|
|
if (*np + ((op_count + 2) * 2) > n_tokens) {
|
|
error_huge();
|
|
}
|
|
for (i = loc; i < *np && equation[i].level >= level; i++) {
|
|
if (equation[i].kind != OPERATOR) {
|
|
for (j = i;;) {
|
|
equation[j].level++;
|
|
j++;
|
|
if (j >= *np || equation[j].level <= level)
|
|
break;
|
|
}
|
|
blt(&equation[j+2], &equation[j], (*np - j) * sizeof(token_type));
|
|
*np += 2;
|
|
equation[j].level = level + 1;
|
|
equation[j].kind = OPERATOR;
|
|
equation[j].token.operatr = DIVIDE;
|
|
j++;
|
|
equation[j].level = level + 1;
|
|
equation[j].kind = CONSTANT;
|
|
equation[j].token.constant = minimum;
|
|
i = j;
|
|
}
|
|
}
|
|
for (i = loc; i < *np && equation[i].level >= level; i++) {
|
|
equation[i].level++;
|
|
}
|
|
blt(&equation[i+2], &equation[i], (*np - i) * sizeof(token_type));
|
|
*np += 2;
|
|
equation[i].level = level;
|
|
equation[i].kind = OPERATOR;
|
|
equation[i].token.operatr = TIMES;
|
|
i++;
|
|
equation[i].level = level;
|
|
equation[i].kind = CONSTANT;
|
|
equation[i].token.constant = minimum;
|
|
return true;
|
|
}
|