[net.sources] Program to find roots of cubic polynomial

bet@ecsvax.UUCP (Bennett E. Todd III) (10/15/85)

#!/bin/sh
# Cut above the preceeding line, or cut here if you must.
# This is a shar archive.  Extract with sh, not csh.
# The rest of this file will extract:
# README complex.h cubic.c complex.c
sed 's/^X//' > README << '/*EOF'
XThis is a program to compute the roots to a cubic polynomial in real
Xcoefficients using the closed form solution as described in CRC's
XStandard Math Tables. It is written in C, which might have been a
Xmistake, but was fun anyhow. Everything is reasonably straightforward
Xand documented in comments, until you get to the complex math. C doesn't
Xdo complex math. C doesn't even like to be forced to do complex math.
XWithout operator overloading you cannot express the equations in complex
Xvariables using infix notation; you are left with a functional notation
Xnot (sufficiently, for my tastes) unlike publication LISP syntax. Worse
Xstill, I really truly despise structure passing and return. Therefore I
Xdon't use them; rather I allocate my own structures and pass pointers to
Xthem. Unfortunately, nested procedure calls for intricate expressions
Xwould result in intermediate structures being dropped on the floor.
XTherefore I have the called routine free its arguments. And therefore,
Xif you wish to pass it a program variable (which you presumably don't
Xwant freed) then you must invoke a utility routine to make a copy of
Xthat variable. I have solicited suggestions for names for this
Xas-far-as-I-know unique parameter passing mechanism, and the current
Xwinner (by default) is "call by copy-and-destroy" by D. Gary Grady
X(dgary@ecsvax.UUCP and dpiggy@tucc.BITNET).
X
X-Bennett
/*EOF
ls -l README
sed 's/^X//' > complex.h << '/*EOF'
X/*
X * complex math routines' header file
X */
X/*
X * Written by Bennett Todd
X * Date: 10/15/85 -- initial release
X * This code is released to the public domain; do with it as you wish.
X * If you use it, credit would be appreciated.
X * This is written for and in DeSmet C for the IBM-PC;
X * as far as I know it should be exceedingly portable.
X * Tabsets are at every 4 columns.
X * Please send any bug fixes, enhancements, or general comments to
X *		Bennett Todd
X *		Duke Computation Center
X *		Durham, NC 27706-7756
X *		+1 919 684 3695
X *		UUCP: ...{decvax,seismo,philabs,ihnp4,akgua}!mcnc!ecsvax!duccpc!bet
X *		BITNET: dbtodd@tucc
X */
X
Xtypedef struct complex_struct_type {
X	double float real, imaginary;
X	struct complex_struct_type *next;
X} COMPLEX;
X
Xextern COMPLEX *Complex(),	/* returns a complex made from the real args */
X			   *C_copy(),	/* returns a copy of its argument */
X			   *C_negate(),	/* negate -- monadic */
X			   *C_sq(),		/* square -- monadic */
X			   *C_pow(),	/* raise to real power -- mixed dyadic */
X			   *C_cb(),		/* cube -- monadic */
X			   *C_sqrt(),	/* square root -- monadic */
X			   *C_cbrt(),	/* cube root -- monadic */
X			   *C_times(),	/* product -- dyadic */
X			   *C_div(),	/* ratio -- dyadic */
X			   *C_plus(),	/* sum -- dyadic */
X			   *C_minus();	/* difference -- dyadic */
/*EOF
ls -l complex.h
sed 's/^X//' > cubic.c << '/*EOF'
X#include <stdio.h>
X#include <math.h>
X#include "complex.h"
X/*
X * cubic -- apply closed-form solution to an arbitrary cubic
X *          in real coefficients
X * based on the exposition of the algorithm in the CRC Standard
X * Mathematical Tables, 27'th edition, CRC Press, Boca Raton FL,
X * 1984, on page 9.
X * syntax: cubic o*x^3 + p*x^2 + q*x + r = s
X * Where o, p, q, r, and s are arbitrary constants in format suitable
X * for conversion using "%f" format in scanf().
X * Obviously, a grossly inefficient command-line syntax. Sorry.
X */
X/*
X * Written by Bennett Todd
X * Date: 10/15/85 -- initial release
X * This code is released to the public domain; do with it as you wish.
X * If you use it, credit would be appreciated.
X * This is written for and in DeSmet C for the IBM-PC;
X * as far as I know it should be exceedingly portable.
X * Tabsets are at every 4 columns.
X * Please send any bug fixes, enhancements, or general comments to
X *		Bennett Todd
X *		Duke Computation Center
X *		Durham, NC 27706-7756
X *		+1 919 684 3695
X *		UUCP: ...{decvax,seismo,philabs,ihnp4,akgua}!mcnc!ecsvax!duccpc!bet
X *		BITNET: dbtodd@tucc
X */
X
X#define STR_NEQ(s1, s2) strcmp(s1, s2) /* take THAT you stylists! */
X
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X{
X	/* Sorry about the variable names; they are chosen to match CRC */
X	double o, p, q, r, s, a, b;
X	COMPLEX *A, *B, *root_1, *root_2, *root_3;
X
X	/* (attempt to) parse command line */
X	if (argc != 10) {
X		fprintf(stderr, "syntax: cubic o*x^3 + p*x^2 + q*x + r = s\n");
X		fprintf(stderr, "Indicate missing terms with zeroes.\n");
X		exit(1);
X	}
X	if (sscanf(argv[1], "%lf*x^3", &o) != 1) {
X		fprintf(stderr, "cubic: cannot parse %s\n", argv[1]);
X		exit(1);
X	}
X	if (sscanf(argv[3], "%lf*x^2", &p) != 1) {
X		fprintf(stderr, "cubic: cannot parse %s\n", argv[3]);
X		exit(1);
X	}
X	if (sscanf(argv[5], "%lf*x", &q) != 1) {
X		fprintf(stderr, "cubic: cannot parse %s\n", argv[5]);
X		exit(1);
X	}
X	if (sscanf(argv[7], "%lf", &r) != 1) {
X		fprintf(stderr, "cubic: cannot parse %s\n", argv[7]);
X		exit(1);
X	}
X	if (sscanf(argv[9], "%lf", &s) != 1) {
X		fprintf(stderr, "cubic: cannot parse %s\n", argv[9]);
X		exit(1);
X	}
X	if (STR_NEQ(argv[2], "+") || STR_NEQ(argv[4], "+") ||
X		STR_NEQ(argv[6], "+") || STR_NEQ(argv[8], "=")) {
X			fprintf(stderr, "syntax: cubic o*x^3 + p*x^2 + q*x + r = s\n");
X			fprintf(stderr, "Indicate missing terms with zeroes.\n");
X			exit(1);
X		}
X	printf("\t%s + %s + %s + %s = %s\n",
X		   argv[1], argv[3], argv[5], argv[7], argv[9]);
X
X	/*
X	 * normalize "ox^3 + px^2 + qx +r = s" into
X	 * "x^3 + p'x^2 + q'x + r' = 0" by subtracting "s" from both sides
X	 * and dividing through by "o"
X	 */
X	r = r-s;
X	/* sanity check */
X	if (o == 0) {
X		fprintf(stderr, "cubic: sorry buddy, that's a quadratic.\n");
X		exit(1);
X	}
X	p /= o;
X	q /= o;
X	r /= o;
X	printf("transforms to\n\tx^3 + %lfx^2 + %lfx + %lf = 0\n", p, q, r);
X
X	/*
X	 * According to CRC, the following assignments implement a change
X	 * of variable from "x^3 + px^2 + qx + r = 0" to
X	 * "x'^3 + ax' + b = 0" by propogating the substitution
X	 * "x = x' - p/3". Let's try and remember to convert back...
X	 */
X	a = (3*q - p*p)/3;
X	b = (2*p*p*p - 9*p*q + 27*r)/27;
X	printf("which in turn transforms to\n\tx^3 + %lfx + %lf = 0\n", a, b);
X	printf("via the substitution\n\tx ==> x - %lf\n", p/(float) 3);
X
X	/*
X	 * No motivating explanation given in CRC; it seems that the
X	 * form of the ultimate solution suggests the following intermediate
X	 * values to simplify the algebra
X	 *
X	 * A =   cube_root(sqrt(b*b/(float)4 + a*a*a/(float)27) - b/(float)2);
X	 * B = - cube_root(sqrt(b*b/(float)4 + a*a*a/(float)27) + b/(float)2);
X	 *
X	 * then the roots of the transformed equation "x'^3 + ax' + b = 0"
X	 * are
X	 *
X	 * A+B, -(A+B)/2 + ((A-B)/2)*sqrt(-3), and
X	 *      -(A+B)/2 - ((A-B)/2)*sqrt(-3)
X	 *
X	 * However, since CRC immediately goes on to say
X	 *
X	 * "If b^2 / 4  +  a^3 / 27 > 0 there will be one real root and two
X	 *                              complex conjugate roots;
X	 *  If b^2 / 4  +  a^3 / 27 = 0 there will be three real roots at least
X	 *                              two of which are equal
X	 *  If b^2 / 4  +  a^3 / 27 < 0 there will be three real unequal roots"
X	 *
X	 * we'd better go ahead and handle complex numbers here. Friends and
X	 * neighbors, if you are inextricably wedded to infix notation you had
X	 * better skip the next part. In fact, the next part undoubtledly
X	 * shouldn't be written in C. What can I say. I'm stubborn.
X	 *
X	 * Note: to avoid dropping intermediate values on the floor, without
X	 * using structure passing and return (which I despise), the complex
X	 * routines have the following semantics: they take one or two complex
X	 * arguments as pointers to the structures, and return a pointer to a
X	 * new structure containing the result. The routines free their complex
X	 * arguments -- if you wish to pass a program variable (which you don't
X	 * want freed) to a complex subroutine, pass in C_copy(variable) which
X	 * doesn't free its argument, and returns a pointer to a copy. What,
X	 * ladies and gentlemen, is this to be called? Call by copy-and-destroy?
X	 */
X	A = C_cbrt(
X			C_plus(
X				C_sqrt(
X					C_plus(
X						C_div(
X							C_sq(Complex(b, 0.0)),
X							Complex(4.0, 0.0)
X						),
X						C_div(
X							C_cb(Complex(a, 0.0)),
X							Complex(27.0, 0.0)
X						)
X					)
X				),
X				C_negate(
X					C_div(
X						Complex(b, 0.0),
X						Complex(2.0, 0.0)
X					)
X				)
X			)
X		);
X	B = C_negate(
X			C_cbrt(
X				C_plus(
X					C_sqrt(
X						C_plus(
X							C_div(
X								C_sq(Complex(b, 0.0)),
X								Complex(4.0, 0.0)
X							),
X							C_div(
X								C_cb(Complex(a, 0.0)),
X								Complex(27.0, 0.0)
X							)
X						)
X					),
X					C_div(
X						Complex(b, 0.0),
X						Complex(2.0, 0.0)
X					)
X				)
X			)
X		);
X
X	root_1 = C_plus(C_copy(A), C_copy(B));
X	root_2 = C_plus(
X				C_negate(
X					C_div(
X						C_copy(root_1),
X						Complex(2.0, 0.0)
X					)
X				),
X				C_times(
X					C_div(
X						C_minus(C_copy(A), C_copy(B)),
X						Complex(2.0, 0.0)
X					),
X					Complex(0.0, sqrt(3))
X				)
X			);
X	root_3 = C_negate(
X				C_plus(
X					C_div(
X						C_copy(root_1),
X						Complex(2.0, 0.0)
X					),
X					C_times(
X						C_div(
X							C_minus(C_copy(A), C_copy(B)),
X							Complex(2.0, 0.0)
X						),
X						Complex(0.0, sqrt(3))
X					)
X				)
X			);
X
X	printf("The original equation has the roots\n");
X	printf("\t %lf", root_1->real - p/(float)3);
X	if (root_1->imaginary != 0)
X		printf("+%lfi", root_1->imaginary);
X	printf(", %lf", root_2->real - p/(float)3);
X	if (root_2->imaginary != 0)
X		printf("+%lfi", root_2->imaginary);
X	printf(", and %lf", root_3->real - p/(float)3);
X	if (root_3->imaginary != 0)
X		printf("+%lfi", root_3->imaginary);
X	printf("\n");
X}
/*EOF
ls -l cubic.c
sed 's/^X//' > complex.c << '/*EOF'
X#include <stdio.h>
X#include <math.h>
X#include "complex.h"
X/*
X * Implementation of complex math routines.
X * Routines expect one or two complex arguments, passed as
X * pointers to COMPLEX structs, and return a pointer to
X * a new COMPLEX struct containing the result. So that calls
X * to the complex routines can be nested in intricate expressions
X * without the structs which were created for return values being
X * lost and dropped on the floor, these routines FREE their arguments.
X * You should therefore use the service routine C_copy() to make
X * copies of any constants or program variables you wish to pass into
X * a complex routine.
X */
X
X/*
X * Written by Bennett Todd
X * Date: 10/15/85 -- initial release
X * This code is released to the public domain; do with it as you wish.
X * If you use it, credit would be appreciated.
X * This is written for and in DeSmet C for the IBM-PC;
X * as far as I know it should be exceedingly portable.
X * Tabsets are at every 4 columns.
X * Please send any bug fixes, enhancements, or general comments to
X *		Bennett Todd
X *		Duke Computation Center
X *		Durham, NC 27706-7756
X *		+1 919 684 3695
X *		UUCP: ...{decvax,seismo,philabs,ihnp4,akgua}!mcnc!ecsvax!duccpc!bet
X *		BITNET: dbtodd@tucc
X */
X
X#define PI 3.14159265359
X
Xstatic COMPLEX *complex_free_head = NULL;
X
Xstatic COMPLEX *newcomplex()
X{
X	COMPLEX *r;
X
X	if (complex_free_head == NULL)
X		if ((r = (COMPLEX *) malloc(sizeof(COMPLEX))) == NULL) {
X			fprintf(stderr, "complex library malloc() failure\n");
X			exit(1);
X		} else
X			return(r);
X	r = complex_free_head;
X	complex_free_head = complex_free_head->next;
X	return(r);
X}
X
Xstatic freecomplex(c)
XCOMPLEX *c;
X{
X	c->next = complex_free_head;
X	complex_free_head = c;
X}
X
XCOMPLEX *Complex(x, y)
Xdouble float x, y;
X{
X	COMPLEX *r;
X
X	r = newcomplex();
X	r->real = x;
X	r->imaginary = y;
X	return(r);
X}
X
XCOMPLEX *C_copy(c)
XCOMPLEX *c;
X{
X	COMPLEX *r;
X
X	r = newcomplex();
X	r->real = c->real;
X	r->imaginary = c->imaginary;
X	return(r);
X}
X
XCOMPLEX *C_negate(c)
XCOMPLEX *c;
X{
X	c->real = -c->real;
X	c->imaginary = -c->imaginary;
X	return(c);
X}
X
XCOMPLEX *C_sq(c)
XCOMPLEX *c;
X{
X	COMPLEX *r;
X
X	r = newcomplex();
X	r->real = c->real*c->real - c->imaginary*c->imaginary;
X	r->imaginary = 2*c->real*c->imaginary;
X	freecomplex(c);
X	return(r);
X}
X
XCOMPLEX *C_pow(c, p)
XCOMPLEX *c;
Xdouble float p;
X{
X	double float magnitude, angle;
X
X	magnitude = sqrt(c->real*c->real + c->imaginary*c->imaginary);
X	if (c->real != 0)
X		angle = atan(c->imaginary/c->real);
X	else if (c->imaginary > 0)
X		angle = PI/(float)2;
X	else if (c->imaginary < 0)
X		angle = -PI/(float)2;
X	else /* What is the angle of 0+0i? */
X		angle = 0.0;
X	if (c->real < 0)
X		angle += PI;
X	c->real = pow(magnitude, p) * cos(p*angle);
X	c->imaginary = pow(magnitude, p) * sin(p*angle);
X	return(c);
X}
X
XCOMPLEX *C_cb(c)
XCOMPLEX *c;
X{
X	return(C_pow(c, 3.0));
X}
X
XCOMPLEX *C_sqrt(c)
XCOMPLEX *c;
X{
X	return(C_pow(c, 1/(float)2));
X}
X
XCOMPLEX *C_cbrt(c)
XCOMPLEX *c;
X{
X	return(C_pow(c, 1/(float)3));
X}
X
XCOMPLEX *C_times(a, b)
XCOMPLEX *a, *b;
X{
X	COMPLEX *r;
X
X	r = newcomplex();
X	r->real = a->real*b->real - a->imaginary*b->imaginary;
X	r->imaginary = a->real*b->imaginary + a->imaginary*b->real;
X	freecomplex(a);
X	freecomplex(b);
X	return(r);
X}
X
XCOMPLEX *C_div(a, b)
XCOMPLEX *a, *b;
X{
X	COMPLEX *r;
X	double float tmp;
X
X	r = newcomplex();
X	tmp = b->real*b->real + b->imaginary*b->imaginary;
X	if (tmp == 0) {
X		fprintf(stderr, "complex divide overflow\n");
X		exit(1);
X	}
X	r->real = (a->real*b->real + a->imaginary*b->imaginary)/tmp;
X	r->imaginary = (b->real*a->imaginary - a->real*b->imaginary)/tmp;
X	freecomplex(a);
X	freecomplex(b);
X	return(r);
X}
X
XCOMPLEX *C_plus(a, b)
XCOMPLEX *a, *b;
X{
X	a->real = a->real + b->real;
X	a->imaginary = a->imaginary + b->imaginary;
X	freecomplex(b);
X	return(a);
X}
X
XCOMPLEX *C_minus(a, b)
XCOMPLEX *a, *b;
X{
X	a->real = a->real - b->real;
X	a->imaginary = a->imaginary - b->imaginary;
X	freecomplex(b);
X	return(a);
X}
/*EOF
ls -l complex.c
exit
-- 

"Hypocrisy is the vaseline of social intercourse." (Who said that?)

Bennett Todd -- Duke Computation Center, Durham, NC 27706-7756; (919) 684-3695
UUCP: ...{decvax,seismo,philabs,ihnp4,akgua}!mcnc!ecsvax!duccpc!bet