common Class Math

Object common:Math

class
Object:Math

Mathematical tools library.

This class contains a number of useful functions for performing root-solving and other high-level math functions. They are implemented as static methods so that you do not have to maintain an instance of this class; you need only reference the class itself.

For example, to solve 4x^2 - 4x + 1 = 0:

myroots.m_Elements will be 0.5 and myroots.m_Elements will be -0.5. Note that you don't have to create a Math object first, you just use the function directly.

Root Solver General Notes

If you are constructing polynomials inside a loop, you should create your roots array object just once, outside the loop, and re-use it each time. This is much faster that constructing a new roots object for each polynomial.

Root solvers are provided for quadratic, cubic, and quartic functions, with either real or complex coefficients. Using the real version (if you know your coefficients are only real values) may be faster in some cases; note that real coefficients may still produce complex roots.

In general, the pflags parameter controls some aspects of root-finding. It is the sum of these values:

1: Return real roots only (only available in the real-coefficient versions).
2: If a root occurs more than once, only return it once.
4: Use iterative improvement for roots (only available on cubic and quartic).
8: Emphasize the accuracy of smaller-magnitude roots, at the expense of accuracy in larger-magnitude roots (only available on cubic and quartic).
16: Force real roots by setting discriminant to zero if it's negative (quadratic only; used internall by the quartic solver).

For example, pflags = 5 will return real roots only and use iterative improvement.

Because of round-off errors, real roots (especially duplicate roots) may be found to have small imaginary parts. If real roots are specified, imaginary parts less than a threshold (in magnitude) are set to zero. This threshold is currently hard-coded inside these formulas and is generally acceptable, but may not be sufficient for your specific use.

Similarly, if duplicate roots are to be removed and two roots differ by less than the threshold, they will be considered equal.

Specific Solver Notes

Consider

a*x^n + b*x^(n-1) + c*x^(n-2) + ... = 0 <h5>Order n = 2, quadratic.</h5>

This solution is well known.

x = (-b +- sqrt(b*b-4*a*c))/(2*a)

But there can be cancellation and loss of accuracy. Use this for the root that doesn't lose accuracy. Since x1*x2=c/a, use x2 = c/(a*x1) for the other root.

For real coefficients, the two roots are either both real or a complex-conjugate pair. <h5>Order n = 3, cubic.</h5>

Divide by a, then use the substitution x = t-b/(3*a) to reduce to the "depressed" form,

t^3 + aa*t + bb = 0

For real coefficients, the three roots are either all real or one real and a complex-conjugate pair. <h5>Order n = 4, quartic.</h5>

Divide by a, then use the substitution x = t-b/(4*a) to reduce to the "depressed" form,

t^4 + aa*t^2 + bb*t + cc = 0 There are several ways (at least 5) to proceed. All of them require the solution of a cubic polynomial.

For real coefficients, the four roots are in pairs. In each pair, there are either two reals or a complex-conjugate pair.

Accuracy

If there are multiple roots, round-off may cause the roots to have a small imaginary part. Use the "real" flag setting if you want only real roots.

Cubic and Quartic: If there is a large variation in the magnitude of the roots, the smaller roots may be found less accurately. Some of this is inherent: the polynomial is ill-conditioned (small changes in the coefficients lead to larger changes in the roots). Sometimes it helps to solve for the roots of w = 1/x.

Iterative refinement also sometimes helps. In solving p(x) = 0, suppose we have a "close enough" guess at a root, x0. Then Newton's method can be used to improve it, i.e.,

x1 = x0 - p(x0)/p'(x0)

which converges quadratically if the root is a simple one. Quadratically means that the number of incorrect digits is halved at each iteration.

However, Newton's method converges only linearly to a multiple root. (For a double root, the error is cut in half, approximately, at each iteration.)

Instead we use a formula that includes the second derivative as well:

x1 = x0 - p(x0)*p'(x0)/[ p'(x0)^2 - k*p(x0)*p"(x0)] with k = 1

This converges quadratically even with multiple roots. Note that setting k = 1/2 gives Halley's method, which converges cubically for simple roots, but only linearly with multiple roots.

NOTE: If higher accuracy is needed, use the "Additional Precision" setting on the Formula tab in Layer Properties.

Currently all thresholds are set to 1e-6, a "reasonable" value.

Bessel Function Notes

These approximations to the Bessel functions J_0(x) and J_1(x) (for real x) are accurate to 15 or 16 digits. They come from
Computer Approximations
J. F. Hart, et al.
Wiley, 1968
Spot values have been checked from the text and as calculated by Mathematica.

These "Bessel" functions J_0(z) and J_1(z) (for complex z) are not quite the actual Bessel functions. Let R = cabs(z). For R <= R1, these are the actual Bessel functions, computed by power series, to full precision except for round-off. For R > R1, these are similar to the actual Bessel functions, with appropriate asymptotic behavior for large R, and matched at R = R1. The actual Bessel functions cannot be calculated with full precision for large R without going to higher-precision calculations, and this is (a) not possible in UF and (b) not really necessary for making fractals.

R1 = 8 is used below.

Jim Blue, June 2008

