mirror of
https://github.com/lcn2/calc.git
synced 2025-08-16 01:03:29 +03:00
Converted all ASCII tabs to ASCII spaces using a 8 character tab stop, for all files, except for all Makefiles (plus rpm.mk). The `git diff -w` reports no changes.
2831 lines
74 KiB
C
2831 lines
74 KiB
C
/*
|
|
* comfunc - extended precision complex arithmetic non-primitive routines
|
|
*
|
|
* Copyright (C) 1999-2007,2021-2023 David I. Bell, Landon Curt Noll and Ernest Bowen
|
|
*
|
|
* Primary author: David I. Bell
|
|
*
|
|
* Calc is open software; you can redistribute it and/or modify it under
|
|
* the terms of the version 2.1 of the GNU Lesser General Public License
|
|
* as published by the Free Software Foundation.
|
|
*
|
|
* Calc 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.
|
|
*
|
|
* A copy of version 2.1 of the GNU Lesser General Public License is
|
|
* distributed with calc under the filename COPYING-LGPL. You should have
|
|
* received a copy with calc; if not, write to Free Software Foundation, Inc.
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* Under source code control: 1990/02/15 01:48:13
|
|
* File existed as early as: before 1990
|
|
*
|
|
* Share and enjoy! :-) http://www.isthe.com/chongo/tech/comp/calc/
|
|
*/
|
|
|
|
|
|
#include "config.h"
|
|
#include "cmath.h"
|
|
|
|
|
|
#include "errtbl.h"
|
|
#include "banned.h" /* include after system header <> includes */
|
|
|
|
|
|
/*
|
|
* cache the natural logarithm of 10
|
|
*/
|
|
STATIC COMPLEX *cln_10 = NULL;
|
|
STATIC NUMBER *cln_10_epsilon = NULL;
|
|
STATIC COMPLEX *cln_2 = NULL;
|
|
STATIC NUMBER *cln_2_epsilon = NULL;
|
|
STATIC NUMBER _q10_ = { { _tenval_, 1, 0 }, { _oneval_, 1, 0 }, 1, NULL };
|
|
STATIC NUMBER _q2_ = { { _twoval_, 1, 0 }, { _oneval_, 1, 0 }, 1, NULL };
|
|
STATIC NUMBER _q0_ = { { _zeroval_, 1, 0 }, { _oneval_, 1, 0 }, 1, NULL };
|
|
COMPLEX _cten_ = { &_q10_, &_q0_, 1 };
|
|
COMPLEX _ctwo_ = { &_q2_, &_q0_, 1 };
|
|
|
|
|
|
/*
|
|
* Compute the result of raising a complex number to an integer power.
|
|
*
|
|
* given:
|
|
* c complex number to be raised
|
|
* q power to raise it to
|
|
*/
|
|
COMPLEX *
|
|
c_powi(COMPLEX *c, NUMBER *q)
|
|
{
|
|
COMPLEX *tmp, *res; /* temporary values */
|
|
long power; /* power to raise to */
|
|
FULL bit; /* current bit value */
|
|
int sign;
|
|
|
|
if (qisfrac(q)) {
|
|
math_error("Raising number to non-integral power");
|
|
not_reached();
|
|
}
|
|
if (zge31b(q->num)) {
|
|
math_error("Raising number to very large power");
|
|
not_reached();
|
|
}
|
|
power = ztolong(q->num);
|
|
if (ciszero(c) && (power == 0)) {
|
|
math_error("Raising zero to zeroth power");
|
|
not_reached();
|
|
}
|
|
sign = 1;
|
|
if (qisneg(q))
|
|
sign = -1;
|
|
/*
|
|
* Handle some low powers specially
|
|
*/
|
|
if (power <= 4) {
|
|
switch ((int) (power * sign)) {
|
|
case 0:
|
|
return clink(&_cone_);
|
|
case 1:
|
|
return clink(c);
|
|
case -1:
|
|
return c_inv(c);
|
|
case 2:
|
|
return c_square(c);
|
|
case -2:
|
|
tmp = c_square(c);
|
|
res = c_inv(tmp);
|
|
comfree(tmp);
|
|
return res;
|
|
case 3:
|
|
tmp = c_square(c);
|
|
res = c_mul(c, tmp);
|
|
comfree(tmp);
|
|
return res;
|
|
case 4:
|
|
tmp = c_square(c);
|
|
res = c_square(tmp);
|
|
comfree(tmp);
|
|
return res;
|
|
}
|
|
}
|
|
/*
|
|
* Compute the power by squaring and multiplying.
|
|
* This uses the left to right method of power raising.
|
|
*/
|
|
bit = TOPFULL;
|
|
while ((bit & power) == 0)
|
|
bit >>= 1L;
|
|
bit >>= 1L;
|
|
res = c_square(c);
|
|
if (bit & power) {
|
|
tmp = c_mul(res, c);
|
|
comfree(res);
|
|
res = tmp;
|
|
}
|
|
bit >>= 1L;
|
|
while (bit) {
|
|
tmp = c_square(res);
|
|
comfree(res);
|
|
res = tmp;
|
|
if (bit & power) {
|
|
tmp = c_mul(res, c);
|
|
comfree(res);
|
|
res = tmp;
|
|
}
|
|
bit >>= 1L;
|
|
}
|
|
if (sign < 0) {
|
|
tmp = c_inv(res);
|
|
comfree(res);
|
|
res = tmp;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* Calculate the square root of a complex number to specified accuracy.
|
|
* Type of rounding of each component specified by R as for qsqrt().
|
|
*/
|
|
COMPLEX *
|
|
c_sqrt(COMPLEX *c, NUMBER *epsilon, long R)
|
|
{
|
|
COMPLEX *r;
|
|
NUMBER *es, *aes, *bes, *u, *v, qtemp;
|
|
NUMBER *ntmp;
|
|
ZVALUE g, a, b, d, aa, cc;
|
|
ZVALUE tmp1, tmp2, tmp3, mul1, mul2;
|
|
long s1, s2, s3, up1, up2;
|
|
int imsign, sign;
|
|
|
|
if (ciszero(c))
|
|
return clink(c);
|
|
if (cisreal(c)) {
|
|
r = comalloc();
|
|
if (!qisneg(c->real)) {
|
|
qfree(r->real);
|
|
r->real = qsqrt(c->real, epsilon, R);
|
|
return r;
|
|
}
|
|
ntmp = qneg(c->real);
|
|
qfree(r->imag);
|
|
r->imag = qsqrt(ntmp, epsilon, R);
|
|
qfree(ntmp);
|
|
return r;
|
|
}
|
|
|
|
up1 = up2 = 0;
|
|
sign = (R & 64) != 0;
|
|
imsign = c->imag->num.sign;
|
|
es = qsquare(epsilon);
|
|
aes = qqdiv(c->real, es);
|
|
bes = qqdiv(c->imag, es);
|
|
qfree(es);
|
|
zgcd(aes->den, bes->den, &g);
|
|
zequo(bes->den, g, &tmp1);
|
|
zmul(aes->num, tmp1, &a);
|
|
zmul(aes->den, tmp1, &tmp2);
|
|
zshift(tmp2, 1, &d);
|
|
zfree(tmp1);
|
|
zfree(tmp2);
|
|
zequo(aes->den, g, &tmp1);
|
|
zmul(bes->num, tmp1, &b);
|
|
zfree(tmp1);
|
|
zfree(g);
|
|
qfree(aes);
|
|
qfree(bes);
|
|
zsquare(a, &tmp1);
|
|
zsquare(b, &tmp2);
|
|
zfree(b);
|
|
zadd(tmp1, tmp2, &tmp3);
|
|
zfree(tmp1);
|
|
zfree(tmp2);
|
|
if (R & 16) {
|
|
zshift(tmp3, 4, &tmp1);
|
|
zfree(tmp3);
|
|
zshift(a, 2, &aa);
|
|
zfree(a);
|
|
s1 = zsqrt(tmp1, &cc, 16);
|
|
zfree(tmp1);
|
|
zadd(cc, aa, &tmp1);
|
|
if (s1 == 0 && R & 32) {
|
|
zmul(tmp1, d, &tmp2);
|
|
zfree(tmp1);
|
|
s2 = zsqrt(tmp2, &tmp3, 16);
|
|
zfree(tmp2);
|
|
if (s2 == 0) {
|
|
aes = qalloc();
|
|
zshift(d, 1, &tmp1);
|
|
zreduce(tmp3, tmp1, &aes->num, &aes->den);
|
|
zfree(tmp1);
|
|
zfree(tmp3);
|
|
zfree(aa);
|
|
zfree(cc);
|
|
zfree(d);
|
|
r = comalloc();
|
|
qtemp = *aes;
|
|
qtemp.num.sign = sign;
|
|
qfree(r->real);
|
|
r->real = qmul(&qtemp, epsilon);
|
|
qfree(aes);
|
|
bes = qscale(r->real, 1);
|
|
qtemp = *bes;
|
|
qtemp.num.sign = sign ^ imsign;
|
|
qfree(r->imag);
|
|
r->imag = qqdiv(c->imag, &qtemp);
|
|
qfree(bes);
|
|
return r;
|
|
}
|
|
s3 = zquo(tmp3, d, &tmp1, s2 < 0);
|
|
} else {
|
|
s2 = zquo(tmp1, d, &tmp3, s1 ? (s1 < 0) : 16);
|
|
zfree(tmp1);
|
|
s3 = zsqrt(tmp3,&tmp1,(s1||s2) ? (s1<0 || s2<0) : 16);
|
|
}
|
|
zfree(tmp3);
|
|
zshift(tmp1, -1, &mul1);
|
|
if (*tmp1.v & 1)
|
|
up1 = s1 + s2 + s3;
|
|
else
|
|
up1 = -1;
|
|
zfree(tmp1);
|
|
zsub(cc, aa, &tmp1);
|
|
s2 = zquo(tmp1, d, &tmp2, s1 ? (s1 < 0) : 16);
|
|
zfree(tmp1);
|
|
s3 = zsqrt(tmp2, &tmp1, (s1 || s2) ? (s1 < 0 || s2 < 0) : 16);
|
|
zfree(tmp2);
|
|
zshift(tmp1, -1, &mul2);
|
|
if (*tmp1.v & 1)
|
|
up2 = s1 + s2 + s3;
|
|
else
|
|
up2 = -1;
|
|
zfree(tmp1);
|
|
zfree(aa);
|
|
} else {
|
|
s1 = zsqrt(tmp3, &cc, 0);
|
|
zfree(tmp3);
|
|
zadd(cc, a, &tmp1);
|
|
if (s1 == 0 && R & 32) {
|
|
zmul(tmp1, d, &tmp2);
|
|
zfree(tmp1);
|
|
s2 = zsqrt(tmp2, &tmp3, 0);
|
|
zfree(tmp2);
|
|
if (s2 == 0) {
|
|
aes = qalloc();
|
|
zreduce(tmp3, d, &aes->num, &aes->den);
|
|
zfree(tmp3);
|
|
zfree(a);
|
|
zfree(cc);
|
|
zfree(d);
|
|
r = comalloc();
|
|
qtemp = *aes;
|
|
qtemp.num.sign = sign;
|
|
qfree(r->real);
|
|
r->real = qmul(&qtemp, epsilon);
|
|
qfree(aes);
|
|
bes = qscale(r->real, 1);
|
|
qtemp = *bes;
|
|
qtemp.num.sign = sign ^ imsign;
|
|
qfree(r->imag);
|
|
r->imag = qqdiv(c->imag, &qtemp);
|
|
qfree(bes);
|
|
return r;
|
|
}
|
|
s3 = zquo(tmp3, d, &mul1, 0);
|
|
} else {
|
|
s2 = zquo(tmp1, d, &tmp3, 0);
|
|
zfree(tmp1);
|
|
s3 = zsqrt(tmp3, &mul1, 0);
|
|
}
|
|
up1 = (s1 + s2 + s3) ? 0 : -1;
|
|
zfree(tmp3);
|
|
zsub(cc, a, &tmp1);
|
|
s2 = zquo(tmp1, d, &tmp2, 0);
|
|
zfree(tmp1);
|
|
s3 = zsqrt(tmp2, &mul2, 0);
|
|
up2 = (s1 + s2 + s3) ? 0 : -1;
|
|
zfree(tmp2);
|
|
zfree(a);
|
|
}
|
|
zfree(cc); zfree(d);
|
|
if (up1 == 0) {
|
|
if (R & 8)
|
|
up1 = (long)((R ^ *mul1.v) & 1);
|
|
else
|
|
up1 = (R ^ epsilon->num.sign ^ sign) & 1;
|
|
if (R & 2)
|
|
up1 ^= epsilon->num.sign ^ sign;
|
|
if (R & 4)
|
|
up1 ^= epsilon->num.sign;
|
|
}
|
|
if (up2 == 0) {
|
|
if (R & 8)
|
|
up2 = (long)((R ^ *mul2.v) & 1);
|
|
else
|
|
up2 = (R ^ epsilon->num.sign ^ sign ^ imsign) & 1;
|
|
if (R & 2)
|
|
up2 ^= epsilon->num.sign ^ imsign ^ sign;
|
|
if (R & 4)
|
|
up2 ^= epsilon->num.sign;
|
|
}
|
|
if (up1 > 0) {
|
|
zadd(mul1, _one_, &tmp1);
|
|
zfree(mul1);
|
|
mul1 = tmp1;
|
|
}
|
|
if (up2 > 0) {
|
|
zadd(mul2, _one_, &tmp2);
|
|
zfree(mul2);
|
|
mul2 = tmp2;
|
|
}
|
|
if (ziszero(mul1)) {
|
|
u = qlink(&_qzero_);
|
|
} else {
|
|
mul1.sign = sign ^ epsilon->num.sign;
|
|
u = qalloc();
|
|
zreduce(mul1, epsilon->den, &tmp2, &u->den);
|
|
zmul(tmp2, epsilon->num, &u->num);
|
|
zfree(tmp2);
|
|
}
|
|
zfree(mul1);
|
|
if (ziszero(mul2)) {
|
|
v = qlink(&_qzero_);
|
|
} else {
|
|
mul2.sign = imsign ^ sign ^ epsilon->num.sign;
|
|
v = qalloc();
|
|
zreduce(mul2, epsilon->den, &tmp2, &v->den);
|
|
zmul(tmp2, epsilon->num, &v->num);
|
|
zfree(tmp2);
|
|
}
|
|
zfree(mul2);
|
|
if (qiszero(u) && qiszero(v)) {
|
|
qfree(u);
|
|
qfree(v);
|
|
return clink(&_czero_);
|
|
}
|
|
r = comalloc();
|
|
qfree(r->real);
|
|
qfree(r->imag);
|
|
r->real = u;
|
|
r->imag = v;
|
|
return r;
|
|
}
|
|
|
|
|
|
/*
|
|
* Take the Nth root of a complex number, where N is a positive integer.
|
|
* Each component of the result is within the specified error.
|
|
*/
|
|
COMPLEX *
|
|
c_root(COMPLEX *c, NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *r;
|
|
NUMBER *a2pb2, *root, *tmp1, *tmp2, *epsilon2;
|
|
long n, m;
|
|
|
|
if (qisneg(q) || qiszero(q) || qisfrac(q)) {
|
|
math_error("Taking bad root of complex number");
|
|
not_reached();
|
|
}
|
|
if (cisone(c) || qisone(q))
|
|
return clink(c);
|
|
if (qistwo(q))
|
|
return c_sqrt(c, epsilon, conf->triground);
|
|
if (cisreal(c) && !qisneg(c->real)) {
|
|
tmp1 = qroot(c->real, q, epsilon);
|
|
if (tmp1 == NULL)
|
|
return NULL;
|
|
r = comalloc();
|
|
qfree(r->real);
|
|
r->real = tmp1;
|
|
return r;
|
|
}
|
|
/*
|
|
* Calculate the root using the formula:
|
|
* c_root(a + bi, n) =
|
|
* c_polar(qroot(a^2 + b^2, 2 * n), qatan2(b, a) / n).
|
|
*/
|
|
n = qilog2(epsilon);
|
|
epsilon2 = qbitvalue(n - 4);
|
|
tmp1 = qsquare(c->real);
|
|
tmp2 = qsquare(c->imag);
|
|
a2pb2 = qqadd(tmp1, tmp2);
|
|
qfree(tmp1);
|
|
qfree(tmp2);
|
|
tmp1 = qscale(q, 1L);
|
|
root = qroot(a2pb2, tmp1, epsilon2);
|
|
qfree(a2pb2);
|
|
qfree(tmp1);
|
|
qfree(epsilon2);
|
|
if (root == NULL)
|
|
return NULL;
|
|
m = qilog2(root);
|
|
if (m < n) {
|
|
qfree(root);
|
|
return clink(&_czero_);
|
|
}
|
|
epsilon2 = qbitvalue(n - m - 4);
|
|
tmp1 = qatan2(c->imag, c->real, epsilon2);
|
|
qfree(epsilon2);
|
|
tmp2 = qqdiv(tmp1, q);
|
|
qfree(tmp1);
|
|
r = c_polar(root, tmp2, epsilon);
|
|
qfree(root);
|
|
qfree(tmp2);
|
|
return r;
|
|
}
|
|
|
|
|
|
/*
|
|
* Calculate the complex exponential function to the desired accuracy.
|
|
* We use the formula:
|
|
* exp(a + bi) = exp(a) * (cos(b) + i * sin(b)).
|
|
*/
|
|
COMPLEX *
|
|
c_exp(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *r;
|
|
NUMBER *sin, *cos, *tmp1, *tmp2, *epsilon1;
|
|
long k, n;
|
|
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon value for complex exp");
|
|
not_reached();
|
|
}
|
|
if (cisreal(c)) {
|
|
tmp1 = qexp(c->real, epsilon);
|
|
if (tmp1 == NULL)
|
|
return NULL;
|
|
r = comalloc();
|
|
qfree(r->real);
|
|
r->real = qexp(c->real, epsilon);
|
|
return r;
|
|
}
|
|
n = qilog2(epsilon);
|
|
epsilon1 = qbitvalue(n - 2);
|
|
tmp1 = qexp(c->real, epsilon1);
|
|
qfree(epsilon1);
|
|
if (tmp1 == NULL)
|
|
return NULL;
|
|
if (qiszero(tmp1)) {
|
|
qfree(tmp1);
|
|
return clink(&_czero_);
|
|
}
|
|
k = qilog2(tmp1) + 1;
|
|
if (k < n) {
|
|
qfree(tmp1);
|
|
return clink(&_czero_);
|
|
}
|
|
qsincos(c->imag, k - n + 2, &sin, &cos);
|
|
tmp2 = qmul(tmp1, cos);
|
|
qfree(cos);
|
|
r = comalloc();
|
|
qfree(r->real);
|
|
r->real = qmappr(tmp2, epsilon, conf->triground);
|
|
qfree(tmp2);
|
|
tmp2 = qmul(tmp1, sin);
|
|
qfree(tmp1);
|
|
qfree(sin);
|
|
qfree(r->imag);
|
|
r->imag = qmappr(tmp2, epsilon, conf->triground);
|
|
qfree(tmp2);
|
|
return r;
|
|
}
|
|
|
|
|
|
/*
|
|
* Calculate the natural logarithm of a complex number within the specified
|
|
* error. We use the formula:
|
|
* ln(a + bi) = ln(a^2 + b^2) / 2 + i * atan2(b, a).
|
|
*/
|
|
COMPLEX *
|
|
c_ln(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *r;
|
|
NUMBER *a2b2, *tmp1, *tmp2, *epsilon1;
|
|
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon value for complex ln");
|
|
not_reached();
|
|
}
|
|
if (cisone(c))
|
|
return clink(&_czero_);
|
|
r = comalloc();
|
|
if (cisreal(c) && !qisneg(c->real)) {
|
|
qfree(r->real);
|
|
r->real = qln(c->real, epsilon);
|
|
return r;
|
|
}
|
|
tmp1 = qsquare(c->real);
|
|
tmp2 = qsquare(c->imag);
|
|
a2b2 = qqadd(tmp1, tmp2);
|
|
qfree(tmp1);
|
|
qfree(tmp2);
|
|
epsilon1 = qscale(epsilon, 1L);
|
|
tmp1 = qln(a2b2, epsilon1);
|
|
qfree(a2b2);
|
|
qfree(epsilon1);
|
|
qfree(r->real);
|
|
r->real = qscale(tmp1, -1L);
|
|
qfree(tmp1);
|
|
qfree(r->imag);
|
|
r->imag = qatan2(c->imag, c->real, epsilon);
|
|
return r;
|
|
}
|
|
|
|
|
|
/*
|
|
* Calculate base 10 logarithm by:
|
|
*
|
|
* log(c) = ln(c) / ln(10)
|
|
*/
|
|
COMPLEX *
|
|
c_log(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
int need_new_cln_10 = true; /* false => use cached cln_10 value */
|
|
COMPLEX *ln_c; /* ln(x) */
|
|
COMPLEX *ret; /* base 10 logarithm of x */
|
|
|
|
/*
|
|
* compute ln(c) first
|
|
*/
|
|
ln_c = c_ln(c, epsilon);
|
|
/* quick return for log(1) == 0 */
|
|
if (ciszero(ln_c)) {
|
|
return ln_c;
|
|
}
|
|
|
|
/*
|
|
* save epsilon for ln(10) if needed
|
|
*/
|
|
if (cln_10_epsilon == NULL) {
|
|
/* first time call */
|
|
cln_10_epsilon = qcopy(epsilon);
|
|
} else if (qcmp(cln_10_epsilon, epsilon) == true) {
|
|
/* replaced cached value with epsilon arg */
|
|
qfree(cln_10_epsilon);
|
|
cln_10_epsilon = qcopy(epsilon);
|
|
} else if (cln_10 != NULL) {
|
|
/* the previously computed ln(2) is OK to use */
|
|
need_new_cln_10 = false;
|
|
}
|
|
|
|
/*
|
|
* compute ln(10) if needed
|
|
*/
|
|
if (need_new_cln_10 == true) {
|
|
if (cln_10 != NULL) {
|
|
comfree(cln_10);
|
|
}
|
|
cln_10 = c_ln(&_cten_, cln_10_epsilon);
|
|
}
|
|
|
|
/*
|
|
* return ln(c) / ln(10)
|
|
*/
|
|
ret = c_div(ln_c, cln_10);
|
|
comfree(ln_c);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* Calculate base 2 logarithm by:
|
|
*
|
|
* log(c) = ln(c) / ln(2)
|
|
*/
|
|
COMPLEX *
|
|
c_log2(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
int need_new_cln_2 = true; /* false => use cached cln_2 value */
|
|
COMPLEX *ln_c; /* ln(x) */
|
|
COMPLEX *ret; /* base 2 of x */
|
|
|
|
/*
|
|
* compute ln(c) first
|
|
*/
|
|
ln_c = c_ln(c, epsilon);
|
|
/* quick return for log(1) == 0 */
|
|
if (ciszero(ln_c)) {
|
|
return ln_c;
|
|
}
|
|
|
|
/*
|
|
* save epsilon for ln(2) if needed
|
|
*/
|
|
if (cln_2_epsilon == NULL) {
|
|
/* first time call */
|
|
cln_2_epsilon = qcopy(epsilon);
|
|
} else if (qcmp(cln_2_epsilon, epsilon) == true) {
|
|
/* replaced cached value with epsilon arg */
|
|
qfree(cln_2_epsilon);
|
|
cln_2_epsilon = qcopy(epsilon);
|
|
} else if (cln_2 != NULL) {
|
|
/* the previously computed ln(2) is OK to use */
|
|
need_new_cln_2 = false;
|
|
}
|
|
|
|
/*
|
|
* compute ln(2) if needed
|
|
*/
|
|
if (need_new_cln_2 == true) {
|
|
if (cln_2 != NULL) {
|
|
comfree(cln_2);
|
|
}
|
|
cln_2 = c_ln(&_ctwo_, cln_2_epsilon);
|
|
}
|
|
|
|
/*
|
|
* return ln(c) / ln(2)
|
|
*/
|
|
ret = c_div(ln_c, cln_2);
|
|
comfree(ln_c);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* Calculate the complex cosine within the specified accuracy.
|
|
* This uses the formula:
|
|
* cos(x) = (exp(1i * x) + exp(-1i * x))/2;
|
|
*/
|
|
COMPLEX *
|
|
c_cos(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *r, *ctmp1, *ctmp2, *ctmp3;
|
|
NUMBER *epsilon1;
|
|
long n;
|
|
bool neg;
|
|
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon value for complex cos");
|
|
not_reached();
|
|
}
|
|
n = qilog2(epsilon);
|
|
ctmp1 = comalloc();
|
|
qfree(ctmp1->real);
|
|
qfree(ctmp1->imag);
|
|
neg = qisneg(c->imag);
|
|
ctmp1->real = neg ? qneg(c->imag) : qlink(c->imag);
|
|
ctmp1->imag = neg ? qlink(c->real) : qneg(c->real);
|
|
epsilon1 = qbitvalue(n - 2);
|
|
ctmp2 = c_exp(ctmp1, epsilon1);
|
|
comfree(ctmp1);
|
|
qfree(epsilon1);
|
|
if (ctmp2 == NULL)
|
|
return NULL;
|
|
if (ciszero(ctmp2)) {
|
|
comfree(ctmp2);
|
|
return clink(&_czero_);
|
|
}
|
|
ctmp1 = c_inv(ctmp2);
|
|
ctmp3 = c_add(ctmp2, ctmp1);
|
|
comfree(ctmp1);
|
|
comfree(ctmp2);
|
|
ctmp1 = c_scale(ctmp3, -1);
|
|
comfree(ctmp3);
|
|
r = comalloc();
|
|
qfree(r->real);
|
|
r->real = qmappr(ctmp1->real, epsilon, conf->triground);
|
|
qfree(r->imag);
|
|
r->imag = qmappr(ctmp1->imag, epsilon, conf->triground);
|
|
comfree(ctmp1);
|
|
return r;
|
|
}
|
|
|
|
|
|
/*
|
|
* Calculate the complex sine within the specified accuracy.
|
|
* This uses the formula:
|
|
* sin(x) = (exp(1i * x) - exp(-i1*x))/(2i).
|
|
*/
|
|
COMPLEX *
|
|
c_sin(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *r, *ctmp1, *ctmp2, *ctmp3;
|
|
NUMBER *qtmp, *epsilon1;
|
|
long n;
|
|
bool neg;
|
|
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon value for complex sin");
|
|
not_reached();
|
|
}
|
|
if (ciszero(c)) {
|
|
return clink(&_czero_);
|
|
}
|
|
n = qilog2(epsilon);
|
|
ctmp1 = comalloc();
|
|
neg = qisneg(c->imag);
|
|
qfree(ctmp1->real);
|
|
qfree(ctmp1->imag);
|
|
ctmp1->real = neg ? qneg(c->imag) : qlink(c->imag);
|
|
ctmp1->imag = neg ? qlink(c->real) : qneg(c->real);
|
|
epsilon1 = qbitvalue(n - 2);
|
|
ctmp2 = c_exp(ctmp1, epsilon1);
|
|
comfree(ctmp1);
|
|
qfree(epsilon1);
|
|
if (ctmp2 == NULL) {
|
|
return NULL;
|
|
}
|
|
if (ciszero(ctmp2)) {
|
|
comfree(ctmp2);
|
|
return clink(&_czero_);
|
|
}
|
|
ctmp1 = c_inv(ctmp2);
|
|
ctmp3 = c_sub(ctmp2, ctmp1);
|
|
comfree(ctmp1);
|
|
comfree(ctmp2);
|
|
ctmp1 = c_scale(ctmp3, -1);
|
|
comfree(ctmp3);
|
|
r = comalloc();
|
|
qtmp = neg ? qlink(ctmp1->imag) : qneg(ctmp1->imag);
|
|
qfree(r->real);
|
|
r->real = qmappr(qtmp, epsilon, conf->triground);
|
|
qfree(qtmp);
|
|
qtmp = neg ? qneg(ctmp1->real) : qlink(ctmp1->real);
|
|
qfree(r->imag);
|
|
r->imag = qmappr(qtmp, epsilon, conf->triground);
|
|
qfree(qtmp);
|
|
comfree(ctmp1);
|
|
return r;
|
|
}
|
|
|
|
|
|
COMPLEX *
|
|
c_cosh(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *tmp1, *tmp2, *tmp3;
|
|
|
|
tmp1 = c_exp(c, epsilon);
|
|
if (tmp1 == NULL)
|
|
return NULL;
|
|
tmp2 = c_neg(c);
|
|
tmp3 = c_exp(tmp2, epsilon);
|
|
comfree(tmp2);
|
|
if (tmp3 == NULL)
|
|
return NULL;
|
|
tmp2 = c_add(tmp1, tmp3);
|
|
comfree(tmp1);
|
|
comfree(tmp3);
|
|
tmp1 = c_scale(tmp2, -1);
|
|
comfree(tmp2);
|
|
return tmp1;
|
|
}
|
|
|
|
|
|
COMPLEX *
|
|
c_sinh(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *tmp1, *tmp2, *tmp3;
|
|
|
|
tmp1 = c_exp(c, epsilon);
|
|
if (tmp1 == NULL)
|
|
return NULL;
|
|
tmp2 = c_neg(c);
|
|
tmp3 = c_exp(tmp2, epsilon);
|
|
comfree(tmp2);
|
|
if (tmp3 == NULL)
|
|
return NULL;
|
|
tmp2 = c_sub(tmp1, tmp3);
|
|
comfree(tmp1);
|
|
comfree(tmp3);
|
|
tmp1 = c_scale(tmp2, -1);
|
|
comfree(tmp2);
|
|
return tmp1;
|
|
}
|
|
|
|
|
|
COMPLEX *
|
|
c_asin(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *tmp1, *tmp2;
|
|
|
|
tmp1 = c_mul(&_conei_, c);
|
|
tmp2 = c_asinh(tmp1, epsilon);
|
|
comfree(tmp1);
|
|
tmp1 = c_div(tmp2, &_conei_);
|
|
comfree(tmp2);
|
|
return tmp1;
|
|
}
|
|
|
|
|
|
COMPLEX *
|
|
c_acos(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *tmp1, *tmp2;
|
|
|
|
tmp1 = c_square(c);
|
|
tmp2 = c_sub(&_cone_, tmp1);
|
|
comfree(tmp1);
|
|
tmp1 = c_sqrt(tmp2, epsilon, conf->triground);
|
|
comfree(tmp2);
|
|
tmp2 = c_mul(&_conei_, tmp1);
|
|
comfree(tmp1);
|
|
tmp1 = c_add(c, tmp2);
|
|
comfree(tmp2);
|
|
tmp2 = c_ln(tmp1, epsilon);
|
|
comfree(tmp1);
|
|
tmp1 = c_div(tmp2, &_conei_);
|
|
comfree(tmp2);
|
|
return tmp1;
|
|
}
|
|
|
|
|
|
COMPLEX *
|
|
c_asinh(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *tmp1, *tmp2, *tmp3;
|
|
bool neg;
|
|
|
|
neg = qisneg(c->real);
|
|
tmp1 = neg ? c_neg(c) : clink(c);
|
|
tmp2 = c_square(tmp1);
|
|
tmp3 = c_add(&_cone_, tmp2);
|
|
comfree(tmp2);
|
|
tmp2 = c_sqrt(tmp3, epsilon, conf->triground);
|
|
comfree(tmp3);
|
|
tmp3 = c_add(tmp2, tmp1);
|
|
comfree(tmp1);
|
|
comfree(tmp2);
|
|
tmp1 = c_ln(tmp3, epsilon);
|
|
comfree(tmp3);
|
|
if (neg) {
|
|
tmp2 = c_neg(tmp1);
|
|
comfree(tmp1);
|
|
return tmp2;
|
|
}
|
|
return tmp1;
|
|
}
|
|
|
|
|
|
COMPLEX *
|
|
c_acosh(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *tmp1, *tmp2;
|
|
|
|
tmp1 = c_square(c);
|
|
tmp2 = c_sub(tmp1, &_cone_);
|
|
comfree(tmp1);
|
|
tmp1 = c_sqrt(tmp2, epsilon, conf->triground);
|
|
comfree(tmp2);
|
|
tmp2 = c_add(c, tmp1);
|
|
comfree(tmp1);
|
|
tmp1 = c_ln(tmp2, epsilon);
|
|
comfree(tmp2);
|
|
return tmp1;
|
|
}
|
|
|
|
|
|
/*
|
|
* c_tan - complex trigonometric tangent
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* tan(x) = sin(x) / cos(x)
|
|
*
|
|
* given:
|
|
* c argument to the trigonometric function
|
|
* epsilon precision of the trigonometric calculation
|
|
*
|
|
* returns:
|
|
* != NULL ==> allocated pointer to COMPLEX result
|
|
* NULL ==> invalid trigonometric argument
|
|
*
|
|
* NOTE: When the trigonometric result is returned as non-NULL result,
|
|
* the value may be a real value. The caller may wish to:
|
|
*
|
|
* COMPLEX *c; (* return result of this function *)
|
|
* NUMBER *q; (* COMPLEX result when c is a real number *)
|
|
*
|
|
* if (c == NULL) {
|
|
* math_error("... some error message");
|
|
* not_reached();
|
|
* }
|
|
* if (cisreal(c)) {
|
|
* q = c_to_q(c, ok_to_free);
|
|
* }
|
|
*/
|
|
COMPLEX *
|
|
c_tan(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *denom; /* trigonometric identity numerator */
|
|
COMPLEX *numer; /* trigonometric identity denominator */
|
|
COMPLEX *res; /* trigonometric result */
|
|
|
|
/*
|
|
* firewall - check args
|
|
*/
|
|
if (c == NULL) {
|
|
return NULL;
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* evaluate the cos(x) denominator
|
|
*
|
|
* Return NULL if cos(x) failed or we otherwise divide by zero.
|
|
*/
|
|
denom = c_cos(c, epsilon);
|
|
if (denom == NULL || ciszero(denom)) {
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* evaluate the sin(x) numerator
|
|
*
|
|
* Return NULL if sin(x) failed.
|
|
*/
|
|
numer = c_sin(c, epsilon);
|
|
if (numer == NULL) {
|
|
comfree(denom);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* catch the special case of numerator of 0
|
|
*/
|
|
if (ciszero(numer)) {
|
|
comfree(denom);
|
|
comfree(numer);
|
|
return clink(&_czero_);
|
|
}
|
|
|
|
/*
|
|
* compute the trigonometric function value
|
|
*/
|
|
res = c_div(numer, denom);
|
|
comfree(denom);
|
|
comfree(numer);
|
|
|
|
/*
|
|
* return the trigonometric result
|
|
*/
|
|
return res;
|
|
}
|
|
|
|
|
|
COMPLEX *
|
|
c_atan(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *tmp1, *tmp2, *tmp3;
|
|
|
|
if (qiszero(c->real) && qisunit(c->imag))
|
|
return NULL;
|
|
tmp1 = c_sub(&_conei_, c);
|
|
tmp2 = c_add(&_conei_, c);
|
|
tmp3 = c_div(tmp1, tmp2);
|
|
comfree(tmp1);
|
|
comfree(tmp2);
|
|
tmp1 = c_ln(tmp3, epsilon);
|
|
comfree(tmp3);
|
|
tmp2 = c_scale(tmp1, -1);
|
|
comfree(tmp1);
|
|
tmp1 = c_div(tmp2, &_conei_);
|
|
comfree(tmp2);
|
|
return tmp1;
|
|
}
|
|
|
|
|
|
/*
|
|
* c_cot - complex trigonometric cotangent
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* cot(x) = cos(x) / sin(x)
|
|
*
|
|
* given:
|
|
* c argument to the trigonometric function
|
|
* epsilon precision of the trigonometric calculation
|
|
*
|
|
* returns:
|
|
* != NULL ==> allocated pointer to COMPLEX result
|
|
* NULL ==> invalid trigonometric argument
|
|
*
|
|
* NOTE: When the trigonometric result is returned as non-NULL result,
|
|
* the value may be a real value. The caller may wish to:
|
|
*
|
|
* COMPLEX *c; (* return result of this function *)
|
|
* NUMBER *q; (* COMPLEX result when c is a real number *)
|
|
*
|
|
* if (c == NULL) {
|
|
* math_error("... some error message");
|
|
* not_reached();
|
|
* }
|
|
* if (cisreal(c)) {
|
|
* q = c_to_q(c, ok_to_free);
|
|
* }
|
|
*/
|
|
COMPLEX *
|
|
c_cot(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *denom; /* trigonometric identity numerator */
|
|
COMPLEX *numer; /* trigonometric identity denominator */
|
|
COMPLEX *res; /* trigonometric result */
|
|
|
|
/*
|
|
* firewall - check args
|
|
*/
|
|
if (c == NULL) {
|
|
return NULL;
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* evaluate the sin(x) denominator
|
|
*
|
|
* Return NULL if sin(x) failed or we otherwise divide by zero.
|
|
*/
|
|
denom = c_sin(c, epsilon);
|
|
if (denom == NULL || ciszero(denom)) {
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* evaluate the cos(x) numerator
|
|
*
|
|
* Return NULL if cos(x) failed.
|
|
*/
|
|
numer = c_cos(c, epsilon);
|
|
if (numer == NULL) {
|
|
comfree(denom);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* catch the special case of numerator of 0
|
|
*/
|
|
if (ciszero(numer)) {
|
|
comfree(denom);
|
|
comfree(numer);
|
|
return clink(&_czero_);
|
|
}
|
|
|
|
/*
|
|
* compute the trigonometric function value
|
|
*/
|
|
res = c_div(numer, denom);
|
|
comfree(denom);
|
|
comfree(numer);
|
|
|
|
/*
|
|
* return the trigonometric result
|
|
*/
|
|
return res;
|
|
}
|
|
|
|
|
|
COMPLEX *
|
|
c_acot(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *tmp1, *tmp2, *tmp3;
|
|
|
|
if (qiszero(c->real) && qisunit(c->imag))
|
|
return NULL;
|
|
tmp1 = c_add(c, &_conei_);
|
|
tmp2 = c_sub(c, &_conei_);
|
|
tmp3 = c_div(tmp1, tmp2);
|
|
comfree(tmp1);
|
|
comfree(tmp2);
|
|
tmp1 = c_ln(tmp3, epsilon);
|
|
comfree(tmp3);
|
|
tmp2 = c_scale(tmp1, -1);
|
|
comfree(tmp1);
|
|
tmp1 = c_div(tmp2, &_conei_);
|
|
comfree(tmp2);
|
|
return tmp1;
|
|
}
|
|
|
|
|
|
/*
|
|
* c_sec - complex trigonometric tangent
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* sec(x) = 1 / cos(x)
|
|
*
|
|
* given:
|
|
* c argument to the trigonometric function
|
|
* epsilon precision of the trigonometric calculation
|
|
*
|
|
* returns:
|
|
* != NULL ==> allocated pointer to COMPLEX result
|
|
* NULL ==> invalid trigonometric argument
|
|
*
|
|
* NOTE: When the trigonometric result is returned as non-NULL result,
|
|
* the value may be a real value. The caller may wish to:
|
|
*
|
|
* COMPLEX *c; (* return result of this function *)
|
|
* NUMBER *q; (* COMPLEX result when c is a real number *)
|
|
*
|
|
* if (c == NULL) {
|
|
* math_error("... some error message");
|
|
* not_reached();
|
|
* }
|
|
* if (cisreal(c)) {
|
|
* q = c_to_q(c, ok_to_free);
|
|
* }
|
|
*/
|
|
COMPLEX *
|
|
c_sec(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *denom; /* trigonometric identity numerator */
|
|
COMPLEX *res; /* trigonometric result */
|
|
|
|
/*
|
|
* firewall - check args
|
|
*/
|
|
if (c == NULL) {
|
|
return NULL;
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* evaluate the cos(x) denominator
|
|
*
|
|
* Return NULL if cos(x) failed or we otherwise divide by zero.
|
|
*/
|
|
denom = c_cos(c, epsilon);
|
|
if (denom == NULL || ciszero(denom)) {
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* compute the trigonometric function value
|
|
*/
|
|
res = c_div(&_cone_, denom);
|
|
comfree(denom);
|
|
|
|
/*
|
|
* return the trigonometric result
|
|
*/
|
|
return res;
|
|
}
|
|
|
|
|
|
COMPLEX *
|
|
c_asec(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *tmp1, *tmp2;
|
|
|
|
tmp1 = c_inv(c);
|
|
tmp2 = c_acos(tmp1, epsilon);
|
|
comfree(tmp1);
|
|
return tmp2;
|
|
}
|
|
|
|
|
|
/*
|
|
* c_sec - complex trigonometric cosecant
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* csc(x) = 1 / sin(x)
|
|
*
|
|
* given:
|
|
* c argument to the trigonometric function
|
|
* epsilon precision of the trigonometric calculation
|
|
*
|
|
* returns:
|
|
* != NULL ==> allocated pointer to COMPLEX result
|
|
* NULL ==> invalid trigonometric argument
|
|
*
|
|
* NOTE: When the trigonometric result is returned as non-NULL result,
|
|
* the value may be a real value. The caller may wish to:
|
|
*
|
|
* COMPLEX *c; (* return result of this function *)
|
|
* NUMBER *q; (* COMPLEX result when c is a real number *)
|
|
*
|
|
* if (c == NULL) {
|
|
* math_error("... some error message");
|
|
* not_reached();
|
|
* }
|
|
* if (cisreal(c)) {
|
|
* q = c_to_q(c, ok_to_free);
|
|
* }
|
|
*/
|
|
COMPLEX *
|
|
c_csc(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *denom; /* trigonometric identity numerator */
|
|
COMPLEX *res; /* trigonometric result */
|
|
|
|
/*
|
|
* firewall - check args
|
|
*/
|
|
if (c == NULL) {
|
|
return NULL;
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* evaluate the sin(x) denominator
|
|
*
|
|
* Return NULL if sin(x) failed or we otherwise divide by zero.
|
|
*/
|
|
denom = c_sin(c, epsilon);
|
|
if (denom == NULL || ciszero(denom)) {
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* compute the trigonometric function value
|
|
*/
|
|
res = c_div(&_cone_, denom);
|
|
comfree(denom);
|
|
|
|
/*
|
|
* return the trigonometric result
|
|
*/
|
|
return res;
|
|
}
|
|
|
|
|
|
COMPLEX *
|
|
c_acsc(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *tmp1, *tmp2;
|
|
|
|
tmp1 = c_inv(c);
|
|
tmp2 = c_asin(tmp1, epsilon);
|
|
comfree(tmp1);
|
|
return tmp2;
|
|
}
|
|
|
|
|
|
COMPLEX *
|
|
c_atanh(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *tmp1, *tmp2, *tmp3;
|
|
|
|
if (qiszero(c->imag) && qisunit(c->real))
|
|
return NULL;
|
|
tmp1 = c_add(&_cone_, c);
|
|
tmp2 = c_sub(&_cone_, c);
|
|
tmp3 = c_div(tmp1, tmp2);
|
|
comfree(tmp1);
|
|
comfree(tmp2);
|
|
tmp1 = c_ln(tmp3, epsilon);
|
|
comfree(tmp3);
|
|
tmp2 = c_scale(tmp1, -1);
|
|
comfree(tmp1);
|
|
return tmp2;
|
|
}
|
|
|
|
|
|
COMPLEX *
|
|
c_acoth(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *tmp1, *tmp2, *tmp3;
|
|
|
|
if (qiszero(c->imag) && qisunit(c->real))
|
|
return NULL;
|
|
tmp1 = c_add(c, &_cone_);
|
|
tmp2 = c_sub(c, &_cone_);
|
|
tmp3 = c_div(tmp1, tmp2);
|
|
comfree(tmp1);
|
|
comfree(tmp2);
|
|
tmp1 = c_ln(tmp3, epsilon);
|
|
comfree(tmp3);
|
|
tmp2 = c_scale(tmp1, -1);
|
|
comfree(tmp1);
|
|
return tmp2;
|
|
}
|
|
|
|
|
|
COMPLEX *
|
|
c_asech(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *tmp1, *tmp2;
|
|
|
|
tmp1 = c_inv(c);
|
|
tmp2 = c_acosh(tmp1, epsilon);
|
|
comfree(tmp1);
|
|
return tmp2;
|
|
}
|
|
|
|
|
|
COMPLEX *
|
|
c_acsch(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *tmp1, *tmp2;
|
|
|
|
tmp1 = c_inv(c);
|
|
tmp2 = c_asinh(tmp1, epsilon);
|
|
comfree(tmp1);
|
|
return tmp2;
|
|
}
|
|
|
|
|
|
COMPLEX *
|
|
c_gd(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *tmp1, *tmp2, *tmp3;
|
|
NUMBER *q1, *q2;
|
|
NUMBER *sin, *cos;
|
|
NUMBER *eps;
|
|
int n, n1;
|
|
bool neg;
|
|
|
|
if (cisreal(c)) {
|
|
q1 = qscale(c->real, -1);
|
|
eps = qscale(epsilon, -1);
|
|
q2 = qtanh(q1, eps);
|
|
qfree(q1);
|
|
q1 = qatan(q2, eps);
|
|
qfree(eps);
|
|
qfree(q2);
|
|
tmp1 = comalloc();
|
|
qfree(tmp1->real);
|
|
tmp1->real = qscale(q1, 1);
|
|
qfree(q1);
|
|
return tmp1;
|
|
}
|
|
if (qiszero(c->real)) {
|
|
n = - qilog2(epsilon);
|
|
qsincos(c->imag, n + 8, &sin, &cos);
|
|
if (qiszero(cos) || (n1 = -qilog2(cos)) > n) {
|
|
qfree(sin);
|
|
qfree(cos);
|
|
return NULL;
|
|
}
|
|
neg = qisneg(sin);
|
|
q1 = neg ? qsub(&_qone_, sin) : qqadd(&_qone_, sin);
|
|
qfree(sin);
|
|
if (n1 > 8) {
|
|
qfree(q1);
|
|
qfree(cos);
|
|
qsincos(c->imag, n + n1, &sin, &cos);
|
|
q1 = neg ? qsub(&_qone_, sin) : qqadd(&_qone_, sin);
|
|
qfree(sin);
|
|
}
|
|
q2 = qqdiv(q1, cos);
|
|
qfree(q1);
|
|
q1 = qln(q2, epsilon);
|
|
qfree(q2);
|
|
if (neg) {
|
|
q2 = qneg(q1);
|
|
qfree(q1);
|
|
q1 = q2;
|
|
}
|
|
tmp1 = comalloc();
|
|
qfree(tmp1->imag);
|
|
tmp1->imag = q1;
|
|
if (qisneg(cos)) {
|
|
qfree(tmp1->real);
|
|
q1 = qpi(epsilon);
|
|
if (qisneg(c->imag)) {
|
|
q2 = qneg(q1);
|
|
qfree(q1);
|
|
q1 = q2;
|
|
}
|
|
tmp1->real = q1;
|
|
}
|
|
qfree(cos);
|
|
return tmp1;
|
|
}
|
|
neg = qisneg(c->real);
|
|
tmp1 = neg ? c_neg(c) : clink(c);
|
|
tmp2 = c_exp(tmp1, epsilon);
|
|
comfree(tmp1);
|
|
if (tmp2 == NULL)
|
|
return NULL;
|
|
tmp1 = c_mul(&_conei_, tmp2);
|
|
tmp3 = c_add(&_conei_, tmp2);
|
|
comfree(tmp2);
|
|
tmp2 = c_add(tmp1, &_cone_);
|
|
comfree(tmp1);
|
|
if (ciszero(tmp2) || ciszero(tmp3)) {
|
|
comfree(tmp2);
|
|
comfree(tmp3);
|
|
return NULL;
|
|
}
|
|
tmp1 = c_div(tmp2, tmp3);
|
|
comfree(tmp2);
|
|
comfree(tmp3);
|
|
tmp2 = c_ln(tmp1, epsilon);
|
|
comfree(tmp1);
|
|
tmp1 = c_div(tmp2, &_conei_);
|
|
comfree(tmp2);
|
|
if (neg) {
|
|
tmp2 = c_neg(tmp1);
|
|
comfree(tmp1);
|
|
return tmp2;
|
|
}
|
|
return tmp1;
|
|
}
|
|
|
|
|
|
COMPLEX *
|
|
c_agd(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *tmp1, *tmp2;
|
|
|
|
tmp1 = c_mul(&_conei_, c);
|
|
tmp2 = c_gd(tmp1, epsilon);
|
|
comfree(tmp1);
|
|
if (tmp2 == NULL)
|
|
return NULL;
|
|
tmp1 = c_div(tmp2, &_conei_);
|
|
comfree(tmp2);
|
|
return tmp1;
|
|
}
|
|
|
|
|
|
/*
|
|
* Convert a number from polar coordinates to normal complex number form
|
|
* within the specified accuracy. This produces the value:
|
|
* q1 * cos(q2) + q1 * sin(q2) * i.
|
|
*/
|
|
COMPLEX *
|
|
c_polar(NUMBER *q1, NUMBER *q2, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *r;
|
|
NUMBER *tmp, *cos, *sin;
|
|
long m, n;
|
|
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon value for complex polar");
|
|
not_reached();
|
|
}
|
|
if (qiszero(q1))
|
|
return clink(&_czero_);
|
|
m = qilog2(q1) + 1;
|
|
n = qilog2(epsilon);
|
|
if (m < n)
|
|
return clink(&_czero_);
|
|
r = comalloc();
|
|
if (qiszero(q2)) {
|
|
qfree(r->real);
|
|
r->real = qlink(q1);
|
|
return r;
|
|
}
|
|
qsincos(q2, m - n + 2, &sin, &cos);
|
|
tmp = qmul(q1, cos);
|
|
qfree(cos);
|
|
qfree(r->real);
|
|
r->real = qmappr(tmp, epsilon, conf->triground);
|
|
qfree(tmp);
|
|
tmp = qmul(q1, sin);
|
|
qfree(sin);
|
|
qfree(r->imag);
|
|
r->imag = qmappr(tmp, epsilon, conf->triground);
|
|
qfree(tmp);
|
|
return r;
|
|
}
|
|
|
|
|
|
/*
|
|
* Raise one complex number to the power of another one to within the
|
|
* specified error.
|
|
*/
|
|
COMPLEX *
|
|
c_power(COMPLEX *c1, COMPLEX *c2, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *ctmp1, *ctmp2;
|
|
long k1, k2, k, m1, m2, m, n;
|
|
NUMBER *a2b2, *qtmp1, *qtmp2, *epsilon1;
|
|
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon value for complex power");
|
|
not_reached();
|
|
}
|
|
if (ciszero(c1)) {
|
|
if (cisreal(c2) && qisneg(c2->real)) {
|
|
math_error ("Non-positive real exponent of zero for complex power");
|
|
not_reached();
|
|
}
|
|
return clink(&_czero_);
|
|
}
|
|
n = qilog2(epsilon);
|
|
m1 = m2 = -1000000;
|
|
k1 = k2 = 0;
|
|
if (!qiszero(c2->real)) {
|
|
qtmp1 = qsquare(c1->real);
|
|
qtmp2 = qsquare(c1->imag);
|
|
a2b2 = qqadd(qtmp1, qtmp2);
|
|
qfree(qtmp1);
|
|
qfree(qtmp2);
|
|
m1 = qilog2(c2->real);
|
|
epsilon1 = qbitvalue(-m1 - 1);
|
|
qtmp1 = qln(a2b2, epsilon1);
|
|
qfree(epsilon1);
|
|
qfree(a2b2);
|
|
qtmp2 = qmul(qtmp1, c2->real);
|
|
qfree(qtmp1);
|
|
qtmp1 = qmul(qtmp2, &_qlge_);
|
|
qfree(qtmp2);
|
|
k1 = qtoi(qtmp1);
|
|
qfree(qtmp1);
|
|
}
|
|
if (!qiszero(c2->imag)) {
|
|
m2 = qilog2(c2->imag);
|
|
epsilon1 = qbitvalue(-m2 - 1);
|
|
qtmp1 = qatan2(c1->imag, c1->real, epsilon1);
|
|
qfree(epsilon1);
|
|
qtmp2 = qmul(qtmp1, c2->imag);
|
|
qfree(qtmp1);
|
|
qtmp1 = qscale(qtmp2, -1);
|
|
qfree(qtmp2);
|
|
qtmp2 = qmul(qtmp1, &_qlge_);
|
|
qfree(qtmp1);
|
|
k2 = qtoi(qtmp2);
|
|
qfree(qtmp2);
|
|
}
|
|
m = (m2 > m1) ? m2 : m1;
|
|
k = k1 - k2 + 1;
|
|
if (k < n)
|
|
return clink(&_czero_);
|
|
epsilon1 = qbitvalue(n - k - m - 2);
|
|
ctmp1 = c_ln(c1, epsilon1);
|
|
qfree(epsilon1);
|
|
ctmp2 = c_mul(ctmp1, c2);
|
|
comfree(ctmp1);
|
|
ctmp1 = c_exp(ctmp2, epsilon);
|
|
comfree(ctmp2);
|
|
return ctmp1;
|
|
}
|
|
|
|
|
|
/*
|
|
* Print a complex number in the current output mode.
|
|
*/
|
|
void
|
|
comprint(COMPLEX *c)
|
|
{
|
|
NUMBER qtmp;
|
|
|
|
if (conf->outmode == MODE_FRAC) {
|
|
cprintfr(c);
|
|
return;
|
|
}
|
|
if (!qiszero(c->real) || qiszero(c->imag))
|
|
qprintnum(c->real, MODE_DEFAULT, conf->outdigits);
|
|
qtmp = c->imag[0];
|
|
if (qiszero(&qtmp))
|
|
return;
|
|
if (conf->complex_space) {
|
|
math_chr(' ');
|
|
}
|
|
if (!qiszero(c->real) && !qisneg(&qtmp))
|
|
math_chr('+');
|
|
if (qisneg(&qtmp)) {
|
|
math_chr('-');
|
|
qtmp.num.sign = 0;
|
|
}
|
|
if (conf->complex_space) {
|
|
math_chr(' ');
|
|
}
|
|
qprintnum(&qtmp, MODE_DEFAULT, conf->outdigits);
|
|
math_chr('i');
|
|
}
|
|
|
|
|
|
/*
|
|
* Print a complex number in rational representation.
|
|
* Example: 2/3-4i/5
|
|
*/
|
|
void
|
|
cprintfr(COMPLEX *c)
|
|
{
|
|
NUMBER *r;
|
|
NUMBER *i;
|
|
|
|
r = c->real;
|
|
i = c->imag;
|
|
if (!qiszero(r) || qiszero(i))
|
|
qprintfr(r, 0L, false);
|
|
if (qiszero(i))
|
|
return;
|
|
if (!qiszero(r) && !qisneg(i))
|
|
math_chr('+');
|
|
zprintval(i->num, 0L, 0L);
|
|
math_chr('i');
|
|
if (qisfrac(i)) {
|
|
if (conf->fraction_space) {
|
|
math_chr(' ');
|
|
}
|
|
math_chr('/');
|
|
if (conf->fraction_space) {
|
|
math_chr(' ');
|
|
}
|
|
zprintval(i->den, 0L, 0L);
|
|
}
|
|
}
|
|
|
|
|
|
NUMBER *
|
|
c_ilog(COMPLEX *c, ZVALUE base)
|
|
{
|
|
NUMBER *qr, *qi;
|
|
|
|
qr = qilog(c->real, base);
|
|
qi = qilog(c->imag, base);
|
|
|
|
if (qr == NULL) {
|
|
if (qi == NULL)
|
|
return NULL;
|
|
return qi;
|
|
}
|
|
if (qi == NULL)
|
|
return qr;
|
|
if (qrel(qr, qi) >= 0) {
|
|
qfree(qi);
|
|
return qr;
|
|
}
|
|
qfree(qr);
|
|
return qi;
|
|
}
|
|
|
|
|
|
/*
|
|
* c_versin - COMPLEX valued versed trigonometric sine
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* versin(x) = 1 - cos(x)
|
|
*
|
|
* given:
|
|
* c complex value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* complex value result of trig function on q with error epsilon
|
|
*/
|
|
COMPLEX *
|
|
c_versin(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *r; /* return COMPLEX value */
|
|
COMPLEX *ctmp; /* complex cos(c) */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (c == NULL) {
|
|
math_error("%s: c is NULL", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate complex trig function value
|
|
*/
|
|
ctmp = c_cos(c, epsilon);
|
|
if (ctmp == NULL) {
|
|
math_error("Failed to compute complex cosine for complex versin");
|
|
not_reached();
|
|
}
|
|
r = c_sub(&_cone_, ctmp);
|
|
|
|
/*
|
|
* return trigonometric result
|
|
*/
|
|
comfree(ctmp);
|
|
return r;
|
|
}
|
|
|
|
|
|
/*
|
|
* c_aversin - COMPLEX valued inverse versed trigonometric sine
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* aversin(x) = acos(1 - x)
|
|
*
|
|
* given:
|
|
* c complex value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* complex value result of trig function on q with error epsilon
|
|
*/
|
|
COMPLEX *
|
|
c_aversin(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *r; /* inverse trig value result */
|
|
COMPLEX *ctmp; /* argument to inverse trig function */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (c == NULL) {
|
|
math_error("%s: c is NULL", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate complex inverse trig function value
|
|
*/
|
|
ctmp = c_sub(&_cone_, c);
|
|
r = c_acos(ctmp, epsilon);
|
|
comfree(ctmp);
|
|
|
|
/*
|
|
* return inverse trigonometric result
|
|
*/
|
|
return r;
|
|
}
|
|
|
|
|
|
/*
|
|
* c_coversin - COMPLEX valued coversed trigonometric sine
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* coversin(x) = 1 - cos(x)
|
|
*
|
|
* given:
|
|
* c complex value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* complex value result of trig function on q with error epsilon
|
|
*/
|
|
COMPLEX *
|
|
c_coversin(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *r; /* return COMPLEX value */
|
|
COMPLEX *ctmp; /* complex sin(c) */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (c == NULL) {
|
|
math_error("%s: c is NULL", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate complex trig function value
|
|
*/
|
|
ctmp = c_sin(c, epsilon);
|
|
if (ctmp == NULL) {
|
|
math_error("Failed to compute complex sine for complex coversin");
|
|
not_reached();
|
|
}
|
|
r = c_sub(&_cone_, ctmp);
|
|
comfree(ctmp);
|
|
|
|
/*
|
|
* return trigonometric result
|
|
*/
|
|
return r;
|
|
}
|
|
|
|
|
|
/*
|
|
* c_acoversin - COMPLEX valued inverse coversed trigonometric sine
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* acoversin(x) = asin(1 - x)
|
|
*
|
|
* given:
|
|
* c complex value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* complex value result of trig function on q with error epsilon
|
|
*/
|
|
COMPLEX *
|
|
c_acoversin(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *r; /* inverse trig value result */
|
|
COMPLEX *ctmp; /* argument to inverse trig function */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (c == NULL) {
|
|
math_error("%s: c is NULL", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate complex inverse trig function value
|
|
*/
|
|
ctmp = c_sub(&_cone_, c);
|
|
r = c_asin(ctmp, epsilon);
|
|
comfree(ctmp);
|
|
|
|
/*
|
|
* return inverse trigonometric result
|
|
*/
|
|
return r;
|
|
}
|
|
|
|
|
|
/*
|
|
* c_vercos - COMPLEX valued versed trigonometric cosine
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* vercos(x) = 1 + cos(x)
|
|
*
|
|
* given:
|
|
* c complex value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* complex value result of trig function on q with error epsilon
|
|
*/
|
|
COMPLEX *
|
|
c_vercos(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *r; /* return COMPLEX value */
|
|
COMPLEX *ctmp; /* complex cos(c) */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (c == NULL) {
|
|
math_error("%s: c is NULL", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate complex trig function value
|
|
*/
|
|
ctmp = c_cos(c, epsilon);
|
|
if (ctmp == NULL) {
|
|
math_error("Failed to compute complex cosine for complex vercos");
|
|
not_reached();
|
|
}
|
|
r = c_add(&_cone_, ctmp);
|
|
comfree(ctmp);
|
|
|
|
/*
|
|
* return trigonometric result
|
|
*/
|
|
return r;
|
|
}
|
|
|
|
|
|
/*
|
|
* c_avercos - COMPLEX valued inverse versed trigonometric cosine
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* avercos(x) = acos(x - 1)
|
|
*
|
|
* given:
|
|
* c complex value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* complex value result of trig function on q with error epsilon
|
|
*/
|
|
COMPLEX *
|
|
c_avercos(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *r; /* inverse trig value result */
|
|
COMPLEX *ctmp; /* argument to inverse trig function */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (c == NULL) {
|
|
math_error("%s: c is NULL", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate complex inverse trig function value
|
|
*/
|
|
ctmp = c_sub(c, &_cone_);
|
|
r = c_acos(ctmp, epsilon);
|
|
comfree(ctmp);
|
|
|
|
/*
|
|
* return inverse trigonometric result
|
|
*/
|
|
return r;
|
|
}
|
|
|
|
|
|
/*
|
|
* c_covercos - COMPLEX valued coversed trigonometric cosine
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* covercos(x) = 1 + sin(x)
|
|
*
|
|
* given:
|
|
* c complex value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* complex value result of trig function on q with error epsilon
|
|
*/
|
|
COMPLEX *
|
|
c_covercos(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *r; /* return COMPLEX value */
|
|
COMPLEX *ctmp; /* complex sin(c) */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (c == NULL) {
|
|
math_error("%s: c is NULL", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate complex trig function value
|
|
*/
|
|
ctmp = c_sin(c, epsilon);
|
|
if (ctmp == NULL) {
|
|
math_error("Failed to compute complex sine for complex covercos");
|
|
not_reached();
|
|
}
|
|
r = c_add(&_cone_, ctmp);
|
|
comfree(ctmp);
|
|
|
|
/*
|
|
* return trigonometric result
|
|
*/
|
|
return r;
|
|
}
|
|
|
|
|
|
/*
|
|
* c_acovercos - COMPLEX valued inverse coversed trigonometric cosine
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* acovercos(x) = asin(x - 1)
|
|
*
|
|
* given:
|
|
* c complex value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* complex value result of trig function on q with error epsilon
|
|
*/
|
|
COMPLEX *
|
|
c_acovercos(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *r; /* inverse trig value result */
|
|
COMPLEX *ctmp; /* argument to inverse trig function */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (c == NULL) {
|
|
math_error("%s: c is NULL", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate complex inverse trig function value
|
|
*/
|
|
ctmp = c_sub(c, &_cone_);
|
|
r = c_asin(ctmp, epsilon);
|
|
comfree(ctmp);
|
|
|
|
/*
|
|
* return inverse trigonometric result
|
|
*/
|
|
return r;
|
|
}
|
|
|
|
|
|
/*
|
|
* c_haversin - COMPLEX valued half versed trigonometric sine
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* haversin(x) = versin(x) / 2
|
|
*
|
|
* given:
|
|
* c complex value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* complex value result of trig function on q with error epsilon
|
|
*/
|
|
COMPLEX *
|
|
c_haversin(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *r; /* return COMPLEX value */
|
|
COMPLEX *ctmp; /* complex cos(c) */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (c == NULL) {
|
|
math_error("%s: c is NULL", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate complex trig function value
|
|
*/
|
|
ctmp = c_versin(c, epsilon);
|
|
if (ctmp == NULL) {
|
|
math_error("Failed to compute complex versed sine for complex haversin");
|
|
not_reached();
|
|
}
|
|
r = c_divq(ctmp, &_qtwo_);
|
|
comfree(ctmp);
|
|
|
|
/*
|
|
* return trigonometric result
|
|
*/
|
|
return r;
|
|
}
|
|
|
|
|
|
/*
|
|
* c_ahaversin - COMPLEX valued inverse half versed trigonometric sine
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* ahaversin(x) = acos(1 - 2*x)
|
|
*
|
|
* given:
|
|
* c complex value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* complex value result of trig function on q with error epsilon
|
|
*/
|
|
COMPLEX *
|
|
c_ahaversin(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *r; /* inverse trig value result */
|
|
COMPLEX *ctmp; /* argument to inverse trig function */
|
|
COMPLEX *x2; /* twice x */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (c == NULL) {
|
|
math_error("%s: c is NULL", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate complex inverse trig function value
|
|
*/
|
|
x2 = c_mulq(c, &_qtwo_);
|
|
ctmp = c_sub(&_cone_, x2);
|
|
comfree(x2);
|
|
r = c_acos(ctmp, epsilon);
|
|
comfree(ctmp);
|
|
|
|
/*
|
|
* return inverse trigonometric result
|
|
*/
|
|
return r;
|
|
}
|
|
|
|
|
|
/*
|
|
* c_hacoversin - COMPLEX valued half coversed trigonometric sine
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* hacoversin(x) = coversin(x) / 2
|
|
*
|
|
* given:
|
|
* c complex value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* complex value result of trig function on q with error epsilon
|
|
*/
|
|
COMPLEX *
|
|
c_hacoversin(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *r; /* return COMPLEX value */
|
|
COMPLEX *ctmp; /* complex sin(c) */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (c == NULL) {
|
|
math_error("%s: c is NULL", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate complex trig function value
|
|
*/
|
|
ctmp = c_coversin(c, epsilon);
|
|
if (ctmp == NULL) {
|
|
math_error("Failed to compute complex coversed sine for complex hacoversin");
|
|
not_reached();
|
|
}
|
|
r = c_divq(ctmp, &_qtwo_);
|
|
comfree(ctmp);
|
|
|
|
/*
|
|
* return trigonometric result
|
|
*/
|
|
return r;
|
|
}
|
|
|
|
|
|
/*
|
|
* c_ahacoversin - COMPLEX valued inverse half coversed trigonometric sine
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* ahacoversin(x) = asin(1 - 2*x)
|
|
*
|
|
* given:
|
|
* c complex value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* complex value result of trig function on q with error epsilon
|
|
*/
|
|
COMPLEX *
|
|
c_ahacoversin(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *r; /* inverse trig value result */
|
|
COMPLEX *ctmp; /* argument to inverse trig function */
|
|
COMPLEX *x2; /* twice x */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (c == NULL) {
|
|
math_error("%s: c is NULL", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate complex inverse trig function value
|
|
*/
|
|
x2 = c_mulq(c, &_qtwo_);
|
|
ctmp = c_sub(&_cone_, x2);
|
|
comfree(x2);
|
|
r = c_asin(ctmp, epsilon);
|
|
comfree(ctmp);
|
|
|
|
/*
|
|
* return inverse trigonometric result
|
|
*/
|
|
return r;
|
|
}
|
|
|
|
|
|
/*
|
|
* c_havercos - COMPLEX valued half coversed trigonometric sine
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* havercos(x) = vercos(x) / 2
|
|
*
|
|
* given:
|
|
* c complex value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* complex value result of trig function on q with error epsilon
|
|
*/
|
|
COMPLEX *
|
|
c_havercos(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *r; /* return COMPLEX value */
|
|
COMPLEX *ctmp; /* complex sin(c) */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (c == NULL) {
|
|
math_error("%s: c is NULL", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate complex trig function value
|
|
*/
|
|
ctmp = c_vercos(c, epsilon);
|
|
if (ctmp == NULL) {
|
|
math_error("Failed to compute complex versed cosine for complex havercos");
|
|
not_reached();
|
|
}
|
|
r = c_divq(ctmp, &_qtwo_);
|
|
comfree(ctmp);
|
|
|
|
/*
|
|
* return trigonometric result
|
|
*/
|
|
return r;
|
|
}
|
|
|
|
|
|
/*
|
|
* c_ahavercos - COMPLEX valued inverse half coversed trigonometric sine
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* ahavercos(x) = acos(2*x - 1)
|
|
*
|
|
* given:
|
|
* c complex value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* complex value result of trig function on q with error epsilon
|
|
*/
|
|
COMPLEX *
|
|
c_ahavercos(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *r; /* inverse trig value result */
|
|
COMPLEX *ctmp; /* argument to inverse trig function */
|
|
COMPLEX *x2; /* twice x */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (c == NULL) {
|
|
math_error("%s: c is NULL", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate complex inverse trig function value
|
|
*/
|
|
x2 = c_mulq(c, &_qtwo_);
|
|
ctmp = c_sub(&_cone_, x2);
|
|
comfree(x2);
|
|
r = c_acos(ctmp, epsilon);
|
|
comfree(ctmp);
|
|
|
|
/*
|
|
* return inverse trigonometric result
|
|
*/
|
|
return r;
|
|
}
|
|
|
|
|
|
/*
|
|
* c_hacovercos - COMPLEX valued half coversed trigonometric cosine
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* hacovercos(x) = covercos(x) / 2
|
|
*
|
|
* given:
|
|
* c complex value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* complex value result of trig function on q with error epsilon
|
|
*/
|
|
COMPLEX *
|
|
c_hacovercos(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *r; /* return COMPLEX value */
|
|
COMPLEX *ctmp; /* complex sin(c) */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (c == NULL) {
|
|
math_error("%s: c is NULL", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate complex trig function value
|
|
*/
|
|
ctmp = c_covercos(c, epsilon);
|
|
if (ctmp == NULL) {
|
|
math_error("Failed to compute complex coversed cosine for complex hacovercos");
|
|
not_reached();
|
|
}
|
|
r = c_divq(ctmp, &_qtwo_);
|
|
comfree(ctmp);
|
|
|
|
/*
|
|
* return trigonometric result
|
|
*/
|
|
return r;
|
|
}
|
|
|
|
|
|
/*
|
|
* c_ahacovercos - COMPLEX valued inverse half coversed trigonometric cosine
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* ahacovercos(x) = asin(2*x - 1)
|
|
*
|
|
* given:
|
|
* c complex value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* complex value result of trig function on q with error epsilon
|
|
*/
|
|
COMPLEX *
|
|
c_ahacovercos(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *r; /* inverse trig value result */
|
|
COMPLEX *ctmp; /* argument to inverse trig function */
|
|
COMPLEX *x2; /* twice x */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (c == NULL) {
|
|
math_error("%s: c is NULL", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate complex inverse trig function value
|
|
*/
|
|
x2 = c_mulq(c, &_qtwo_);
|
|
ctmp = c_sub(&_cone_, x2);
|
|
comfree(x2);
|
|
r = c_asin(ctmp, epsilon);
|
|
comfree(ctmp);
|
|
|
|
/*
|
|
* return inverse trigonometric result
|
|
*/
|
|
return r;
|
|
}
|
|
|
|
|
|
/*
|
|
* c_exsec - COMPLEX valued exterior trigonometric secant
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* exsec(x) = sec(x) - 1
|
|
*
|
|
* given:
|
|
* c complex value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* complex value result of trig function on q with error epsilon
|
|
*/
|
|
COMPLEX *
|
|
c_exsec(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *r; /* return COMPLEX value */
|
|
COMPLEX *ctmp; /* complex sec(c) */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (c == NULL) {
|
|
math_error("%s: c is NULL", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate complex trig function value
|
|
*/
|
|
ctmp = c_sec(c, epsilon);
|
|
if (ctmp == NULL) {
|
|
math_error("Failed to compute complex cosine for complex exsec");
|
|
not_reached();
|
|
}
|
|
r = c_sub(ctmp, &_cone_);
|
|
comfree(ctmp);
|
|
|
|
/*
|
|
* return trigonometric result
|
|
*/
|
|
return r;
|
|
}
|
|
|
|
|
|
/*
|
|
* c_aexsec - COMPLEX valued inverse exterior trigonometric secant
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* aexsec(x) = asec(x + 1)
|
|
*
|
|
* given:
|
|
* c complex value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* complex value result of trig function on q with error epsilon
|
|
*/
|
|
COMPLEX *
|
|
c_aexsec(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *r; /* inverse trig value result */
|
|
COMPLEX *ctmp; /* argument to inverse trig function */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (c == NULL) {
|
|
math_error("%s: c is NULL", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate complex inverse trig function value
|
|
*/
|
|
ctmp = c_addq(c, &_qone_);
|
|
r = c_asec(ctmp, epsilon);
|
|
comfree(ctmp);
|
|
|
|
/*
|
|
* return inverse trigonometric result
|
|
*/
|
|
return r;
|
|
}
|
|
|
|
|
|
/*
|
|
* c_excsc - COMPLEX valued exterior trigonometric cosecant
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* excsc(x) = csc(x) - 1
|
|
*
|
|
* given:
|
|
* c complex value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* complex value result of trig function on q with error epsilon
|
|
*/
|
|
COMPLEX *
|
|
c_excsc(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *r; /* return COMPLEX value */
|
|
COMPLEX *ctmp; /* complex sin(c) */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (c == NULL) {
|
|
math_error("%s: c is NULL", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate complex trig function value
|
|
*/
|
|
ctmp = c_csc(c, epsilon);
|
|
if (ctmp == NULL) {
|
|
math_error("Failed to compute complex sine for complex excsc");
|
|
not_reached();
|
|
}
|
|
r = c_sub(ctmp, &_cone_);
|
|
comfree(ctmp);
|
|
|
|
/*
|
|
* return trigonometric result
|
|
*/
|
|
return r;
|
|
}
|
|
|
|
|
|
/*
|
|
* c_aexcsc - COMPLEX valued inverse exterior trigonometric cosecant
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* aexcsc(x) = acsc(x + 1)
|
|
*
|
|
* given:
|
|
* c complex value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* complex value result of trig function on q with error epsilon
|
|
*/
|
|
COMPLEX *
|
|
c_aexcsc(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *r; /* inverse trig value result */
|
|
COMPLEX *ctmp; /* argument to inverse trig function */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (c == NULL) {
|
|
math_error("%s: c is NULL", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate complex inverse trig function value
|
|
*/
|
|
ctmp = c_addq(c, &_qone_);
|
|
r = c_acsc(ctmp, epsilon);
|
|
comfree(ctmp);
|
|
|
|
/*
|
|
* return inverse trigonometric result
|
|
*/
|
|
return r;
|
|
}
|
|
|
|
|
|
/*
|
|
* c_crd - COMPLEX valued trigonometric chord of a unit circle
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* crd(x) = 2 * sin(x / 2)
|
|
*
|
|
* given:
|
|
* c complex value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* complex value result of trig function on q with error epsilon
|
|
*/
|
|
COMPLEX *
|
|
c_crd(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *r; /* return COMPLEX value */
|
|
COMPLEX *cdiv2; /* complex c/2 */
|
|
COMPLEX *ctmp; /* complex sin(c/2) */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (c == NULL) {
|
|
math_error("%s: c is NULL", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate complex trig function value
|
|
*/
|
|
cdiv2 = c_divq(c, &_qtwo_);
|
|
ctmp = c_sin(cdiv2, epsilon);
|
|
comfree(cdiv2);
|
|
if (ctmp == NULL) {
|
|
math_error("Failed to compute complex sine for complex crd");
|
|
not_reached();
|
|
}
|
|
r = c_mulq(ctmp, &_qtwo_);
|
|
comfree(ctmp);
|
|
|
|
/*
|
|
* return trigonometric result
|
|
*/
|
|
return r;
|
|
}
|
|
|
|
|
|
/*
|
|
* c_acrd - COMPLEX valued inverse trigonometric chord of a unit circle
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* acrd(x) = 2 * asin(x / 2)
|
|
*
|
|
* given:
|
|
* c complex value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* complex value result of trig function on q with error epsilon
|
|
*/
|
|
COMPLEX *
|
|
c_acrd(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *r; /* inverse trig value result */
|
|
COMPLEX *cdiv2; /* complex c/2 */
|
|
COMPLEX *ctmp; /* complex asin(c/2) */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (c == NULL) {
|
|
math_error("%s: c is NULL", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate complex inverse trig function value
|
|
*/
|
|
cdiv2 = c_divq(c, &_qtwo_);
|
|
ctmp = c_asin(cdiv2, epsilon);
|
|
comfree(cdiv2);
|
|
r = c_mulq(ctmp, &_qtwo_);
|
|
comfree(ctmp);
|
|
|
|
/*
|
|
* return inverse trigonometric result
|
|
*/
|
|
return r;
|
|
}
|
|
|
|
|
|
/*
|
|
* c_cas - COMPLEX valued cosine plus sine
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* cas(x) = cos(x) + sin(x)
|
|
*
|
|
* given:
|
|
* c complex value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* complex value result of trig function on q with error epsilon
|
|
*/
|
|
COMPLEX *
|
|
c_cas(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *r; /* return COMPLEX value */
|
|
COMPLEX *csin; /* complex sin(c) */
|
|
COMPLEX *ccos; /* complex cos(c) */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (c == NULL) {
|
|
math_error("%s: c is NULL", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate complex trig function value
|
|
*/
|
|
csin = c_sin(c, epsilon);
|
|
if (csin == NULL) {
|
|
math_error("Failed to compute complex sine for complex cas");
|
|
not_reached();
|
|
}
|
|
ccos = c_cos(c, epsilon);
|
|
if (ccos == NULL) {
|
|
comfree(csin);
|
|
math_error("Failed to compute complex cosine for complex cas");
|
|
not_reached();
|
|
}
|
|
r = c_add(csin, ccos);
|
|
comfree(csin);
|
|
comfree(ccos);
|
|
|
|
/*
|
|
* return trigonometric result
|
|
*/
|
|
return r;
|
|
}
|
|
|
|
|
|
/*
|
|
* c_cis - COMPLEX valued Euler's formula
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* cis(x) = cos(x) + 1i*sin(x)
|
|
* cis(x) = exp(1i * x)
|
|
*
|
|
* given:
|
|
* c complex value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* complex value result of trig function on q with error epsilon
|
|
*/
|
|
COMPLEX *
|
|
c_cis(COMPLEX *c, NUMBER *epsilon)
|
|
{
|
|
COMPLEX *r; /* return COMPLEX value */
|
|
COMPLEX *ctmp; /* 1i * c */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (c == NULL) {
|
|
math_error("%s: c is NULL", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate complex trig function value
|
|
*/
|
|
ctmp = c_mul(c, &_conei_);
|
|
r = c_exp(ctmp, epsilon);
|
|
comfree(ctmp);
|
|
if (r == NULL) {
|
|
math_error("Failed to compute complex exp for complex cis");
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* return trigonometric result
|
|
*/
|
|
return r;
|
|
}
|