#include <stdio.h>
#include <inttypes.h>
#include <math.h>
#define NDEBUG
#include <assert.h>
#include "cutils.h"
#include "libm.h"
#define USE_TAN_SHORTCUT
typedef enum {
RM_RNE,
RM_RTZ,
RM_RDN,
RM_RUP,
RM_RMM,
RM_RMMUP,
} RoundingModeEnum;
#define FFLAG_INVALID_OP (1 << 4)
#define FFLAG_DIVIDE_ZERO (1 << 3)
#define FFLAG_OVERFLOW (1 << 2)
#define FFLAG_UNDERFLOW (1 << 1)
#define FFLAG_INEXACT (1 << 0)
typedef enum {
FMINMAX_PROP,
FMINMAX_IEEE754_2008,
FMINMAX_IEEE754_201X,
} SoftFPMinMaxTypeEnum;
typedef uint32_t sfloat32;
typedef uint64_t sfloat64;
#define F_STATIC static __maybe_unused
#define F_USE_FFLAGS 0
#define F_SIZE 32
#define F_NORMALIZE_ONLY
#include "softfp_template.h"
#define F_SIZE 64
#include "softfp_template.h"
#ifdef USE_SOFTFLOAT
double __adddf3(double a, double b)
{
return uint64_as_float64(add_sf64(float64_as_uint64(a),
float64_as_uint64(b), RM_RNE));
}
double __subdf3(double a, double b)
{
return uint64_as_float64(sub_sf64(float64_as_uint64(a),
float64_as_uint64(b), RM_RNE));
}
double __muldf3(double a, double b)
{
return uint64_as_float64(mul_sf64(float64_as_uint64(a),
float64_as_uint64(b), RM_RNE));
}
double __divdf3(double a, double b)
{
return uint64_as_float64(div_sf64(float64_as_uint64(a),
float64_as_uint64(b), RM_RNE));
}
int __eqdf2(double a, double b)
{
int ret = cmp_sf64(float64_as_uint64(a),
float64_as_uint64(b));
return ret;
}
int __nedf2(double a, double b)
{
int ret = cmp_sf64(float64_as_uint64(a),
float64_as_uint64(b));
if (unlikely(ret == 2))
return 0;
else
return ret;
}
int __ledf2(double a, double b)
{
int ret = cmp_sf64(float64_as_uint64(a),
float64_as_uint64(b));
return ret;
}
int __ltdf2(double a, double b)
{
int ret = cmp_sf64(float64_as_uint64(a),
float64_as_uint64(b));
return ret;
}
int __gedf2(double a, double b)
{
int ret = cmp_sf64(float64_as_uint64(a),
float64_as_uint64(b));
if (unlikely(ret == 2))
return -1;
else
return ret;
}
int __gtdf2(double a, double b)
{
int ret = cmp_sf64(float64_as_uint64(a),
float64_as_uint64(b));
if (unlikely(ret == 2))
return -1;
else
return ret;
}
int __unorddf2(double a, double b)
{
return isnan_sf64(float64_as_uint64(a)) ||
isnan_sf64(float64_as_uint64(b));
}
double __floatsidf(int32_t a)
{
return uint64_as_float64(cvt_i32_sf64(a, RM_RNE));
}
double __floatdidf(int64_t a)
{
return uint64_as_float64(cvt_i64_sf64(a, RM_RNE));
}
double __floatunsidf(unsigned int a)
{
return uint64_as_float64(cvt_u32_sf64(a, RM_RNE));
}
int32_t __fixdfsi(double a)
{
return cvt_sf64_i32(float64_as_uint64(a), RM_RTZ);
}
double __extendsfdf2(float a)
{
return uint64_as_float64(cvt_sf32_sf64(float_as_uint(a)));
}
float __truncdfsf2(double a)
{
return uint_as_float(cvt_sf64_sf32(float64_as_uint64(a), RM_RNE));
}
double js_sqrt(double a)
{
return uint64_as_float64(sqrt_sf64(float64_as_uint64(a), RM_RNE));
}
#if defined(__tc32__)
int __fpclassifyd(double a)
{
uint64_t u = float64_as_uint64(a);
uint32_t h = u >> 32;
uint32_t l = u;
h &= 0x7fffffff;
if (h >= 0x7ff00000) {
if (h == 0x7ff00000 && l == 0)
return FP_INFINITE;
else
return FP_NAN;
} else if (h < 0x00100000) {
if (h == 0 && l == 0)
return FP_ZERO;
else
return FP_SUBNORMAL;
} else {
return FP_NORMAL;
}
}
#endif
#endif
int32_t js_lrint(double a)
{
return cvt_sf64_i32(float64_as_uint64(a), RM_RNE);
}
double js_fmod(double a, double b)
{
return uint64_as_float64(fmod_sf64(float64_as_uint64(a), float64_as_uint64(b)));
}
static double rint_sf64(double a, RoundingModeEnum rm)
{
uint64_t u = float64_as_uint64(a);
uint64_t frac_mask, one, m, addend;
int e;
unsigned int s;
e = ((u >> 52) & 0x7ff) - 0x3ff;
s = u >> 63;
if (e < 0) {
m = u & (((uint64_t)1 << 52) - 1);
if (e == -0x3ff && m == 0) {
} else {
s = u >> 63;
one = (uint64_t)0x3ff << 52;
u = 0;
switch(rm) {
case RM_RUP:
case RM_RDN:
if (s ^ (rm & 1))
u = one;
break;
default:
case RM_RMM:
case RM_RMMUP:
if (e == -1 && (m != 0 || (m == 0 && (!s || rm == RM_RMM))))
u = one;
break;
case RM_RTZ:
break;
}
u |= (uint64_t)s << 63;
}
} else if (e < 52) {
one = (uint64_t)1 << (52 - e);
frac_mask = one - 1;
addend = 0;
switch(rm) {
case RM_RMMUP:
addend = (one >> 1) - s;
break;
default:
case RM_RMM:
addend = (one >> 1);
break;
case RM_RTZ:
break;
case RM_RUP:
case RM_RDN:
if (s ^ (rm & 1))
addend = one - 1;
break;
}
u += addend;
u &= ~frac_mask;
}
return uint64_as_float64(u);
}
double js_floor(double x)
{
return rint_sf64(x, RM_RDN);
}
double js_ceil(double x)
{
return rint_sf64(x, RM_RUP);
}
double js_trunc(double x)
{
return rint_sf64(x, RM_RTZ);
}
double js_round_inf(double x)
{
return rint_sf64(x, RM_RMMUP);
}
double js_fabs(double x)
{
uint64_t a = float64_as_uint64(x);
return uint64_as_float64(a & 0x7fffffffffffffff);
}
#define EXTRACT_WORDS(ix0,ix1,d) \
do { \
uint64_t __u = float64_as_uint64(d); \
(ix0) = (uint32_t)(__u >> 32); \
(ix1) = (uint32_t)__u; \
} while (0)
static uint32_t get_high_word(double d)
{
return float64_as_uint64(d) >> 32;
}
static double set_high_word(double d, uint32_t h)
{
uint64_t u = float64_as_uint64(d);
u = (u & 0xffffffff) | ((uint64_t)h << 32);
return uint64_as_float64(u);
}
static uint32_t get_low_word(double d)
{
return float64_as_uint64(d);
}
static double zero_low(double x)
{
uint64_t u = float64_as_uint64(x);
u &= 0xffffffff00000000;
return uint64_as_float64(u);
}
static double float64_from_u32(uint32_t h, uint32_t l)
{
return uint64_as_float64(((uint64_t)h << 32) | l);
}
static const double zero = 0.0;
static const double one = 1.0;
static const double half = 5.00000000000000000000e-01;
static const double tiny = 1.0e-300;
static const double huge = 1.0e300;
static const double
two54 = 1.80143985094819840000e+16,
twom54 = 5.55111512312578270212e-17;
double js_scalbn(double x, int n)
{
int k,hx,lx;
EXTRACT_WORDS(hx, lx, x);
k = (hx&0x7ff00000)>>20;
if (k==0) {
if ((lx|(hx&0x7fffffff))==0) return x;
x *= two54;
hx = get_high_word(x);
k = ((hx&0x7ff00000)>>20) - 54;
if (n< -50000) return tiny*x;
}
if (k==0x7ff) return x+x;
k = k+n;
if (k > 0x7fe) return huge*copysign(huge,x);
if (k > 0)
{x = set_high_word(x, (hx&0x800fffff)|(k<<20)); return x;}
if (k <= -54) {
if (n > 50000)
return huge*copysign(huge,x);
else
return tiny*copysign(tiny,x);
}
k += 54;
x = set_high_word(x, (hx&0x800fffff)|(k<<20));
return x*twom54;
}
#ifndef USE_SOFTFLOAT
#if defined(__aarch64__) || defined(__x86_64__) || defined(__i386__)
double js_sqrt(double x)
{
return sqrt(x);
}
#else
double js_sqrt(double x)
{
double z;
int sign = (int)0x80000000;
unsigned r,t1,s1,ix1,q1;
int ix0,s0,q,m,t,i;
EXTRACT_WORDS(ix0, ix1, x);
if((ix0&0x7ff00000)==0x7ff00000) {
return x*x+x;
}
if(ix0<=0) {
if(((ix0&(~sign))|ix1)==0) return x;
else if(ix0<0)
return (x-x)/(x-x);
}
m = (ix0>>20);
if(m==0) {
while(ix0==0) {
m -= 21;
ix0 |= (ix1>>11); ix1 <<= 21;
}
for(i=0;(ix0&0x00100000)==0;i++) ix0<<=1;
m -= i-1;
ix0 |= (ix1>>(32-i));
ix1 <<= i;
}
m -= 1023;
ix0 = (ix0&0x000fffff)|0x00100000;
if(m&1){
ix0 += ix0 + ((ix1&sign)>>31);
ix1 += ix1;
}
m >>= 1;
ix0 += ix0 + ((ix1&sign)>>31);
ix1 += ix1;
q = q1 = s0 = s1 = 0;
r = 0x00200000;
while(r!=0) {
t = s0+r;
if(t<=ix0) {
s0 = t+r;
ix0 -= t;
q += r;
}
ix0 += ix0 + ((ix1&sign)>>31);
ix1 += ix1;
r>>=1;
}
r = sign;
while(r!=0) {
t1 = s1+r;
t = s0;
if((t<ix0)||((t==ix0)&&(t1<=ix1))) {
s1 = t1+r;
if(((t1&sign)==sign)&&(s1&sign)==0) s0 += 1;
ix0 -= t;
if (ix1 < t1) ix0 -= 1;
ix1 -= t1;
q1 += r;
}
ix0 += ix0 + ((ix1&sign)>>31);
ix1 += ix1;
r>>=1;
}
if((ix0|ix1)!=0) {
z = one-tiny;
if (z>=one) {
z = one+tiny;
if (q1==(unsigned)0xffffffff) { q1=0; q += 1;}
else if (z>one) {
if (q1==(unsigned)0xfffffffe) q+=1;
q1+=2;
} else
q1 += (q1&1);
}
}
ix0 = (q>>1)+0x3fe00000;
ix1 = q1>>1;
if ((q&1)==1) ix1 |= sign;
ix0 += (m <<20);
return float64_from_u32(ix0, ix1);
}
#endif
#endif
static double eval_poly(double x, const double *coefs, int n)
{
double r;
int i;
r = coefs[n - 1];
for(i = n - 2; i >= 0; i--) {
r = r * x + coefs[i];
}
return r;
}
static const double
S1 = -1.66666666666666324348e-01;
static const double S_tab[] = {
8.33333333332248946124e-03,
-1.98412698298579493134e-04,
2.75573137070700676789e-06,
-2.50507602534068634195e-08,
1.58969099521155010221e-10,
};
static double __kernel_sin(double x, double y, int iy)
{
double z,r,v;
int ix;
ix = get_high_word(x)&0x7fffffff;
if(ix<0x3e400000)
{if((int)x==0) return x;}
z = x*x;
v = z*x;
r = eval_poly(z, S_tab, 5);
if(iy==0) return x+v*(S1+z*r);
else return x-((z*(half*y-v*r)-y)-v*S1);
}
static const double C_tab[] = {
4.16666666666666019037e-02,
-1.38888888888741095749e-03,
2.48015872894767294178e-05,
-2.75573143513906633035e-07,
2.08757232129817482790e-09,
-1.13596475577881948265e-11,
};
static double __kernel_cos(double x, double y)
{
double a,hz,z,r,qx;
int ix;
ix = get_high_word(x)&0x7fffffff;
if(ix<0x3e400000) {
if(((int)x)==0) return one;
}
z = x*x;
r = z * eval_poly(z, C_tab, 6);
if(ix < 0x3FD33333)
return one - (0.5*z - (z*r - x*y));
else {
if(ix > 0x3fe90000) {
qx = 0.28125;
} else {
qx = float64_from_u32(ix-0x00200000, 0);
}
hz = 0.5*z-qx;
a = one-qx;
return a - (hz - (z*r-x*y));
}
}
#define T_LEN 19
static const uint64_t T[T_LEN] = {
0x1580cc11bf1edaea,
0x9afed7ec47e35742,
0xcf41ce7de294a4ba,
0x5d49eeb1faf97c5e,
0xd3d18fd9a797fa8b,
0xdb4d9fb3c9f2c26d,
0xfbcbc462d6829b47,
0xc7fe25fff7816603,
0x272117e2ef7e4a0e,
0x4e64758e60d4ce7d,
0x3a671c09ad17df90,
0xba208d7d4baed121,
0x3f877ac72c4a69cf,
0x01924bba82746487,
0x6dc91b8e909374b8,
0x7f9458eaf7aef158,
0x36d8a5664f10e410,
0x7f09d5f47d4d3770,
0x28be60db9391054a,
};
static const uint64_t PIO4[2] = {
0xc4c6628b80dc1cd1,
0xc90fdaa22168c234,
};
static uint64_t get_u64_at_bit(const uint64_t *tab, uint32_t tab_len,
uint32_t pos)
{
uint64_t v;
uint32_t p = pos / 64;
int shift = pos % 64;
v = tab[p] >> shift;
if (shift != 0 && (p + 1) < tab_len)
v |= tab[p + 1] << (64 - shift);
return v;
}
static int rem_pio2_large(double x, double *y)
{
uint64_t m;
int e, sgn, n, rnd, j, i, y_sgn;
uint64_t c[2], d[3];
uint64_t r0, r1;
uint32_t carry, carry1;
m = float64_as_uint64(x);
sgn = m >> 63;
e = (m >> 52) & 0x7ff;
m = (m & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52);
j = T_LEN * 64 - (e - 1075) - 192;
for(i = 0; i < 3; i++) {
d[i] = get_u64_at_bit(T, T_LEN, j + i * 64);
}
r1 = mul_u64(&r0, m, d[0]);
c[0] = r1;
r1 = mul_u64(&r0, m, d[1]);
c[0] += r0;
carry = c[0] < r0;
c[1] = r1 + carry;
mul_u64(&r0, m, d[2]);
c[1] += r0;
n = c[1] >> 62;
rnd = (c[1] >> 61) & 1;
n += rnd;
c[1] = (c[1] << 2) | (c[0] >> 62);
c[0] = (c[0] << 2);
y_sgn = sgn;
if (rnd) {
y_sgn ^= 1;
c[0] = ~c[0];
c[1] = ~c[1];
if (++c[0] == 0)
c[1]++;
}
r1 = mul_u64(&r0, c[0], PIO4[1]);
d[0] = r0;
d[1] = r1;
r1 = mul_u64(&r0, c[1], PIO4[0]);
d[0] += r0;
carry = d[0] < r0;
d[1] += r1;
carry1 = d[1] < r1;
d[1] += carry;
carry1 |= (d[1] < carry);
d[2] = carry1;
r1 = mul_u64(&r0, c[1], PIO4[1]);
d[1] += r0;
carry = d[1] < r0;
d[2] += r1 + carry;
if (d[2] == 0) {
y[0] = y[1] = 0;
} else {
uint64_t m0, m1;
int e1;
e = clz64(d[2]);
d[2] = (d[2] << e) | (d[1] >> (64 - e));
d[1] = (d[1] << e);
m0 = (d[2] >> 11) & (((uint64_t)1 << 52) - 1);
m1 = ((d[2] & 0x7ff) << 42) | (d[1] >> (64 - 42));
y[0] = uint64_as_float64(((uint64_t)y_sgn << 63) |
((uint64_t)(1023 - e) << 52) |
m0);
if (m1 == 0) {
y[1] = 0;
} else {
e1 = clz64(m1) - 11;
m1 = (m1 << e1) & (((uint64_t)1 << 52) - 1);
y[1] = uint64_as_float64(((uint64_t)y_sgn << 63) |
((uint64_t)(1023 - e - 53 - e1) << 52) |
m1);
}
}
if (sgn)
n = -n;
return n;
}
#ifdef USE_SOFTFLOAT
int js_rem_pio2(double x, double *y)
{
int ix,hx;
hx = get_high_word(x);
ix = hx&0x7fffffff;
if(ix<=0x3fe921fb) {
y[0] = x;
y[1] = 0;
return 0;
}
if(ix>=0x7ff00000) {
y[0]=y[1]=x-x;
return 0;
}
return rem_pio2_large(x, y);
}
#else
static const double
invpio2 = 6.36619772367581382433e-01;
static const double pio2_tab[3] = {
1.57079632673412561417e+00,
6.07710050630396597660e-11,
2.02226624871116645580e-21,
};
static const double pio2_t_tab[3] = {
6.07710050650619224932e-11,
2.02226624879595063154e-21,
8.47842766036889956997e-32,
};
static uint8_t rem_pio2_emax[2] = { 16, 49 };
int js_rem_pio2(double x, double *y)
{
double w,t,r,fn;
int i,j,n,ix,hx,it;
hx = get_high_word(x);
ix = hx&0x7fffffff;
if(ix<=0x3fe921fb) {
y[0] = x;
y[1] = 0;
return 0;
}
if(ix<=0x413921fb) {
t = fabs(x);
if (ix<0x4002d97c) {
n = 1;
fn = 1;
} else {
n = (int) (t*invpio2+half);
fn = (double)n;
}
it = 0;
for(;;) {
r = t-fn*pio2_tab[it];
w = fn*pio2_t_tab[it];
y[0] = r-w;
j = ix>>20;
i = j-(((get_high_word(y[0]))>>20)&0x7ff);
if (it == 2 || i <= rem_pio2_emax[it])
break;
t = r;
it++;
}
y[1] = (r-y[0])-w;
if (hx<0) {
y[0] = -y[0];
y[1] = -y[1];
return -n;
} else {
return n;
}
}
if(ix>=0x7ff00000) {
y[0]=y[1]=x-x;
return 0;
}
return rem_pio2_large(x, y);
}
#endif
static double js_sin_cos(double x, int flag)
{
double y[2], z, s, c;
int ix;
uint32_t n;
ix = get_high_word(x);
if (ix>=0x7ff00000)
return x-x;
n = js_rem_pio2(x,y);
s = c = 0;
if (flag == 3 || (n & 1) == flag) {
s = __kernel_sin(y[0],y[1],1);
if (flag != 3)
goto done;
}
if (flag == 3 || (n & 1) != flag) {
c = __kernel_cos(y[0],y[1]);
if (flag != 3) {
s = c;
goto done;
}
}
if (n & 1)
z = -c / s;
else
z = s / c;
return z;
done:
if ((n + flag) & 2)
s = -s;
return s;
}
double js_sin(double x)
{
return js_sin_cos(x, 0);
}
double js_cos(double x)
{
return js_sin_cos(x, 1);
}
#ifdef USE_TAN_SHORTCUT
double js_tan(double x)
{
return js_sin_cos(x, 3);
}
#endif
#ifndef USE_TAN_SHORTCUT
static const double T0 = 3.33333333333334091986e-01;
static const double T_even[] = {
5.39682539762260521377e-02,
8.86323982359930005737e-03,
1.45620945432529025516e-03,
2.46463134818469906812e-04,
7.14072491382608190305e-05,
2.59073051863633712884e-05,
};
static const double T_odd[] = {
1.33333333333201242699e-01,
2.18694882948595424599e-02,
3.59207910759131235356e-03,
5.88041240820264096874e-04,
7.81794442939557092300e-05,
-1.85586374855275456654e-05,
};
static const double pio4 = 7.85398163397448278999e-01,
pio4lo = 3.06161699786838301793e-17;
static double minus_inv(double x, double y)
{
double a, t, z, v, s, w;
w = x + y;
z = zero_low(w);
v = y - (z - x);
a = -one / w;
t = zero_low(a);
s = one + t * z;
return t + a * (s + t * v);
}
static double
__kernel_tan(double x, double y, int iy) {
double z, r, v, w, s;
int ix, hx;
hx = get_high_word(x);
ix = hx & 0x7fffffff;
if (ix < 0x3e300000) {
if ((int) x == 0) {
if (((ix | get_low_word(x)) | (iy + 1)) == 0)
return one / fabs(x);
else {
if (iy == 1)
return x;
else
return minus_inv(x, y);
}
}
}
if (ix >= 0x3FE59428) {
if (hx < 0) {
x = -x;
y = -y;
}
z = pio4 - x;
w = pio4lo - y;
x = z + w;
y = 0.0;
}
z = x * x;
w = z * z;
r = eval_poly(w, T_odd, 6);
v = z * eval_poly(w, T_even, 6);
s = z * x;
r = y + z * (s * (r + v) + y);
r += T0 * s;
w = x + r;
if (ix >= 0x3FE59428) {
v = (double) iy;
return (double) (1 - ((hx >> 30) & 2)) *
(v - 2.0 * (x - (w * w / (w + v) - r)));
}
if (iy == 1) {
return w;
} else {
return minus_inv(x, r);
}
}
double js_tan(double x)
{
double y[2],z=0.0;
int n, ix;
ix = get_high_word(x);
ix &= 0x7fffffff;
if(ix <= 0x3fe921fb) return __kernel_tan(x,z,1);
else if (ix>=0x7ff00000) return x-x;
else {
n = js_rem_pio2(x,y);
return __kernel_tan(y[0],y[1],1-((n&1)<<1));
}
}
#endif
static const double
pio2_hi = 1.57079632679489655800e+00,
pio2_lo = 6.12323399573676603587e-17,
pio4_hi = 7.85398163397448278999e-01;
static const double pS[] = {
1.66666666666666657415e-01,
-3.25565818622400915405e-01,
2.01212532134862925881e-01,
-4.00555345006794114027e-02,
7.91534994289814532176e-04,
3.47933107596021167570e-05,
};
static const double qS[] = {
-2.40339491173441421878e+00,
2.02094576023350569471e+00,
-6.88283971605453293030e-01,
7.70381505559019352791e-02,
};
static double R(double t)
{
double p, q, w;
p = t * eval_poly(t, pS, 6);
q = one + t * eval_poly(t, qS, 4);
w = p/q;
return w;
}
double js_asin(double x)
{
double t,w,p,q,c,r,s;
int hx,ix;
hx = get_high_word(x);
ix = hx&0x7fffffff;
if(ix>= 0x3ff00000) {
if(((ix-0x3ff00000)|get_low_word(x))==0)
return x*pio2_hi+x*pio2_lo;
return (x-x)/(x-x);
} else if (ix<0x3fe00000) {
if(ix<0x3e400000) {
if(huge+x>one) return x;
} else {
t = x*x;
w = R(t);
return x+x*w;
}
}
w = one-fabs(x);
t = w*0.5;
r = R(t);
s = js_sqrt(t);
if(ix>=0x3FEF3333) {
w = r;
t = pio2_hi-(2.0*(s+s*w)-pio2_lo);
} else {
w = zero_low(s);
c = (t-w*w)/(s+w);
p = 2.0*s*r-(pio2_lo-2.0*c);
q = pio4_hi-2.0*w;
t = pio4_hi-(p-q);
}
if(hx>0) return t; else return -t;
}
static const double
pi = 3.14159265358979311600e+00;
double js_acos(double x)
{
double z,r,w,s,c,df;
int hx,ix;
hx = get_high_word(x);
ix = hx&0x7fffffff;
if(ix>=0x3ff00000) {
if(((ix-0x3ff00000)|get_low_word(x))==0) {
if(hx>0) return 0.0;
else return pi+2.0*pio2_lo;
}
return (x-x)/(x-x);
}
if(ix<0x3fe00000) {
if(ix<=0x3c600000) return pio2_hi+pio2_lo;
z = x*x;
r = R(z);
return pio2_hi - (x - (pio2_lo-x*r));
} else {
z = (one-fabs(x))*0.5;
r = R(z);
s = js_sqrt(z);
if (hx<0) {
w = r*s-pio2_lo;
return pi - 2.0*(s+w);
} else {
df = zero_low(s);
c = (z-df*df)/(s+df);
w = r*s+c;
return 2.0*(df+w);
}
}
}
static const double atanhi[] = {
4.63647609000806093515e-01,
7.85398163397448278999e-01,
9.82793723247329054082e-01,
1.57079632679489655800e+00,
};
static const double atanlo[] = {
2.26987774529616870924e-17,
3.06161699786838301793e-17,
1.39033110312309984516e-17,
6.12323399573676603587e-17,
};
static const double aT_even[] = {
3.33333333333329318027e-01,
1.42857142725034663711e-01,
9.09088713343650656196e-02,
6.66107313738753120669e-02,
4.97687799461593236017e-02,
1.62858201153657823623e-02,
};
static const double aT_odd[] = {
-1.99999999998764832476e-01,
-1.11111104054623557880e-01,
-7.69187620504482999495e-02,
-5.83357013379057348645e-02,
-3.65315727442169155270e-02,
};
double js_atan(double x)
{
double w,s1,s2,z;
int ix,hx,id;
hx = get_high_word(x);
ix = hx&0x7fffffff;
if(ix>=0x44100000) {
if(ix>0x7ff00000||
(ix==0x7ff00000&&(get_low_word(x)!=0)))
return x+x;
if(hx>0) return atanhi[3]+atanlo[3];
else return -atanhi[3]-atanlo[3];
} if (ix < 0x3fdc0000) {
if (ix < 0x3e200000) {
if(huge+x>one) return x;
}
id = -1;
} else {
x = fabs(x);
if (ix < 0x3ff30000) {
if (ix < 0x3fe60000) {
id = 0; x = (2.0*x-one)/(2.0+x);
} else {
id = 1; x = (x-one)/(x+one);
}
} else {
if (ix < 0x40038000) {
id = 2; x = (x-1.5)/(one+1.5*x);
} else {
id = 3; x = -1.0/x;
}
}}
z = x*x;
w = z*z;
s1 = z*eval_poly(w, aT_even, 6);
s2 = w*eval_poly(w, aT_odd, 5);
if (id<0) return x - x*(s1+s2);
else {
z = atanhi[id] - ((x*(s1+s2) - atanlo[id]) - x);
return (hx<0)? -z:z;
}
}
static const double
pi_o_4 = 7.8539816339744827900E-01,
pi_o_2 = 1.5707963267948965580E+00,
pi_lo = 1.2246467991473531772E-16;
double js_atan2(double y, double x)
{
double z;
int k,m,hx,hy,ix,iy;
unsigned lx,ly;
EXTRACT_WORDS(hx, lx, x);
EXTRACT_WORDS(hy, ly, y);
ix = hx&0x7fffffff;
iy = hy&0x7fffffff;
if(((ix|((lx|-lx)>>31))>0x7ff00000)||
((iy|((ly|-ly)>>31))>0x7ff00000))
return x+y;
if(((hx-0x3ff00000)|lx)==0)
return js_atan(y);
m = ((hy>>31)&1)|((hx>>30)&2);
if((iy|ly)==0) {
z = 0;
goto done;
}
if((ix|lx)==0) return (hy<0)? -pi_o_2-tiny: pi_o_2+tiny;
if(ix==0x7ff00000) {
if(iy==0x7ff00000) {
z = pi_o_4;
} else {
z = 0;
}
goto done;
}
if(iy==0x7ff00000) return (hy<0)? -pi_o_2-tiny: pi_o_2+tiny;
k = (iy-ix)>>20;
if(k > 60) {
z=pi_o_2+0.5*pi_lo;
} else if(hx<0&&k<-60) {
z=0.0;
} else {
z=js_atan(fabs(y/x));
}
done:
switch (m) {
case 0:
return z ;
case 1:
z = set_high_word(z, get_high_word(z) ^ 0x80000000);
return z ;
case 2:
return pi-(z-pi_lo);
default:
return (z-pi_lo)-pi;
}
}
static const double
two = 2.0,
halF[2] = {0.5,-0.5,},
twom1000= 9.33263618503218878990e-302,
o_threshold= 7.09782712893383973096e+02,
u_threshold= -7.45133219101941108420e+02,
ln2HI[2] ={ 6.93147180369123816490e-01,
-6.93147180369123816490e-01,},
ln2LO[2] ={ 1.90821492927058770002e-10,
-1.90821492927058770002e-10,},
invln2 = 1.44269504088896338700e+00;
static const double P[] = {
1.66666666666666019037e-01,
-2.77777777770155933842e-03,
6.61375632143793436117e-05,
-1.65339022054652515390e-06,
4.13813679705723846039e-08,
};
static double kernel_exp(double z, double w, double lo, double hi, int n)
{
int j;
double t, t1, r;
t = z*z;
t1 = z - t*eval_poly(t, P, 5);
r = (z*t1)/(t1-two) - (w+z*w);
z = one-((lo + r)-hi);
j = get_high_word(z);
j += (n<<20);
if((j>>20)<=0)
z = js_scalbn(z,n);
else
z = set_high_word(z, get_high_word(z) + (n<<20));
return z;
}
double js_exp(double x)
{
double hi,lo,t;
int k,xsb;
unsigned hx;
hx = get_high_word(x);
xsb = (hx>>31)&1;
hx &= 0x7fffffff;
if(hx >= 0x40862E42) {
if(hx>=0x7ff00000) {
if(((hx&0xfffff)|get_low_word(x))!=0)
return x+x;
else return (xsb==0)? x:0.0;
}
if(x > o_threshold) return huge*huge;
if(x < u_threshold) return twom1000*twom1000;
}
if(hx > 0x3fd62e42) {
if(hx < 0x3FF0A2B2) {
hi = x-ln2HI[xsb]; lo=ln2LO[xsb]; k = 1-xsb-xsb;
} else {
k = (int)(invln2*x+halF[xsb]);
t = k;
hi = x - t*ln2HI[0];
lo = t*ln2LO[0];
}
x = hi - lo;
}
else if(hx < 0x3e300000) {
if(huge+x>one) return one+x;
k = 0;
}
else k = 0;
if (k == 0) {
lo = 0;
hi = x;
}
return kernel_exp(x, 0, lo, hi, k);
}
static const double
bp[] = {1.0, 1.5,},
dp_h[] = { 0.0, 5.84962487220764160156e-01,},
dp_l[] = { 0.0, 1.35003920212974897128e-08,},
two53 = 9007199254740992.0,
lg2 = 6.93147180559945286227e-01,
lg2_h = 6.93147182464599609375e-01,
lg2_l = -1.90465429995776804525e-09,
ovt = 8.0085662595372944372e-0017,
cp = 9.61796693925975554329e-01,
cp_h = 9.61796700954437255859e-01,
cp_l = -7.02846165095275826516e-09,
ivln2 = 1.44269504088896338700e+00,
ivln2_h = 1.44269502162933349609e+00,
ivln2_l = 1.92596299112661746887e-08,
ivlg10b2 = 0.3010299956639812,
ivlg10b2_h = 0.30102992057800293,
ivlg10b2_l = 7.508597826552624e-8;
static const double L_tab[] = {
5.99999999999994648725e-01,
4.28571428578550184252e-01,
3.33333329818377432918e-01,
2.72728123808534006489e-01,
2.30660745775561754067e-01,
2.06975017800338417784e-01,
};
static void kernel_log2(double *pt1, double *pt2, double ax)
{
double t, u, v, t1, t2, r;
int n, j, ix, k;
double ss, s2, s_h, s_l, t_h, t_l, p_l, p_h, z_h, z_l;
n = 0;
ix = get_high_word(ax);
if(ix<0x00100000)
{ax *= two53; n -= 53; ix = get_high_word(ax); }
n += ((ix)>>20)-0x3ff;
j = ix&0x000fffff;
ix = j|0x3ff00000;
if(j<=0x3988E) k=0;
else if(j<0xBB67A) k=1;
else {k=0;n+=1;ix -= 0x00100000;}
ax = set_high_word(ax, ix);
u = ax-bp[k];
v = one/(ax+bp[k]);
ss = u*v;
s_h = zero_low(ss);
t_h = zero;
t_h = set_high_word(t_h, ((ix>>1)|0x20000000)+0x00080000+(k<<18));
t_l = ax - (t_h-bp[k]);
s_l = v*((u-s_h*t_h)-s_h*t_l);
s2 = ss*ss;
r = s2*s2*eval_poly(s2, L_tab, 6);
r += s_l*(s_h+ss);
s2 = s_h*s_h;
t_h = zero_low(3.0+s2+r);
t_l = r-((t_h-3.0)-s2);
u = s_h*t_h;
v = s_l*t_h+t_l*ss;
p_h = zero_low(u+v);
p_l = v-(p_h-u);
z_h = cp_h*p_h;
z_l = cp_l*p_h+p_l*cp+dp_l[k];
t = (double)n;
t1 = zero_low(((z_h+z_l)+dp_h[k])+t);
t2 = z_l-(((t1-t)-dp_h[k])-z_h);
*pt1 = t1;
*pt2 = t2;
}
static double js_log_internal(double x, int flag)
{
double p_h, p_l, t, u, v;
int hx, lx;
EXTRACT_WORDS(hx, lx, x);
if (hx <= 0) {
if (((hx&0x7fffffff)|lx)==0)
return -INFINITY;
if (hx<0)
return NAN;
} else if (hx >= 0x7ff00000) {
return x+x;
}
kernel_log2(&p_h, &p_l, x);
t = p_h + p_l;
if (flag == 0) {
return t;
} else {
t = zero_low(t);
if (flag == 1) {
u = t*lg2_h;
v = (p_l-(t-p_h))*lg2+t*lg2_l;
} else {
u = t*ivlg10b2_h;
v = (p_l-(t-p_h))*ivlg10b2+t*ivlg10b2_l;
}
return u+v;
}
}
double js_log2(double x)
{
return js_log_internal(x, 0);
}
double js_log(double x)
{
return js_log_internal(x, 1);
}
double js_log10(double x)
{
return js_log_internal(x, 2);
}
double js_pow(double x, double y)
{
double z,ax,p_h,p_l;
double y1,t1,t2,s,t,u,v,w;
int i,j,k,yisint,n;
int hx,hy,ix,iy;
unsigned lx,ly;
EXTRACT_WORDS(hx, lx, x);
EXTRACT_WORDS(hy, ly, y);
ix = hx&0x7fffffff; iy = hy&0x7fffffff;
if((iy|ly)==0) return one;
if(ix > 0x7ff00000 || ((ix==0x7ff00000)&&(lx!=0)) ||
iy > 0x7ff00000 || ((iy==0x7ff00000)&&(ly!=0)))
return x+y;
yisint = 0;
if(hx<0) {
if(iy>=0x43400000) yisint = 2;
else if(iy>=0x3ff00000) {
k = (iy>>20)-0x3ff;
if(k>20) {
j = ly>>(52-k);
if((j<<(52-k))==ly) yisint = 2-(j&1);
} else if(ly==0) {
j = iy>>(20-k);
if((j<<(20-k))==iy) yisint = 2-(j&1);
}
}
}
if(ly==0) {
if (iy==0x7ff00000) {
if(((ix-0x3ff00000)|lx)==0)
return y - y;
else if (ix >= 0x3ff00000)
return (hy>=0)? y: zero;
else
return (hy<0)?-y: zero;
}
if(iy==0x3ff00000) {
if(hy<0) return one/x; else return x;
}
if(hy==0x40000000) return x*x;
if(hy==0x3fe00000) {
if(hx>=0)
return js_sqrt(x);
}
}
ax = fabs(x);
if(lx==0) {
if(ix==0x7ff00000||ix==0||ix==0x3ff00000){
z = ax;
if(hy<0) z = one/z;
if(hx<0) {
if(((ix-0x3ff00000)|yisint)==0) {
z = (z-z)/(z-z);
} else if(yisint==1)
z = -z;
}
return z;
}
}
n = (hx>>31)+1;
if((n|yisint)==0) return (x-x)/(x-x);
s = one;
if((n|(yisint-1))==0) s = -one;
if(iy>0x41e00000) {
if(iy>0x43f00000){
if(ix<=0x3fefffff) return (hy<0)? huge*huge:tiny*tiny;
if(ix>=0x3ff00000) return (hy>0)? huge*huge:tiny*tiny;
}
if(ix<0x3fefffff) return (hy<0)? s*huge*huge:s*tiny*tiny;
if(ix>0x3ff00000) return (hy>0)? s*huge*huge:s*tiny*tiny;
t = ax-one;
w = (t*t)*(0.5-t*(0.3333333333333333333333-t*0.25));
u = ivln2_h*t;
v = t*ivln2_l-w*ivln2;
t1 = zero_low(u+v);
t2 = v-(t1-u);
} else {
kernel_log2(&t1, &t2, ax);
}
y1 = zero_low(y);
p_l = (y-y1)*t1+y*t2;
p_h = y1*t1;
z = p_l+p_h;
EXTRACT_WORDS(j, i, z);
if (j>=0x40900000) {
if(((j-0x40900000)|i)!=0)
return s*huge*huge;
else {
if(p_l+ovt>z-p_h) return s*huge*huge;
}
} else if((j&0x7fffffff)>=0x4090cc00 ) {
if(((j-0xc090cc00)|i)!=0)
return s*tiny*tiny;
else {
if(p_l<=z-p_h) return s*tiny*tiny;
}
}
i = j&0x7fffffff;
k = (i>>20)-0x3ff;
n = 0;
if(i>0x3fe00000) {
n = j+(0x00100000>>(k+1));
k = ((n&0x7fffffff)>>20)-0x3ff;
t = zero;
t = set_high_word(t, n&~(0x000fffff>>k));
n = ((n&0x000fffff)|0x00100000)>>(20-k);
if(j<0) n = -n;
p_h -= t;
}
t = zero_low(p_l+p_h);
u = t*lg2_h;
v = (p_l-(t-p_h))*lg2+t*lg2_l;
z = u+v;
w = v-(z-u);
return s * kernel_exp(z, w, 0, z, n);
}