Ultra Fractal Source
class Math {
; Mathematical tools library.
;
; <p>
; This class contains a number of useful functions for
; performing root-solving and other high-level math functions.
; They are implemented as static
; methods so that you do not have to maintain an instance
; of this class; you need only reference the class itself.
; <p>
; For example, to solve 4x^2 - 4x + 1 = 0:
; <p>
; myroots = new ComplexArray(2)
; <p>
; myroots.m_Elements will be 0.5 and myroots.m_Elements
; will be -0.5. Note that you don't have to create a Math
; object first, you just use the function directly.

; <h4>Root Solver General Notes</h4>
; <p>
; If you are constructing polynomials inside a loop, you
; should create your roots array object just once, outside
; the loop, and re-use it each time. This is much faster
; that constructing a new roots object for each polynomial.
; <p>
; Root solvers are provided for quadratic, cubic, and quartic
; functions, with either real or complex coefficients. Using the
; real version (if you know your coefficients are only real
; values) may be faster in some cases; note that real coefficients
; may still produce complex roots.
;
; <p>
; In general, the pflags parameter controls some aspects of root-finding.
; It is the sum of these values:
; <p>
; 1: Return real roots only (only available in the real-coefficient versions).<br>
; 2: If a root occurs more than once, only return it once.<br>
; 4: Use iterative improvement for roots (only available on cubic and quartic).<br>
; 8: Emphasize the accuracy of smaller-magnitude roots, at the expense of accuracy in larger-magnitude roots (only available on cubic and quartic).<br>
; 16: Force real roots by setting discriminant to zero if it's negative (quadratic only; used internall by the quartic solver).
; <p>
; For example, pflags = 5 will return real roots only and use iterative improvement.
; <p>
; Because of round-off errors, real roots (especially duplicate roots) may be
; found to have small imaginary parts. If real roots are specified, imaginary
; parts less than a threshold (in magnitude) are set to zero. This threshold is
; currently hard-coded inside these formulas and is generally acceptable, but
; may not be sufficient for your specific use.
; <p>
; Similarly, if duplicate roots are to be removed and two roots differ by less
; than the threshold, they will be considered equal.
;
; <h4>Specific Solver Notes</h4>
; <p>
; Consider
; <p>
;     a*x^n + b*x^(n-1) + c*x^(n-2) + ... = 0
; <h5>Order n = 2, quadratic.</h5>
; <p>
; This solution is well known.
; <p>
;      x = (-b +- sqrt(b*b-4*a*c))/(2*a)
; <p>
; But there can be cancellation and loss of accuracy. Use this for the root that
; doesn't lose accuracy. Since x1*x2=c/a, use x2 = c/(a*x1) for the other root.
; <p>
; For real coefficients, the two roots are either both real or a complex-conjugate
; pair.
;
; <h5>Order n = 3, cubic.</h5>
; <p>
; Divide by a, then use the substitution x = t-b/(3*a) to reduce to the "depressed"
; form,
; <p>
;      t^3 + aa*t + bb = 0
; <p>
; For real coefficients, the three roots are either all real or one real and a
; complex-conjugate pair.
;
; <h5>Order n = 4, quartic.</h5>
; <p>
; Divide by a, then use the substitution x = t-b/(4*a) to reduce to the "depressed"
; form,
; <p>
;      t^4 + aa*t^2 + bb*t + cc = 0
; There are several ways (at least 5) to proceed. All of them require the solution
; of a cubic polynomial.
; <p>
; For real coefficients, the four roots are in pairs. In each pair, there are
; either two reals or a complex-conjugate pair.
;
; <h4>Accuracy</h4>
; <p>
; If there are multiple roots, round-off may cause the roots to have a small
; imaginary part. Use the "real" flag setting if you want only real roots.
; <p>
; <p>
; Cubic and Quartic: If there is a large variation in the magnitude of the roots,
; the smaller roots may be found less accurately. Some of this is inherent: the
; polynomial is ill-conditioned (small changes in the coefficients lead to larger
; changes in the roots). Sometimes it helps to solve for the roots of w = 1/x.
; <p>
; Iterative refinement also sometimes helps. In solving p(x) = 0, suppose we have
; a "close enough" guess at a root, x0. Then Newton's method can be used to
; improve it, i.e.,
; <p>
;       x1 = x0 - p(x0)/p'(x0)
; <p>
; which converges quadratically if the root is a simple one. Quadratically means
; that the number of incorrect digits is halved at each iteration.
; <p>
; However, Newton's method converges only linearly to a multiple root. (For a
; double root, the error is cut in half, approximately, at each iteration.)
; <p>
; Instead we use a formula that includes the second derivative as well:
; <p>
;     x1 = x0 - p(x0)*p'(x0)/[ p'(x0)^2 - k*p(x0)*p"(x0)] with k = 1
; <p>
; This converges quadratically even with multiple roots. Note that setting k = 1/2
; gives Halley's method, which converges cubically for simple roots, but only
; linearly with multiple roots.
; <p>
; NOTE: If higher accuracy is needed, use the "Additional Precision" setting on
; the Formula tab in Layer Properties.
; <p>
; Currently all thresholds are set to 1e-6, a "reasonable" value.
; <p>
;
; <h4>Bessel Function Notes</h4>
; <p>
; These approximations to the Bessel functions J_0(x) and J_1(x)
; (for <strong>real</strong> x) are accurate to 15 or 16 digits. They come from <br>
;     Computer Approximations<br>
;     J. F. Hart, et al.<br>
;     Wiley, 1968<br>
; Spot values have been checked from the text and as calculated by
; Mathematica.
; <p>
; These "Bessel" functions J_0(z) and J_1(z)
; (for <strong>complex</strong> z) are not quite the actual Bessel functions.
; Let R = cabs(z). For R <= R1, these are the actual Bessel functions,
; computed by power series, to full precision except for round-off.
; For R > R1, these are
; similar to the actual Bessel functions, with appropriate asymptotic
; behavior for large R, and matched at R = R1. The actual Bessel functions
; cannot be calculated with full precision for large R without going to
; higher-precision calculations, and this is (a) not possible in UF and
; (b) not really necessary for making fractals.
; <p>
; R1 = 8 is used below.
; <p>
; Jim Blue, June 2008

public:
; Constructor
; <p>
; You should never need to actually construct a Math object, as all of its
; methods are static and can be called without an object (just use the class
; name).
func Math()
endfunc

; Returns the sign of a number.
;
; @param px the number to test
; @return the sign; negative numbers give -1, positive numbers (including zero) return 1
static float func Sgn(const float px)
if (px >= 0.0)
return 1.0
else
return -1.0
endif
endfunc

; Determine the maximum of two numbers.
;
; @param px first number
; @param py second number
; @return the largest of the two numbers
static float func MaxF(const float px, const float py)
if (px >= py)
return px
else
return py
endif
endfunc

; Determine the minimum of two numbers.
;
; @param px first number
; @param py second number
; @return the smallest of the two numbers
static float func MinF(const float px, const float py)
if (px <= py)
return px
else
return py
endif
endfunc

; <p>
; See the class overview for details on the solvers.
; This solves a generic quadratic equation of the form
; <p>
; (a1)(x^2) + (a2)(x) + (a3) = 0
;
; @param pa1 a1 term
; @param pa2 a2 term
; @param pa3 a3 term
; @param pflags options
; @param proots reference to ComplexArray object to hold roots; this should already be sized to at least two elements (regardless of whether return values are limited)
; @return the number of roots found (will be 2 unless return values are limited)
const float pa1, const float pa2, const float pa3, \
const int pflags, ComplexArray &proots)

bool forcerealroots    = (floor(pflags/16)%2 == 1)
;bool smallrootsbest  = (floor(pflags/8)%2 == 1)
;bool iterimprove    = (floor(pflags/4)%2 == 1)
bool removeduplicates  = (floor(pflags/2)%2 == 1)
bool realrootsonly    = (     (pflags  )%2 == 1)

; Degenerate case: not a quadratic
if (pa1 == 0)
if (pa2 == 0)
return 0
else
proots.m_Elements = -pa3/pa2
return 1
endif
endif
; Degenerate case: zero root
if (pa3 == 0)
proots.m_Elements = 0
if (removeduplicates && pa2 == 0)
return 1
endif
proots.m_Elements = -pa2/pa1
return 2
endif

float RealThreshold = 1.e-6
float d = pa2*pa2 - 4*pa1*pa3        ; discriminant
if (d < 0 && forcerealroots)
d = 0
endif
if (d < 0)
int nroots = 2
float i2pa1 = 0.5 / pa1
float im = sqrt(-d) * i2pa1
if (realrootsonly || removeduplicates)
if (abs(im) <= RealThreshold)
im = 0
if (removeduplicates)
nroots = 1
endif
else
return 0
endif
endif
float re = -pa2 * i2pa1
proots.m_Elements = re + flip(im)
proots.m_Elements = re - flip(im)
return nroots
else                    ; d >= 0 so do carefully to avoid possible cancellation
float t = -0.5*(pa2 + sgn(pa2)*sqrt(d))
proots.m_Elements = t/pa1      ; this root is larger in magnitude
proots.m_Elements = pa3/t
if (removeduplicates && \
abs(real(proots.m_Elements-proots.m_Elements)) < RealThreshold)
return 1
else
return 2
endif
endif

