mirror of
https://github.com/mfillpot/mathomatic.git
synced 2026-01-09 04:59:37 +00:00
181 lines
5.2 KiB
C
181 lines
5.2 KiB
C
/*
|
|
* Command-line numerical polynomial equation solver using the GNU Scientific Library (GSL).
|
|
* GSL is available from: "http://www.gnu.org/software/gsl/".
|
|
*
|
|
* Copyright (C) 2008-2011 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.
|
|
|
|
*/
|
|
|
|
/*
|
|
* To compile:
|
|
|
|
./compile.roots
|
|
|
|
* or
|
|
|
|
gcc -O3 -Wall -o roots roots.c -lgsl -lgslcblas
|
|
|
|
* This program nicely solves any degree polynomial
|
|
* with real coefficients by calling the
|
|
* GSL function gsl_poly_complex_solve().
|
|
* This file is also useful for testing
|
|
* and as an example of using the GSL from C.
|
|
* The results are double precision floating point values
|
|
* that are sometimes accurate to 14 digits.
|
|
* Complex numbers are output if successful.
|
|
* Here is an example of it solving a cubic polynomial:
|
|
|
|
$ roots 1 1 1 1
|
|
The 3 approximate floating point solutions of:
|
|
+x^3 +x^2 +x +1 = 0
|
|
are:
|
|
|
|
x = +0 +1*i
|
|
x = +0 -1*i
|
|
x = -1
|
|
$
|
|
|
|
Try this for a large amount of error:
|
|
roots -1 -1 1 1
|
|
|
|
Proof the GSL isn't perfect.
|
|
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <float.h>
|
|
#include <math.h>
|
|
#include <errno.h>
|
|
#include <gsl/gsl_poly.h>
|
|
#include <gsl/gsl_errno.h>
|
|
|
|
#define EPSILON 0.00000000000005 /* a good value for doubles */
|
|
#define HELP 1 /* display helpful text */
|
|
|
|
void display_root(int i);
|
|
|
|
char *prog_name = "roots"; /* name of this program */
|
|
double *a, *z; /* input and output arrays, respectively */
|
|
int precision = DBL_DIG - 1; /* display precision, it is not useful to set this higher than 15 */
|
|
|
|
void
|
|
usage(void)
|
|
{
|
|
printf("\n%s version 1.0 - numerical polynomial equation solver\n", prog_name);
|
|
printf("Uses the GNU Scientific Library (GSL).\n");
|
|
printf("\nSolves polynomial = 0 when given all real coefficients of the polynomial.\n");
|
|
printf("Double precision floating point math is used, accurate to about 14 digits.\n");
|
|
printf("\nUsage: %s highest-power-coefficient ... constant-term\n", prog_name);
|
|
printf("\nThe coefficients must be decimal, floating point, real numbers.\n");
|
|
printf("For example, if 4 real numbers are given, there will be 3 complex number\n");
|
|
printf("results or \"roots\" that are all valid solutions to polynomial = 0.\n");
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
int i, highest_power;
|
|
char *cp, *arg;
|
|
gsl_poly_complex_workspace *w;
|
|
|
|
if (argc <= 1) {
|
|
fprintf(stderr, "%s: The polynomial coefficients must be specified on the command line.\n", prog_name);
|
|
usage();
|
|
exit(2);
|
|
}
|
|
|
|
highest_power = argc - 2;
|
|
a = calloc(highest_power + 1, sizeof(double)); /* allocate real double input array */
|
|
z = calloc(2 * highest_power, sizeof(double)); /* allocate complex double output array */
|
|
|
|
/* parse the command line into the coefficient array a[] */
|
|
for (i = 0; i < argc - 1; i++) {
|
|
arg = argv[argc-i-1];
|
|
errno = 0;
|
|
a[i] = strtod(arg, &cp);
|
|
if (arg == cp || *cp) {
|
|
fprintf(stderr, "%s: Argument \"%s\" is not a floating point number.\n", prog_name, arg);
|
|
usage();
|
|
exit(2);
|
|
}
|
|
if (errno) {
|
|
fprintf(stderr, "%s: Argument \"%s\" is out of range.\n", prog_name, arg);
|
|
exit(2);
|
|
}
|
|
}
|
|
|
|
#if HELP
|
|
/* nicely display the actual polynomial equation we are solving */
|
|
printf("The %d approximate floating point solutions of:\n", highest_power);
|
|
for (i = highest_power; i >= 0; i--) {
|
|
if (a[i]) {
|
|
if (i && a[i] == 1.0) {
|
|
printf("+x");
|
|
} else {
|
|
printf("%+.*g", precision, a[i]);
|
|
if (i) {
|
|
printf("*x");
|
|
}
|
|
}
|
|
if (i > 1) {
|
|
printf("^%d", i);
|
|
}
|
|
printf(" ");
|
|
}
|
|
}
|
|
printf("= 0\nare:\n\n");
|
|
#endif
|
|
|
|
/* solve the polynomial equation */
|
|
w = gsl_poly_complex_workspace_alloc(highest_power + 1);
|
|
if (gsl_poly_complex_solve(a, highest_power + 1, w, z) != GSL_SUCCESS) {
|
|
fprintf(stderr, "%s: GSL approximation failed.\n", prog_name);
|
|
exit(1);
|
|
}
|
|
gsl_poly_complex_workspace_free(w);
|
|
|
|
/* display all solutions */
|
|
for (i = 0; i < highest_power; i++) {
|
|
#ifdef EPSILON /* zero out relatively very small values (which are floating point error) */
|
|
if (fabs(z[2*i] * EPSILON) > fabs(z[2*i+1]))
|
|
z[2*i+1] = 0.0;
|
|
else if (fabs(z[2*i+1] * EPSILON) > fabs(z[2*i]))
|
|
z[2*i] = 0.0;
|
|
#endif
|
|
#if HELP
|
|
printf("x = ");
|
|
#endif
|
|
display_root(i);
|
|
printf("\n");
|
|
}
|
|
|
|
exit(0);
|
|
}
|
|
|
|
void
|
|
display_root(int i)
|
|
{
|
|
printf("%+.*g", precision, z[2*i]); /* output real part */
|
|
if (z[2*i+1])
|
|
printf(" %+.*g*i", precision, z[2*i+1]); /* output imaginary part */
|
|
}
|