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.
3694 lines
96 KiB
C
3694 lines
96 KiB
C
/*
|
|
* qtrans - transcendental functions for real numbers
|
|
*
|
|
* 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:22
|
|
* File existed as early as: before 1990
|
|
*
|
|
* Share and enjoy! :-) http://www.isthe.com/chongo/tech/comp/calc/
|
|
*/
|
|
|
|
/*
|
|
* Transcendental functions for real numbers.
|
|
* These are sin, cos, exp, ln, power, cosh, sinh.
|
|
*/
|
|
|
|
|
|
#include "qmath.h"
|
|
#include "config.h"
|
|
|
|
|
|
#include "errtbl.h"
|
|
#include "banned.h" /* include after system header <> includes */
|
|
|
|
|
|
HALF _qlgenum_[] = { 36744 };
|
|
HALF _qlgeden_[] = { 25469 };
|
|
NUMBER _qlge_ = { { _qlgenum_, 1, 0 }, { _qlgeden_, 1, 0 }, 1, NULL };
|
|
|
|
/*
|
|
* cache the natural logarithm of 10 and 2
|
|
*/
|
|
STATIC NUMBER *ln_10 = NULL;
|
|
STATIC NUMBER *ln_10_epsilon = NULL;
|
|
STATIC NUMBER *ln_2 = NULL;
|
|
STATIC NUMBER *ln_2_epsilon = NULL;
|
|
STATIC NUMBER *ln_n = NULL;
|
|
STATIC NUMBER *ln_n_epsilon = NULL;
|
|
|
|
/*
|
|
* cache pi
|
|
*
|
|
* pivalue[LAST_PI_EPSILON] - last epsilon used to calculate pi
|
|
* pivalue[LAST_PI_VALUE] - last calculated pi
|
|
* given pivalue[LAST_PI_EPSILON] epsilon
|
|
* pivalue[LAST_PI_DIV_180_EPSILON] - last epsilon used to calculate pi/180
|
|
* pivalue[LAST_PI_DIV_180_VALUE] - last calculated pi/180 given
|
|
* pivalue[LAST_PI_DIV_180_EPSILON] epsilon
|
|
* pivalue[LAST_PI_DIV_200_EPSILON] - last epsilon used to calculate pi/200
|
|
* pivalue[LAST_PI_DIV_200_VALUE] - last calculated pi/200 given
|
|
* pivalue[LAST_PI_DIV_200_EPSILON] epsilon
|
|
*/
|
|
enum pi_cache {
|
|
LAST_PI_EPSILON = 0,
|
|
LAST_PI_VALUE,
|
|
LAST_PI_DIV_180_EPSILON,
|
|
LAST_PI_DIV_180_VALUE,
|
|
LAST_PI_DIV_200_EPSILON,
|
|
LAST_PI_DIV_200_VALUE,
|
|
PI_CACHE_LEN /* must be last */
|
|
};
|
|
STATIC NUMBER *pivalue[PI_CACHE_LEN] = {
|
|
NULL, /* LAST_PI_EPSILON */
|
|
NULL, /* LAST_PI_VALUE */
|
|
NULL, /* LAST_PI_DIV_180_EPSILON */
|
|
NULL, /* LAST_PI_DIV_180_VALUE */
|
|
NULL, /* LAST_PI_DIV_200_EPSILON */
|
|
NULL, /* LAST_PI_DIV_200_VALUE */
|
|
};
|
|
|
|
/*
|
|
* other static function declarations
|
|
*/
|
|
STATIC NUMBER *qexprel(NUMBER *q, long bitnum);
|
|
|
|
/*
|
|
* Evaluate and store in specified locations the sin and cos of a given
|
|
* number to accuracy corresponding to a specified number of binary digits.
|
|
*/
|
|
void
|
|
qsincos(NUMBER *q, long bitnum, NUMBER **vs, NUMBER **vc)
|
|
{
|
|
long n, m, k, h, s, t, d;
|
|
NUMBER *qtmp1, *qtmp2;
|
|
ZVALUE X, cossum, sinsum, mul, ztmp1, ztmp2, ztmp3;
|
|
|
|
qtmp1 = qqabs(q);
|
|
h = qilog2(qtmp1);
|
|
qfree(qtmp1);
|
|
k = bitnum + h + 1;
|
|
if (k < 0) {
|
|
*vs = qlink(&_qzero_);
|
|
*vc = qlink(&_qone_);
|
|
return;
|
|
}
|
|
s = k;
|
|
if (k) {
|
|
do {
|
|
t = s;
|
|
s = (s + k/s)/2;
|
|
}
|
|
while (t > s);
|
|
} /* s is int(sqrt(k)) */
|
|
s++;
|
|
if (s < -h)
|
|
s = -h;
|
|
n = h + s; /* n is number of squaring that will be required */
|
|
m = bitnum + n;
|
|
while (s > 0) { /* increasing m by ilog2(s) */
|
|
s >>= 1;
|
|
m++;
|
|
} /* m is working number of bits */
|
|
qtmp1 = qscale(q, m - n);
|
|
zquo(qtmp1->num, qtmp1->den, &X, conf->triground);
|
|
qfree(qtmp1);
|
|
if (ziszero(X)) {
|
|
zfree(X);
|
|
*vs = qlink(&_qzero_);
|
|
*vc = qlink(&_qone_);
|
|
return;
|
|
}
|
|
zbitvalue(m, &cossum);
|
|
zcopy(X, &sinsum);
|
|
zcopy(X, &mul);
|
|
d = 1;
|
|
for (;;) {
|
|
X.sign = !X.sign;
|
|
zmul(X, mul, &ztmp1);
|
|
zfree(X);
|
|
zshift(ztmp1, -m, &ztmp2);
|
|
zfree(ztmp1);
|
|
zdivi(ztmp2, ++d, &X);
|
|
zfree(ztmp2);
|
|
if (ziszero(X))
|
|
break;
|
|
zadd(cossum, X, &ztmp1);
|
|
zfree(cossum);
|
|
cossum = ztmp1;
|
|
zmul(X, mul, &ztmp1);
|
|
zfree(X);
|
|
zshift(ztmp1, -m, &ztmp2);
|
|
zfree(ztmp1);
|
|
zdivi(ztmp2, ++d, &X);
|
|
zfree(ztmp2);
|
|
if (ziszero(X))
|
|
break;
|
|
zadd(sinsum, X, &ztmp1);
|
|
zfree(sinsum);
|
|
sinsum = ztmp1;
|
|
}
|
|
zfree(X);
|
|
zfree(mul);
|
|
while (n-- > 0) {
|
|
zsquare(cossum, &ztmp1);
|
|
zsquare(sinsum, &ztmp2);
|
|
zsub(ztmp1, ztmp2, &ztmp3);
|
|
zfree(ztmp1);
|
|
zfree(ztmp2);
|
|
zmul(cossum, sinsum, &ztmp1);
|
|
zfree(cossum);
|
|
zfree(sinsum);
|
|
zshift(ztmp3, -m, &cossum);
|
|
zfree(ztmp3);
|
|
zshift(ztmp1, 1 - m, &sinsum);
|
|
zfree(ztmp1);
|
|
}
|
|
h = zlowbit(cossum);
|
|
qtmp1 = qalloc();
|
|
if (m > h) {
|
|
zshift(cossum, -h, &qtmp1->num);
|
|
zbitvalue(m - h, &qtmp1->den);
|
|
} else {
|
|
zshift(cossum, - m, &qtmp1->num);
|
|
}
|
|
zfree(cossum);
|
|
*vc = qtmp1;
|
|
h = zlowbit(sinsum);
|
|
qtmp2 = qalloc();
|
|
if (m > h) {
|
|
zshift(sinsum, -h, &qtmp2->num);
|
|
zbitvalue(m - h, &qtmp2->den);
|
|
} else {
|
|
zshift(sinsum, -m, &qtmp2->num);
|
|
}
|
|
zfree(sinsum);
|
|
*vs = qtmp2;
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* Calculate the cosine of a number to a near multiple of epsilon.
|
|
* This calls qsincos() and discards the value of sin.
|
|
*/
|
|
NUMBER *
|
|
qcos(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *sin, *cos, *res;
|
|
long n;
|
|
|
|
if (qiszero(epsilon)) {
|
|
math_error("Zero epsilon value for cosine");
|
|
not_reached();
|
|
}
|
|
if (qiszero(q))
|
|
return qlink(&_qone_);
|
|
n = -qilog2(epsilon);
|
|
if (n < 0)
|
|
return qlink(&_qzero_);
|
|
qsincos(q, n + 2, &sin, &cos);
|
|
qfree(sin);
|
|
res = qmappr(cos, epsilon, conf->triground);
|
|
qfree(cos);
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* This calls qsincos() and discards the value of cos.
|
|
*/
|
|
NUMBER *
|
|
qsin(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *sin, *cos, *res;
|
|
long n;
|
|
|
|
if (qiszero(epsilon)) {
|
|
math_error("Zero epsilon value for sine");
|
|
not_reached();
|
|
}
|
|
n = -qilog2(epsilon);
|
|
if (qiszero(q) || n < 0)
|
|
return qlink(&_qzero_);
|
|
qsincos(q, n + 2, &sin, &cos);
|
|
qfree(cos);
|
|
res = qmappr(sin, epsilon, conf->triground);
|
|
qfree(sin);
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* Calculate the tangent function.
|
|
*/
|
|
NUMBER *
|
|
qtan(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *sin, *cos, *tan, *res;
|
|
long n, k, m;
|
|
|
|
if (qiszero(epsilon)) {
|
|
math_error("Zero epsilon value for tangent");
|
|
not_reached();
|
|
}
|
|
if (qiszero(q))
|
|
return qlink(q);
|
|
n = qilog2(epsilon);
|
|
k = (n > 0) ? 4 + n/2 : 4;
|
|
for (;;) {
|
|
qsincos(q, 2 * k - n, &sin, &cos);
|
|
if (qiszero(cos)) {
|
|
qfree(sin);
|
|
qfree(cos);
|
|
k = 2 * k - n + 4;
|
|
continue;
|
|
}
|
|
m = -qilog2(cos);
|
|
if (m < k)
|
|
break;
|
|
qfree(sin);
|
|
qfree(cos);
|
|
k = m + 1;
|
|
}
|
|
tan = qqdiv(sin, cos);
|
|
qfree(sin);
|
|
qfree(cos);
|
|
res = qmappr(tan, epsilon, conf->triground);
|
|
qfree(tan);
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* Calculate the cotangent function.
|
|
*/
|
|
NUMBER *
|
|
qcot(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *sin, *cos, *cot, *res;
|
|
long n, k, m;
|
|
|
|
if (qiszero(epsilon)) {
|
|
math_error("Zero epsilon value for cotangent");
|
|
not_reached();
|
|
}
|
|
if (qiszero(q)) {
|
|
math_error("Zero argument for cotangent");
|
|
not_reached();
|
|
}
|
|
k = -qilog2(q);
|
|
n = qilog2(epsilon);
|
|
if (k < 0)
|
|
k = (n > 0) ? n/2 : 0;
|
|
k += 4;
|
|
for (;;) {
|
|
qsincos(q, 2 * k - n, &sin, &cos);
|
|
if (qiszero(sin)) {
|
|
qfree(sin);
|
|
qfree(cos);
|
|
k = 2 * k - n + 4;
|
|
continue;
|
|
}
|
|
m = -qilog2(sin);
|
|
if (m < k)
|
|
break;
|
|
qfree(sin);
|
|
qfree(cos);
|
|
k = m + 1;
|
|
}
|
|
cot = qqdiv(cos, sin);
|
|
qfree(sin);
|
|
qfree(cos);
|
|
res = qmappr(cot, epsilon, conf->triground);
|
|
qfree(cot);
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* Calculate the secant function.
|
|
*/
|
|
NUMBER *
|
|
qsec(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *sin, *cos, *sec, *res;
|
|
long n, k, m;
|
|
|
|
if (qiszero(epsilon)) {
|
|
math_error("Zero epsilon value for secant");
|
|
not_reached();
|
|
}
|
|
if (qiszero(q))
|
|
return qlink(&_qone_);
|
|
n = qilog2(epsilon);
|
|
k = (n > 0) ? 4 + n/2 : 4;
|
|
for (;;) {
|
|
qsincos(q, 2 * k - n, &sin, &cos);
|
|
qfree(sin);
|
|
if (qiszero(cos)) {
|
|
qfree(cos);
|
|
k = 2 * k - n + 4;
|
|
continue;
|
|
}
|
|
m = -qilog2(cos);
|
|
if (m < k)
|
|
break;
|
|
qfree(cos);
|
|
k = m + 1;
|
|
}
|
|
sec = qinv(cos);
|
|
qfree(cos);
|
|
res = qmappr(sec, epsilon, conf->triground);
|
|
qfree(sec);
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* Calculate the cosecant function.
|
|
*/
|
|
NUMBER *
|
|
qcsc(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *sin, *cos, *csc, *res;
|
|
long n, k, m;
|
|
|
|
if (qiszero(epsilon)) {
|
|
math_error("Zero epsilon value for cosecant");
|
|
not_reached();
|
|
}
|
|
if (qiszero(q)) {
|
|
math_error("Zero argument for cosecant");
|
|
not_reached();
|
|
}
|
|
k = -qilog2(q);
|
|
n = qilog2(epsilon);
|
|
if (k < 0)
|
|
k = (n > 0) ? n/2 : 0;
|
|
k += 4;
|
|
for (;;) {
|
|
qsincos(q, 2 * k - n, &sin, &cos);
|
|
qfree(cos);
|
|
if (qiszero(sin)) {
|
|
qfree(sin);
|
|
k = 2 * k - n + 4;
|
|
continue;
|
|
}
|
|
m = -qilog2(sin);
|
|
if (m < k)
|
|
break;
|
|
qfree(sin);
|
|
k = m + 1;
|
|
}
|
|
csc = qinv(sin);
|
|
qfree(sin);
|
|
res = qmappr(csc, epsilon, conf->triground);
|
|
qfree(csc);
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* Calculate the arcsine function.
|
|
* The result is in the range -pi/2 to pi/2.
|
|
*/
|
|
NUMBER *
|
|
qasin(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *qtmp1, *qtmp2, *epsilon1;
|
|
ZVALUE ztmp;
|
|
bool neg;
|
|
FLAG r;
|
|
|
|
if (qiszero(epsilon)) {
|
|
math_error("Zero epsilon value for asin");
|
|
not_reached();
|
|
}
|
|
if (qiszero(q)) {
|
|
return qlink(&_qzero_);
|
|
}
|
|
ztmp = q->num;
|
|
neg = ztmp.sign;
|
|
ztmp.sign = 0;
|
|
r = zrel(ztmp, q->den);
|
|
if (r > 0) {
|
|
return NULL;
|
|
}
|
|
if (r == 0) {
|
|
epsilon1 = qscale(epsilon, 1L);
|
|
qtmp2 = qpi(epsilon1);
|
|
qtmp1 = qscale(qtmp2, -1L);
|
|
} else {
|
|
epsilon1 = qscale(epsilon, -2L);
|
|
qtmp1 = qalloc();
|
|
zsquare(q->num, &qtmp1->num);
|
|
zsquare(q->den, &ztmp);
|
|
zsub(ztmp, qtmp1->num, &qtmp1->den);
|
|
zfree(ztmp);
|
|
qtmp2 = qsqrt(qtmp1, epsilon1, conf->triground);
|
|
qfree(qtmp1);
|
|
qtmp1 = qatan(qtmp2, epsilon);
|
|
}
|
|
qfree(qtmp2);
|
|
qfree(epsilon1);
|
|
if (neg) {
|
|
qtmp2 = qneg(qtmp1);
|
|
qfree(qtmp1);
|
|
return(qtmp2);
|
|
}
|
|
return qtmp1;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Calculate the acos function.
|
|
* The result is in the range 0 to pi.
|
|
*/
|
|
NUMBER *
|
|
qacos(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *q1, *q2, *epsilon1;
|
|
ZVALUE z;
|
|
|
|
if (qiszero(epsilon)) {
|
|
math_error("Zero epsilon value for acos");
|
|
not_reached();
|
|
}
|
|
if (qisone(q))
|
|
return qlink(&_qzero_);
|
|
if (qisnegone(q))
|
|
return qpi(epsilon);
|
|
|
|
z = q->num;
|
|
z.sign = 0;
|
|
if (zrel(z, q->den) > 0)
|
|
return NULL;
|
|
epsilon1 = qscale(epsilon, -3L); /* ??? */
|
|
q1 = qalloc();
|
|
zsub(q->den, q->num, &q1->num);
|
|
zadd(q->den, q->num, &q1->den);
|
|
q2 = qsqrt(q1, epsilon1, conf->triground);
|
|
qfree(q1);
|
|
qfree(epsilon1);
|
|
epsilon1 = qscale(epsilon, -1L);
|
|
q1 = qatan(q2, epsilon1);
|
|
qfree(epsilon1);
|
|
qfree(q2);
|
|
q2 = qscale(q1, 1L);
|
|
qfree(q1)
|
|
return q2;
|
|
}
|
|
|
|
|
|
/*
|
|
* Calculate the arctangent function to the nearest or next to nearest
|
|
* multiple of epsilon. Algorithm uses
|
|
* atan(x) = 2 * atan(x/(1 + sqrt(1+x^2)))
|
|
* to reduce x to a small value and then
|
|
* atan(x) = x - x^3/3 + ...
|
|
*/
|
|
NUMBER *
|
|
qatan(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
long m, k, i;
|
|
FULL d;
|
|
ZVALUE X, D, DD, sum, mul, term, ztmp1, ztmp2;
|
|
NUMBER *qtmp, *res;
|
|
bool sign;
|
|
|
|
if (qiszero(epsilon)) {
|
|
math_error("Zero epsilon value for arctangent");
|
|
not_reached();
|
|
}
|
|
if (qiszero(q))
|
|
return qlink(&_qzero_);
|
|
m = 12 - qilog2(epsilon);
|
|
/* 4 bits for 4 doublings; 8 for rounding */
|
|
if (m < 8)
|
|
m = 8; /* m is number of working binary digits */
|
|
qtmp = qscale(q, m);
|
|
zquo(qtmp->num, qtmp->den, &X, conf->triground);
|
|
qfree(qtmp);
|
|
zbitvalue(m, &D); /* q has become X/D */
|
|
zsquare(D, &DD);
|
|
i = 4; /* maybe this should be larger */
|
|
while (i-- > 0 && !ziszero(X)) {
|
|
zsquare(X, &ztmp1);
|
|
zadd(ztmp1, DD, &ztmp2);
|
|
zfree(ztmp1);
|
|
zsqrt(ztmp2, &ztmp1, conf->triground);
|
|
zfree(ztmp2);
|
|
zadd(ztmp1, D, &ztmp2);
|
|
zfree(ztmp1);
|
|
zshift(X, m, &ztmp1);
|
|
zfree(X);
|
|
zquo(ztmp1, ztmp2, &X, conf->triground);
|
|
zfree(ztmp1);
|
|
zfree(ztmp2);
|
|
}
|
|
zfree(DD);
|
|
zfree(D);
|
|
if (ziszero(X)) {
|
|
zfree(X);
|
|
return qlink(&_qzero_);
|
|
}
|
|
zcopy(X, &sum);
|
|
zsquare(X, &ztmp1);
|
|
zshift(ztmp1, -m, &mul);
|
|
zfree(ztmp1);
|
|
d = 3;
|
|
sign = !X.sign;
|
|
for (;;) {
|
|
if (d > BASE) {
|
|
math_error("Too many terms required for atan");
|
|
not_reached();
|
|
}
|
|
zmul(X, mul, &ztmp1);
|
|
zfree(X);
|
|
zshift(ztmp1, -m, &X); /* X now (original X)^d */
|
|
zfree(ztmp1);
|
|
zdivi(X, d, &term);
|
|
if (ziszero(term)) {
|
|
zfree(term);
|
|
break;
|
|
}
|
|
term.sign = sign;
|
|
zadd(sum, term, &ztmp1);
|
|
zfree(sum);
|
|
zfree(term);
|
|
sum = ztmp1;
|
|
sign = !sign;
|
|
d += 2;
|
|
}
|
|
zfree(mul);
|
|
zfree(X);
|
|
qtmp = qalloc();
|
|
k = zlowbit(sum);
|
|
if (k) {
|
|
zshift(sum, -k, &qtmp->num);
|
|
zfree(sum);
|
|
} else {
|
|
qtmp->num = sum;
|
|
}
|
|
zbitvalue(m - 4 - k, &qtmp->den);
|
|
res = qmappr(qtmp, epsilon, conf->triground);
|
|
qfree(qtmp);
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
* Inverse secant function
|
|
*/
|
|
NUMBER *
|
|
qasec(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *tmp, *res;
|
|
|
|
tmp = qinv(q);
|
|
res = qacos(tmp, epsilon);
|
|
qfree(tmp);
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* Inverse cosecant function
|
|
*/
|
|
NUMBER *
|
|
qacsc(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *tmp, *res;
|
|
|
|
tmp = qinv(q);
|
|
res = qasin(tmp, epsilon);
|
|
qfree(tmp);
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* Inverse cotangent function
|
|
*/
|
|
NUMBER *
|
|
qacot(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *tmp1, *tmp2, *tmp3, *epsilon1;
|
|
|
|
if (qiszero(epsilon)) {
|
|
math_error("Zero epsilon for acot");
|
|
not_reached();
|
|
}
|
|
if (qiszero(q)) {
|
|
epsilon1 = qscale(epsilon, 1L);
|
|
tmp1 = qpi(epsilon1);
|
|
qfree(epsilon1);
|
|
tmp2 = qscale(tmp1, -1L);
|
|
qfree(tmp1);
|
|
return tmp2;
|
|
}
|
|
tmp1 = qinv(q);
|
|
if (!qisneg(q)) {
|
|
tmp2 = qatan(tmp1, epsilon);
|
|
qfree(tmp1);
|
|
return tmp2;
|
|
}
|
|
epsilon1 = qscale(epsilon, -2L);
|
|
tmp2 = qatan(tmp1, epsilon1);
|
|
qfree(tmp1);
|
|
tmp1 = qpi(epsilon1);
|
|
qfree(epsilon1);
|
|
tmp3 = qqadd(tmp1, tmp2);
|
|
qfree(tmp1);
|
|
qfree(tmp2);
|
|
tmp1 = qmappr(tmp3, epsilon, conf->triground);
|
|
qfree(tmp3);
|
|
return tmp1;
|
|
}
|
|
|
|
|
|
/*
|
|
* Calculate the angle which is determined by the point (x,y).
|
|
* This is the same as atan(y/x) for positive x, but is continuous
|
|
* except for y = 0, x <= 0. By convention, y is the first argument.
|
|
* For all x, y, -pi < atan2 <= pi. For example, qatan2(1, -1) = 3/4 * pi.
|
|
*/
|
|
NUMBER *
|
|
qatan2(NUMBER *qy, NUMBER *qx, NUMBER *epsilon)
|
|
{
|
|
NUMBER *tmp1, *tmp2, *tmp3, *epsilon2;
|
|
|
|
if (qiszero(epsilon)) {
|
|
math_error("Zero epsilon value for atan2");
|
|
not_reached();
|
|
}
|
|
if (qiszero(qy) && qiszero(qx)) {
|
|
/* conform to 4.3BSD ANSI/IEEE 754-1985 math lib */
|
|
return qlink(&_qzero_);
|
|
}
|
|
/*
|
|
* If the point is on the negative real axis, then the answer is pi.
|
|
*/
|
|
if (qiszero(qy) && qisneg(qx))
|
|
return qpi(epsilon);
|
|
/*
|
|
* If the point is in the right half plane, then use the normal atan.
|
|
*/
|
|
if (!qisneg(qx) && !qiszero(qx)) {
|
|
if (qiszero(qy))
|
|
return qlink(&_qzero_);
|
|
tmp1 = qqdiv(qy, qx);
|
|
tmp2 = qatan(tmp1, epsilon);
|
|
qfree(tmp1);
|
|
return tmp2;
|
|
}
|
|
/*
|
|
* The point is in the left half plane (x <= 0) with nonzero y.
|
|
* Calculate the angle by using the formula:
|
|
* atan2(y,x) = 2 * atan(sgn(y) * sqrt((x/y)^2 + 1) - x/y).
|
|
*/
|
|
epsilon2 = qscale(epsilon, -4L);
|
|
tmp1 = qqdiv(qx, qy);
|
|
tmp2 = qsquare(tmp1);
|
|
tmp3 = qqadd(tmp2, &_qone_);
|
|
qfree(tmp2);
|
|
tmp2 = qsqrt(tmp3, epsilon2, 24L | (qy->num.sign * 64));
|
|
qfree(tmp3);
|
|
tmp3 = qsub(tmp2, tmp1);
|
|
qfree(tmp2);
|
|
qfree(tmp1);
|
|
qfree(epsilon2);
|
|
epsilon2 = qscale(epsilon, -1L);
|
|
tmp1 = qatan(tmp3, epsilon2);
|
|
qfree(epsilon2);
|
|
qfree(tmp3);
|
|
tmp2 = qscale(tmp1, 1L);
|
|
qfree(tmp1);
|
|
return tmp2;
|
|
}
|
|
|
|
|
|
/*
|
|
* Calculate the value of pi to within the required epsilon.
|
|
* This uses the following formula which only needs integer calculations
|
|
* except for the final operation:
|
|
* pi = 1 / SUMOF(comb(2 * N, N) ^ 3 * (42 * N + 5) / 2 ^ (12 * N + 4)),
|
|
* where the summation runs from N=0. This formula gives about 6 bits of
|
|
* accuracy per term. Since the denominator for each term is a power of two,
|
|
* we can simply use shifts to sum the terms. The combinatorial numbers
|
|
* in the formula are calculated recursively using the formula:
|
|
* comb(2*(N+1), N+1) = 2 * comb(2 * N, N) * (2 * N + 1) / N.
|
|
*/
|
|
NUMBER *
|
|
qpi(NUMBER *epsilon)
|
|
{
|
|
ZVALUE comb; /* current combinatorial value */
|
|
ZVALUE sum; /* current sum */
|
|
ZVALUE tmp1, tmp2;
|
|
NUMBER *r, *t1, qtmp;
|
|
long shift; /* current shift of result */
|
|
long N; /* current term number */
|
|
long bits; /* needed number of bits of precision */
|
|
long t;
|
|
|
|
/* firewall */
|
|
if (qiszero(epsilon)) {
|
|
math_error("zero epsilon value for pi");
|
|
not_reached();
|
|
}
|
|
|
|
/* use pi cache if epsilon marches, else flush if needed */
|
|
if (pivalue[LAST_PI_EPSILON] != NULL &&
|
|
pivalue[LAST_PI_VALUE] != NULL &&
|
|
epsilon == pivalue[LAST_PI_EPSILON]) {
|
|
return qlink(pivalue[LAST_PI_VALUE]);
|
|
}
|
|
if (pivalue[LAST_PI_EPSILON] != NULL) {
|
|
qfree(pivalue[LAST_PI_EPSILON]);
|
|
}
|
|
if (pivalue[LAST_PI_VALUE] != NULL) {
|
|
qfree(pivalue[LAST_PI_VALUE]);
|
|
}
|
|
|
|
bits = -qilog2(epsilon) + 4;
|
|
if (bits < 4)
|
|
bits = 4;
|
|
comb = _one_;
|
|
itoz(5L, &sum);
|
|
N = 0;
|
|
shift = 4;
|
|
do {
|
|
t = 1 + (++N & 0x1);
|
|
(void) zdivi(comb, N / (3 - t), &tmp1);
|
|
zfree(comb);
|
|
zmuli(tmp1, t * (2 * N - 1), &comb);
|
|
zfree(tmp1);
|
|
zsquare(comb, &tmp1);
|
|
zmul(comb, tmp1, &tmp2);
|
|
zfree(tmp1);
|
|
zmuli(tmp2, 42 * N + 5, &tmp1);
|
|
zfree(tmp2);
|
|
zshift(sum, 12L, &tmp2);
|
|
zfree(sum);
|
|
zadd(tmp1, tmp2, &sum);
|
|
t = zhighbit(tmp1);
|
|
zfree(tmp1);
|
|
zfree(tmp2);
|
|
shift += 12;
|
|
} while ((shift - t) < bits);
|
|
zfree(comb);
|
|
qtmp.num = _one_;
|
|
qtmp.den = sum;
|
|
t1 = qscale(&qtmp, shift);
|
|
zfree(sum);
|
|
r = qmappr(t1, epsilon, conf->triground);
|
|
qfree(t1);
|
|
pivalue[LAST_PI_EPSILON] = qlink(epsilon);
|
|
pivalue[LAST_PI_VALUE] = qlink(r);
|
|
return r;
|
|
}
|
|
|
|
|
|
/*
|
|
* qpidiv180 - calculate pi / 180
|
|
*
|
|
* This function returns pi/180 as used to covert between radians and degrees.
|
|
*/
|
|
NUMBER *
|
|
qpidiv180(NUMBER *epsilon)
|
|
{
|
|
NUMBER *pi, *pidiv180;
|
|
|
|
/* firewall */
|
|
if (qiszero(epsilon)) {
|
|
math_error("zero epsilon value for qpidiv180");
|
|
not_reached();
|
|
}
|
|
|
|
/* use pi/180 cache if epsilon marches, else flush if needed */
|
|
if (pivalue[LAST_PI_DIV_180_EPSILON] != NULL &&
|
|
pivalue[LAST_PI_DIV_180_VALUE] != NULL &&
|
|
epsilon == pivalue[LAST_PI_DIV_180_EPSILON]) {
|
|
return qlink(pivalue[LAST_PI_DIV_180_VALUE]);
|
|
}
|
|
if (pivalue[LAST_PI_DIV_180_EPSILON] != NULL) {
|
|
qfree(pivalue[LAST_PI_DIV_180_EPSILON]);
|
|
}
|
|
if (pivalue[LAST_PI_DIV_180_VALUE] != NULL) {
|
|
qfree(pivalue[LAST_PI_DIV_180_VALUE]);
|
|
}
|
|
|
|
/* let qpi() returned cached pi or calculate new as needed */
|
|
pi = qpi(epsilon);
|
|
|
|
/* calculate pi/180 */
|
|
pidiv180 = qdivi(pi, 180);
|
|
|
|
/* cache epsilon and pi/180 */
|
|
pivalue[LAST_PI_DIV_180_EPSILON] = qlink(epsilon);
|
|
pivalue[LAST_PI_DIV_180_VALUE] = qlink(pidiv180);
|
|
|
|
/* return pi/180 */
|
|
return pidiv180;
|
|
}
|
|
|
|
|
|
/*
|
|
* qpidiv200 - calculate pi / 200
|
|
*
|
|
* This function returns pi/200 as used to covert between radians and gradians.
|
|
*/
|
|
NUMBER *
|
|
qpidiv200(NUMBER *epsilon)
|
|
{
|
|
NUMBER *pi, *pidiv200;
|
|
|
|
/* firewall */
|
|
if (qiszero(epsilon)) {
|
|
math_error("zero epsilon value for qpidiv200");
|
|
not_reached();
|
|
}
|
|
|
|
/* use pi/200 cache if epsilon marches, else flush if needed */
|
|
if (pivalue[LAST_PI_DIV_200_EPSILON] != NULL &&
|
|
pivalue[LAST_PI_DIV_200_VALUE] != NULL &&
|
|
epsilon == pivalue[LAST_PI_DIV_200_EPSILON]) {
|
|
return qlink(pivalue[LAST_PI_DIV_200_VALUE]);
|
|
}
|
|
if (pivalue[LAST_PI_DIV_200_EPSILON] != NULL) {
|
|
qfree(pivalue[LAST_PI_DIV_200_EPSILON]);
|
|
}
|
|
if (pivalue[LAST_PI_DIV_200_VALUE] != NULL) {
|
|
qfree(pivalue[LAST_PI_DIV_200_VALUE]);
|
|
}
|
|
|
|
/* let qpi() returned cached pi or calculate new as needed */
|
|
pi = qpi(epsilon);
|
|
|
|
/* calculate pi/200 */
|
|
pidiv200 = qdivi(pi, 200);
|
|
|
|
/* cache epsilon and pi/200 */
|
|
pivalue[LAST_PI_DIV_200_EPSILON] = qlink(epsilon);
|
|
pivalue[LAST_PI_DIV_200_VALUE] = qlink(pidiv200);
|
|
|
|
/* return pi/200 */
|
|
return pidiv200;
|
|
}
|
|
|
|
|
|
/*
|
|
* Calculate the exponential function to the nearest or next to nearest
|
|
* multiple of the positive number epsilon.
|
|
*/
|
|
NUMBER *
|
|
qexp(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
long m, n;
|
|
NUMBER *tmp1, *tmp2;
|
|
|
|
if (qiszero(epsilon)) {
|
|
math_error("Zero epsilon value for exp");
|
|
not_reached();
|
|
}
|
|
if (qiszero(q))
|
|
return qlink(&_qone_);
|
|
tmp1 = qmul(q, &_qlge_);
|
|
m = qtoi(tmp1); /* exp(q) < 2^(m+1) or m == MAXLONG */
|
|
qfree(tmp1);
|
|
|
|
if (m > (1 << 30))
|
|
return NULL;
|
|
|
|
n = qilog2(epsilon); /* 2^n <= epsilon < 2^(n+1) */
|
|
if (m < n)
|
|
return qlink(&_qzero_);
|
|
tmp1 = qqabs(q);
|
|
tmp2 = qexprel(tmp1, m - n + 1);
|
|
qfree(tmp1);
|
|
if (tmp2 == NULL)
|
|
return NULL;
|
|
if (qisneg(q)) {
|
|
tmp1 = qinv(tmp2);
|
|
qfree(tmp2);
|
|
tmp2 = tmp1;
|
|
}
|
|
tmp1 = qmappr(tmp2, epsilon, conf->triground);
|
|
qfree(tmp2);
|
|
return tmp1;
|
|
}
|
|
|
|
|
|
/*
|
|
* Calculate the exponential function with relative error corresponding
|
|
* to a specified number of significant bits
|
|
* Requires *q >= 0, bitnum >= 0.
|
|
* This returns NULL if more than 2^30 working bits would be required.
|
|
*/
|
|
S_FUNC NUMBER *
|
|
qexprel(NUMBER *q, long bitnum)
|
|
{
|
|
long n, m, k, h, s, t, d;
|
|
NUMBER *qtmp1;
|
|
ZVALUE X, B, sum, term, ztmp1, ztmp2;
|
|
|
|
h = qilog2(q);
|
|
k = bitnum + h + 1;
|
|
if (k < 0)
|
|
return qlink(&_qone_);
|
|
s = k;
|
|
if (k) {
|
|
do {
|
|
t = s;
|
|
s = (s + k/s)/2;
|
|
}
|
|
while (t > s);
|
|
} /* s is int(sqrt(k)) */
|
|
s++;
|
|
if (s < -h)
|
|
s = -h;
|
|
n = h + s; /* n is number of squarings that will be required */
|
|
m = bitnum + n;
|
|
if (m > (1 << 30))
|
|
return NULL;
|
|
while (s > 0) { /* increasing m by ilog2(s) */
|
|
s >>= 1;
|
|
m++;
|
|
} /* m is working number of bits */
|
|
qtmp1 = qscale(q, m - n);
|
|
zquo(qtmp1->num, qtmp1->den, &X, conf->triground);
|
|
qfree(qtmp1);
|
|
if (ziszero(X)) {
|
|
zfree(X);
|
|
return qlink(&_qone_);
|
|
}
|
|
zbitvalue(m, &sum);
|
|
zcopy(X, &term);
|
|
d = 1;
|
|
do {
|
|
zadd(sum, term, &ztmp1);
|
|
zfree(sum);
|
|
sum = ztmp1;
|
|
zmul(term, X, &ztmp1);
|
|
zfree(term);
|
|
zshift(ztmp1, -m, &ztmp2);
|
|
zfree(ztmp1);
|
|
zdivi(ztmp2, ++d, &term);
|
|
zfree(ztmp2);
|
|
}
|
|
while (!ziszero(term));
|
|
zfree(term);
|
|
zfree(X);
|
|
k = 0;
|
|
zbitvalue(2 * m + 1, &B);
|
|
while (n-- > 0) {
|
|
k *= 2;
|
|
zsquare(sum, &ztmp1);
|
|
zfree(sum);
|
|
if (zrel(ztmp1, B) >= 0) {
|
|
zshift(ztmp1, -m - 1, &sum);
|
|
k++;
|
|
} else {
|
|
zshift(ztmp1, -m, &sum);
|
|
}
|
|
zfree(ztmp1);
|
|
}
|
|
zfree(B);
|
|
h = zlowbit(sum);
|
|
qtmp1 = qalloc();
|
|
if (m > h + k) {
|
|
zshift(sum, -h, &qtmp1->num);
|
|
zbitvalue(m - h - k, &qtmp1->den);
|
|
}
|
|
else
|
|
zshift(sum, k - m, &qtmp1->num);
|
|
zfree(sum);
|
|
return qtmp1;
|
|
}
|
|
|
|
|
|
/*
|
|
* Calculate the natural logarithm of a number accurate to the specified
|
|
* positive epsilon.
|
|
*/
|
|
NUMBER *
|
|
qln(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
long m, n, k, h, d;
|
|
ZVALUE term, sum, mul, pow, X, D, B, ztmp;
|
|
NUMBER *qtmp, *res;
|
|
bool neg;
|
|
|
|
if (qiszero(q)) {
|
|
math_error("ln of 0");
|
|
not_reached();
|
|
}
|
|
if (qiszero(epsilon)) {
|
|
math_error("Zero epsilon value for ln");
|
|
not_reached();
|
|
}
|
|
if (qisunit(q))
|
|
return qlink(&_qzero_);
|
|
q = qqabs(q); /* Ignore sign of q */
|
|
neg = (zrel(q->num, q->den) < 0);
|
|
if (neg) {
|
|
qtmp = qinv(q);
|
|
qfree(q);
|
|
q = qtmp;
|
|
}
|
|
k = qilog2(q);
|
|
m = -qilog2(epsilon); /* m will be number of working bits */
|
|
if (m < 0)
|
|
m = 0;
|
|
h = k;
|
|
while (h > 0) {
|
|
h /= 2;
|
|
m++; /* Add 1 for each sqrt until X < 2 */
|
|
}
|
|
m += 18; /* 8 more sqrts, 8 for rounding, 2 for epsilon/4 */
|
|
qtmp = qscale(q, m - k);
|
|
zquo(qtmp->num, qtmp->den, &X, conf->triground);
|
|
qfree(q);
|
|
qfree(qtmp);
|
|
|
|
zbitvalue(m, &D); /* Now "q" = X/D */
|
|
zbitvalue(m - 8, &ztmp);
|
|
zadd(D, ztmp, &B); /* Will take sqrts until X <= B */
|
|
zfree(ztmp);
|
|
|
|
n = 1; /* n is to count 1 + number of sqrts */
|
|
|
|
while (k > 0 || zrel(X, B) > 0) {
|
|
n++;
|
|
zshift(X, m + (k & 1), &ztmp);
|
|
zfree(X);
|
|
zsqrt(ztmp, &X, conf->triground);
|
|
zfree(ztmp)
|
|
k /= 2;
|
|
}
|
|
zfree(B);
|
|
zsub(X, D, &pow); /* pow, mul used as tmps */
|
|
zadd(X, D, &mul);
|
|
zfree(X);
|
|
zfree(D);
|
|
zshift(pow, m, &ztmp);
|
|
zfree(pow);
|
|
zquo(ztmp, mul, &pow, conf->triground); /* pow now (X - D)/(X + D) */
|
|
zfree(ztmp);
|
|
zfree(mul);
|
|
|
|
zcopy(pow, &sum); /* pow is first term of sum */
|
|
zsquare(pow, &ztmp);
|
|
zshift(ztmp, -m, &mul); /* mul is now multiplier for powers */
|
|
zfree(ztmp);
|
|
|
|
d = 1;
|
|
for (;;) {
|
|
zmul(pow, mul, &ztmp);
|
|
zfree(pow);
|
|
zshift(ztmp, -m, &pow);
|
|
zfree(ztmp);
|
|
d += 2;
|
|
zdivi(pow, d, &term); /* Round down div should be round off */
|
|
if (ziszero(term)) {
|
|
zfree(term);
|
|
break;
|
|
}
|
|
zadd(sum, term, &ztmp);
|
|
zfree(term);
|
|
zfree(sum);
|
|
sum = ztmp;
|
|
}
|
|
zfree(pow);
|
|
zfree(mul);
|
|
qtmp = qalloc(); /* qtmp is to be 2^n * sum / 2^m */
|
|
k = zlowbit(sum);
|
|
sum.sign = neg;
|
|
if (k + n >= m) {
|
|
zshift(sum, n - m, &qtmp->num);
|
|
zfree(sum);
|
|
} else {
|
|
if (k) {
|
|
zshift(sum, -k, &qtmp->num);
|
|
zfree(sum);
|
|
} else {
|
|
qtmp->num = sum;
|
|
}
|
|
zbitvalue(m - k - n, &qtmp->den);
|
|
}
|
|
res = qmappr(qtmp, epsilon, conf->triground);
|
|
qfree(qtmp);
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* Calculate the base 10 logarithm
|
|
*
|
|
* log(q) = ln(q) / ln(10)
|
|
*/
|
|
NUMBER *
|
|
qlog(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
int need_new_ln_10 = true; /* false => use cached ln_10 value */
|
|
NUMBER *ln_q; /* ln(x) */
|
|
NUMBER *ret; /* base 10 logarithm of x */
|
|
|
|
/* firewall */
|
|
if (qiszero(q)) {
|
|
math_error("log of 0");
|
|
not_reached();
|
|
}
|
|
if (qiszero(epsilon)) {
|
|
math_error("Zero epsilon value for log");
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* shortcut for small integer powers of 10
|
|
*/
|
|
if (qisint(q) && qispos(q) && !zge8192b(q->num) && ziseven(q->num)) {
|
|
bool is_10_power; /* true ==> q is a power of 10 */
|
|
long ilog_10; /* integer log base 10 */
|
|
|
|
/* try for a quick small power of 10 log */
|
|
ilog_10 = zlog10(q->num, &is_10_power );
|
|
if (is_10_power == true) {
|
|
/* is small power of 10, return log */
|
|
return itoq(ilog_10);
|
|
}
|
|
/* q is an even integer that is not a power of 10 */
|
|
}
|
|
|
|
/*
|
|
* compute ln(c) first
|
|
*/
|
|
ln_q = qln(q, epsilon);
|
|
/* quick return for log(1) == 0 */
|
|
if (qiszero(ln_q)) {
|
|
return ln_q;
|
|
}
|
|
|
|
/*
|
|
* save epsilon for ln(10) if needed
|
|
*/
|
|
if (ln_10_epsilon == NULL) {
|
|
/* first time call */
|
|
ln_10_epsilon = qcopy(epsilon);
|
|
} else if (qcmp(ln_10_epsilon, epsilon) == true) {
|
|
/* replaced cached value with epsilon arg */
|
|
qfree(ln_10_epsilon);
|
|
ln_10_epsilon = qcopy(epsilon);
|
|
} else if (ln_10 != NULL) {
|
|
/* the previously computed ln(2) is OK to use */
|
|
need_new_ln_10 = false;
|
|
}
|
|
|
|
/*
|
|
* compute ln(10) if needed
|
|
*/
|
|
if (need_new_ln_10 == true) {
|
|
if (ln_10 != NULL) {
|
|
qfree(ln_10);
|
|
}
|
|
ln_10 = qln(&_qten_, ln_10_epsilon);
|
|
}
|
|
|
|
/*
|
|
* return ln(q) / ln(10)
|
|
*/
|
|
ret = qqdiv(ln_q, ln_10);
|
|
qfree(ln_q);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* Calculate the base 2 logarithm
|
|
*
|
|
* log(q) = ln(q) / ln(2)
|
|
*/
|
|
NUMBER *
|
|
qlog2(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
int need_new_ln_2 = true; /* false => use cached ln_2 value */
|
|
NUMBER *ln_q; /* ln(x) */
|
|
NUMBER *ret; /* base 2 logarithm of x */
|
|
|
|
/* firewall */
|
|
if (qiszero(q)) {
|
|
math_error("log2 of 0");
|
|
not_reached();
|
|
}
|
|
if (qiszero(epsilon)) {
|
|
math_error("Zero epsilon value for log2");
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* special case: q is integer power of 2
|
|
*/
|
|
ret = qalloc();
|
|
if (qispowerof2(q, &ret)) {
|
|
return ret;
|
|
}
|
|
qfree(ret);
|
|
|
|
/*
|
|
* compute ln(c) first
|
|
*/
|
|
ln_q = qln(q, epsilon);
|
|
/* quick return for ln(1) == 0 */
|
|
if (qiszero(ln_q)) {
|
|
return ln_q;
|
|
}
|
|
|
|
/*
|
|
* save epsilon for ln(2) if needed
|
|
*/
|
|
if (ln_2_epsilon == NULL) {
|
|
/* first time call */
|
|
ln_2_epsilon = qcopy(epsilon);
|
|
} else if (qcmp(ln_2_epsilon, epsilon) == true) {
|
|
/* replaced cached value with epsilon arg */
|
|
qfree(ln_2_epsilon);
|
|
ln_2_epsilon = qcopy(epsilon);
|
|
} else if (ln_2 != NULL) {
|
|
/* the previously computed ln(2) is OK to use */
|
|
need_new_ln_2 = false;
|
|
}
|
|
|
|
/*
|
|
* compute ln(2) if needed
|
|
*/
|
|
if (need_new_ln_2 == true) {
|
|
if (ln_2 != NULL) {
|
|
qfree(ln_2);
|
|
}
|
|
ln_2 = qln(&_qtwo_, ln_2_epsilon);
|
|
}
|
|
|
|
/*
|
|
* return ln(q) / ln(2)
|
|
*/
|
|
ret = qqdiv(ln_q, ln_2);
|
|
qfree(ln_q);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* Calculate the base n logarithm
|
|
*
|
|
* logn(q, n) = ln(q) / ln(n)
|
|
*/
|
|
NUMBER *
|
|
qlogn(NUMBER *q, NUMBER *n, NUMBER *epsilon)
|
|
{
|
|
int need_new_ln_n = true; /* false => use cached ln_n value */
|
|
NUMBER *ln_q; /* ln(x) */
|
|
NUMBER *ret; /* base 2 logarithm of x */
|
|
|
|
/* firewall */
|
|
if (qiszero(q)) {
|
|
math_error("log base n of 0");
|
|
not_reached();
|
|
}
|
|
if (qiszero(epsilon)) {
|
|
math_error("Zero epsilon value for logn");
|
|
not_reached();
|
|
}
|
|
if (qiszero(n)) {
|
|
math_error("invalid logarithm base of 0 for logn");
|
|
not_reached();
|
|
}
|
|
if (qisone(n)) {
|
|
math_error("invalid logarithm base of 1 for logn");
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* special case: q is integer power of 2
|
|
*/
|
|
ret = qalloc();
|
|
if (qispowerof2(q, &ret)) {
|
|
return ret;
|
|
}
|
|
/* XXX - deal with n is integer power of 2 - XXX */
|
|
qfree(ret);
|
|
|
|
/*
|
|
* compute ln(q) first
|
|
*/
|
|
ln_q = qln(q, epsilon);
|
|
/* quick return for ln(1) == 0 */
|
|
if (qiszero(ln_q)) {
|
|
return ln_q;
|
|
}
|
|
|
|
/*
|
|
* save epsilon for ln(n) if needed
|
|
*/
|
|
if (ln_n_epsilon == NULL) {
|
|
/* first time call */
|
|
ln_n_epsilon = qcopy(epsilon);
|
|
} else if (qcmp(ln_n_epsilon, epsilon) == true) {
|
|
/* replaced cached value with epsilon arg */
|
|
qfree(ln_n_epsilon);
|
|
ln_n_epsilon = qcopy(epsilon);
|
|
} else if (ln_n != NULL) {
|
|
/* the previously computed ln(2) is OK to use */
|
|
need_new_ln_n = false;
|
|
}
|
|
|
|
/*
|
|
* compute ln(n) if needed
|
|
*/
|
|
if (need_new_ln_n == true) {
|
|
if (ln_n != NULL) {
|
|
qfree(ln_n);
|
|
}
|
|
ln_n = qln(&_qtwo_, ln_n_epsilon);
|
|
}
|
|
|
|
/*
|
|
* return ln(q) / ln(2)
|
|
*/
|
|
ret = qqdiv(ln_q, ln_n);
|
|
qfree(ln_q);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* Calculate the result of raising one number to the power of another.
|
|
* The result is calculated to the nearest or next to nearest multiple of
|
|
* epsilon.
|
|
*/
|
|
NUMBER *
|
|
qpower(NUMBER *q1, NUMBER *q2, NUMBER *epsilon)
|
|
{
|
|
NUMBER *tmp1, *tmp2, *epsilon2;
|
|
NUMBER *q1tmp, *q2tmp;
|
|
long m, n;
|
|
|
|
if (qiszero(epsilon)) {
|
|
math_error("Zero epsilon for power");
|
|
not_reached();
|
|
}
|
|
if (qiszero(q1) && qisneg(q2)) {
|
|
math_error("Negative power of zero");
|
|
not_reached();
|
|
}
|
|
if (qiszero(q2) || qisone(q1))
|
|
return qlink(&_qone_);
|
|
if (qiszero(q1))
|
|
return qlink(&_qzero_);
|
|
if (qisneg(q1)) {
|
|
math_error("Negative base for qpower");
|
|
not_reached();
|
|
}
|
|
if (qisone(q2))
|
|
return qmappr(q1, epsilon, conf->triground);
|
|
if (zrel(q1->num, q1->den) < 0) {
|
|
q1tmp = qinv(q1);
|
|
q2tmp = qneg(q2);
|
|
} else {
|
|
q1tmp = qlink(q1);
|
|
q2tmp = qlink(q2);
|
|
}
|
|
if (qisone(q2tmp)) {
|
|
qfree(q2tmp);
|
|
q2tmp = qmappr(q1tmp, epsilon, conf->triground);
|
|
qfree(q1tmp);
|
|
return q2tmp;
|
|
}
|
|
m = qilog2(q1tmp);
|
|
n = qilog2(epsilon);
|
|
if (qisneg(q2tmp)) {
|
|
if (m > 0) {
|
|
tmp1 = itoq(m);
|
|
tmp2 = qmul(tmp1, q2tmp);
|
|
m = qtoi(tmp2);
|
|
} else {
|
|
tmp1 = qdec(q1tmp);
|
|
tmp2 = qqdiv(tmp1, q1tmp);
|
|
qfree(tmp1);
|
|
tmp1 = qmul(tmp2, q2tmp);
|
|
qfree(tmp2);
|
|
tmp2 = qmul(tmp1, &_qlge_);
|
|
m = qtoi(tmp2);
|
|
}
|
|
} else {
|
|
if (m > 0) {
|
|
tmp1 = itoq(m + 1);
|
|
tmp2 = qmul(tmp1, q2tmp);
|
|
m = qtoi(tmp2);
|
|
} else {
|
|
tmp1 = qdec(q1tmp);
|
|
tmp2 = qmul(tmp1, q2tmp);
|
|
qfree(tmp1);
|
|
tmp1 = qmul(tmp2, &_qlge_);
|
|
m = qtoi(tmp1);
|
|
}
|
|
}
|
|
qfree(tmp1);
|
|
qfree(tmp2);
|
|
if (m > (1 << 30)) {
|
|
qfree(q1tmp);
|
|
qfree(q2tmp);
|
|
return NULL;
|
|
}
|
|
m += 1;
|
|
if (m < n) {
|
|
qfree(q1tmp);
|
|
qfree(q2tmp);
|
|
return qlink(&_qzero_);
|
|
}
|
|
tmp1 = qqdiv(epsilon, q2tmp);
|
|
tmp2 = qscale(tmp1, -m - 4);
|
|
epsilon2 = qqabs(tmp2);
|
|
qfree(tmp1);
|
|
qfree(tmp2);
|
|
tmp1 = qln(q1tmp, epsilon2);
|
|
qfree(epsilon2);
|
|
tmp2 = qmul(tmp1, q2tmp);
|
|
qfree(tmp1);
|
|
qfree(q1tmp);
|
|
qfree(q2tmp);
|
|
if (qisneg(tmp2)) {
|
|
tmp1 = qneg(tmp2);
|
|
qfree(tmp2);
|
|
tmp2 = qexprel(tmp1, m - n + 3);
|
|
if (tmp2 == NULL) {
|
|
qfree(tmp1);
|
|
return NULL;
|
|
}
|
|
qfree(tmp1);
|
|
tmp1 = qinv(tmp2);
|
|
} else {
|
|
tmp1 = qexprel(tmp2, m - n + 3) ;
|
|
if (tmp1 == NULL) {
|
|
qfree(tmp2);
|
|
return NULL;
|
|
}
|
|
}
|
|
qfree(tmp2);
|
|
tmp2 = qmappr(tmp1, epsilon, conf->triground);
|
|
qfree(tmp1);
|
|
return tmp2;
|
|
}
|
|
|
|
|
|
/*
|
|
* Calculate the K-th root of a number to within the specified accuracy.
|
|
*/
|
|
NUMBER *
|
|
qroot(NUMBER *q1, NUMBER *q2, NUMBER *epsilon)
|
|
{
|
|
NUMBER *tmp1, *tmp2;
|
|
int neg;
|
|
|
|
if (qiszero(epsilon)) {
|
|
math_error("Zero epsilon for root");
|
|
not_reached();
|
|
}
|
|
if (qisneg(q2) || qiszero(q2) || qisfrac(q2)) {
|
|
math_error("Taking bad root of number");
|
|
not_reached();
|
|
}
|
|
if (qiszero(q1) || qisone(q1) || qisone(q2))
|
|
return qlink(q1);
|
|
if (qistwo(q2))
|
|
return qsqrt(q1, epsilon, conf->triground);
|
|
neg = qisneg(q1);
|
|
if (neg) {
|
|
if (ziseven(q2->num)) {
|
|
math_error("Taking even root of negative number");
|
|
not_reached();
|
|
}
|
|
q1 = qqabs(q1);
|
|
}
|
|
tmp2 = qinv(q2);
|
|
tmp1 = qpower(q1, tmp2, epsilon);
|
|
qfree(tmp2);
|
|
if (tmp1 == NULL)
|
|
return NULL;
|
|
if (neg) {
|
|
tmp2 = qneg(tmp1);
|
|
qfree(tmp1);
|
|
tmp1 = tmp2;
|
|
}
|
|
return tmp1;
|
|
}
|
|
|
|
|
|
/* Calculate the hyperbolic cosine function to the nearest or next to
|
|
* nearest multiple of epsilon.
|
|
* This is calculated using cosh(x) = (exp(x) + 1/exp(x))/2;
|
|
*/
|
|
NUMBER *
|
|
qcosh(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *tmp1, *tmp2, *tmp3, *epsilon1;
|
|
|
|
epsilon1 = qscale(epsilon, -2);
|
|
tmp1 = qqabs(q);
|
|
tmp2 = qexp(tmp1, epsilon1);
|
|
qfree(tmp1);
|
|
qfree(epsilon1);
|
|
if (tmp2 == NULL)
|
|
return NULL;
|
|
tmp1 = qinv(tmp2);
|
|
tmp3 = qqadd(tmp1, tmp2);
|
|
qfree(tmp1)
|
|
qfree(tmp2)
|
|
tmp1 = qscale(tmp3, -1);
|
|
qfree(tmp3);
|
|
tmp2 = qmappr(tmp1, epsilon, conf->triground);
|
|
qfree(tmp1);
|
|
return tmp2;
|
|
}
|
|
|
|
|
|
/*
|
|
* Calculate the hyperbolic sine to the nearest or next to nearest
|
|
* multiple of epsilon.
|
|
* This is calculated using sinh(x) = (exp(x) - 1/exp(x))/2.
|
|
*/
|
|
NUMBER *
|
|
qsinh(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *tmp1, *tmp2, *tmp3, *epsilon1;
|
|
|
|
if (qiszero(q))
|
|
return qlink(&_qzero_);
|
|
epsilon1 = qscale(epsilon, -3);
|
|
tmp1 = qqabs(q);
|
|
tmp2 = qexp(tmp1, epsilon1);
|
|
qfree(tmp1);
|
|
qfree(epsilon1);
|
|
if (tmp2 == NULL)
|
|
return NULL;
|
|
tmp1 = qinv(tmp2);
|
|
tmp3 = qispos(q) ? qsub(tmp2, tmp1) : qsub(tmp1, tmp2);
|
|
qfree(tmp1)
|
|
qfree(tmp2)
|
|
tmp1 = qscale(tmp3, -1);
|
|
qfree(tmp3);
|
|
tmp2 = qmappr(tmp1, epsilon, conf->triground);
|
|
qfree(tmp1);
|
|
return tmp2;
|
|
}
|
|
|
|
|
|
/*
|
|
* Calculate the hyperbolic tangent to the nearest or next to nearest
|
|
* multiple of epsilon.
|
|
* This is calculated using the formulae:
|
|
* tanh(x) = 1 or -1 for very large abs(x)
|
|
* tanh(x) = (+ or -)(1 - 2 * exp(2 * x)) for large abx(x)
|
|
* tanh(x) = (exp(2*x) - 1)/(exp(2*x) + 1) otherwise
|
|
*/
|
|
NUMBER *
|
|
qtanh(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *tmp1, *tmp2, *tmp3;
|
|
long n, m;
|
|
|
|
n = qilog2(epsilon);
|
|
if (n > 0 || qiszero(q))
|
|
return qlink(&_qzero_);
|
|
n = -n;
|
|
tmp1 = qqabs(q);
|
|
tmp2 = qmul(tmp1, &_qlge_);
|
|
m = qtoi(tmp2); /* exp(|q|) < 2^(m+1) or m == MAXLONG */
|
|
qfree(tmp2);
|
|
if (m > 1 + n/2) {
|
|
qfree(tmp1);
|
|
return q->num.sign ? qlink(&_qnegone_) : qlink(&_qone_);
|
|
}
|
|
tmp2 = qscale(tmp1, 1);
|
|
qfree(tmp1);
|
|
tmp1 = qexprel(tmp2, 2 + n);
|
|
qfree(tmp2);
|
|
if (m > 1 + n/4) {
|
|
tmp2 = qqdiv(&_qtwo_, tmp1);
|
|
qfree(tmp1);
|
|
tmp1 = qsub(&_qone_, tmp2);
|
|
qfree(tmp2);
|
|
} else {
|
|
tmp2 = qdec(tmp1);
|
|
tmp3 = qinc(tmp1);
|
|
qfree(tmp1);
|
|
tmp1 = qqdiv(tmp2, tmp3);
|
|
qfree(tmp2);
|
|
qfree(tmp3);
|
|
}
|
|
tmp2 = qmappr(tmp1, epsilon, conf->triground);
|
|
qfree(tmp1);
|
|
if (qisneg(q)) {
|
|
tmp1 = qneg(tmp2);
|
|
qfree(tmp2);
|
|
return tmp1;
|
|
}
|
|
return tmp2;
|
|
}
|
|
|
|
|
|
/*
|
|
* Hyperbolic cotangent.
|
|
* Calculated using coth(x) = 1 + 2/(exp(2*x) - 1)
|
|
*/
|
|
NUMBER *
|
|
qcoth(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *tmp1, *tmp2, *res;
|
|
long n, k;
|
|
|
|
if (qiszero(epsilon)) {
|
|
math_error("Zero epsilon value for coth");
|
|
not_reached();
|
|
}
|
|
if (qiszero(q)) {
|
|
math_error("Zero argument for coth");
|
|
not_reached();
|
|
}
|
|
tmp1 = qscale(q, 1);
|
|
tmp2 = qqabs(tmp1);
|
|
qfree(tmp1);
|
|
k = qilog2(tmp2);
|
|
n = qilog2(epsilon);
|
|
if (k > 0) {
|
|
tmp1 = qmul(&_qlge_, tmp2);
|
|
k = qtoi(tmp1);
|
|
qfree(tmp1);
|
|
} else {
|
|
k = 2 * k;
|
|
}
|
|
k = 4 - k - n;
|
|
if (k < 4)
|
|
k = 4;
|
|
tmp1 = qexprel(tmp2, k);
|
|
qfree(tmp2);
|
|
tmp2 = qdec(tmp1);
|
|
qfree(tmp1);
|
|
if (qiszero(tmp2)) {
|
|
math_error("This should not happen ????");
|
|
not_reached();
|
|
}
|
|
tmp1 = qinv(tmp2);
|
|
qfree(tmp2);
|
|
tmp2 = qscale(tmp1, 1);
|
|
qfree(tmp1);
|
|
tmp1 = qinc(tmp2);
|
|
qfree(tmp2);
|
|
if (qisneg(q)) {
|
|
tmp2 = qneg(tmp1);
|
|
qfree(tmp1);
|
|
tmp1 = tmp2;
|
|
}
|
|
res = qmappr(tmp1, epsilon, conf->triground);
|
|
qfree(tmp1);
|
|
return res;
|
|
}
|
|
|
|
|
|
NUMBER *
|
|
qsech(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *tmp1, *tmp2, *tmp3, *res;
|
|
long n, k;
|
|
|
|
if (qiszero(epsilon)) {
|
|
math_error("Zero epsilon value for sech");
|
|
not_reached();
|
|
}
|
|
if (qiszero(q))
|
|
return qlink(&_qone_);
|
|
|
|
tmp1 = qqabs(q);
|
|
k = 0;
|
|
if (zrel(tmp1->num, tmp1->den) >= 0) {
|
|
tmp2 = qmul(&_qlge_, tmp1);
|
|
k = qtoi(tmp2);
|
|
qfree(tmp2);
|
|
}
|
|
n = qilog2(epsilon);
|
|
if (k + n > 1) {
|
|
qfree(tmp1);
|
|
return qlink(&_qzero_);
|
|
}
|
|
tmp2 = qexprel(tmp1, 4 - k - n);
|
|
qfree(tmp1);
|
|
tmp1 = qinv(tmp2);
|
|
tmp3 = qqadd(tmp1, tmp2);
|
|
qfree(tmp1);
|
|
qfree(tmp2);
|
|
tmp1 = qinv(tmp3);
|
|
qfree(tmp3);
|
|
tmp2 = qscale(tmp1, 1);
|
|
qfree(tmp1);
|
|
res = qmappr(tmp2, epsilon, conf->triground);
|
|
qfree(tmp2);
|
|
return res;
|
|
}
|
|
|
|
|
|
NUMBER *
|
|
qcsch(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *tmp1, *tmp2, *tmp3, *res;
|
|
long n, k;
|
|
|
|
if (qiszero(epsilon)) {
|
|
math_error("Zero epsilon value for csch");
|
|
not_reached();
|
|
}
|
|
if (qiszero(q)) {
|
|
math_error("Zero argument for csch");
|
|
not_reached();
|
|
}
|
|
|
|
n = qilog2(epsilon);
|
|
tmp1 = qqabs(q);
|
|
if (zrel(tmp1->num, tmp1->den) >= 0) {
|
|
tmp2 = qmul(&_qlge_, tmp1);
|
|
k = qtoi(tmp2);
|
|
qfree(tmp2);
|
|
} else {
|
|
k = 2 * qilog2(tmp1);
|
|
}
|
|
if (k + n >= 1) {
|
|
qfree(tmp1);
|
|
return qlink(&_qzero_);
|
|
}
|
|
tmp2 = qexprel(tmp1, 4 - k - n);
|
|
qfree(tmp1);
|
|
tmp1 = qinv(tmp2);
|
|
if (qisneg(q))
|
|
tmp3 = qsub(tmp1, tmp2);
|
|
else
|
|
tmp3 = qsub(tmp2, tmp1);
|
|
qfree(tmp1);
|
|
qfree(tmp2);
|
|
tmp1 = qinv(tmp3);
|
|
qfree(tmp3)
|
|
tmp2 = qscale(tmp1, 1);
|
|
qfree(tmp1);
|
|
res = qmappr(tmp2, epsilon, conf->triground);
|
|
qfree(tmp2);
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* Compute the hyperbolic arccosine within the specified accuracy.
|
|
* This is calculated using the formula:
|
|
* acosh(x) = ln(x + sqrt(x^2 - 1)).
|
|
*/
|
|
NUMBER *
|
|
qacosh(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *tmp1, *tmp2, *epsilon1;
|
|
long n;
|
|
|
|
if (qiszero(epsilon)) {
|
|
math_error("Zero epsilon value for acosh");
|
|
not_reached();
|
|
}
|
|
if (qisone(q))
|
|
return qlink(&_qzero_);
|
|
if (zrel(q->num, q->den) < 0)
|
|
return NULL;
|
|
n = qilog2(epsilon);
|
|
epsilon1 = qbitvalue(n - 3);
|
|
tmp1 = qsquare(q);
|
|
tmp2 = qdec(tmp1);
|
|
qfree(tmp1);
|
|
tmp1 = qsqrt(tmp2, epsilon1, conf->triground);
|
|
qfree(tmp2);
|
|
tmp2 = qqadd(tmp1, q);
|
|
qfree(tmp1);
|
|
tmp1 = qln(tmp2, epsilon1);
|
|
qfree(tmp2);
|
|
qfree(epsilon1);
|
|
tmp2 = qmappr(tmp1, epsilon, conf->triground);
|
|
qfree(tmp1);
|
|
return tmp2;
|
|
}
|
|
|
|
|
|
/*
|
|
* Compute the hyperbolic arcsine within the specified accuracy.
|
|
* This is calculated using the formula:
|
|
* asinh(x) = ln(x + sqrt(x^2 + 1)).
|
|
*/
|
|
NUMBER *
|
|
qasinh(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *tmp1, *tmp2, *epsilon1;
|
|
long n;
|
|
bool neg;
|
|
|
|
if (qiszero(epsilon)) {
|
|
math_error("Zero epsilon value for asinh");
|
|
not_reached();
|
|
}
|
|
if (qiszero(q))
|
|
return qlink(&_qzero_);
|
|
neg = qisneg(q);
|
|
q = qqabs(q);
|
|
n = qilog2(epsilon);
|
|
epsilon1 = qbitvalue(n - 3);
|
|
tmp1 = qsquare(q);
|
|
tmp2 = qinc(tmp1);
|
|
qfree(tmp1);
|
|
tmp1 = qsqrt(tmp2, epsilon1, conf->triground);
|
|
qfree(tmp2);
|
|
tmp2 = qqadd(tmp1, q);
|
|
qfree(tmp1);
|
|
tmp1 = qln(tmp2, epsilon1);
|
|
qfree(tmp2);
|
|
qfree(q);
|
|
qfree(epsilon1);
|
|
tmp2 = qmappr(tmp1, epsilon, conf->triground);
|
|
if (neg) {
|
|
tmp1 = qneg(tmp2);
|
|
qfree(tmp2);
|
|
return tmp1;
|
|
}
|
|
return tmp2;
|
|
}
|
|
|
|
|
|
/*
|
|
* Compute the hyperbolic arctangent within the specified accuracy.
|
|
* This is calculated using the formula:
|
|
* atanh(x) = ln((1 + x) / (1 - x)) / 2.
|
|
*/
|
|
NUMBER *
|
|
qatanh(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *tmp1, *tmp2, *tmp3, *epsilon1;
|
|
ZVALUE z;
|
|
|
|
if (qiszero(epsilon)) {
|
|
math_error("Zero epsilon value for atanh");
|
|
not_reached();
|
|
}
|
|
if (qiszero(q))
|
|
return qlink(&_qzero_);
|
|
z = q->num;
|
|
z.sign = 0;
|
|
if (zrel(z, q->den) >= 0)
|
|
return NULL;
|
|
tmp1 = qinc(q);
|
|
tmp2 = qsub(&_qone_, q);
|
|
tmp3 = qqdiv(tmp1, tmp2);
|
|
qfree(tmp1);
|
|
qfree(tmp2);
|
|
epsilon1 = qscale(epsilon, 1L);
|
|
tmp1 = qln(tmp3, epsilon1);
|
|
qfree(tmp3);
|
|
tmp2 = qscale(tmp1, -1L);
|
|
qfree(tmp1);
|
|
qfree(epsilon1);
|
|
return tmp2;
|
|
}
|
|
|
|
|
|
/*
|
|
* Inverse hyperbolic secant function
|
|
*/
|
|
NUMBER *
|
|
qasech(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *tmp, *res;
|
|
|
|
tmp = qinv(q);
|
|
res = qacosh(tmp, epsilon);
|
|
qfree(tmp);
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* Inverse hyperbolic cosecant function
|
|
*/
|
|
NUMBER *
|
|
qacsch(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *tmp, *res;
|
|
|
|
tmp = qinv(q);
|
|
res = qasinh(tmp, epsilon);
|
|
qfree(tmp);
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* Inverse hyperbolic cotangent function
|
|
*/
|
|
NUMBER *
|
|
qacoth(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *tmp, *res;
|
|
|
|
tmp = qinv(q);
|
|
res = qatanh(tmp, epsilon);
|
|
qfree(tmp);
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* qversin - versed trigonometric sine
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* versin(x) = 1 - cos(x)
|
|
*
|
|
* given:
|
|
* q real value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* real value result of trig function on q with error epsilon
|
|
*/
|
|
NUMBER *
|
|
qversin(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *res; /* inverse trig value result */
|
|
NUMBER *qtmp; /* argument to inverse trig function */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (q == NULL) {
|
|
math_error("q is NULL for %s", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate trig function value
|
|
*/
|
|
qtmp = qcos(q, epsilon);
|
|
res = qsub(&_qone_, qtmp);
|
|
qfree(qtmp);
|
|
|
|
/*
|
|
* return trigonometric result
|
|
*/
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* qaversin_or_NULL - return NULL or non-complex inverse versed trigonometric sine
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* aversin(x) = acos(1 - x)
|
|
*
|
|
* given:
|
|
* q real value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* != NULL ==> real value result of trig function on q with error epsilon,
|
|
* NULL ==> trig function value cannot be expressed as a NUMBER
|
|
*
|
|
* NOTE: If this function returns NULL, consider calling the equivalent
|
|
* COMPLEX function from comfunc.c. See the help file for the
|
|
* related builtin for details.
|
|
*/
|
|
NUMBER *
|
|
qaversin_or_NULL(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *res; /* inverse trig value result */
|
|
NUMBER *qtmp; /* argument to inverse trig function */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (q == NULL) {
|
|
math_error("q is NULL for %s", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate inverse trig function value
|
|
*/
|
|
qtmp = qsub(&_qone_, q);
|
|
res = qacos(qtmp, epsilon);
|
|
qfree(qtmp);
|
|
if (res == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* return inverse trigonometric result
|
|
*/
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* qaversin - non-complex inverse versed trigonometric sine
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* aversin(x) = acos(1 - x)
|
|
*
|
|
* given:
|
|
* q real value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* real value result of trig function on q with error epsilon
|
|
*/
|
|
NUMBER *
|
|
qaversin(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *res; /* inverse trig value result */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (q == NULL) {
|
|
math_error("q is NULL for %s", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate inverse trig function value
|
|
*/
|
|
res = qaversin_or_NULL(q, epsilon);
|
|
if (res == NULL) {
|
|
math_error("cannot compute inverse cosine for aversin");
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* return inverse trigonometric result
|
|
*/
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* qcoversin - coversed trigonometric sine
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* coversin((x) = 1 - sin(x)
|
|
*
|
|
* given:
|
|
* q real value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* real value result of trig function on q with error epsilon
|
|
*/
|
|
NUMBER *
|
|
qcoversin(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *res; /* inverse trig value result */
|
|
NUMBER *qtmp; /* argument to inverse trig function */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (q == NULL) {
|
|
math_error("q is NULL for %s", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate trig function value
|
|
*/
|
|
qtmp = qsin(q, epsilon);
|
|
res = qsub(&_qone_, qtmp);
|
|
qfree(qtmp);
|
|
|
|
/*
|
|
* return trigonometric result
|
|
*/
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* qacoversin_or_NULL - return NULL or non-complex inverse coversed trigonometric sine
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* acoversin(x) = asin(1 - x)
|
|
*
|
|
* given:
|
|
* q real value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* real value result of trig function on q with error epsilon
|
|
*
|
|
* returns:
|
|
* != NULL ==> real value result of trig function on q with error epsilon,
|
|
* NULL ==> trig function value cannot be expressed as a NUMBER
|
|
*
|
|
* NOTE: If this function returns NULL, consider calling the equivalent
|
|
* COMPLEX function from comfunc.c. See the help file for the
|
|
* related builtin for details.
|
|
*/
|
|
NUMBER *
|
|
qacoversin_or_NULL(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *res; /* inverse trig value result */
|
|
NUMBER *qtmp; /* argument to inverse trig function */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (q == NULL) {
|
|
math_error("q is NULL for %s", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate inverse trig function value
|
|
*/
|
|
qtmp = qsub(&_qone_, q);
|
|
res = qasin(qtmp, epsilon);
|
|
qfree(qtmp);
|
|
if (res == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* return inverse trigonometric result
|
|
*/
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* qacoversin - non-complex inverse coversed trigonometric sine
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* acoversin(x) = asin(1 - x)
|
|
*
|
|
* given:
|
|
* q real value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* real value result of trig function on q with error epsilon
|
|
*/
|
|
NUMBER *
|
|
qacoversin(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *res; /* inverse trig value result */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (q == NULL) {
|
|
math_error("q is NULL for %s", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate inverse trig function value
|
|
*/
|
|
res = qacoversin_or_NULL(q, epsilon);
|
|
if (res == NULL) {
|
|
math_error("cannot compute inverse sine for acoversin");
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* return inverse trigonometric result
|
|
*/
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* qvercos - versed trigonometric cosine
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* vercos(x) = 1 + cos(x)
|
|
*
|
|
* given:
|
|
* q real value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* real value result of trig function on q with error epsilon
|
|
*/
|
|
NUMBER *
|
|
qvercos(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *res; /* inverse trig value result */
|
|
NUMBER *qtmp; /* argument to inverse trig function */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (q == NULL) {
|
|
math_error("q is NULL for %s", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate trig function value
|
|
*/
|
|
qtmp = qcos(q, epsilon);
|
|
res = qqadd(&_qone_, qtmp);
|
|
qfree(qtmp);
|
|
|
|
/*
|
|
* return trigonometric result
|
|
*/
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* qavercos_or_NULL - return NULL or non-complex inverse versed trigonometric cosine
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* avercos(x) = acos(x - 1)
|
|
*
|
|
* given:
|
|
* q real value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* != NULL ==> real value result of trig function on q with error epsilon,
|
|
* NULL ==> trig function value cannot be expressed as a NUMBER
|
|
*
|
|
* NOTE: If this function returns NULL, consider calling the equivalent
|
|
* COMPLEX function from comfunc.c. See the help file for the
|
|
* related builtin for details.
|
|
*/
|
|
NUMBER *
|
|
qavercos_or_NULL(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *res; /* inverse trig value result */
|
|
NUMBER *qtmp; /* argument to inverse trig function */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (q == NULL) {
|
|
math_error("q is NULL for %s", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate inverse trig function value
|
|
*/
|
|
qtmp = qsub(q, &_qone_);
|
|
res = qacos(qtmp, epsilon);
|
|
qfree(qtmp);
|
|
if (res == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* return inverse trigonometric result
|
|
*/
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* qavercos - non-complex inverse versed trigonometric cosine
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* avercos(x) = acos(x - 1)
|
|
*
|
|
* given:
|
|
* q real value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* real value result of trig function on q with error epsilon
|
|
*/
|
|
NUMBER *
|
|
qavercos(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *res; /* inverse trig value result */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (q == NULL) {
|
|
math_error("q is NULL for %s", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate inverse trig function value
|
|
*/
|
|
res = qavercos_or_NULL(q, epsilon);
|
|
if (res == NULL) {
|
|
math_error("cannot compute inverse cosine for avercos");
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* return inverse trigonometric result
|
|
*/
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* qcovercos - coversed trigonometric cosine
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* covercos((x) = 1 + sin(x)
|
|
*
|
|
* given:
|
|
* q real value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* real value result of trig function on q with error epsilon
|
|
*/
|
|
NUMBER *
|
|
qcovercos(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *res; /* inverse trig value result */
|
|
NUMBER *qtmp; /* argument to inverse trig function */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (q == NULL) {
|
|
math_error("q is NULL for %s", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate trig function value
|
|
*/
|
|
qtmp = qsin(q, epsilon);
|
|
res = qqadd(&_qone_, qtmp);
|
|
qfree(qtmp);
|
|
|
|
/*
|
|
* return trigonometric result
|
|
*/
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* qacovercos_or_NULL - return NULL or non-complex inverse coversed trigonometric cosine
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* acovercos(x) = asin(x - 1)
|
|
*
|
|
* given:
|
|
* q real value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* real value result of trig function on q with error epsilon
|
|
*
|
|
* returns:
|
|
* != NULL ==> real value result of trig function on q with error epsilon,
|
|
* NULL ==> trig function value cannot be expressed as a NUMBER
|
|
*
|
|
* NOTE: If this function returns NULL, consider calling the equivalent
|
|
* COMPLEX function from comfunc.c. See the help file for the
|
|
* related builtin for details.
|
|
*/
|
|
NUMBER *
|
|
qacovercos_or_NULL(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *res; /* inverse trig value result */
|
|
NUMBER *qtmp; /* argument to inverse trig function */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (q == NULL) {
|
|
math_error("q is NULL for %s", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate inverse trig function value
|
|
*/
|
|
qtmp = qsub(&_qone_, q);
|
|
res = qasin(qtmp, epsilon);
|
|
qfree(qtmp);
|
|
if (res == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* return inverse trigonometric result
|
|
*/
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* qacovercos - non-complex inverse coversed trigonometric cosine
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* acovercos(x) = asin(x - 1)
|
|
*
|
|
* given:
|
|
* q real value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* real value result of trig function on q with error epsilon
|
|
*/
|
|
NUMBER *
|
|
qacovercos(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *res; /* inverse trig value result */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (q == NULL) {
|
|
math_error("q is NULL for %s", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate inverse trig function value
|
|
*/
|
|
res = qacovercos_or_NULL(q, epsilon);
|
|
if (res == NULL) {
|
|
math_error("cannot compute inverse sine for acovercos");
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* return inverse trigonometric result
|
|
*/
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* qhaversin - half versed trigonometric sine
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* haversin(x) = versin(x) / 2
|
|
*
|
|
* given:
|
|
* q real value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* real value result of trig function on q with error epsilon
|
|
*/
|
|
NUMBER *
|
|
qhaversin(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *res; /* inverse trig value result */
|
|
NUMBER *qtmp; /* argument to inverse trig function */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (q == NULL) {
|
|
math_error("q is NULL for %s", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate trig function value
|
|
*/
|
|
qtmp = qversin(q, epsilon);
|
|
res = qdivi(qtmp, 2);
|
|
qfree(qtmp);
|
|
|
|
/*
|
|
* return trigonometric result
|
|
*/
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* qahaversin_or_NULL - return NULL or non-complex inverse half versed trigonometric sine
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* ahaversin(x) = acos(1 - 2*x)
|
|
*
|
|
* given:
|
|
* q real value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* != NULL ==> real value result of trig function on q with error epsilon,
|
|
* NULL ==> trig function value cannot be expressed as a NUMBER
|
|
*
|
|
* NOTE: If this function returns NULL, consider calling the equivalent
|
|
* COMPLEX function from comfunc.c. See the help file for the
|
|
* related builtin for details.
|
|
*/
|
|
NUMBER *
|
|
qahaversin_or_NULL(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *res; /* inverse trig value result */
|
|
NUMBER *qtmp; /* argument to inverse trig function */
|
|
NUMBER *x2; /* twice x */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (q == NULL) {
|
|
math_error("q is NULL for %s", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate inverse trig function value
|
|
*/
|
|
x2 = qmuli(q, 2);
|
|
qtmp = qsub(&_qone_, x2);
|
|
qfree(x2);
|
|
res = qacos(qtmp, epsilon);
|
|
qfree(qtmp);
|
|
if (res == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* return inverse trigonometric result
|
|
*/
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* qahaversin - non-complex inverse half versed trigonometric sine
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* ahaversin(x) = acos(1 - 2*x)
|
|
*
|
|
* given:
|
|
* q real value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* real value result of trig function on q with error epsilon
|
|
*/
|
|
NUMBER *
|
|
qahaversin(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *res; /* inverse trig value result */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (q == NULL) {
|
|
math_error("q is NULL for %s", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate inverse trig function value
|
|
*/
|
|
res = qahaversin_or_NULL(q, epsilon);
|
|
if (res == NULL) {
|
|
math_error("cannot compute inverse cosine for ahaversin");
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* return inverse trigonometric result
|
|
*/
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* qhacoversin - coversed trigonometric sine
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* hacoversin((x) = coversin(x) / 2
|
|
*
|
|
* given:
|
|
* q real value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* real value result of trig function on q with error epsilon
|
|
*/
|
|
NUMBER *
|
|
qhacoversin(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *res; /* inverse trig value result */
|
|
NUMBER *qtmp; /* argument to inverse trig function */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (q == NULL) {
|
|
math_error("q is NULL for %s", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate trig function value
|
|
*/
|
|
qtmp = qcoversin(q, epsilon);
|
|
res = qdivi(qtmp, 2);
|
|
qfree(qtmp);
|
|
|
|
/*
|
|
* return trigonometric result
|
|
*/
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* qahacoversin_or_NULL - return NULL or non-complex inverse coversed trigonometric sine
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* ahacoversin(x) = asin(1 - 2*x)
|
|
*
|
|
* given:
|
|
* q real value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* real value result of trig function on q with error epsilon
|
|
*
|
|
* returns:
|
|
* != NULL ==> real value result of trig function on q with error epsilon,
|
|
* NULL ==> trig function value cannot be expressed as a NUMBER
|
|
*
|
|
* NOTE: If this function returns NULL, consider calling the equivalent
|
|
* COMPLEX function from comfunc.c. See the help file for the
|
|
* related builtin for details.
|
|
*/
|
|
NUMBER *
|
|
qahacoversin_or_NULL(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *res; /* inverse trig value result */
|
|
NUMBER *qtmp; /* argument to inverse trig function */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (q == NULL) {
|
|
math_error("q is NULL for %s", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate inverse trig function value
|
|
*/
|
|
qtmp = qsub(&_qone_, q);
|
|
res = qasin(qtmp, epsilon);
|
|
qfree(qtmp);
|
|
if (res == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* return inverse trigonometric result
|
|
*/
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* qahacoversin - non-complex inverse coversed trigonometric sine
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* ahacoversin(x) = asin(1 - 2*x)
|
|
*
|
|
* given:
|
|
* q real value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* real value result of trig function on q with error epsilon
|
|
*/
|
|
NUMBER *
|
|
qahacoversin(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *res; /* inverse trig value result */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (q == NULL) {
|
|
math_error("q is NULL for %s", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate inverse trig function value
|
|
*/
|
|
res = qahacoversin_or_NULL(q, epsilon);
|
|
if (res == NULL) {
|
|
math_error("cannot compute inverse sine for ahacoversin");
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* return inverse trigonometric result
|
|
*/
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* qhavercos - half versed trigonometric cosine
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* havercos(x) = vercos(x) / 2
|
|
*
|
|
* given:
|
|
* q real value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* real value result of trig function on q with error epsilon
|
|
*/
|
|
NUMBER *
|
|
qhavercos(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *res; /* inverse trig value result */
|
|
NUMBER *qtmp; /* argument to inverse trig function */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (q == NULL) {
|
|
math_error("q is NULL for %s", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate trig function value
|
|
*/
|
|
qtmp = qvercos(q, epsilon);
|
|
res = qdivi(qtmp, 2);
|
|
qfree(qtmp);
|
|
|
|
/*
|
|
* return trigonometric result
|
|
*/
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* qahavercos_or_NULL - return NULL or non-complex inverse half versed trigonometric cosine
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* ahavercos(x) = acos(1 - 2*x)
|
|
*
|
|
* given:
|
|
* q real value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* != NULL ==> real value result of trig function on q with error epsilon,
|
|
* NULL ==> trig function value cannot be expressed as a NUMBER
|
|
*
|
|
* NOTE: If this function returns NULL, consider calling the equivalent
|
|
* COMPLEX function from comfunc.c. See the help file for the
|
|
* related builtin for details.
|
|
*/
|
|
NUMBER *
|
|
qahavercos_or_NULL(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *res; /* inverse trig value result */
|
|
NUMBER *qtmp; /* argument to inverse trig function */
|
|
NUMBER *x2; /* twice x */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (q == NULL) {
|
|
math_error("q is NULL for %s", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate inverse trig function value
|
|
*/
|
|
x2 = qmuli(q, 2);
|
|
qtmp = qsub(&_qone_, x2);
|
|
qfree(x2);
|
|
res = qacos(qtmp, epsilon);
|
|
qfree(qtmp);
|
|
if (res == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* return inverse trigonometric result
|
|
*/
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* qahavercos - non-complex inverse half versed trigonometric cosine
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* ahavercos(x) = acos(1 - 2*x)
|
|
*
|
|
* given:
|
|
* q real value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* real value result of trig function on q with error epsilon
|
|
*/
|
|
NUMBER *
|
|
qahavercos(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *res; /* inverse trig value result */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (q == NULL) {
|
|
math_error("q is NULL for %s", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate inverse trig function value
|
|
*/
|
|
res = qahaversin_or_NULL(q, epsilon);
|
|
if (res == NULL) {
|
|
math_error("cannot compute inverse cocosine for ahavercos");
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* return inverse trigonometric result
|
|
*/
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* qhacovercos - half coversed trigonometric cosine
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* hacovercos((x) = covercos(x) / 2
|
|
*
|
|
* given:
|
|
* q real value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* real value result of trig function on q with error epsilon
|
|
*/
|
|
NUMBER *
|
|
qhacovercos(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *res; /* inverse trig value result */
|
|
NUMBER *qtmp; /* argument to inverse trig function */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (q == NULL) {
|
|
math_error("q is NULL for %s", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate trig function value
|
|
*/
|
|
qtmp = qcovercos(q, epsilon);
|
|
res = qdivi(qtmp, 2);
|
|
qfree(qtmp);
|
|
|
|
/*
|
|
* return trigonometric result
|
|
*/
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* qahacovercos_or_NULL - return NULL or non-complex inverse half coversed trigonometric cosine
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* ahacovercos(x) = acos(2*x - 1)
|
|
*
|
|
* given:
|
|
* q real value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* real value result of trig function on q with error epsilon
|
|
*
|
|
* returns:
|
|
* != NULL ==> real value result of trig function on q with error epsilon,
|
|
* NULL ==> trig function value cannot be expressed as a NUMBER
|
|
*
|
|
* NOTE: If this function returns NULL, consider calling the equivalent
|
|
* COMPLEX function from comfunc.c. See the help file for the
|
|
* related builtin for details.
|
|
*/
|
|
NUMBER *
|
|
qahacovercos_or_NULL(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *res; /* inverse trig value result */
|
|
NUMBER *qtmp; /* argument to inverse trig function */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (q == NULL) {
|
|
math_error("q is NULL for %s", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate inverse trig function value
|
|
*/
|
|
qtmp = qsub(&_qone_, q);
|
|
res = qacos(qtmp, epsilon);
|
|
qfree(qtmp);
|
|
if (res == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* return inverse trigonometric result
|
|
*/
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* qahacovercos - non-complex inverse half coversed trigonometric cosine
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* ahacovercos(x) = acos(2*x - 1)
|
|
*
|
|
* given:
|
|
* q real value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* real value result of trig function on q with error epsilon
|
|
*/
|
|
NUMBER *
|
|
qahacovercos(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *res; /* inverse trig value result */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (q == NULL) {
|
|
math_error("q is NULL for %s", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate inverse trig function value
|
|
*/
|
|
res = qahacoversin_or_NULL(q, epsilon);
|
|
if (res == NULL) {
|
|
math_error("cannot compute inverse cosine for ahacovercos");
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* return inverse trigonometric result
|
|
*/
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* qexsec - exterior trigonometric secant
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* exsec(x) = sec(x) - 1
|
|
*
|
|
* given:
|
|
* q real value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* real value result of trig function on q with error epsilon
|
|
*/
|
|
NUMBER *
|
|
qexsec(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *res; /* inverse trig value result */
|
|
NUMBER *qtmp; /* argument to inverse trig function */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (q == NULL) {
|
|
math_error("q is NULL for %s", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate trig function value
|
|
*/
|
|
qtmp = qsec(q, epsilon);
|
|
res = qsub(qtmp, &_qone_);
|
|
qfree(qtmp);
|
|
|
|
/*
|
|
* return trigonometric result
|
|
*/
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* qaexsec_or_NULL - return NULL or non-complex inverse versed trigonometric sine
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* aexsec(x) = asec(x + 1)
|
|
*
|
|
* given:
|
|
* q real value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* != NULL ==> real value result of trig function on q with error epsilon,
|
|
* NULL ==> trig function value cannot be expressed as a NUMBER
|
|
*
|
|
* NOTE: If this function returns NULL, consider calling the equivalent
|
|
* COMPLEX function from comfunc.c. See the help file for the
|
|
* related builtin for details.
|
|
*/
|
|
NUMBER *
|
|
qaexsec_or_NULL(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *res; /* inverse trig value result */
|
|
NUMBER *qtmp; /* argument to inverse trig function */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (q == NULL) {
|
|
math_error("q is NULL for %s", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate inverse trig function value
|
|
*/
|
|
qtmp = qaddi(q, 1);
|
|
res = qasec(qtmp, epsilon);
|
|
qfree(qtmp);
|
|
if (res == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* return inverse trigonometric result
|
|
*/
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* qaexsec - non-complex inverse versed trigonometric sine
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* aexsec(x) = asec(x + 1)
|
|
*
|
|
* given:
|
|
* q real value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* real value result of trig function on q with error epsilon
|
|
*/
|
|
NUMBER *
|
|
qaexsec(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *res; /* inverse trig value result */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (q == NULL) {
|
|
math_error("q is NULL for %s", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate inverse trig function value
|
|
*/
|
|
res = qaexsec_or_NULL(q, epsilon);
|
|
if (res == NULL) {
|
|
math_error("cannot compute inverse cosine for aexsec");
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* return inverse trigonometric result
|
|
*/
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* qexcsc - coversed trigonometric sine
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* excsc(x) = csc(x) - 1
|
|
*
|
|
* given:
|
|
* q real value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* real value result of trig function on q with error epsilon
|
|
*/
|
|
NUMBER *
|
|
qexcsc(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *res; /* inverse trig value result */
|
|
NUMBER *qtmp; /* argument to inverse trig function */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (q == NULL) {
|
|
math_error("q is NULL for %s", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate trig function value
|
|
*/
|
|
qtmp = qcsc(q, epsilon);
|
|
res = qsub(qtmp, &_qone_);
|
|
qfree(qtmp);
|
|
|
|
/*
|
|
* return trigonometric result
|
|
*/
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* qaexcsc_or_NULL - return NULL or non-complex inverse coversed trigonometric sine
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* aexcsc(x) = acsc(x + 1)
|
|
*
|
|
* given:
|
|
* q real value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* real value result of trig function on q with error epsilon
|
|
*
|
|
* returns:
|
|
* != NULL ==> real value result of trig function on q with error epsilon,
|
|
* NULL ==> trig function value cannot be expressed as a NUMBER
|
|
*
|
|
* NOTE: If this function returns NULL, consider calling the equivalent
|
|
* COMPLEX function from comfunc.c. See the help file for the
|
|
* related builtin for details.
|
|
*/
|
|
NUMBER *
|
|
qaexcsc_or_NULL(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *res; /* inverse trig value result */
|
|
NUMBER *qtmp; /* argument to inverse trig function */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (q == NULL) {
|
|
math_error("q is NULL for %s", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate inverse trig function value
|
|
*/
|
|
qtmp = qaddi(q, 1);
|
|
res = qacsc(qtmp, epsilon);
|
|
qfree(qtmp);
|
|
if (res == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* return inverse trigonometric result
|
|
*/
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* qaexcsc - non-complex inverse coversed trigonometric sine
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* aexcsc(x) = acsc(x + 1)
|
|
*
|
|
* given:
|
|
* q real value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* real value result of trig function on q with error epsilon
|
|
*/
|
|
NUMBER *
|
|
qaexcsc(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *res; /* inverse trig value result */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (q == NULL) {
|
|
math_error("q is NULL for %s", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate inverse trig function value
|
|
*/
|
|
res = qaexcsc_or_NULL(q, epsilon);
|
|
if (res == NULL) {
|
|
math_error("cannot compute inverse sine for aexcsc");
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* return inverse trigonometric result
|
|
*/
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* qcrd - trigonometric chord of a unit circle
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* crd(x) = 2 * sin(x / 2)
|
|
*
|
|
* given:
|
|
* q real value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* real value result of trig function on q with error epsilon
|
|
*/
|
|
NUMBER *
|
|
qcrd(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *res; /* inverse trig value result */
|
|
NUMBER *qdiv2; /* q/2 */
|
|
NUMBER *qtmp; /* argument to inverse trig function */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (q == NULL) {
|
|
math_error("q is NULL for %s", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate trig function value
|
|
*/
|
|
qdiv2 = qdivi(q, 2);
|
|
qtmp = qsin(qdiv2, epsilon);
|
|
qfree(qdiv2);
|
|
res = qmuli(qtmp, 2);
|
|
qfree(qtmp);
|
|
|
|
/*
|
|
* return trigonometric result
|
|
*/
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* qacrd_or_NULL - return NULL or non-complex inverse trigonometric chord of a unit circle
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* acrd(x) = 2 * asin(x / 2)
|
|
*
|
|
* given:
|
|
* q real value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* real value result of trig function on q with error epsilon
|
|
*
|
|
* returns:
|
|
* != NULL ==> real value result of trig function on q with error epsilon,
|
|
* NULL ==> trig function value cannot be expressed as a NUMBER
|
|
*
|
|
* NOTE: If this function returns NULL, consider calling the equivalent
|
|
* COMPLEX function from comfunc.c. See the help file for the
|
|
* related builtin for details.
|
|
*/
|
|
NUMBER *
|
|
qacrd_or_NULL(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *res; /* inverse trig value result */
|
|
NUMBER *qdiv2; /* q/2 */
|
|
NUMBER *qtmp; /* argument to inverse trig function */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (q == NULL) {
|
|
math_error("q is NULL for %s", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate inverse trig function value
|
|
*/
|
|
qdiv2 = qdivi(q, 2);
|
|
qtmp = qasin(qdiv2, epsilon);
|
|
qfree(qdiv2);
|
|
if (qtmp == NULL) {
|
|
return NULL;
|
|
}
|
|
res = qmuli(qtmp, 2);
|
|
qfree(qtmp);
|
|
|
|
/*
|
|
* return inverse trigonometric result
|
|
*/
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* qacrd - non-complex inverse trigonometric chord of a unit circle
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* acrd(x) = 2 * asin(x / 2)
|
|
*
|
|
* given:
|
|
* q real value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* real value result of trig function on q with error epsilon
|
|
*/
|
|
NUMBER *
|
|
qacrd(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *res; /* inverse trig value result */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (q == NULL) {
|
|
math_error("q is NULL for %s", __func__);
|
|
not_reached();
|
|
}
|
|
if (check_epsilon(epsilon) == false) {
|
|
math_error("Invalid epsilon arg for %s", __func__);
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* calculate inverse trig function value
|
|
*/
|
|
res = qacrd_or_NULL(q, epsilon);
|
|
if (res == NULL) {
|
|
math_error("cannot compute inverse sine for acrd");
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* return inverse trigonometric result
|
|
*/
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* qcas - trigonometric chord of a unit circle
|
|
*
|
|
* This uses the formula:
|
|
*
|
|
* cas(x) = cos(x) + sin(x)
|
|
*
|
|
* given:
|
|
* q real value to pass to the trig function
|
|
* epsilon error tolerance / precision for trig calculation
|
|
*
|
|
* returns:
|
|
* real value result of trig function on q with error epsilon
|
|
*/
|
|
NUMBER *
|
|
qcas(NUMBER *q, NUMBER *epsilon)
|
|
{
|
|
NUMBER *sin; /* sin(x) */
|
|
NUMBER *tsin; /* sin(x) rounded to nearest epsilon multiple */
|
|
NUMBER *cos; /* cos(x) */
|
|
NUMBER *tcos; /* cos(x) rounded to nearest epsilon multiple */
|
|
NUMBER *res;
|
|
long n;
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (qiszero(epsilon)) {
|
|
math_error("Zero epsilon value for cosine");
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* case 0: quick return 1
|
|
*/
|
|
if (qiszero(q)) {
|
|
return qlink(&_qone_);
|
|
}
|
|
|
|
/*
|
|
* case epsilon > 1: quick return 0
|
|
*/
|
|
n = -qilog2(epsilon);
|
|
if (n < 0) {
|
|
return qlink(&_qzero_);
|
|
}
|
|
|
|
/*
|
|
* compute cosine and sine
|
|
*/
|
|
qsincos(q, n + 2, &sin, &cos);
|
|
tcos = qmappr(cos, epsilon, conf->triground);
|
|
qfree(cos);
|
|
tsin = qmappr(sin, epsilon, conf->triground);
|
|
qfree(sin);
|
|
res = qqadd(tcos, tsin);
|
|
qfree(tcos);
|
|
qfree(tsin);
|
|
|
|
/*
|
|
* return trigonometric result
|
|
*/
|
|
return res;
|
|
}
|