Program to find roots of cubic polynomial
Bennett E. Todd III
bet at ecsvax.UUCP
Wed Oct 16 03:58:38 AEST 1985
#!/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 at ecsvax.UUCP and dpiggy at 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 at 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 at 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 at 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
More information about the Comp.sources.unix
mailing list