; <p>
; See the class overview for details on the solvers.
; This solves a generic quadratic equation of the form
; <p>
; (a1)(x^2) + (a2)(x) + (a3) = 0
;
; @param pa1 a1 term
; @param pa2 a2 term
; @param pa3 a3 term
; @param pflags options
; @param proots reference to ComplexArray object to hold roots; this should already be sized to at least two elements (regardless of whether return values are limited)
; @return the number of roots found (will be 2 unless return values are limited)
const complex pa1, const complex pa2, const complex pa3, \
const int pflags, ComplexArray &proots)

bool removeduplicates = (floor(pflags/2)%2 == 1)

; Degenerate case: not a quadratic
if (pa1 == 0)
if (pa2 == 0)
return 0
else
proots.m_Elements = -pa3/pa2
return 1
endif
endif
; Degenerate case: zero root
if (pa3 == 0)
proots.m_Elements = 0
if (removeduplicates && pa2 == 0)
return 1
endif
proots.m_Elements = -pa2/pa1
return 2
endif

float Thresholdsq = sqr(1.e-6)

complex sqrtd = sqrt(pa2*pa2 - 4*pa1*pa3)
; do carefully to avoid possible cancellation
; (this gives the same result as the real case when sqrtd is real)
complex t1 = (pa2 + sqrtd)
complex t2 = (pa2 - sqrtd)
complex t
if (|t1| >= |t2|)
t = -0.5*t1
else
t = -0.5*t2
endif
proots.m_elements = t/pa1
proots.m_elements = pa3/t
if (removeduplicates && \
|proots.m_elements - proots.m_elements| < Thresholdsq)
return 1
else
return 2
endif

; Solve (real) cubic equation.
; <p>
; See the class overview for details on the solvers.
; This solves a generic cubic equation of the form
; <p>
; (a1)(x^3) + (a2)(x^2) + (a3)(x) + (a4) = 0
;
; @param pa1 a1 term
; @param pa2 a2 term
; @param pa3 a3 term
; @param pa4 a4 term
; @param pflags options
; @param proots reference to ComplexArray object to hold roots; this should already be sized to at least three elements (regardless of whether return values are limited)
; @return the number of roots found (will be 3 unless return values are limited)
static int func SolveRealCubicPolynomial( \
const float pa1, const float pa2, const float pa3, const float pa4, \
const int pflags, ComplexArray &proots)

if (pa1 == 0)        ; degenerate-not a cubic
return SolveRealQuadraticPolynomial(pa2, pa3, pa4, pflags, proots)
endif
if (pa4 == 0)        ; one root of zero
int n = SolveRealQuadraticPolynomial(pa1, pa2, pa3, pflags, proots)
proots.m_Elements[n] = 0
return n+1
endif

bool smallrootsbest    = (floor(pflags/8)%2 == 1)
bool iterimprove    = (floor(pflags/4)%2 == 1)
bool removeduplicates  = (floor(pflags/2)%2 == 1)
bool realrootsonly    = (     (pflags  )%2 == 1)

float a
float b
float c
if (smallrootsbest)      ; w = 1/x, solve for roots of w
a = pa3/pa4
b = pa2/pa4
c = pa1/pa4
else
a = pa2/pa1
b = pa3/pa1
c = pa4/pa1
endif

; Now have x^3 + aa*x + bb = 0
; change of variable t = x - a/3 removes the quadratic term, giving
; t^3 - 3*Q*t + 2*R = 0
float a3 = a/3
float aa = b - 3*a3*a3
float bb = c - a3*(b - 2*a3*a3)
float Q = -aa/3        ; (a*a-3*b)/9
float R = bb/2        ; (a*(2*a*a-9*b)+27*c)/54
float D = Q*Q*Q-R*R

; Could here do special cases Q=0, R=0, D=0
; If two equal roots, D may be slightly negative, making them into a
; c.c. pair with small imaginary part.

