mathomatic/complex.c

458 lines
11 KiB
C

/*
* Floating point complex number routines specifically for Mathomatic.
*
* 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"
/*
* Convert doubles x and y from rectangular coordinates to polar coordinates.
*
* The amplitude is stored in *radiusp and the angle in radians is stored in *thetap.
*/
void
rect_to_polar(x, y, radiusp, thetap)
double x, y, *radiusp, *thetap;
{
*radiusp = sqrt(x * x + y * y);
*thetap = atan2(y, x);
}
/*
* The roots command.
*/
int
roots_cmd(cp)
char *cp;
{
#define MAX_ROOT 10000.0 /* Root limit needed because more roots become more inaccurate and take longer to check. */
complexs c, c2;
#if !SILENT
complexs check;
double d;
#endif
double k, root, radius, theta, radius_root = 0.0;
char buf[MAX_CMD_LEN];
do_repeat:
if (*cp == '\0') {
my_strlcpy(prompt_str, _("Enter root (positive integer): "), sizeof(prompt_str));
if ((cp = get_string(buf, sizeof(buf))) == NULL)
return false;
}
root = strtod(cp, &cp);
if ((*cp && *cp != ',' && !isspace(*cp)) || !isfinite(root) || root < 0.0 || root > MAX_ROOT || fmod(root, 1.0) != 0.0) {
error(_("Root invalid or out of range."));
printf(_("Root must be a positive integer less than or equal to %.0f.\n"), MAX_ROOT);
return false;
}
cp = skip_comma_space(cp);
if (*cp == '\0') {
my_strlcpy(prompt_str, _("Enter real part (X): "), sizeof(prompt_str));
if ((cp = get_string(buf, sizeof(buf))) == NULL)
return false;
}
c.re = strtod(cp, &cp);
if (*cp && *cp != ',' && !isspace(*cp)) {
error(_("Number expected."));
return false;
}
cp = skip_comma_space(cp);
if (*cp == '\0') {
my_strlcpy(prompt_str, _("Enter imaginary part (Y): "), sizeof(prompt_str));
if ((cp = get_string(buf, sizeof(buf))) == NULL)
return false;
}
c.im = strtod(cp, &cp);
if (*cp) {
error(_("Number expected."));
return false;
}
if (c.re == 0.0 && c.im == 0.0) {
return repeat_flag;
}
/* convert to polar coordinates */
errno = 0;
rect_to_polar(c.re, c.im, &radius, &theta);
if (root) {
radius_root = pow(radius, 1.0 / root);
}
check_err();
fprintf(gfp, _("\nThe polar coordinates are:\n%.*g amplitude and\n%.*g radians (%.*g degrees).\n\n"),
precision, radius, precision, theta, precision, theta * 180.0 / M_PI);
if (root) {
if (c.im == 0.0) {
fprintf(gfp, _("The %.12g roots of (%.12g)^(1/%.12g) are:\n\n"), root, c.re, root);
} else {
fprintf(gfp, _("The %.12g roots of (%.12g%+.12g*i)^(1/%.12g) are:\n\n"), root, c.re, c.im, root);
}
for (k = 0.0; k < root; k += 1.0) {
/* add constants to theta and convert back to rectangular coordinates */
c2.re = radius_root * cos((theta + 2.0 * k * M_PI) / root);
c2.im = radius_root * sin((theta + 2.0 * k * M_PI) / root);
complex_fixup(&c2);
if (c2.re || c2.im == 0.0) {
fprintf(gfp, "%.12g ", c2.re);
}
if (c2.im) {
fprintf(gfp, "%+.12g*i", c2.im);
}
fprintf(gfp, "\n");
#if !SILENT
if (debug_level <= 0) {
continue;
}
check = c2;
for (d = 1.0; d < root; d += 1.0) {
check = complex_mult(check, c2);
}
complex_fixup(&check);
printf(_("Inverse check:"));
if (check.re || check.im == 0.0) {
printf(" %.10g", check.re);
}
if (check.im) {
printf(" %+.10g*i", check.im);
}
printf("\n\n");
#endif
}
}
if (repeat_flag)
goto do_repeat;
return true;
}
/*
* Approximate roots of complex numbers in an equation side:
* (complex^real) and (real^complex) and (complex^complex) all result in a complex number.
* This only gives one root, even when there may be many roots.
* Works best when the equation side has been approximated before this.
*
* Return true if the equation side was modified.
*/
int
complex_root_simp(equation, np)
token_type *equation; /* equation side pointer */
int *np; /* pointer to length of equation side */
{
int i, j;
int level;
int len;
complexs c, p, r;
int modified = false;
start_over:
for (i = 1; i < *np; i += 2) {
if (equation[i].token.operatr != POWER)
continue;
level = equation[i].level;
for (j = i + 2; j < *np && equation[j].level >= level; j += 2)
;
len = j - (i + 1);
if (!parse_complex(&equation[i+1], len, &p))
continue;
for (j = i - 1; j >= 0 && equation[j].level >= level; j--)
;
j++;
if (!parse_complex(&equation[j], i - j, &c))
continue;
if (c.im == 0.0 && p.im == 0.0)
continue;
i += len + 1;
r = complex_pow(c, p);
#if 0
printf("(%.14g+%.14gi)^(%.14g+%.14gi) = %.14g+%.14gi\n", c.re, c.im, p.re, p.im, r.re, r.im);
#endif
if (*np + 5 - (i - j) > n_tokens) {
error_huge();
}
if ((j + 5) != i) {
blt(&equation[j+5], &equation[i], (*np - i) * sizeof(token_type));
*np += 5 - (i - j);
}
equation[j].level = level;
equation[j].kind = CONSTANT;
equation[j].token.constant = r.re;
j++;
equation[j].level = level;
equation[j].kind = OPERATOR;
equation[j].token.operatr = PLUS;
j++;
level++;
equation[j].level = level;
equation[j].kind = CONSTANT;
equation[j].token.constant = r.im;
j++;
equation[j].level = level;
equation[j].kind = OPERATOR;
equation[j].token.operatr = TIMES;
j++;
equation[j].level = level;
equation[j].kind = VARIABLE;
equation[j].token.variable = IMAGINARY;
modified = true;
goto start_over;
}
if (modified) {
debug_string(1, _("Complex number roots approximated."));
}
return modified;
}
/*
* Approximate all roots of complex numbers in an equation side.
*
* Return true if anything was approximated.
*/
int
approximate_complex_roots(equation, np)
token_type *equation; /* equation side pointer */
int *np; /* pointer to length of equation side */
{
int rv = false;
for (;;) {
elim_loop(equation, np);
if (!complex_root_simp(equation, np))
break;
rv = true;
}
return rv;
}
/*
* Get a constant, if the passed expression evaluates to a constant.
* This should not be called from low level routines.
*
* Return true if successful, with the floating point constant returned in *dp.
*/
int
get_constant(p1, n, dp)
token_type *p1; /* expression pointer */
int n; /* length of expression */
double *dp; /* pointer to returned double */
{
int i, j;
int level;
double d1, d2;
int prev_approx_flag;
#if DEBUG
if (n < 1 || (n & 1) != 1) {
error_bug("Call to get_constant() has invalid expression length.");
}
#endif
if (n == 1) {
switch (p1[0].kind) {
case CONSTANT:
*dp = p1[0].token.constant;
return true;
case VARIABLE:
if (var_is_const(p1[0].token.variable, dp)) {
return true;
}
break;
case OPERATOR:
break;
}
} else if (n >= 3) {
level = p1[1].level;
if (!get_constant(p1, 1, &d1))
return false;
for (i = 1; i < n; i = j) {
if (p1[i].kind != OPERATOR || p1[i].level > level) {
#if DEBUG
error_bug("Possible error in get_constant().");
#endif
return false;
}
level = p1[i].level;
for (j = i + 2; j < n && p1[j].level > level; j += 2)
;
if (!get_constant(&p1[i+1], j - (i + 1), &d2))
return false;
prev_approx_flag = approximate_roots;
approximate_roots = true;
if (calc(NULL, &d1, p1[i].token.operatr, d2)) {
approximate_roots = prev_approx_flag;
if (p1[i].token.operatr == POWER && !domain_check)
return false;
domain_check = false;
} else {
approximate_roots = prev_approx_flag;
domain_check = false;
return false;
}
}
*dp = d1;
return true;
}
return false;
}
/*
* Get the value of a constant complex number expression.
* Doesn't always work unless expression is approximated first
* with something like the approximate command.
* Functionality was greatly improved recently, making success more likely
* without approximating.
*
* If successful, return true with complex number in *cp.
*/
int
parse_complex(p1, n, cp)
token_type *p1; /* expression pointer */
int n; /* length of expression */
complexs *cp; /* pointer to returned complex number */
{
int j, k;
int imag_cnt = 0, times_cnt = 0;
complexs c, tmp;
int level, level2;
if (!exp_is_numeric(p1, n)) {
return false;
}
if (get_constant(p1, n, &c.re)) {
c.im = 0.0;
*cp = c;
return true;
}
if (found_var(p1, n, IMAGINARY) != 1)
return false;
level = min_level(p1, n);
c.re = 0.0;
c.im = 1.0;
j = n - 1;
do {
for (k = j - 1; k > 0 && p1[k].level > level; k -= 2)
;
if (k > 0) {
#if DEBUG
if (p1[k].level != level || p1[k].kind != OPERATOR) {
error_bug("Error in parse_complex().");
}
#endif
switch (p1[k].token.operatr) {
case MINUS:
case PLUS:
if (get_constant(&p1[k+1], j - k, &tmp.re)) {
if (p1[k].token.operatr == MINUS)
c.re -= tmp.re;
else
c.re += tmp.re;
j = k - 1;
}
}
} else
break;
} while (j < k);
for (; j >= 0; j--) {
switch (p1[j].kind) {
case CONSTANT:
break;
case VARIABLE:
if (var_is_const(p1[j].token.variable, NULL))
break;
if (p1[j].token.variable != IMAGINARY)
return false;
++imag_cnt;
break;
case OPERATOR:
level2 = p1[j].level;
switch (p1[j].token.operatr) {
case TIMES:
case DIVIDE:
if (++times_cnt > 1)
return false;
if (level2 > (level + 1) || p1[j+1].level != level2)
return false;
for (k = j; k > 0 && p1[k].level == level2; k -= 2) {
if (p1[k-1].level != level2)
return false;
if (!(p1[k+1].kind == VARIABLE && p1[k+1].token.variable == IMAGINARY)) {
if (get_constant(&p1[k+1], 1, &tmp.im)) {
if (p1[k].token.operatr == DIVIDE)
c.im /= tmp.im;
else
c.im *= tmp.im;
} else
return false;
} else if (p1[k].token.operatr == DIVIDE) {
c.im = -c.im;
}
if (p1[k-1].kind == VARIABLE && p1[k-1].token.variable == IMAGINARY) {
if (++imag_cnt > 1)
return false;
k -= 2;
if (k > 0 && p1[k].level == level2) {
if (p1[k-1].level != level2)
return false;
if (p1[k-1].kind == VARIABLE && p1[k-1].token.variable == IMAGINARY)
return false;
if (p1[k].token.operatr == DIVIDE)
c.im = -c.im;
} else
break;
}
}
if (!(p1[k+1].kind == VARIABLE && p1[k+1].token.variable == IMAGINARY)) {
if (get_constant(&p1[k+1], 1, &tmp.im)) {
c.im *= tmp.im;
} else
return false;
}
j = k + 1;
continue;
case MINUS:
if (imag_cnt) {
c.im = -c.im;
}
case PLUS:
if (level != level2)
return false;
if (get_constant(p1, j, &tmp.re)) {
c.re += tmp.re;
goto done;
}
return false;
}
default:
return false;
}
}
done:
if (imag_cnt != 1) {
#if DEBUG
error_bug("Imaginary count wrong in parse_complex().");
#else
return false;
#endif
}
*cp = c;
return true;
}