int nroots = 3
if (D >= 0)          ; three real roots
; Note - theta usually done as acos, but atan2 is better
float theta = atan2(R +flip(sqrt(D)))/3
; float theta = acos(R/sqrt(Q*Q*Q))/3  ; is the usual version
float cc = -2*sqrt(Q)  ; D > 0, so Q > 0
proots.m_Elements = cc*cos(theta          ) -a3
proots.m_Elements = cc*cos(theta + 2*#pi/3) -a3
proots.m_Elements = cc*cos(theta - 2*#pi/3) -a3

else            ; one real root, two complex-conjugate roots
; no duplicates possible
float Ar = -Sgn(R) * ((abs(R) + sqrt(-D)))^(1/3)
float Br = 0
if (Ar != 0)      ; always Ar!=0 with real coefficients
Br = Q/Ar
endif
proots.m_Elements = Ar+Br - a3
float im = 0.5*sqrt(3)*(Ar-Br)
proots.m_Elements = -0.5*(Ar+Br) -a3 + flip(im)
proots.m_Elements = conj(proots.m_Elements)
endif

if (smallrootsbest)
int n = 0
while (n < nroots)
proots.m_Elements[n] = 1/proots.m_Elements[n]
n = n + 1
endwhile
endif

; iterative refinement
if (iterimprove)
int n = 0
while (n < nroots)
complex x = proots.m_Elements[n]
complex v0 = pa4 + x*(pa3 + x*(pa2 + pa1*x))
complex v1 = pa3 + x*(2*pa2 + 3*pa1*x)
if (v0*v1 != 0)
complex v2 = 2*pa2 + 6*pa1*x
; complex xnew = x - v0*v1/(v1*v1-v0*v2)
; x = xnew
x = x - v0*v1/(v1*v1-v0*v2)
endif
proots.m_Elements[n] = x
n = n + 1
endwhile
endif

if (realrootsonly)
nroots = RealOnly(nroots, proots)
endif

if (removeduplicates)
nroots = SingleOnly(nroots, proots)
endif
return nroots

endfunc      ; end of SolveRealCubicPolynomial

; Solve (complex) cubic equation.
; <p>
; See the class overview for details on the solvers.
; This solves a generic cubic equation of the form
; <p>
; (a1)(x^3) + (a2)(x^2) + (a3)(x) + (a4) = 0
;
; @param pa1 a1 term
; @param pa2 a2 term
; @param pa3 a3 term
; @param pa4 a4 term
; @param pflags options
; @param proots reference to ComplexArray object to hold roots; this should already be sized to at least three elements (regardless of whether return values are limited)
; @return the number of roots found (will be 3 unless return values are limited)
static int func SolveComplexCubicPolynomial( \
const complex pa1, const complex pa2, const complex pa3, const complex pa4, \
const int pflags, ComplexArray &proots)

if (pa1 == 0)        ; degenerate-not a cubic
return SolveComplexQuadraticPolynomial(pa2, pa3, pa4, pflags, proots)
endif
if (pa4 == 0)        ; one root of zero
int n = SolveComplexQuadraticPolynomial(pa1, pa2, pa3, pflags, proots)
proots.m_Elements[n] = 0
return n+1
endif

bool smallrootsbest    = (floor(pflags/8)%2 == 1)
bool iterimprove    = (floor(pflags/4)%2 == 1)
bool removeduplicates  = (floor(pflags/2)%2 == 1)
;bool realrootsonly    = (     (pflags  )%2 == 1)

complex a
complex b
complex c
if (smallrootsbest)      ; w = 1/x, solve for roots of w
a = pa3/pa4
b = pa2/pa4
c = pa1/pa4
else
a = pa2/pa1
b = pa3/pa1
c = pa4/pa1
endif

; Now have x^3 + a*x^2 + b*x + c = 0
; change of variable t = x - a/3 removes the quadratic term, giving
; t^3 - 3*Q*t + 2*R = 0
complex a3 = a/3
complex aa = b - 3*a3*a3
complex bb = c - a3*(b - 2*a3*a3)
complex Q = -aa/3      ; (a*a-3*b)/9
complex R = bb/2      ; (a*(2*a*a-9*b)+27*c)/54
complex D = Q*Q*Q-R*R

complex sm = sqrt(-D)
complex t1 = r + sm
complex t2 = r - sm
complex Ar
if (|t1| >= |t2|)
Ar = -(t1^(1/3))
else
Ar = -(t2^(1/3))
endif
complex Br = 0
if (Ar != 0)
Br = Q/Ar
endif
proots.m_Elements = Ar + Br - a3
proots.m_Elements = -0.5*(Ar+Br) -a3 + (0,0.5)*sqrt(3)*(Ar-Br)
proots.m_Elements = -0.5*(Ar+Br) -a3 - (0,0.5)*sqrt(3)*(Ar-Br)

int nroots = 3

if (smallrootsbest)
int n = 0
while (n < nroots)
proots.m_Elements[n] = 1/proots.m_Elements[n]
n = n + 1
endwhile
endif

; iterative refinement
if (iterimprove)
int n = 0
while (n < nroots)
complex x = proots.m_Elements[n]
complex v0 = pa4 + x*(pa3 + x*(pa2 + pa1*x))
complex v1 = pa3 + x*(2*pa2 + 3*pa1*x)
if (v0*v1 != 0)
complex v2 = 2*pa2 + 6*pa1*x
;           complex xnew = x - v0*v1/(v1*v1-v0*v2)
;           x = xnew
x = x - v0*v1/(v1*v1-v0*v2)
endif
proots.m_Elements[n] = x
n = n+1
endwhile
endif

if (removeduplicates)
nroots = SingleOnly(nroots, proots)
endif
return nroots

endfunc      ; end of SolveComplexCubicPolynomial

; Solve (real) quartic equation.
; <p>
; See the class overview for details on the solvers.
; This solves a generic quartic equation of the form
; <p>
; (a1)(x^4) + (a2)(x^3) + (a3)(x^2) + (a4)(x) + (a5) = 0
;
; @param pa1 a1 term
; @param pa2 a2 term
; @param pa3 a3 term
; @param pa4 a4 term
; @param pa5 a5 term
; @param pflags options
; @param proots reference to ComplexArray object to hold roots; this should already be sized to at least four (regardless of whether return values are limited)
; @return the number of roots found (will be 4 unless return values are limited)
static int func SolveRealQuarticPolynomial( \
const float pa1, const float pa2, const float pa3, const float pa4, \
const float pa5, const int pflags, ComplexArray &proots)

if (pa1 == 0)        ; degenerate: not a quartic
return SolveRealCubicPolynomial(pa2, pa3, pa4, pa5, pflags, proots)
endif
if (pa5 == 0)        ; one root of zero
int n = SolveRealCubicPolynomial(pa1, pa2, pa3, pa4, pflags, proots)
proots.m_Elements[n] = 0
return n+1
endif

bool smallrootsbest   = (floor(pflags/8)%2 == 1)
bool iterimprove      = (floor(pflags/4)%2 == 1)
bool removeduplicates = (floor(pflags/2)%2 == 1)
bool realrootsonly    = (     (pflags  )%2 == 1)

float a
float b
float c
float d
if (smallrootsbest)      ; w = 1/x, solve for roots of w
a = pa4/pa5
b = pa3/pa5
c = pa2/pa5
d = pa1/pa5
else
a = pa2/pa1
b = pa3/pa1
c = pa4/pa1
d = pa5/pa1
endif

; Now have x^4 + a*x^3 + b*x^2 + c*x + d = 0
; change of variable t = x - a/4 removes the cubic term, giving
; the "depressed quartic"
; t^4 + aa*t^2 + bb*t + cc = 0

float a4 = a/4
float aa = b - 6*a4*a4
float bb = c - 2*a4*(b - 4*a4*a4)
float cc = d - a4*(c-a4*(b-3*a4*a4))

; Could here do any special cases

; The next substitution is from
;  http://en.wikipedia.org/wiki/Quartic_equation
; It gives the same result as Faucette, who is quoted on Wolfram's site,
;  http://mathworld.wolfram.com/QuarticEquation.html
;
; Factor the depressed quartic into two quadratic terms
; (t^2 +p*t + q)*(t^2 + r*t + s) = 0
; Equating terms gives
; 0 = p + r   so r = -p
; aa = q - p^2 + s
; bb = p * (s - q)
; cc = q * s
; Some algebra gives a cubic in p^2
;  p^6 + 2*aa*p^4 + (aa^2-4*cc)*p^2 - b^2 = 0
; Solve this and choose a real root for p^2.
; if p^2 > 0, one sign of the square root gives
; s and the other sign gives q,
; then 2s = aa +-b/p +p^2
; then 2q = aa -+b/p +p^2
; but we choose the better equation for s and then q = cc/s.
; if p^2 = 0, use the aa and cc equations to get a quadratic for s and q.

float c1 = 2*aa
float c2 = aa*aa-4*cc
float c3 = -bb*bb
int n1 = SolveRealCubicPolynomial(1, c1, c2, c3, 1, proots)
; Since -p is the sum of 2 roots of the quartic, at least one of the 3 roots
; of the cubic is real and non-negative (within round-off).
; If all roots of the quartic are real, all 3 roots of the cubic are real
; and non-negative. Also, n1 is 1 or 3.
float p2
if (n1 == 1)
p2 = real(proots.m_Elements)
else
p2 = Maxf(real(proots.m_Elements), real(proots.m_Elements))
p2 = Maxf(p2, real(proots.m_Elements))

; minimum positive root maybe gives most accuracy
;  float p2_0, float p2_1, float p2_2
;  p2_0 = real(proots.m_Elements)
;  p2_1 = real(proots.m_Elements)
;  p2_2 = real(proots.m_Elements)
;  Sort3f(p2_0, p2_1, p2_2)
;  p2 = p2_0
;  if (p2 + RealThreshold < 0)
;    p2 = p2_1
;    if (p2 + RealThreshold < 0)
;      p2 = p2_2
;    endif
;  endif
endif
if (p2 < 0)        ; only because of roundoff
p2 = 0
endif
float s
float q
int n3 = SolveRealQuadraticPolynomial(1, -(aa+p2), cc, 16, proots)
if (n3 == 0)      ; can't happen -- means p2 = 0, but round-off...
return 0
endif
s = real(proots.m_Elements)
q = real(proots.m_Elements)
float p = sgn(bb)*sgn(s-q)*sqrt(p2)

; Now solve the two quadratics; since the leading term is 1, each has two
; roots, either both real or c.c.

int n2 = SolveRealQuadraticPolynomial(1, p, q, 0, proots)
proots.m_Elements = proots.m_Elements - a4
proots.m_Elements = proots.m_Elements - a4

int n0 = SolveRealQuadraticPolynomial(1, -p, s, 0, proots)
proots.m_Elements = proots.m_Elements - a4
proots.m_Elements = proots.m_Elements - a4

int nroots = n0 + n2
if (nroots == 0)    ; shouldn't happen
return 0
endif

if (smallrootsbest)
int n = 0
while (n < nroots)
proots.m_Elements[n] = 1/proots.m_Elements[n]
n = n + 1
endwhile
endif

; iterative refinement
if (iterimprove)
int n = 0
while (n < nroots)
complex x = proots.m_Elements[n]
complex v0 = pa5 + x*(pa4 + x*(pa3 + x*(pa2 + pa1*x)))
complex v1 = pa4 + x*(2*pa3 + x*(3*pa2 + 4*pa1*x))
if (v0*v1 != 0)
; x = x - v0/v1
complex v2 = 2*pa3 + x*(6*pa2 + 12*pa1*x)
x = x - v0*v1/(v1*v1-v0*v2)
endif
proots.m_Elements[n] = x
n = n + 1
endwhile
endif

if (realrootsonly)
nroots = RealOnly(nroots, proots)
endif

if (removeduplicates)
nroots = SingleOnly(nroots, proots)
endif

return nroots
endfunc      ; end of SolveRealQuartic Polynomial

; Solve (complex) quartic equation.
; <p>
; See the class overview for details on the solvers.
; This solves a generic quartic equation of the form
; <p>
; (a1)(x^4) + (a2)(x^3) + (a3)(x^2) + (a4)(x) + (a5) = 0
;
; @param pa1 a1 term
; @param pa2 a2 term
; @param pa3 a3 term
; @param pa4 a4 term
; @param pa5 a5 term
; @param pflags options
; @param proots reference to ComplexArray object to hold roots; this should already be sized to at least four (regardless of whether return values are limited)
; @return the number of roots found (will be 4 unless return values are limited)
static int func SolveComplexQuarticPolynomial( \
const complex pa1, const complex pa2, const complex pa3, const complex pa4, \
const complex pa5, const int pflags, ComplexArray &proots)

if (pa1 == 0)      ; degenerate: not a quartic
return SolveComplexCubicPolynomial(pa2, pa3, pa4, pa5, pflags, proots)
endif
if (pa5 == 0)      ; one root of zero
int n = SolveComplexCubicPolynomial(pa1, pa2, pa3, pa4, pflags, proots)
proots.m_Elements[n] = 0
return n+1
endif

bool smallrootsbest    = (floor(pflags/8)%2 == 1)
bool iterimprove    = (floor(pflags/4)%2 == 1)
bool removeduplicates  = (floor(pflags/2)%2 == 1)
;bool realrootsonly    = (     (pflags  )%2 == 1)

complex a
complex b
complex c
complex d
if (smallrootsbest)    ; w = 1/x, solve for roots of w
a = pa4/pa5
b = pa3/pa5
c = pa2/pa5
d = pa1/pa5
else
a = pa2/pa1
b = pa3/pa1
c = pa4/pa1
d = pa5/pa1
endif

; Now have x^4 + a*x^3 + b*x^2 + c*x + d = 0
; change of variable t = x - a/4 removes the cubic term, giving
; the "depressed quartic"
; t^4 + aa*t^2 + bb*t + cc = 0

complex a4 = a/4
complex aa = b - 6*a4*a4
complex bb = c - 2*a4*(b - 4*a4*a4)
complex cc = d - a4*(c-a4*(b-3*a4*a4))

; Could here do any special cases

; The next substitution is from
;  http://en.wikipedia.org/wiki/Quartic_equation
; It gives the same result as Faucette, who is quoted on Wolfram's site,
;  http://mathworld.wolfram.com/QuarticEquation.html
;
; Factor the depressed quartic into two quadratic terms
; (t^2 +p*t + q)*(t^2 + r*t + s) = 0
; Equating terms gives
; 0 = p + r   so r = -p
; aa = q - p^2 + s
; bb = p * (s - q)
; cc = q * s
; Some algebra gives a cubic in p^2
;  p^6 + 2*aa*p^4 + (aa^2-4*cc)*p^2 - b^2 = 0
; Solve this and choose a real root for p^2.
; if p^2 > 0, one sign of the square root gives
; s and the other sign gives q,
; then 2s = aa +-b/p +p^2
; then 2q = aa -+b/p +p^2
; but we choose the better equation for s and then q = cc/s.
; if p^2 = 0, use the aa and cc equations to get a quadratic for s and q.

complex c1 = 2*aa
complex c2 = aa*aa-4*cc
complex c3 = -bb*bb
SolveComplexCubicPolynomial(1, c1, c2, c3, 0, proots)
; Since -p is the sum of 2 roots of the quartic, at least one of the 3 roots
; of the cubic is real and non-negative (within round-off).
; If all roots of the quartic are real, all 3 roots of the cubic are real
; and non-negative. Also, n1 is 1 or 3.
complex p2 = proots.m_Elements
if (|p2| < |proots.m_Elements|)
p2 = proots.m_Elements
endif
if (|p2| < |proots.m_Elements|)
p2 = proots.m_Elements
endif
complex s
complex q
int n3 = SolveComplexQuadraticPolynomial(1, -(aa+p2), cc, 0, proots)
if (n3 == 0)      ; can't happen -- means p2 = 0, but round-off...
return 0
endif
s = proots.m_Elements
q = proots.m_Elements
complex p = sqrt(p2)
complex t1 = bb - p * (s-q)
complex t2 = bb = p * (s-q)
if (|t1| > |t2|)
p = -p
endif

; Now solve the two quadratics
int n2 = SolveComplexQuadraticPolynomial(1, p, q, 0, proots)
proots.m_Elements = proots.m_Elements - a4
proots.m_Elements = proots.m_Elements - a4

int n0 = SolveComplexQuadraticPolynomial(1, -p, s, 0, proots)
proots.m_Elements = proots.m_Elements - a4
proots.m_Elements = proots.m_Elements - a4

int nroots = n0 + n2

if (smallrootsbest)
int n = 0
while (n < nroots)
proots.m_Elements[n] = 1/proots.m_Elements[n]
n = n + 1
endwhile
endif

; iterative refinement
if (iterimprove)
int n = 0
while (n < nroots)
complex x = proots.m_Elements[n]
complex v0 = pa5 + x*(pa4 + x*(pa3 + x*(pa2 + pa1*x)))
complex v1 = pa4 + x*(2*pa3 + x*(3*pa2 + 4*pa1*x))
if (v0*v1 != 0)
; x = x - v0/v1
complex v2 = 2*pa3 + x*(6*pa2 + 12*pa1*x)
x = x - v0*v1/(v1*v1-v0*v2)
endif
proots.m_Elements[n] = x
n = n + 1
endwhile
endif

if (removeduplicates)
nroots = SingleOnly(nroots, proots)
endif

return nroots
endfunc      ; end of SolveRealQuartic Polynomial

; Complex Gamma() David Makin June 2008
; @param pz complex argument
; @return Gamma function of argument
static complex func ComplexGamma(complex pz)
complex w = pz
; float gplus = 5.2421875
if pz==0.0
w = recip(0)
else
if real(pz)<0.0
w = -pz
if imag(pz)==0.0
if (real(pz)%1)==0.0
w = recip(0)
endif
endif
endif
if real(w)<1e300
w = (sqrt(2.0*#pi)* \
(0.99999999999999709182 \
+ 57.156235665862923517/(w+1.0) \
- 59.597960355475491248/(w+2.0) \
+ 14.136097974741747174/(w+3.0) \
- 0.49191381609762019978/(w+4.0) \
+ .33994649984811888699E-4/(w+5.0) \
+ .46523628927048575665E-4/(w+6.0) \
- .98374475304879564677e-4/(w+7.0) \
+ .15808870322491248884E-3/(w+8.0) \
- .21026444172410488319e-3/(w+9.0) \
+ .21743961811521264320e-3/(w+10.0) \
- .16431810653676389022e-3/(w+11.0) \
+ .84418223983852743293e-4/(w+12.0) \
- .26190838401581408670e-4/(w+13.0) \
+ .36899182659531622704e-5/(w+14.0)) \
/w) * exp(-w-5.2421875)*(w+5.2421875)^(w+0.5)
if real(pz)<0.0
w = pz*w*sin(#pi*pz)
if (w==0.0)
w = recip(0)
else
w = -#pi/w
endif
endif
endif
endif
return w
endfunc

; Real Gamma() David Makin June 2008
; @param x float argument
; @return Gamma function of argument
static float func RealGamma(float x)
float w = x
; float gplus = 5.2421875
if x==0.0
w = recip(0)
else
if x<0.0
w = -x
if (x%1)==0.0
w = recip(0)
endif
endif
if w<1e300
w = (sqrt(2.0*#pi)* \
(0.99999999999999709182 \
+ 57.156235665862923517/(w+1.0) \
- 59.597960355475491248/(w+2.0) \
+ 14.136097974741747174/(w+3.0) \
- 0.49191381609762019978/(w+4.0) \
+ .33994649984811888699E-4/(w+5.0) \
+ .46523628927048575665E-4/(w+6.0) \
- .98374475304879564677e-4/(w+7.0) \
+ .15808870322491248884E-3/(w+8.0) \
- .21026444172410488319e-3/(w+9.0) \
+ .21743961811521264320e-3/(w+10.0) \
- .16431810653676389022e-3/(w+11.0) \
+ .84418223983852743293e-4/(w+12.0) \
- .26190838401581408670e-4/(w+13.0) \
+ .36899182659531622704e-5/(w+14.0)) \
/w) * exp(-w-5.2421875)*(w+5.2421875)^(w+0.5)
if x<0.0
w = x*w*sin(#pi*x)
if (w==0.0)
w = recip(0)
else
w = -#pi/w
endif
endif
endif
endif
return w
endfunc

; Complex Beta() David Makin June 2008
; @param a complex argument
; @param b complex argument
; @return Beta function of arguments
static complex func ComplexBeta(complex a, complex b)
return ComplexGamma(a)*ComplexGamma(b)/ComplexGamma(a+b)
endfunc

; Real Beta() David Makin June 2008
; @param a float argument
; @param b float argument
; @return Beta function of arguments
static float func RealBeta(float a, float b)
return RealGamma(a)*RealGamma(b)/RealGamma(a+b)
endfunc

; Real Bessel J0.
; @param x float argument
; @return Bessel function of order 0
static float func RealBesselJ0(const float x)
; JZERO 5846
float ax = abs(x)
if (ax <= 8.0)
float xx = x*x
return ( \
+.181083899214164308956057747e9 + xx*( \
-.4446475956150241214875200307e8 + xx*( \
+.2629297609723391264477929426e7 + xx*( \
-.6635120296335057670760789901e5 + xx*( \
+.9000010866385970012308555062e3 + xx*( \
-.7411779995119348858162812772e1 + xx*( \
+.39771954892980603699828143e-1 + xx*( \
-.1446351863454593575005971793e-3 + xx*( \
+.3612785949562383677336235392e-6 + xx*( \
-.6089358750808789415483968656e-9 + xx*( \
+.6424736784979100876734782112e-12 + xx*( \
-.3313360272836170910319585771e-15)))))))))))) / ( \
+.1810838992141643042977083347e9 + xx*( \
+.806215242038697916042541338e6 + xx*( \
+.1415495011707417263126447751e4 + xx)))
else
float z = sqr(8.0/ax)
; PZERO 6546
float P0 = ( \
+.8554822541506661710252074e4 + z*( \
+.8894437532960619440762804e4 + z*( \
+.2204501043965180428995069e4 + z*( \
+.128677585748714193298851e3  + z*( \
+.9004793474802880316384e0))))) / ( \
+.8554822541506662842462151e4 + z*( \
+.8903836141709595355210343e4 + z*( \
+.2214048851914710419825683e4 + z*( \
+.130884900499923882835109e3  + z ))))
; QZERO 6946
float Q0 =  (8.0/x) * ( \
-.37510534954957111594836e2 + z*( \
-.46093826814625174976377e2 + z*( \
-.13990976865960680088016e2 + z*( \
-.1049732798234554833126e1  + z*( \
-.93525953294031893049e-2)))))/ ( \
+.2400674237117267479318819e4 + z*( \
+.2971983745208491990065486e4 + z*( \
+.921566975526530895082307e3  + z*( \
+.74428389741411178824152e2   + z ))))
z = ax - #pi/4
return sqrt(2/#pi/ax)*(P0*cos(z)-Q0*sin(z))
endif
endfunc

; Real Bessel J1.
; @param x float argument
; @return Bessel function of order 1
static float func RealBesselJ1(const float x)
; JONE 6045
float ax = abs(x)
if (ax <= 8.0)
float xx = x*x
return x * ( \
+.695364226329838502166085207e8   + xx*( \
-.8356785487348914291918495672e7  + xx*( \
+.3209027468853947029888682298e6  + xx*( \
-.58787877666568200462726094e4    + xx*( \
+.6121876997356943874446879769e2  + xx*( \
-.3983107983952332023421699105e0  + xx*( \
+.1705769264349617107854016566e-2 + xx*( \
-.4910599276555129440130592573e-5 + xx*( \
+.9382193365140744507653268479e-8 + xx*( \
-.1107352224453730633782671362e-10 + xx*( \
+.63194310317443161294700346e-14 ))))))))))) / ( \
+.139072845265967685120764336e9  + xx*( \
+.6705346835482299302199750802e6 + xx*( \
+.1284593453966301898121332163e4 + xx)))
else
float z = sqr(8.0/ax)
; PONE 6747
float P1 = ( \
+.1290918471896188077350689e5 + z*( \
+.1309042051103506486292571e5 + z*( \
+.313275295635506951011069e4  + z*( \
+.17431379748379024599685e3   + z*( \
+.122850537643590432633e1  )))))/ ( \
+.1290918471896187879332737e5 + z*( \
+.1306678308784401036110575e5 + z*( \
+.310928141677002883350924e4  + z*( \
+.16904721775008609992033e3   + z ))))
; QONE 7147
float Q1 =  (8.0/ax) * ( \
+.14465282874995208675225e3 + z*( \
+.1744291689092425885102e3  + z*( \
+.5173653281836591636536e2  + z*( \
+.379944537969806734901e1   + z*( \
+.36363466476034710809e-1  )))))/ ( \
+.308592701333231723110639e4 + z*( \
+.373434010601630179517765e4 + z*( \
+.11191098527047487025919e4  + z*( \
+.8522392064341340397334e2   + z ))))
z = ax - #pi*3/4
z = sqrt(2/#pi/ax)*(P1*cos(z)-Q1*sin(z))
if (x < 0)
return -z
else
return z
endif
endif
endfunc

; Complex Bessel J0.
; @param z complex argument
; @return "Bessel" function of order 0
static complex func ComplexBesselJ0(const complex z)
float R = cabs(z)
if (R <= 8)
return ComplexBesselJ0_Inner(z)
else
complex zz = z * 8/R
return ComplexBesselJ0_Outer(z) * (ComplexBesselJ0_Inner(zz)/ComplexBesselJ0_Outer(zz))
endif
endfunc

; Complex Bessel J1.
; @param z complex argument
; @return "Bessel" function of order 0
static complex func ComplexBesselJ1(const complex z)
float R = cabs(z)
if (R <= 8)
return ComplexBesselJ1_Inner(z)
else
complex zz = z * 8/R
return ComplexBesselJ1_Outer(z) * (ComplexBesselJ1_Inner(zz)/ComplexBesselJ1_Outer(zz))
endif
endfunc

private:
; Helper function to eliminate duplicate roots.
static int func SingleOnly(int nroots, ComplexArray &proots)
float DuplThreshold = 1.e-12
int n = nroots
while (n > 0)
n = n - 1
complex xx = proots.m_Elements[n]
bool dupl = false
int k = 0
while (k < n && !dupl)    ; check earlier roots against nth
if (|xx -proots.m_Elements[k]| < DuplThreshold)   ; nth is duplicate
nroots = nroots - 1
dupl = true
if (n < nroots)    ; need to move down
int m = n
while (m < nroots)
proots.m_Elements[m] = proots.m_Elements[m+1]
m = m + 1
endwhile
endif
endif
k = k+1
endwhile
endwhile
return nroots
endfunc

; Helper function to eliminate complex roots.
static int func RealOnly(int nroots, ComplexArray &proots)
float RealThreshold = 1.e-6
int n = nroots
while (n > 0)
n = n - 1
if (abs(imag(proots.m_Elements[n])) <= RealThreshold)  ; real
proots.m_Elements[n] = real(proots.m_Elements[n])
else                          ; not real
nroots = nroots - 1
if (n < nroots)      ; need to move down
int m = n
while (m < nroots)
proots.m_Elements[m] = proots.m_Elements[m+1]
m = m + 1
endwhile
endif
endif
endwhile
return nroots
endfunc

; Power series for Bessel Function J0.
; <p>
; See Abramowitz & Stegun, Handbook of Mathematical Functions, p. 360.
; <p>
; Number of terms to use determined experimentally.
;
; @param z complex argument
; @return Bessel function of order 0 via power series for small argument
static complex func ComplexBesselJ0_Inner(const complex z)
int Nmax = 3 + round(6*sqrt(cabs(z)))
complex zz = sqr(z/2)
complex term = 1
complex sum = term
int n = 1
while (n <= Nmax)
term = -term * zz / sqr(n)
sum = sum + term
n = n + 1
endwhile
return sum
endfunc

; Function with appropriate asymptotic behavior for Bessel Function J0.
; <p>
; See Abramowitz & Stegun, Handbook of Mathematical Functions, p. 364.
; <p>
; Two terms of P0 and Q0 are used, rather arbitrarily.
;
; @param z complex argument
; @return "Bessel" function of order 0 via asymptotic series for large argument
static complex func ComplexBesselJ0_Outer(const complex z)
complex z1 = 1/(8*z)
complex z2 = sqr(z1)
complex arg = z - #pi/4
return sqrt(2/#pi/z)*((1-z2*9/2)*cos(arg)+z1*(1-z2*9*25/6)*sin(arg))
endfunc

; Power series for Bessel Function J1.
; <p>
; See Abramowitz & Stegun, Handbook of Mathematical Functions, p. 360.
; <p>
; Number of terms to use determined experimentally.
;
; @param z complex argument
; @return Bessel function of order 1 via power series for small argument
static complex func ComplexBesselJ1_Inner(const complex z)
int Nmax = 3 + round(6*sqrt(cabs(z)))
complex zz = sqr(z/2)
complex term = z/2
complex sum = term
int n = 1
while (n <= Nmax)
term = -term * zz / (n*(n+1))
sum = sum + term
n = n + 1
endwhile
return sum
endfunc

; Function with appropriate asymptotic behavior for Bessel Function J1.
; <p>
; See Abramowitz & Stegun, Handbook of Mathematical Functions, p. 364.
; <p>
; Two terms of P1 and Q1 are used, rather arbitrarily.
;
; @param z complex argument
; @return "Bessel" function of order 1 via asymptotic series for large argument
static complex func ComplexBesselJ1_Outer(const complex z)
complex z1 = 1/(8*z)
complex z2 = sqr(z1)
complex arg = z - #pi*3/4
return sqrt(2/#pi/z)*((1-z2*3*5/2)*cos(arg)+3*z1*(1-z2*5*21/6)*sin(arg))
endfunc
}

Constructor Summary
Math()
Constructor

Method Summary
static complex ComplexBesselJ0(complex z)
Complex Bessel J0.
static complex ComplexBesselJ1(complex z)
Complex Bessel J1.
static complex ComplexBeta(complex a, complex b)
Complex Beta() David Makin June 2008
static complex ComplexGamma(complex pz)
Complex Gamma() David Makin June 2008
static float MaxF(float px, float py)
Determine the maximum of two numbers.
static float MinF(float px, float py)
Determine the minimum of two numbers.
static float RealBesselJ0(float x)
Real Bessel J0.
static float RealBesselJ1(float x)
Real Bessel J1.
static float RealBeta(float a, float b)
Real Beta() David Makin June 2008
static float RealGamma(float x)
Real Gamma() David Makin June 2008
static float Sgn(float px)
Returns the sign of a number.
static int SolveComplexCubicPolynomial(complex pa1, complex pa2, complex pa3, complex pa4, int pflags, ComplexArray proots)
Solve (complex) cubic equation.
static int SolveComplexQuadraticPolynomial(complex pa1, complex pa2, complex pa3, int pflags, ComplexArray proots)
static int SolveComplexQuarticPolynomial(complex pa1, complex pa2, complex pa3, complex pa4, complex pa5, int pflags, ComplexArray proots)
Solve (complex) quartic equation.
static int SolveRealCubicPolynomial(float pa1, float pa2, float pa3, float pa4, int pflags, ComplexArray proots)
Solve (real) cubic equation.
static int SolveRealQuadraticPolynomial(float pa1, float pa2, float pa3, int pflags, ComplexArray proots)
static int SolveRealQuarticPolynomial(float pa1, float pa2, float pa3, float pa4, float pa5, int pflags, ComplexArray proots)
Solve (real) quartic equation.

Methods inherited from class Object

Constructor Detail

Math

public Math()
Constructor

You should never need to actually construct a Math object, as all of its methods are static and can be called without an object (just use the class name).

Method Detail

Sgn

public static float Sgn(float px)
Returns the sign of a number.

Parameters:
px - the number to test
Returns:
the sign; negative numbers give -1, positive numbers (including zero) return 1

MaxF

public static float MaxF(float px,
float py)
Determine the maximum of two numbers.

Parameters:
px - first number
py - second number
Returns:
the largest of the two numbers

MinF

public static float MinF(float px,
float py)
Determine the minimum of two numbers.

Parameters:
px - first number
py - second number
Returns:
the smallest of the two numbers

float pa2,
float pa3,
int pflags,
ComplexArray proots)

See the class overview for details on the solvers. This solves a generic quadratic equation of the form

(a1)(x^2) + (a2)(x) + (a3) = 0

Parameters:
pa1 - a1 term
pa2 - a2 term
pa3 - a3 term
pflags - options
proots - reference to ComplexArray object to hold roots; this should already be sized to at least two elements (regardless of whether return values are limited)
Returns:
the number of roots found (will be 2 unless return values are limited)

complex pa2,
complex pa3,
int pflags,
ComplexArray proots)

See the class overview for details on the solvers. This solves a generic quadratic equation of the form

(a1)(x^2) + (a2)(x) + (a3) = 0

Parameters:
pa1 - a1 term
pa2 - a2 term
pa3 - a3 term
pflags - options
proots - reference to ComplexArray object to hold roots; this should already be sized to at least two elements (regardless of whether return values are limited)
Returns:
the number of roots found (will be 2 unless return values are limited)

SolveRealCubicPolynomial

public static int SolveRealCubicPolynomial(float pa1,
float pa2,
float pa3,
float pa4,
int pflags,
ComplexArray proots)
Solve (real) cubic equation.

See the class overview for details on the solvers. This solves a generic cubic equation of the form

(a1)(x^3) + (a2)(x^2) + (a3)(x) + (a4) = 0

Parameters:
pa1 - a1 term
pa2 - a2 term
pa3 - a3 term
pa4 - a4 term
pflags - options
proots - reference to ComplexArray object to hold roots; this should already be sized to at least three elements (regardless of whether return values are limited)
Returns:
the number of roots found (will be 3 unless return values are limited)

SolveComplexCubicPolynomial

public static int SolveComplexCubicPolynomial(complex pa1,
complex pa2,
complex pa3,
complex pa4,
int pflags,
ComplexArray proots)
Solve (complex) cubic equation.

See the class overview for details on the solvers. This solves a generic cubic equation of the form

(a1)(x^3) + (a2)(x^2) + (a3)(x) + (a4) = 0

Parameters:
pa1 - a1 term
pa2 - a2 term
pa3 - a3 term
pa4 - a4 term
pflags - options
proots - reference to ComplexArray object to hold roots; this should already be sized to at least three elements (regardless of whether return values are limited)
Returns:
the number of roots found (will be 3 unless return values are limited)

SolveRealQuarticPolynomial

public static int SolveRealQuarticPolynomial(float pa1,
float pa2,
float pa3,
float pa4,
float pa5,
int pflags,
ComplexArray proots)
Solve (real) quartic equation.

See the class overview for details on the solvers. This solves a generic quartic equation of the form

(a1)(x^4) + (a2)(x^3) + (a3)(x^2) + (a4)(x) + (a5) = 0

Parameters:
pa1 - a1 term
pa2 - a2 term
pa3 - a3 term
pa4 - a4 term
pa5 - a5 term
pflags - options
proots - reference to ComplexArray object to hold roots; this should already be sized to at least four (regardless of whether return values are limited)
Returns:
the number of roots found (will be 4 unless return values are limited)

SolveComplexQuarticPolynomial

public static int SolveComplexQuarticPolynomial(complex pa1,
complex pa2,
complex pa3,
complex pa4,
complex pa5,
int pflags,
ComplexArray proots)
Solve (complex) quartic equation.

See the class overview for details on the solvers. This solves a generic quartic equation of the form

(a1)(x^4) + (a2)(x^3) + (a3)(x^2) + (a4)(x) + (a5) = 0

Parameters:
pa1 - a1 term
pa2 - a2 term
pa3 - a3 term
pa4 - a4 term
pa5 - a5 term
pflags - options
proots - reference to ComplexArray object to hold roots; this should already be sized to at least four (regardless of whether return values are limited)
Returns:
the number of roots found (will be 4 unless return values are limited)

ComplexGamma

public static complex ComplexGamma(complex pz)
Complex Gamma() David Makin June 2008

Parameters:
pz - complex argument
Returns:
Gamma function of argument

RealGamma

public static float RealGamma(float x)
Real Gamma() David Makin June 2008

Parameters:
x - float argument
Returns:
Gamma function of argument

ComplexBeta

public static complex ComplexBeta(complex a,
complex b)
Complex Beta() David Makin June 2008

Parameters:
a - complex argument
b - complex argument
Returns:
Beta function of arguments

RealBeta

public static float RealBeta(float a,
float b)
Real Beta() David Makin June 2008

Parameters:
a - float argument
b - float argument
Returns:
Beta function of arguments

RealBesselJ0

public static float RealBesselJ0(float x)
Real Bessel J0.

Parameters:
x - float argument
Returns:
Bessel function of order 0

RealBesselJ1

public static float RealBesselJ1(float x)
Real Bessel J1.

Parameters:
x - float argument
Returns:
Bessel function of order 1

ComplexBesselJ0

public static complex ComplexBesselJ0(complex z)
Complex Bessel J0.

Parameters:
z - complex argument
Returns:
"Bessel" function of order 0

ComplexBesselJ1

public static complex ComplexBesselJ1(complex z)
Complex Bessel J1.

Parameters:
z - complex argument
Returns:
"Bessel" function of order 0