mirror of https://gitlab.com/nakst/essence
1942 lines
46 KiB
C++
1942 lines
46 KiB
C++
// This file is part of the Essence operating system.
|
|
// It is released under the terms of the MIT license -- see LICENSE.md.
|
|
// Written by: nakst.
|
|
|
|
/////////////////////////////////
|
|
// Basic utilities.
|
|
/////////////////////////////////
|
|
|
|
#if defined(SHARED_MATH_WANT_BASIC_UTILITIES) || defined(SHARED_MATH_WANT_ALL)
|
|
|
|
template <class T>
|
|
inline T RoundDown(T value, T divisor) {
|
|
value /= divisor;
|
|
value *= divisor;
|
|
return value;
|
|
}
|
|
|
|
template <class T>
|
|
inline T RoundUp(T value, T divisor) {
|
|
value += divisor - 1;
|
|
value /= divisor;
|
|
value *= divisor;
|
|
return value;
|
|
}
|
|
|
|
inline int DistanceSquared(int x, int y) {
|
|
return x * x + y * y;
|
|
}
|
|
|
|
inline int DistanceSquared(int x1, int y1, int x2, int y2) {
|
|
int dx = x2 - x1;
|
|
int dy = y2 - y1;
|
|
return dx * dx + dy * dy;
|
|
}
|
|
|
|
__attribute__((no_instrument_function))
|
|
inline int ClampInteger(int low, int high, int integer) {
|
|
if (integer < low) return low;
|
|
if (integer > high) return high;
|
|
return integer;
|
|
}
|
|
|
|
inline double ClampDouble(double low, double high, double x) {
|
|
if (x < low) return low;
|
|
if (x > high) return high;
|
|
return x;
|
|
}
|
|
|
|
inline intptr_t ClampIntptr(intptr_t low, intptr_t high, intptr_t integer) {
|
|
if (integer < low) return low;
|
|
if (integer > high) return high;
|
|
return integer;
|
|
}
|
|
|
|
__attribute__((no_instrument_function))
|
|
inline int MaximumInteger(int a, int b) {
|
|
return a > b ? a : b;
|
|
}
|
|
|
|
#define MaximumInteger3 MaximumInteger
|
|
#define MinimumInteger3 MinimumInteger
|
|
|
|
__attribute__((no_instrument_function))
|
|
inline int MaximumInteger(int a, int b, int c) {
|
|
return MaximumInteger(MaximumInteger(a, b), c);
|
|
}
|
|
|
|
__attribute__((no_instrument_function))
|
|
inline int MaximumInteger(int a, int b, int c, int d) {
|
|
return MaximumInteger(MaximumInteger(a, b, c), d);
|
|
}
|
|
|
|
__attribute__((no_instrument_function))
|
|
inline int MinimumInteger(int a, int b) {
|
|
return a < b ? a : b;
|
|
}
|
|
|
|
inline float AbsoluteFloat(float f) {
|
|
return f > 0 ? f : -f;
|
|
}
|
|
|
|
inline float SignFloat(float f) {
|
|
return f < 0 ? -1 : f > 0 ? 1 : 0;
|
|
}
|
|
|
|
inline int AbsoluteInteger(int a) {
|
|
return a > 0 ? a : -a;
|
|
}
|
|
|
|
inline int64_t AbsoluteInteger64(int64_t a) {
|
|
return a > 0 ? a : -a;
|
|
}
|
|
|
|
#endif
|
|
|
|
/////////////////////////////////
|
|
// Interpolation.
|
|
/////////////////////////////////
|
|
|
|
#if defined(SHARED_MATH_WANT_INTERPOLATION) || defined(SHARED_MATH_WANT_ALL)
|
|
|
|
float LinearMap(float inFrom, float inTo, float outFrom, float outTo, float value) {
|
|
float raw = (value - inFrom) / (inTo - inFrom);
|
|
return raw * (outTo - outFrom) + outFrom;
|
|
}
|
|
|
|
__attribute__((no_instrument_function))
|
|
float LinearInterpolate(float from, float to, float progress) {
|
|
return from + progress * (to - from);
|
|
}
|
|
|
|
float RubberBand(float original, float target) {
|
|
float sign = SignFloat(original - target);
|
|
float distance = AbsoluteFloat(original - target);
|
|
float amount = EsCRTlog2f(distance);
|
|
return target + sign * amount * 2.0f;
|
|
}
|
|
|
|
float GammaInterpolate(float from, float to, float progress) {
|
|
from = from * from;
|
|
to = to * to;
|
|
return EsCRTsqrtf(from + progress * (to - from));
|
|
}
|
|
|
|
double SmoothAnimationTime(double progress) {
|
|
if (progress > 1) return 1;
|
|
progress -= 1;
|
|
return 1 + progress * progress * progress;
|
|
}
|
|
|
|
double SmoothAnimationTimeSharp(double progress) {
|
|
if (progress > 1) return 1;
|
|
progress -= 1;
|
|
double progressSquared = progress * progress;
|
|
return 1 + progressSquared * progressSquared * progress;
|
|
}
|
|
|
|
#ifndef EsColorInterpolate
|
|
uint32_t EsColorInterpolate(uint32_t from, uint32_t to, float progress) {
|
|
float fa = ((from >> 24) & 0xFF) / 255.0f;
|
|
float fb = ((from >> 16) & 0xFF) / 255.0f;
|
|
float fg = ((from >> 8) & 0xFF) / 255.0f;
|
|
float fr = ((from >> 0) & 0xFF) / 255.0f;
|
|
float ta = ((to >> 24) & 0xFF) / 255.0f;
|
|
float tb = ((to >> 16) & 0xFF) / 255.0f;
|
|
float tg = ((to >> 8) & 0xFF) / 255.0f;
|
|
float tr = ((to >> 0) & 0xFF) / 255.0f;
|
|
|
|
if (fa && !ta) { tr = fr, tg = fg, tb = fb; }
|
|
if (ta && !fa) { fr = tr, fg = tg, fb = tb; }
|
|
|
|
return (uint32_t) (LinearInterpolate(fr, tr, progress) * 255.0f) << 0
|
|
| (uint32_t) (LinearInterpolate(fg, tg, progress) * 255.0f) << 8
|
|
| (uint32_t) (LinearInterpolate(fb, tb, progress) * 255.0f) << 16
|
|
| (uint32_t) (LinearInterpolate(fa, ta, progress) * 255.0f) << 24;
|
|
}
|
|
#endif
|
|
|
|
#ifndef EsRectangleLinearInterpolate
|
|
EsRectangle EsRectangleLinearInterpolate(EsRectangle from, EsRectangle to, float progress) {
|
|
return ES_RECT_4(LinearInterpolate(from.l, to.l, progress), LinearInterpolate(from.r, to.r, progress),
|
|
LinearInterpolate(from.t, to.t, progress), LinearInterpolate(from.b, to.b, progress));
|
|
}
|
|
#endif
|
|
|
|
#endif
|
|
|
|
/////////////////////////////////
|
|
// HSV colors.
|
|
/////////////////////////////////
|
|
|
|
#ifdef SHARED_MATH_WANT_ALL
|
|
|
|
uint32_t EsColorConvertToRGB(float h, float s, float v) {
|
|
float r = 0, g = 0, b = 0;
|
|
|
|
if (!s) {
|
|
r = g = b = v;
|
|
} else {
|
|
float f = h - EsCRTfloorf(h);
|
|
float x = v * (1 - s), y = v * (1 - s * f), z = v * (1 - s * (1 - f));
|
|
|
|
switch ((int) h) {
|
|
case 0: r = v, g = z, b = x; break;
|
|
case 1: r = y, g = v, b = x; break;
|
|
case 2: r = x, g = v, b = z; break;
|
|
case 3: r = x, g = y, b = v; break;
|
|
case 4: r = z, g = x, b = v; break;
|
|
case 5: r = v, g = x, b = y; break;
|
|
}
|
|
}
|
|
|
|
return (((uint32_t) (r * 255)) << 16) | (((uint32_t) (g * 255)) << 8) | (((uint32_t) (b * 255)) << 0);
|
|
}
|
|
|
|
bool EsColorConvertToHSV(uint32_t color, float *h, float *s, float *v) {
|
|
float r = (float) ((color >> 16) & 0xFF) / 255.0f;
|
|
float g = (float) ((color >> 8) & 0xFF) / 255.0f;
|
|
float b = (float) ((color >> 0) & 0xFF) / 255.0f;
|
|
|
|
float maximum = (r > g && r > b) ? r : (g > b ? g : b),
|
|
minimum = (r < g && r < b) ? r : (g < b ? g : b),
|
|
difference = maximum - minimum;
|
|
*v = maximum;
|
|
|
|
if (!difference) {
|
|
*s = 0;
|
|
return false;
|
|
} else {
|
|
if (r == maximum) *h = (g - b) / difference + 0;
|
|
if (g == maximum) *h = (b - r) / difference + 2;
|
|
if (b == maximum) *h = (r - g) / difference + 4;
|
|
if (*h < 0) *h += 6;
|
|
*s = difference / maximum;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool EsColorIsLight(uint32_t color) {
|
|
float r = (color & 0xFF0000) >> 16;
|
|
float g = (color & 0x00FF00) >> 8;
|
|
float b = (color & 0x0000FF) >> 0;
|
|
float brightness = r * r * 0.241f + g * g * 0.691f + b * b * 0.068f;
|
|
return brightness >= 180.0f * 180.0f;
|
|
}
|
|
|
|
#endif
|
|
|
|
/////////////////////////////////
|
|
// Standard mathematical functions.
|
|
/////////////////////////////////
|
|
|
|
#ifdef SHARED_MATH_WANT_ALL
|
|
|
|
union ConvertFloatInteger {
|
|
float f;
|
|
uint32_t i;
|
|
};
|
|
|
|
union ConvertDoubleInteger {
|
|
double d;
|
|
uint64_t i;
|
|
};
|
|
|
|
float EsCRTfloorf(float x) {
|
|
ConvertFloatInteger convert = {x};
|
|
uint32_t sign = convert.i & 0x80000000;
|
|
int exponent = (int) ((convert.i >> 23) & 0xFF) - 0x7F;
|
|
|
|
if (exponent >= 23) {
|
|
// There aren't any bits representing a fractional part.
|
|
} else if (exponent >= 0) {
|
|
// Positive exponent.
|
|
uint32_t mask = 0x7FFFFF >> exponent;
|
|
if (!(mask & convert.i)) return x; // Already an integer.
|
|
if (sign) convert.i += mask;
|
|
convert.i &= ~mask; // Mask out the fractional bits.
|
|
} else if (exponent < 0) {
|
|
// Negative exponent.
|
|
return sign ? -1.0 : 0.0;
|
|
}
|
|
|
|
return convert.f;
|
|
}
|
|
|
|
double EsCRTfloor(double x) {
|
|
if (x == 0) return x;
|
|
|
|
const double doubleToInteger = 1.0 / 2.22044604925031308085e-16;
|
|
|
|
ConvertDoubleInteger convert = {x};
|
|
uint64_t sign = convert.i & 0x8000000000000000;
|
|
int exponent = (int) ((convert.i >> 52) & 0x7FF) - 0x3FF;
|
|
|
|
if (exponent >= 52) {
|
|
// There aren't any bits representing a fractional part.
|
|
return x;
|
|
} else if (exponent >= 0) {
|
|
// Positive exponent.
|
|
double y = sign ? (x - doubleToInteger + doubleToInteger - x) : (x + doubleToInteger - doubleToInteger - x);
|
|
return y > 0 ? x + y - 1 : x + y;
|
|
} else if (exponent < 0) {
|
|
// Negative exponent.
|
|
return sign ? -1.0 : 0.0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
double EsCRTceil(double x) {
|
|
if (x == 0) return x;
|
|
|
|
const double doubleToInteger = 1.0 / 2.22044604925031308085e-16;
|
|
|
|
ConvertDoubleInteger convert = {x};
|
|
uint64_t sign = convert.i & 0x8000000000000000;
|
|
int exponent = (int) ((convert.i >> 52) & 0x7FF) - 0x3FF;
|
|
|
|
if (exponent >= 52) {
|
|
// There aren't any bits representing a fractional part.
|
|
return x;
|
|
} else if (exponent >= 0) {
|
|
// Positive exponent.
|
|
double y = sign ? (x - doubleToInteger + doubleToInteger - x) : (x + doubleToInteger - doubleToInteger - x);
|
|
return y < 0 ? x + y + 1 : x + y;
|
|
} else if (exponent < 0) {
|
|
// Negative exponent.
|
|
return sign ? -0.0 : 1.0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
double EsCRTfabs(double x) {
|
|
ConvertDoubleInteger convert = {x};
|
|
convert.i &= ~0x8000000000000000;
|
|
return convert.d;
|
|
}
|
|
|
|
float EsCRTfabsf(float x) {
|
|
ConvertFloatInteger convert = {x};
|
|
convert.i &= ~0x80000000;
|
|
return convert.f;
|
|
}
|
|
|
|
float EsCRTceilf(float x) {
|
|
if (x == 0) return x;
|
|
|
|
ConvertFloatInteger convert = {x};
|
|
uint32_t sign = convert.i & 0x80000000;
|
|
int exponent = (int) ((convert.i >> 23) & 0xFF) - 0x7F;
|
|
|
|
if (exponent >= 23) {
|
|
// There aren't any bits representing a fractional part.
|
|
} else if (exponent >= 0) {
|
|
// Positive exponent.
|
|
uint32_t mask = 0x7FFFFF >> exponent;
|
|
if (!(mask & convert.i)) return x; // Already an integer.
|
|
if (!sign) convert.i += mask;
|
|
convert.i &= ~mask; // Mask out the fractional bits.
|
|
} else if (exponent < 0) {
|
|
// Negative exponent.
|
|
return sign ? -0.0 : 1.0;
|
|
}
|
|
|
|
return convert.f;
|
|
}
|
|
|
|
#define D(x) (((ConvertDoubleInteger) { .i = (x) }).d)
|
|
#define F(x) (((ConvertFloatInteger) { .i = (x) }).f)
|
|
|
|
double _Sine(double x) {
|
|
// Calculates sin(x) for x in [0, pi/4].
|
|
|
|
double x2 = x * x;
|
|
|
|
return x * (D(0x3FF0000000000000) + x2 * (D(0xBFC5555555555540) + x2 * (D(0x3F8111111110ED80) + x2 * (D(0xBF2A01A019AE6000)
|
|
+ x2 * (D(0x3EC71DE349280000) + x2 * (D(0xBE5AE5DC48000000) + x2 * D(0x3DE5D68200000000)))))));
|
|
}
|
|
|
|
float _SineFloat(float x) {
|
|
// Calculates sin(x) for x in [0, pi/4].
|
|
|
|
float x2 = x * x;
|
|
|
|
return x * (F(0x3F800000) + x2 * (F(0xBE2AAAA0) + x2 * (F(0x3C0882C0) + x2 * F(0xB94C6000))));
|
|
}
|
|
|
|
double _ArcSine(double x) {
|
|
// Calculates arcsin(x) for x in [0, 0.5].
|
|
|
|
double x2 = x * x;
|
|
|
|
return x * (D(0x3FEFFFFFFFFFFFE6) + x2 * (D(0x3FC555555555FE00) + x2 * (D(0x3FB333333292DF90) + x2 * (D(0x3FA6DB6DFD3693A0)
|
|
+ x2 * (D(0x3F9F1C608DE51900) + x2 * (D(0x3F96EA0659B9A080) + x2 * (D(0x3F91B4ABF1029100)
|
|
+ x2 * (D(0x3F8DA8DAF31ECD00) + x2 * (D(0x3F81C01FD5000C00) + x2 * (D(0x3F94BDA038CF6B00)
|
|
+ x2 * (D(0xBF8E849CA75B1E00) + x2 * D(0x3FA146C2D37F2C60))))))))))));
|
|
}
|
|
|
|
float _ArcSineFloat(float x) {
|
|
// Calculates arcsin(x) for x in [0, 0.5].
|
|
|
|
float x2 = x * x;
|
|
|
|
return x * (F(0x3F800004) + x2 * (F(0x3E2AA130) + x2 * (F(0x3D9B2C28) + x2 * (F(0x3D1C1800) + x2 * F(0x3D5A97C0)))));
|
|
}
|
|
|
|
double _ArcTangent(double x) {
|
|
// Calculates arctan(x) for x in [0, 0.5].
|
|
|
|
double x2 = x * x;
|
|
|
|
return x * (D(0x3FEFFFFFFFFFFFF8) + x2 * (D(0xBFD5555555553B44) + x2 * (D(0x3FC9999999803988) + x2 * (D(0xBFC249248C882E80)
|
|
+ x2 * (D(0x3FBC71C5A4E4C220) + x2 * (D(0xBFB745B3B75243F0) + x2 * (D(0x3FB3AFAE9A2939E0)
|
|
+ x2 * (D(0xBFB1030C4A4A1B90) + x2 * (D(0x3FAD6F65C35579A0) + x2 * (D(0xBFA805BCFDAFEDC0)
|
|
+ x2 * (D(0x3F9FC6B5E115F2C0) + x2 * D(0xBF87DCA5AB25BF80))))))))))));
|
|
}
|
|
|
|
float _ArcTangentFloat(float x) {
|
|
// Calculates arctan(x) for x in [0, 0.5].
|
|
|
|
float x2 = x * x;
|
|
|
|
return x * (F(0x3F7FFFF8) + x2 * (F(0xBEAAA53C) + x2 * (F(0x3E4BC990) + x2 * (F(0xBE084A60) + x2 * F(0x3D8864B0)))));
|
|
}
|
|
|
|
double _Cosine(double x) {
|
|
// Calculates cos(x) for x in [0, pi/4].
|
|
|
|
double x2 = x * x;
|
|
|
|
return D(0x3FF0000000000000) + x2 * (D(0xBFDFFFFFFFFFFFA0) + x2 * (D(0x3FA555555554F7C0) + x2 * (D(0xBF56C16C16475C00)
|
|
+ x2 * (D(0x3EFA019F87490000) + x2 * (D(0xBE927DF66B000000) + x2 * D(0x3E21B949E0000000))))));
|
|
}
|
|
|
|
float _CosineFloat(float x) {
|
|
// Calculates cos(x) for x in [0, pi/4].
|
|
|
|
float x2 = x * x;
|
|
|
|
return F(0x3F800000) + x2 * (F(0xBEFFFFDA) + x2 * (F(0x3D2A9F60) + x2 * F(0xBAB22C00)));
|
|
}
|
|
|
|
double _Tangent(double x) {
|
|
// Calculates tan(x) for x in [0, pi/4].
|
|
|
|
double x2 = x * x;
|
|
|
|
return x * (D(0x3FEFFFFFFFFFFFE8) + x2 * (D(0x3FD5555555558000) + x2 * (D(0x3FC1111110FACF90) + x2 * (D(0x3FABA1BA266BFD20)
|
|
+ x2 * (D(0x3F9664F30E56E580) + x2 * (D(0x3F822703B08BDC00) + x2 * (D(0x3F6D698D2E4A4C00)
|
|
+ x2 * (D(0x3F57FF4F23EA4400) + x2 * (D(0x3F424F3BEC845800) + x2 * (D(0x3F34C78CA9F61000)
|
|
+ x2 * (D(0xBF042089F8510000) + x2 * (D(0x3F29D7372D3A8000) + x2 * (D(0xBF19D1C5EF6F0000)
|
|
+ x2 * (D(0x3F0980BDF11E8000)))))))))))))));
|
|
}
|
|
|
|
float _TangentFloat(float x) {
|
|
// Calculates tan(x) for x in [0, pi/4].
|
|
|
|
float x2 = x * x;
|
|
|
|
return x * (F(0x3F800001) + x2 * (F(0x3EAAA9AA) + x2 * (F(0x3E08ABA8) + x2 * (F(0x3D58EC90)
|
|
+ x2 * (F(0x3CD24840) + x2 * (F(0x3AC3CA00) + x2 * F(0x3C272F00)))))));
|
|
}
|
|
|
|
double EsCRTsin(double x) {
|
|
bool negate = false;
|
|
|
|
// x in -infty, infty
|
|
|
|
if (x < 0) {
|
|
x = -x;
|
|
negate = true;
|
|
}
|
|
|
|
// x in 0, infty
|
|
|
|
x -= 2 * ES_PI * EsCRTfloor(x / (2 * ES_PI));
|
|
|
|
// x in 0, 2*pi
|
|
|
|
if (x < ES_PI / 2) {
|
|
} else if (x < ES_PI) {
|
|
x = ES_PI - x;
|
|
} else if (x < 3 * ES_PI / 2) {
|
|
x = x - ES_PI;
|
|
negate = !negate;
|
|
} else {
|
|
x = ES_PI * 2 - x;
|
|
negate = !negate;
|
|
}
|
|
|
|
// x in 0, pi/2
|
|
|
|
double y = x < ES_PI / 4 ? _Sine(x) : _Cosine(ES_PI / 2 - x);
|
|
return negate ? -y : y;
|
|
}
|
|
|
|
float EsCRTsinf(float x) {
|
|
bool negate = false;
|
|
|
|
// x in -infty, infty
|
|
|
|
if (x < 0) {
|
|
x = -x;
|
|
negate = true;
|
|
}
|
|
|
|
// x in 0, infty
|
|
|
|
x -= 2 * ES_PI * EsCRTfloorf(x / (2 * ES_PI));
|
|
|
|
// x in 0, 2*pi
|
|
|
|
if (x < ES_PI / 2) {
|
|
} else if (x < ES_PI) {
|
|
x = ES_PI - x;
|
|
} else if (x < 3 * ES_PI / 2) {
|
|
x = x - ES_PI;
|
|
negate = !negate;
|
|
} else {
|
|
x = ES_PI * 2 - x;
|
|
negate = !negate;
|
|
}
|
|
|
|
// x in 0, pi/2
|
|
|
|
float y = x < ES_PI / 4 ? _SineFloat(x) : _CosineFloat(ES_PI / 2 - x);
|
|
return negate ? -y : y;
|
|
}
|
|
|
|
double EsCRTcos(double x) {
|
|
bool negate = false;
|
|
|
|
// x in -infty, infty
|
|
|
|
if (x < 0) {
|
|
x = -x;
|
|
}
|
|
|
|
// x in 0, infty
|
|
|
|
x -= 2 * ES_PI * EsCRTfloor(x / (2 * ES_PI));
|
|
|
|
// x in 0, 2*pi
|
|
|
|
if (x < ES_PI / 2) {
|
|
} else if (x < ES_PI) {
|
|
x = ES_PI - x;
|
|
negate = !negate;
|
|
} else if (x < 3 * ES_PI / 2) {
|
|
x = x - ES_PI;
|
|
negate = !negate;
|
|
} else {
|
|
x = ES_PI * 2 - x;
|
|
}
|
|
|
|
// x in 0, pi/2
|
|
|
|
double y = x < ES_PI / 4 ? _Cosine(x) : _Sine(ES_PI / 2 - x);
|
|
return negate ? -y : y;
|
|
}
|
|
|
|
float EsCRTcosf(float x) {
|
|
bool negate = false;
|
|
|
|
// x in -infty, infty
|
|
|
|
if (x < 0) {
|
|
x = -x;
|
|
}
|
|
|
|
// x in 0, infty
|
|
|
|
x -= 2 * ES_PI * EsCRTfloorf(x / (2 * ES_PI));
|
|
|
|
// x in 0, 2*pi
|
|
|
|
if (x < ES_PI / 2) {
|
|
} else if (x < ES_PI) {
|
|
x = ES_PI - x;
|
|
negate = !negate;
|
|
} else if (x < 3 * ES_PI / 2) {
|
|
x = x - ES_PI;
|
|
negate = !negate;
|
|
} else {
|
|
x = ES_PI * 2 - x;
|
|
}
|
|
|
|
// x in 0, pi/2
|
|
|
|
float y = x < ES_PI / 4 ? _CosineFloat(x) : _SineFloat(ES_PI / 2 - x);
|
|
return negate ? -y : y;
|
|
}
|
|
|
|
double EsCRTtan(double x) {
|
|
bool negate = false;
|
|
|
|
// x in -infty, infty
|
|
|
|
if (x < 0) {
|
|
x = -x;
|
|
negate = !negate;
|
|
}
|
|
|
|
// x in 0, infty
|
|
|
|
x -= ES_PI * EsCRTfloor(x / ES_PI);
|
|
|
|
// x in 0, pi
|
|
|
|
if (x > ES_PI / 2) {
|
|
x = ES_PI - x;
|
|
negate = !negate;
|
|
}
|
|
|
|
// x in 0, pi/2
|
|
|
|
double y = x < ES_PI / 4 ? _Tangent(x) : (1.0 / _Tangent(ES_PI / 2 - x));
|
|
return negate ? -y : y;
|
|
}
|
|
|
|
float EsCRTtanf(float x) {
|
|
bool negate = false;
|
|
|
|
// x in -infty, infty
|
|
|
|
if (x < 0) {
|
|
x = -x;
|
|
negate = !negate;
|
|
}
|
|
|
|
// x in 0, infty
|
|
|
|
x -= ES_PI * EsCRTfloorf(x / ES_PI);
|
|
|
|
// x in 0, pi
|
|
|
|
if (x > ES_PI / 2) {
|
|
x = ES_PI - x;
|
|
negate = !negate;
|
|
}
|
|
|
|
// x in 0, pi/2
|
|
|
|
float y = x < ES_PI / 4 ? _TangentFloat(x) : (1.0 / _TangentFloat(ES_PI / 2 - x));
|
|
return negate ? -y : y;
|
|
}
|
|
|
|
double EsCRTasin(double x) {
|
|
bool negate = false;
|
|
|
|
if (x < 0) {
|
|
x = -x;
|
|
negate = true;
|
|
}
|
|
|
|
double y;
|
|
|
|
if (x < 0.5) {
|
|
y = _ArcSine(x);
|
|
} else {
|
|
y = ES_PI / 2 - 2 * _ArcSine(EsCRTsqrt(0.5 - 0.5 * x));
|
|
}
|
|
|
|
return negate ? -y : y;
|
|
}
|
|
|
|
float EsCRTasinf(float x) {
|
|
bool negate = false;
|
|
|
|
if (x < 0) {
|
|
x = -x;
|
|
negate = true;
|
|
}
|
|
|
|
float y;
|
|
|
|
if (x < 0.5) {
|
|
y = _ArcSineFloat(x);
|
|
} else {
|
|
y = ES_PI / 2 - 2 * _ArcSineFloat(EsCRTsqrtf(0.5 - 0.5 * x));
|
|
}
|
|
|
|
return negate ? -y : y;
|
|
}
|
|
|
|
double EsCRTacos(double x) {
|
|
return EsCRTasin(-x) + ES_PI / 2;
|
|
}
|
|
|
|
float EsCRTacosf(float x) {
|
|
return EsCRTasinf(-x) + ES_PI / 2;
|
|
}
|
|
|
|
double EsCRTatan(double x) {
|
|
bool negate = false;
|
|
|
|
if (x < 0) {
|
|
x = -x;
|
|
negate = true;
|
|
}
|
|
|
|
bool reciprocalTaken = false;
|
|
|
|
if (x > 1) {
|
|
x = 1 / x;
|
|
reciprocalTaken = true;
|
|
}
|
|
|
|
double y;
|
|
|
|
if (x < 0.5) {
|
|
y = _ArcTangent(x);
|
|
} else {
|
|
y = 0.463647609000806116 + _ArcTangent((2 * x - 1) / (2 + x));
|
|
}
|
|
|
|
if (reciprocalTaken) {
|
|
y = ES_PI / 2 - y;
|
|
}
|
|
|
|
return negate ? -y : y;
|
|
}
|
|
|
|
float EsCRTatanf(float x) {
|
|
bool negate = false;
|
|
|
|
if (x < 0) {
|
|
x = -x;
|
|
negate = true;
|
|
}
|
|
|
|
bool reciprocalTaken = false;
|
|
|
|
if (x > 1) {
|
|
x = 1 / x;
|
|
reciprocalTaken = true;
|
|
}
|
|
|
|
float y;
|
|
|
|
if (x < 0.5f) {
|
|
y = _ArcTangentFloat(x);
|
|
} else {
|
|
y = 0.463647609000806116f + _ArcTangentFloat((2 * x - 1) / (2 + x));
|
|
}
|
|
|
|
if (reciprocalTaken) {
|
|
y = ES_PI / 2 - y;
|
|
}
|
|
|
|
return negate ? -y : y;
|
|
}
|
|
|
|
double EsCRTatan2(double y, double x) {
|
|
if (x == 0) return y > 0 ? ES_PI / 2 : -ES_PI / 2;
|
|
else if (x > 0) return EsCRTatan(y / x);
|
|
else if (y >= 0) return ES_PI + EsCRTatan(y / x);
|
|
else return -ES_PI + EsCRTatan(y / x);
|
|
}
|
|
|
|
float EsCRTatan2f(float y, float x) {
|
|
if (x == 0) return y > 0 ? ES_PI / 2 : -ES_PI / 2;
|
|
else if (x > 0) return EsCRTatanf(y / x);
|
|
else if (y >= 0) return ES_PI + EsCRTatanf(y / x);
|
|
else return -ES_PI + EsCRTatanf(y / x);
|
|
}
|
|
|
|
double EsCRTexp2(double x) {
|
|
double a = EsCRTfloor(x * 8);
|
|
int64_t ai = a;
|
|
|
|
if (ai < -1024) {
|
|
return 0;
|
|
}
|
|
|
|
double b = x - a / 8;
|
|
|
|
double y = D(0x3FF0000000000000) + b * (D(0x3FE62E42FEFA3A00) + b * (D(0x3FCEBFBDFF829140)
|
|
+ b * (D(0x3FAC6B08D73C4A40) + b * (D(0x3F83B2AB53873280) + b * (D(0x3F55D88F363C6C00)
|
|
+ b * (D(0x3F242C003E4A2000) + b * D(0x3EF0B291F6C00000)))))));
|
|
|
|
const double m[8] = {
|
|
D(0x3FF0000000000000),
|
|
D(0x3FF172B83C7D517B),
|
|
D(0x3FF306FE0A31B715),
|
|
D(0x3FF4BFDAD5362A27),
|
|
D(0x3FF6A09E667F3BCD),
|
|
D(0x3FF8ACE5422AA0DB),
|
|
D(0x3FFAE89F995AD3AD),
|
|
D(0x3FFD5818DCFBA487),
|
|
};
|
|
|
|
y *= m[ai & 7];
|
|
|
|
ConvertDoubleInteger c;
|
|
c.d = y;
|
|
c.i += (ai >> 3) << 52;
|
|
return c.d;
|
|
}
|
|
|
|
float EsCRTexp2f(float x) {
|
|
float a = EsCRTfloorf(x);
|
|
int32_t ai = a;
|
|
|
|
if (ai < -128) {
|
|
return 0;
|
|
}
|
|
|
|
float b = x - a;
|
|
|
|
float y = F(0x3F7FFFFE) + b * (F(0x3F31729A) + b * (F(0x3E75E700)
|
|
+ b * (F(0x3D64D520) + b * (F(0x3C128280) + b * F(0x3AF89400)))));
|
|
|
|
ConvertFloatInteger c;
|
|
c.f = y;
|
|
c.i += ai << 23;
|
|
return c.f;
|
|
}
|
|
|
|
double EsCRTlog2(double x) {
|
|
ConvertDoubleInteger c;
|
|
c.d = x;
|
|
int64_t e = ((c.i >> 52) & 2047) - 0x3FF;
|
|
c.i = (c.i & ~((uint64_t) 0x7FF << 52)) + ((uint64_t) 0x3FF << 52);
|
|
x = c.d;
|
|
|
|
double a;
|
|
|
|
if (x < 1.125) {
|
|
a = 0;
|
|
} else if (x < 1.250) {
|
|
x *= 1.125 / 1.250;
|
|
a = D(0xBFC374D65D9E608E);
|
|
} else if (x < 1.375) {
|
|
x *= 1.125 / 1.375;
|
|
a = D(0xBFD28746C334FECB);
|
|
} else if (x < 1.500) {
|
|
x *= 1.125 / 1.500;
|
|
a = D(0xBFDA8FF971810A5E);
|
|
} else if (x < 1.625) {
|
|
x *= 1.125 / 1.625;
|
|
a = D(0xBFE0F9F9FFC8932A);
|
|
} else if (x < 1.750) {
|
|
x *= 1.125 / 1.750;
|
|
a = D(0xBFE465D36ED11B11);
|
|
} else if (x < 1.875) {
|
|
x *= 1.125 / 1.875;
|
|
a = D(0xBFE79538DEA712F5);
|
|
} else {
|
|
x *= 1.125 / 2.000;
|
|
a = D(0xBFEA8FF971810A5E);
|
|
}
|
|
|
|
double y = D(0xC00FF8445026AD97) + x * (D(0x40287A7A02D9353F) + x * (D(0xC03711C58D55CEE2)
|
|
+ x * (D(0x4040E8263C321A26) + x * (D(0xC041EB22EA691BB3) + x * (D(0x403B00FB376D1F10)
|
|
+ x * (D(0xC02C416ABE857241) + x * (D(0x40138BA7FAA3523A) + x * (D(0xBFF019731AF80316)
|
|
+ x * D(0x3FB7F1CD3852C200)))))))));
|
|
|
|
return y - a + e;
|
|
}
|
|
|
|
float EsCRTlog2f(float x) {
|
|
// TODO EsCRTlog2f(0.9999971379999999f) gives 0.00000143051147460938 (should be negative!!).
|
|
|
|
ConvertFloatInteger c;
|
|
c.f = x;
|
|
int32_t e = ((c.i >> 23) & 255) - 0x7F;
|
|
c.i = (c.i & ~(0xFF << 23)) + (0x7F << 23);
|
|
x = c.f;
|
|
|
|
float y = F(0xC05B5154) + x * (F(0x410297C6) + x * (F(0xC1205CEB)
|
|
+ x * (F(0x4114DF63) + x * (F(0xC0C0DBBB) + x * (F(0x402942C6)
|
|
+ x * (F(0xBF3FF98A) + x * (F(0x3DFE1050) + x * F(0xBC151480))))))));
|
|
|
|
return y + e;
|
|
}
|
|
|
|
double EsCRTpow(double x, double y) {
|
|
return EsCRTexp2(y * EsCRTlog2(x));
|
|
}
|
|
|
|
float EsCRTpowf(float x, float y) {
|
|
return EsCRTexp2f(y * EsCRTlog2f(x));
|
|
}
|
|
|
|
double EsCRTcbrt(double x) {
|
|
if (x >= 0.0) return EsCRTpow(x, 1.0f / 3.0);
|
|
else return -EsCRTpow(-x, 1.0f / 3.0);
|
|
}
|
|
|
|
float EsCRTcbrtf(float x) {
|
|
if (x >= 0.0f) return EsCRTpowf(x, 1.0f / 3.0f);
|
|
else return -EsCRTpowf(-x, 1.0f / 3.0f);
|
|
}
|
|
|
|
double EsCRTexp(double x) {
|
|
return EsCRTexp2(x * 1.4426950408889634073);
|
|
}
|
|
|
|
float EsCRTexpf(float x) {
|
|
return EsCRTexp2f(x * 1.442695040f);
|
|
}
|
|
|
|
double EsCRTfmod(double x, double y) {
|
|
double n = x / y;
|
|
return x - y * (n > 0.0 ? EsCRTfloor(n) : EsCRTceil(n));
|
|
}
|
|
|
|
float EsCRTfmodf(float x, float y) {
|
|
float n = x / y;
|
|
return x - y * (n > 0.0f ? EsCRTfloorf(n) : EsCRTceilf(n));
|
|
}
|
|
|
|
bool EsCRTisnanf(float x) {
|
|
ConvertFloatInteger c;
|
|
c.f = x;
|
|
return (c.i & ~(1 << 31)) > 0x7F800000;
|
|
}
|
|
|
|
#undef D
|
|
#undef F
|
|
|
|
#endif
|
|
|
|
/////////////////////////////////
|
|
// High precision floats.
|
|
/////////////////////////////////
|
|
|
|
#ifdef SHARED_MATH_WANT_ALL
|
|
|
|
#define MANTISSA_BITS (256)
|
|
#include <x86intrin.h>
|
|
|
|
struct BigFloat {
|
|
uint64_t mantissa[MANTISSA_BITS / 64];
|
|
int32_t exponent;
|
|
bool negative, zero;
|
|
|
|
#define SET_MANTISSA_BIT(m, i, value) \
|
|
do { \
|
|
if ((value)) { \
|
|
(m).mantissa[(i) >> 6] |= (uint64_t) 1 << (63 - ((i) & 63)); \
|
|
} else { \
|
|
(m).mantissa[(i) >> 6] &= ~((uint64_t) 1 << (63 - ((i) & 63))); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define GET_MANTISSA_BIT(m, i) (((m).mantissa[(i) >> 6] & ((uint64_t) 1 << (63 - ((i) & 63)))) ? 1 : 0)
|
|
|
|
void _FromDouble(double d) {
|
|
if (d == 0) {
|
|
zero = true;
|
|
return;
|
|
}
|
|
|
|
ConvertDoubleInteger c;
|
|
c.d = d;
|
|
|
|
negative = false;
|
|
|
|
if (c.i & ((uint64_t) 1 << 63)) {
|
|
negative = true;
|
|
}
|
|
|
|
exponent = ((c.i >> 52) & 2047) - 1023;
|
|
|
|
for (uintptr_t i = 0; i < sizeof(mantissa) / sizeof(mantissa[0]); i++) {
|
|
mantissa[i] = 0;
|
|
}
|
|
|
|
SET_MANTISSA_BIT(*this, 0, 1);
|
|
|
|
for (intptr_t i = 0; i < 52; i++) {
|
|
if (c.i & ((uint64_t) 1 << (51 - i))) {
|
|
SET_MANTISSA_BIT(*this, i + 1, 1);
|
|
}
|
|
}
|
|
|
|
_Normalize();
|
|
}
|
|
|
|
double ToDouble() {
|
|
if (zero) return 0;
|
|
|
|
double d = 0;
|
|
|
|
for (intptr_t i = MANTISSA_BITS - 1; i >= 0; i--) {
|
|
if (GET_MANTISSA_BIT(*this, i)) {
|
|
ConvertDoubleInteger c;
|
|
uint64_t p = exponent - i - 1 + 1024;
|
|
c.i = p << 52;
|
|
d += c.d;
|
|
}
|
|
}
|
|
|
|
return negative ? -d : d;
|
|
}
|
|
|
|
void _ShiftRight(intptr_t shift) {
|
|
if (!shift) return;
|
|
|
|
#if 0
|
|
for (intptr_t i = MANTISSA_BITS - 1; i >= 0; i--) {
|
|
if (i < shift) {
|
|
SET_MANTISSA_BIT(*this, i, 0);
|
|
} else {
|
|
SET_MANTISSA_BIT(*this, i, GET_MANTISSA_BIT(*this, i - shift));
|
|
}
|
|
}
|
|
#else
|
|
if (shift >= 64) {
|
|
intptr_t shift0 = shift / 64;
|
|
|
|
for (intptr_t i = sizeof(mantissa) / sizeof(mantissa[0]) - 1; i >= 0; i--) {
|
|
if (i < shift0) {
|
|
mantissa[i] = 0;
|
|
} else {
|
|
mantissa[i] = mantissa[i - shift0];
|
|
}
|
|
}
|
|
}
|
|
|
|
uint64_t removed = 0;
|
|
|
|
for (intptr_t i = 0; i < (intptr_t) (sizeof(mantissa) / sizeof(mantissa[0])); i++) {
|
|
uint64_t r = mantissa[i] & (((uint64_t) 1 << shift) - 1);
|
|
mantissa[i] = (mantissa[i] >> shift) | removed;
|
|
removed = r << (64 - shift);
|
|
}
|
|
#endif
|
|
|
|
exponent += shift;
|
|
}
|
|
|
|
void _ShiftLeft(intptr_t shift) {
|
|
if (!shift) return;
|
|
|
|
#if 0
|
|
for (intptr_t i = 0; i < MANTISSA_BITS; i++) {
|
|
if (i >= MANTISSA_BITS - shift) {
|
|
SET_MANTISSA_BIT(*this, i, 0);
|
|
} else {
|
|
SET_MANTISSA_BIT(*this, i, GET_MANTISSA_BIT(*this, i + shift));
|
|
}
|
|
}
|
|
#else
|
|
if (shift >= 64) {
|
|
intptr_t shift0 = shift / 64;
|
|
|
|
for (intptr_t i = 0; i < (intptr_t) (sizeof(mantissa) / sizeof(mantissa[0])); i++) {
|
|
if (i >= (intptr_t) (sizeof(mantissa) / sizeof(mantissa[0])) - shift0) {
|
|
mantissa[i] = 0;
|
|
} else {
|
|
mantissa[i] = mantissa[i + shift0];
|
|
}
|
|
}
|
|
}
|
|
|
|
uint64_t removed = 0;
|
|
|
|
for (intptr_t i = sizeof(mantissa) / sizeof(mantissa[0]) - 1; i >= 0; i--) {
|
|
uint64_t r = mantissa[i] & (((1UL << shift) - 1) << (64 - shift));
|
|
mantissa[i] = (mantissa[i] << shift) | removed;
|
|
removed = r >> (64 - shift);
|
|
}
|
|
#endif
|
|
|
|
exponent -= shift;
|
|
}
|
|
|
|
void _Normalize() {
|
|
// Find the first set bit.
|
|
|
|
intptr_t shift = -1;
|
|
|
|
#if 0
|
|
for (intptr_t i = 0; i < MANTISSA_BITS; i++) {
|
|
if (GET_MANTISSA_BIT(*this, i)) {
|
|
shift = i;
|
|
break;
|
|
}
|
|
}
|
|
#else
|
|
for (uintptr_t i = 0; i < sizeof(mantissa) / sizeof(mantissa[0]); i++) {
|
|
if (mantissa[i]) {
|
|
shift = __builtin_clzll(mantissa[i]) + i * 64;
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// If we didn't find one, then the number is zero.
|
|
|
|
if (shift == -1) {
|
|
exponent = 0;
|
|
negative = false;
|
|
zero = true;
|
|
return;
|
|
}
|
|
|
|
// Shift the mantissa so that the first set bit is the first bit.
|
|
|
|
if (shift) {
|
|
_ShiftLeft(shift);
|
|
}
|
|
|
|
zero = false;
|
|
}
|
|
|
|
BigFloat operator+(BigFloat y) {
|
|
if (zero) return y;
|
|
if (y.zero) return *this;
|
|
|
|
BigFloat x = *this;
|
|
|
|
// Make sure that x has a bigger exponent.
|
|
|
|
if (y.exponent > x.exponent) {
|
|
BigFloat swap = x;
|
|
x = y;
|
|
y = swap;
|
|
}
|
|
|
|
// Shift y's mantissa to the right so that its exponent matches x's.
|
|
|
|
y._ShiftRight(x.exponent - y.exponent);
|
|
|
|
// If x and y have different signs, make sure y is the negative one.
|
|
|
|
bool subtract = x.negative != y.negative;
|
|
|
|
if (x.negative && !y.negative) {
|
|
BigFloat swap = x;
|
|
x = y;
|
|
y = swap;
|
|
}
|
|
|
|
// Add/subtract y's mantissa to x's.
|
|
|
|
if (subtract) {
|
|
for (uintptr_t i = 0; i < sizeof(mantissa) / sizeof(mantissa[0]); i++) {
|
|
y.mantissa[i] = ~y.mantissa[i];
|
|
}
|
|
}
|
|
|
|
uint8_t carry = subtract ? 1 : 0;
|
|
|
|
#ifdef ES_BITS_32
|
|
for (intptr_t i = MANTISSA_BITS - 1; i >= 0; i--) {
|
|
uint8_t xi = GET_MANTISSA_BIT(x, i);
|
|
uint8_t yi = GET_MANTISSA_BIT(y, i);
|
|
uint8_t sum = xi + yi + carry;
|
|
carry = sum >> 1;
|
|
SET_MANTISSA_BIT(x, i, sum & 1);
|
|
}
|
|
#else
|
|
for (intptr_t i = sizeof(mantissa) / sizeof(mantissa[0]) - 1; i >= 0; i--) {
|
|
carry = _addcarry_u64(carry, x.mantissa[i], y.mantissa[i], (long long unsigned int *) &x.mantissa[i]);
|
|
}
|
|
#endif
|
|
|
|
if (subtract) {
|
|
if (!carry) {
|
|
// Negate the result.
|
|
|
|
for (uintptr_t i = 0; i < sizeof(mantissa) / sizeof(mantissa[0]); i++) {
|
|
x.mantissa[i] = ~x.mantissa[i];
|
|
}
|
|
|
|
uint8_t carry = 1;
|
|
|
|
#ifdef ES_BITS_32
|
|
for (intptr_t i = MANTISSA_BITS - 1; i >= 0; i--) {
|
|
uint8_t xi = GET_MANTISSA_BIT(x, i);
|
|
uint8_t sum = xi + carry;
|
|
carry = sum >> 1;
|
|
SET_MANTISSA_BIT(x, i, sum & 1);
|
|
}
|
|
#else
|
|
for (intptr_t i = sizeof(mantissa) / sizeof(mantissa[0]) - 1; i >= 0; i--) {
|
|
carry = _addcarry_u64(carry, x.mantissa[i], 0, (long long unsigned int *) &x.mantissa[i]);
|
|
}
|
|
#endif
|
|
|
|
x.negative = true;
|
|
}
|
|
} else {
|
|
if (carry) {
|
|
x._ShiftRight(1);
|
|
SET_MANTISSA_BIT(x, 0, 1);
|
|
}
|
|
}
|
|
|
|
x._Normalize();
|
|
|
|
return x;
|
|
}
|
|
|
|
BigFloat operator-(BigFloat y) {
|
|
y.negative = !y.negative;
|
|
return *this + y;
|
|
}
|
|
|
|
BigFloat operator*(BigFloat y) {
|
|
// TODO Can this be done faster?
|
|
if (zero) return *this;
|
|
if (y.zero) return y;
|
|
|
|
BigFloat accumulator = { .zero = true };
|
|
|
|
for (intptr_t i = MANTISSA_BITS - 1; i >= 0; i--) {
|
|
if (GET_MANTISSA_BIT(y, i)) {
|
|
BigFloat place = *this;
|
|
place.exponent = place.exponent - i + y.exponent;
|
|
accumulator = accumulator + place;
|
|
}
|
|
}
|
|
|
|
accumulator.negative = negative != y.negative;
|
|
return accumulator;
|
|
}
|
|
};
|
|
|
|
struct {
|
|
BigFloat zero;
|
|
BigFloat one;
|
|
BigFloat two;
|
|
BigFloat half;
|
|
BigFloat halfPi;
|
|
BigFloat pi;
|
|
BigFloat twoPi;
|
|
BigFloat log2;
|
|
BigFloat e;
|
|
BigFloat log2E;
|
|
} bigFloatConstants;
|
|
|
|
BigFloat BigFloatDivide(BigFloat x, BigFloat y, bool *error) {
|
|
if (x.zero) return x;
|
|
if (y.zero) { *error = true; return y; }
|
|
|
|
BigFloat q = {};
|
|
bool negative = x.negative != y.negative;
|
|
x.negative = y.negative = false;
|
|
|
|
intptr_t e = x.exponent - y.exponent;
|
|
|
|
// Loop over MANTISSA_BITS + 1.
|
|
// There will definitely be a subtraction in the first two iterations, by the initial choice of e.
|
|
// Any additions to q with exponent less than initial e - MANTISSA_BITS - 1 therefore has no effect.
|
|
|
|
for (int i = 0; i <= MANTISSA_BITS; i++) {
|
|
BigFloat m = {};
|
|
SET_MANTISSA_BIT(m, 0, 1);
|
|
m.exponent = e;
|
|
|
|
BigFloat s = y;
|
|
s.exponent += e;
|
|
BigFloat x1 = x - s;
|
|
|
|
if (!x1.negative) {
|
|
x = x1;
|
|
q = q + m;
|
|
}
|
|
|
|
e--;
|
|
}
|
|
|
|
q.negative = negative;
|
|
return q;
|
|
}
|
|
|
|
BigFloat BigFloatModulo(BigFloat x, BigFloat y, bool *error) {
|
|
if (x.zero) return x;
|
|
if (y.zero) { *error = true; return y; }
|
|
|
|
x.negative = y.negative = false;
|
|
intptr_t e = x.exponent - y.exponent;
|
|
|
|
for (int i = 0; e >= 0; i++) {
|
|
BigFloat m = {};
|
|
SET_MANTISSA_BIT(m, 0, 1);
|
|
m.exponent = e;
|
|
|
|
BigFloat s = y;
|
|
s.exponent += e;
|
|
BigFloat x1 = x - s;
|
|
|
|
if (!x1.negative) {
|
|
x = x1;
|
|
}
|
|
|
|
e--;
|
|
}
|
|
|
|
return x;
|
|
}
|
|
|
|
BigFloat BigFloatRoundToNegativeInfinity(BigFloat x) {
|
|
bool decrement = false;
|
|
|
|
intptr_t start = x.exponent + 1;
|
|
|
|
if (start < 0) {
|
|
start = 0;
|
|
}
|
|
|
|
for (intptr_t i = start; i < MANTISSA_BITS; i++) {
|
|
if (x.negative && !decrement && GET_MANTISSA_BIT(x, i)) {
|
|
decrement = true;
|
|
}
|
|
|
|
SET_MANTISSA_BIT(x, i, 0);
|
|
}
|
|
|
|
x._Normalize();
|
|
|
|
if (decrement) {
|
|
return x - bigFloatConstants.one;
|
|
} else {
|
|
return x;
|
|
}
|
|
}
|
|
|
|
BigFloat BigFloatSine(BigFloat x, bool *error) {
|
|
// Get x in the range 0 to 2*pi.
|
|
|
|
bool negate = x.negative;
|
|
x.negative = false;
|
|
x = BigFloatModulo(x, bigFloatConstants.twoPi, error);
|
|
|
|
// Evaluate the Maclaurin series.
|
|
|
|
if (error && *error) return x;
|
|
if (x.zero) return x;
|
|
|
|
BigFloat x2 = x * x;
|
|
BigFloat y = {};
|
|
y.zero = true;
|
|
|
|
BigFloat p = x;
|
|
BigFloat k = bigFloatConstants.two;
|
|
|
|
for (int i = 0; i < 10000; i++) {
|
|
if (p.exponent + MANTISSA_BITS < y.exponent && !y.zero) {
|
|
break;
|
|
}
|
|
|
|
y = y + p;
|
|
p.negative = !p.negative;
|
|
p = x2 * BigFloatDivide(p, k * (k + bigFloatConstants.one), nullptr);
|
|
k = k + bigFloatConstants.two;
|
|
}
|
|
|
|
if (negate) y.negative = !y.negative;
|
|
return y;
|
|
}
|
|
|
|
BigFloat BigFloatCosine(BigFloat x, bool *error) {
|
|
return BigFloatSine(bigFloatConstants.halfPi - x, error);
|
|
}
|
|
|
|
BigFloat BigFloatTangent(BigFloat x, bool *error) {
|
|
return BigFloatDivide(BigFloatSine(x, error), BigFloatCosine(x, error), error);
|
|
}
|
|
|
|
BigFloat BigFloatLogarithm2(BigFloat x, bool *error) {
|
|
if (x.negative || x.zero) {
|
|
*error = true;
|
|
return x;
|
|
}
|
|
|
|
BigFloat accumulator = {};
|
|
accumulator._FromDouble(x.exponent);
|
|
x.exponent = 0;
|
|
|
|
BigFloat m = bigFloatConstants.one;
|
|
|
|
do {
|
|
if ((x - bigFloatConstants.one).zero) {
|
|
break;
|
|
}
|
|
|
|
while ((x - bigFloatConstants.two).negative && accumulator.exponent - m.exponent < MANTISSA_BITS) {
|
|
x = x * x;
|
|
m.exponent--;
|
|
}
|
|
|
|
accumulator = accumulator + m;
|
|
x.exponent--;
|
|
} while (accumulator.exponent - m.exponent < MANTISSA_BITS);
|
|
|
|
return accumulator;
|
|
}
|
|
|
|
BigFloat BigFloatExponential2(BigFloat x, bool *error) {
|
|
BigFloat _integer = BigFloatRoundToNegativeInfinity(x);
|
|
BigFloat fractional = x - _integer;
|
|
int32_t integer = _integer.ToDouble();
|
|
|
|
if (integer < -1000000 || integer > 1000000) {
|
|
*error = true;
|
|
return x;
|
|
}
|
|
|
|
BigFloat accumulator = {};
|
|
accumulator.zero = true;
|
|
BigFloat term = bigFloatConstants.one;
|
|
BigFloat n = bigFloatConstants.one;
|
|
|
|
do {
|
|
accumulator = accumulator + term;
|
|
term = BigFloatDivide(term * fractional * bigFloatConstants.log2, n, error);
|
|
n = n + bigFloatConstants.one;
|
|
} while (accumulator.exponent - term.exponent < MANTISSA_BITS && !term.zero);
|
|
|
|
accumulator.exponent += integer;
|
|
return accumulator;
|
|
}
|
|
|
|
BigFloat BigFloatPower(BigFloat x, BigFloat y, bool *error) {
|
|
return BigFloatExponential2(y * BigFloatLogarithm2(x, error), error);
|
|
}
|
|
|
|
BigFloat BigFloatExponential(BigFloat x, bool *error) {
|
|
return BigFloatExponential2(x * bigFloatConstants.log2E, error);
|
|
}
|
|
|
|
BigFloat BigFloatLogarithm(BigFloat x, bool *error) {
|
|
return BigFloatDivide(BigFloatLogarithm2(x, error), bigFloatConstants.log2E, error);
|
|
}
|
|
|
|
BigFloat BigFloatArcSine(BigFloat x, bool *error) {
|
|
BigFloat accumulator = {};
|
|
accumulator.zero = true;
|
|
BigFloat n = {};
|
|
n.zero = true;
|
|
BigFloat term = x;
|
|
BigFloat xm = x * x;
|
|
xm.exponent -= 2;
|
|
BigFloat three = bigFloatConstants.one + bigFloatConstants.two;
|
|
|
|
do {
|
|
accumulator = accumulator + term;
|
|
BigFloat n2 = n;
|
|
n2.exponent++;
|
|
BigFloat s1 = n2 + bigFloatConstants.one;
|
|
BigFloat s2 = n + bigFloatConstants.one;
|
|
term = BigFloatDivide(term * xm * (n2 + bigFloatConstants.two) * s1 * s1, s2 * s2 * (n2 + three), error);
|
|
n = n + bigFloatConstants.one;
|
|
} while (accumulator.exponent - term.exponent < MANTISSA_BITS && !term.zero);
|
|
|
|
return accumulator;
|
|
}
|
|
|
|
BigFloat BigFloatArcCosine(BigFloat x, bool *error) {
|
|
x.negative = !x.negative;
|
|
return BigFloatArcSine(x, error) + bigFloatConstants.halfPi;
|
|
}
|
|
|
|
BigFloat BigFloatArcTangent(BigFloat x, bool *error) {
|
|
return BigFloatArcSine(BigFloatDivide(x, BigFloatPower(bigFloatConstants.one + x * x, bigFloatConstants.half, error), error), error);
|
|
}
|
|
|
|
BigFloat BigFloatHyperbolicSine(BigFloat x, bool *error) {
|
|
BigFloat ex = BigFloatExponential(x, error);
|
|
return bigFloatConstants.half * (ex - BigFloatDivide(bigFloatConstants.one, ex, error));
|
|
}
|
|
|
|
BigFloat BigFloatHyperbolicCosine(BigFloat x, bool *error) {
|
|
BigFloat ex = BigFloatExponential(x, error);
|
|
return bigFloatConstants.half * (ex + BigFloatDivide(bigFloatConstants.one, ex, error));
|
|
}
|
|
|
|
BigFloat BigFloatHyperbolicTangent(BigFloat x, bool *error) {
|
|
BigFloat e2x = BigFloatExponential(bigFloatConstants.two * x, error);
|
|
return BigFloatDivide(e2x - bigFloatConstants.one, e2x + bigFloatConstants.one, error);
|
|
}
|
|
|
|
BigFloat BigFloatArcHyperbolicSine(BigFloat x, bool *error) {
|
|
return BigFloatLogarithm(x + BigFloatPower(x * x + bigFloatConstants.one, bigFloatConstants.half, error), error);
|
|
}
|
|
|
|
BigFloat BigFloatArcHyperbolicCosine(BigFloat x, bool *error) {
|
|
return BigFloatLogarithm(x + BigFloatPower(x * x - bigFloatConstants.one, bigFloatConstants.half, error), error);
|
|
}
|
|
|
|
BigFloat BigFloatArcHyperbolicTangent(BigFloat x, bool *error) {
|
|
return bigFloatConstants.half * BigFloatLogarithm(BigFloatDivide(bigFloatConstants.one + x, bigFloatConstants.one - x, error), error);
|
|
}
|
|
|
|
void BigFloatInitialise() {
|
|
EsMessageMutexCheck();
|
|
|
|
static bool initialised = false;
|
|
|
|
if (initialised) {
|
|
return;
|
|
}
|
|
|
|
initialised = true;
|
|
|
|
bigFloatConstants.zero.zero = true;
|
|
bigFloatConstants.one.mantissa[0] = (uint64_t) 1 << 63;
|
|
bigFloatConstants.one.exponent = 0;
|
|
bigFloatConstants.two.mantissa[0] = (uint64_t) 1 << 63;
|
|
bigFloatConstants.two.exponent = 1;
|
|
bigFloatConstants.half.mantissa[0] = (uint64_t) 1 << 63;
|
|
bigFloatConstants.half.exponent = -1;
|
|
|
|
{
|
|
BigFloat term = bigFloatConstants.two;
|
|
BigFloat n = bigFloatConstants.one;
|
|
|
|
BigFloat accumulator = {};
|
|
accumulator.zero = true;
|
|
|
|
while (term.exponent >= -MANTISSA_BITS) {
|
|
accumulator = accumulator + term;
|
|
term = term * BigFloatDivide(n * n, bigFloatConstants.two * n * n + n, nullptr);
|
|
n = n + bigFloatConstants.one;
|
|
}
|
|
|
|
bigFloatConstants.pi = accumulator;
|
|
bigFloatConstants.halfPi = BigFloatDivide(accumulator, bigFloatConstants.two, nullptr);
|
|
bigFloatConstants.twoPi = bigFloatConstants.two * accumulator;
|
|
}
|
|
|
|
{
|
|
BigFloat n = bigFloatConstants.one;
|
|
|
|
BigFloat accumulator = {};
|
|
accumulator.zero = true;
|
|
|
|
for (int i = 1; i <= MANTISSA_BITS; i++) {
|
|
BigFloat d = BigFloatDivide(bigFloatConstants.one, n, nullptr);
|
|
d.exponent -= i;
|
|
accumulator = accumulator + d;
|
|
n = n + bigFloatConstants.one;
|
|
}
|
|
|
|
bigFloatConstants.log2 = accumulator;
|
|
}
|
|
|
|
{
|
|
BigFloat n = bigFloatConstants.one;
|
|
BigFloat term = bigFloatConstants.one;
|
|
BigFloat accumulator = bigFloatConstants.one;
|
|
|
|
for (int i = 0; i <= MANTISSA_BITS; i++) {
|
|
accumulator = accumulator + term;
|
|
n = n + bigFloatConstants.one;
|
|
term = BigFloatDivide(term, n, nullptr);
|
|
}
|
|
|
|
bigFloatConstants.e = accumulator;
|
|
bigFloatConstants.log2E = BigFloatLogarithm2(bigFloatConstants.e, nullptr);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
/////////////////////////////////
|
|
// Calculating expressions.
|
|
/////////////////////////////////
|
|
|
|
#ifdef SHARED_MATH_WANT_ALL
|
|
|
|
namespace Calculator {
|
|
enum ValueType : uint8_t {
|
|
VALUE_ERROR,
|
|
VALUE_NUMBER,
|
|
};
|
|
|
|
struct Value {
|
|
ValueType type;
|
|
|
|
union {
|
|
BigFloat number;
|
|
};
|
|
};
|
|
|
|
enum TokenType : uint8_t {
|
|
TOKEN_ERROR,
|
|
TOKEN_PUNCTUATOR,
|
|
TOKEN_LITERAL,
|
|
TOKEN_IDENTIFIER,
|
|
TOKEN_EOF,
|
|
};
|
|
|
|
struct Token {
|
|
TokenType type;
|
|
|
|
union {
|
|
uint16_t punctuator;
|
|
Value literal;
|
|
char *identifier;
|
|
};
|
|
};
|
|
|
|
enum NodeType : uint8_t {
|
|
NODE_BINOP,
|
|
NODE_UNARYOP,
|
|
NODE_CALL,
|
|
NODE_LITERAL,
|
|
};
|
|
|
|
struct Node {
|
|
NodeType type;
|
|
Node *firstChild, *sibling;
|
|
Token token;
|
|
};
|
|
|
|
typedef Value (*Builtin)(Value *arguments, size_t argumentCount);
|
|
|
|
struct Parser {
|
|
const char *position;
|
|
HashStore<char, Builtin> builtins;
|
|
#define CALCULATOR_PARSER_ALLOCATION_POOL_SIZE (8192)
|
|
char *allocationPool;
|
|
int allocatedBytes;
|
|
|
|
void FreeAll() {
|
|
allocatedBytes = 0;
|
|
EsHeapFree(allocationPool);
|
|
allocationPool = nullptr;
|
|
}
|
|
|
|
void *Allocate(size_t bytes) {
|
|
bytes = (bytes + sizeof(max_align_t) - 1) & ~(sizeof(max_align_t) - 1);
|
|
|
|
if (bytes + allocatedBytes > CALCULATOR_PARSER_ALLOCATION_POOL_SIZE) {
|
|
return nullptr;
|
|
}
|
|
|
|
if (!allocationPool) {
|
|
allocationPool = (char *) EsHeapAllocate(CALCULATOR_PARSER_ALLOCATION_POOL_SIZE, false);
|
|
|
|
if (!allocationPool) {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
void *pointer = allocationPool + allocatedBytes;
|
|
allocatedBytes += bytes;
|
|
EsMemoryZero(pointer, bytes);
|
|
return pointer;
|
|
}
|
|
|
|
Token PeekToken() {
|
|
const char *oldPosition = position;
|
|
Token token = NextToken();
|
|
position = oldPosition;
|
|
return token;
|
|
}
|
|
|
|
Token NextToken() {
|
|
Token token = {};
|
|
|
|
tryAgain:;
|
|
|
|
unsigned char c = *position;
|
|
|
|
if (c == '#') {
|
|
while (*position != '\n') position++;
|
|
goto tryAgain;
|
|
} else if (EsCRTisspace(c)) {
|
|
position++;
|
|
goto tryAgain;
|
|
}
|
|
|
|
if ((c == '%' && position[1] == '%') || (c == '<' && position[1] == '=') || (c == '>' && position[1] == '=')
|
|
|| (c == '=' && position[1] == '=') || (c == '!' && position[1] == '=')
|
|
|| (c == '&' && position[1] == '&') || (c == '|' && position[1] == '|')
|
|
|| (c == '/' && position[1] == '/')) {
|
|
token.type = TOKEN_PUNCTUATOR;
|
|
token.punctuator = (c << 8) + position[1];
|
|
position += 2;
|
|
} else if (c == '+' || c == '-' || c == '*' || c == '/' || c == '%' || c == '<' || c == '>' || c == '(' || c == ')' || c == '?'
|
|
|| c == ':' || c == ';' || c == ',' || c == '!' || c == '&' || c == '^') {
|
|
token.type = TOKEN_PUNCTUATOR;
|
|
token.punctuator = c;
|
|
position++;
|
|
} else if (EsCRTisalpha(c) || c == '_' || c >= 0x80) {
|
|
const char *start = position;
|
|
while (EsCRTisalpha(c) || c == '_' || EsCRTisdigit(c) || c >= 0x80) c = *(++position);
|
|
token.type = TOKEN_IDENTIFIER;
|
|
token.identifier = (char *) Allocate(position - start + 1);
|
|
if (!token.identifier) return { TOKEN_ERROR };
|
|
EsMemoryCopy(token.identifier, start, position - start);
|
|
token.identifier[position - start] = 0;
|
|
} else if (EsCRTisdigit(c) || c == '.') {
|
|
const char *start = position;
|
|
token.literal.number._FromDouble(EsDoubleParse(start, -1, (char **) &position));
|
|
token.type = TOKEN_LITERAL;
|
|
token.literal.type = VALUE_NUMBER;
|
|
} else if (c == 0) {
|
|
token.type = TOKEN_EOF;
|
|
} else {
|
|
token.type = TOKEN_ERROR;
|
|
}
|
|
|
|
return token;
|
|
}
|
|
|
|
Node *ParseExpression(int precedence = 0) {
|
|
Node *left = (Node *) Allocate(sizeof(Node));
|
|
if (!left) return nullptr;
|
|
Token token = NextToken();
|
|
left->token = token;
|
|
|
|
if (token.type == TOKEN_LITERAL) {
|
|
left->type = NODE_LITERAL;
|
|
} else if (token.type == TOKEN_PUNCTUATOR) {
|
|
left->type = NODE_UNARYOP;
|
|
|
|
if (token.punctuator == '(') {
|
|
left->firstChild = ParseExpression();
|
|
token = NextToken();
|
|
|
|
if (token.type != TOKEN_PUNCTUATOR || token.punctuator != ')' || !left->firstChild) {
|
|
return nullptr;
|
|
}
|
|
} else if (token.punctuator == '!' || token.punctuator == '-' || token.punctuator == '/') {
|
|
left->firstChild = ParseExpression(1000);
|
|
|
|
if (!left->firstChild) {
|
|
return nullptr;
|
|
}
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
} else if (token.type == TOKEN_IDENTIFIER) {
|
|
Token peek = PeekToken();
|
|
|
|
if (peek.type == TOKEN_PUNCTUATOR && peek.punctuator == '(') {
|
|
left->type = NODE_CALL;
|
|
NextToken();
|
|
int count = 0;
|
|
Node **link = &left->firstChild;
|
|
|
|
while (true) {
|
|
token = PeekToken();
|
|
|
|
if (token.type == TOKEN_PUNCTUATOR && token.punctuator == ')') {
|
|
NextToken();
|
|
break;
|
|
} else if (count) {
|
|
if (token.type != TOKEN_PUNCTUATOR || token.punctuator != ',') {
|
|
return nullptr;
|
|
}
|
|
|
|
NextToken();
|
|
}
|
|
|
|
*link = ParseExpression();
|
|
|
|
if (!(*link)) {
|
|
return nullptr;
|
|
}
|
|
|
|
link = &((*link)->sibling);
|
|
count++;
|
|
}
|
|
} else if (0 == EsCRTstrcmp(token.identifier, "\xCF\x80") || 0 == EsCRTstrcmp(token.identifier, "pi")) {
|
|
left->type = NODE_LITERAL;
|
|
left->token.literal.type = VALUE_NUMBER;
|
|
left->token.literal.number = bigFloatConstants.pi;
|
|
} else if (0 == EsCRTstrcmp(token.identifier, "\xCF\x84") || 0 == EsCRTstrcmp(token.identifier, "tau")) {
|
|
left->type = NODE_LITERAL;
|
|
left->token.literal.type = VALUE_NUMBER;
|
|
left->token.literal.number = bigFloatConstants.twoPi;
|
|
} else if (0 == EsCRTstrcmp(token.identifier, "e")) {
|
|
left->type = NODE_LITERAL;
|
|
left->token.literal.type = VALUE_NUMBER;
|
|
left->token.literal.number = bigFloatConstants.e;
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
|
|
while (true) {
|
|
token = PeekToken();
|
|
|
|
if (0) {
|
|
|
|
#define PARSE_BINOP(symb, prec) \
|
|
} else if (token.type == TOKEN_PUNCTUATOR && token.punctuator == symb && prec > precedence) { \
|
|
Node *node = (Node *) Allocate(sizeof(Node)); \
|
|
if (!node) return nullptr; \
|
|
node->type = NODE_BINOP; \
|
|
node->token = NextToken(); \
|
|
node->firstChild = left; \
|
|
left->sibling = ParseExpression(prec); \
|
|
if (!left->sibling) return nullptr; \
|
|
left = node
|
|
|
|
PARSE_BINOP('^', 120);
|
|
PARSE_BINOP('*', 100);
|
|
PARSE_BINOP('/', 100);
|
|
PARSE_BINOP('+', 80);
|
|
PARSE_BINOP('-', 80);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return left;
|
|
}
|
|
|
|
Value Evaluate(Node *node, Value *arguments) {
|
|
if (node->type == NODE_LITERAL) {
|
|
return node->token.literal;
|
|
} else if (node->type == NODE_CALL) {
|
|
#define MAX_CALCULATOR_CALL_ARGUMENTS (16)
|
|
Value callArguments[MAX_CALCULATOR_CALL_ARGUMENTS];
|
|
int argumentCount = 0;
|
|
Node *child = node->firstChild;
|
|
|
|
while (child) {
|
|
callArguments[argumentCount] = Evaluate(child, arguments);
|
|
|
|
if (callArguments[argumentCount].type == VALUE_ERROR) {
|
|
return {};
|
|
}
|
|
|
|
child = child->sibling;
|
|
argumentCount++;
|
|
if (argumentCount == MAX_CALCULATOR_CALL_ARGUMENTS) return {};
|
|
}
|
|
|
|
if (!builtins.Count()) LoadBuiltins();
|
|
Builtin builtin = builtins.Get1(node->token.identifier, EsCStringLength(node->token.identifier));
|
|
if (!builtin) return {};
|
|
return builtin(callArguments, argumentCount);
|
|
} else if (node->type == NODE_UNARYOP) {
|
|
Value value = Evaluate(node->firstChild, arguments);
|
|
|
|
if (node->token.punctuator == '(') {
|
|
return value;
|
|
} else if (node->token.punctuator == '!') {
|
|
if (value.type != VALUE_NUMBER) return {};
|
|
return { VALUE_NUMBER, .number = value.number.zero ? bigFloatConstants.one : bigFloatConstants.zero };
|
|
} else if (node->token.punctuator == '-') {
|
|
if (value.type != VALUE_NUMBER) return {};
|
|
value.number.negative = !value.number.negative;
|
|
return value;
|
|
} else if (node->token.punctuator == '/') {
|
|
if (value.type != VALUE_NUMBER) return {};
|
|
bool error = false;
|
|
value.number = BigFloatDivide(bigFloatConstants.one, value.number, &error);
|
|
if (error) value.type = VALUE_ERROR;
|
|
return value;
|
|
} else {
|
|
return {};
|
|
}
|
|
} else if (node->type == NODE_BINOP) {
|
|
Value left = Evaluate(node->firstChild, arguments),
|
|
right = Evaluate(node->firstChild->sibling, arguments);
|
|
|
|
if (0) {
|
|
|
|
#define DO_BINOP(p, t, output, expression) \
|
|
} else if (node->token.punctuator == p) { \
|
|
if (left.type != t || right.type != t) return {}; \
|
|
Value result = { t }; \
|
|
bool error = false; \
|
|
output = expression; \
|
|
if (error) result.type = VALUE_ERROR; \
|
|
return result
|
|
|
|
DO_BINOP('+', VALUE_NUMBER, result.number, left.number + right.number);
|
|
DO_BINOP('-', VALUE_NUMBER, result.number, left.number - right.number);
|
|
DO_BINOP('*', VALUE_NUMBER, result.number, left.number * right.number);
|
|
DO_BINOP('/', VALUE_NUMBER, result.number, BigFloatDivide(left.number, right.number, &error));
|
|
DO_BINOP('^', VALUE_NUMBER, result.number, BigFloatPower(left.number, right.number, &error));
|
|
#undef DO_BINOP
|
|
} else {
|
|
return {};
|
|
}
|
|
} else {
|
|
return {};
|
|
}
|
|
}
|
|
|
|
void LoadBuiltins() {
|
|
#define FUNCTION1(name, callback) \
|
|
do { Builtin builtin = [] (Value *arguments, size_t argumentCount) { \
|
|
if (argumentCount != 1 || arguments[0].type != VALUE_NUMBER) return (Value) {}; \
|
|
Value result = { VALUE_NUMBER }; \
|
|
BigFloat x = arguments[0].number; \
|
|
bool error = false; \
|
|
result.number = callback; \
|
|
if (error) result.type = VALUE_ERROR; \
|
|
return result; \
|
|
}; *builtins.Put(name, EsCStringLength(name)) = builtin; } while (0)
|
|
|
|
#define FUNCTION2(name, callback) \
|
|
do { Builtin builtin = [] (Value *arguments, size_t argumentCount) { \
|
|
if (argumentCount != 2 || arguments[0].type != VALUE_NUMBER || arguments[1].type != VALUE_NUMBER) return (Value) {}; \
|
|
Value result = { VALUE_NUMBER }; \
|
|
BigFloat x = arguments[0].number, y = arguments[1].number; \
|
|
bool error = false; \
|
|
result.number = callback; \
|
|
if (error) result.type = VALUE_ERROR; \
|
|
return result; \
|
|
}; *builtins.Put(name, EsCStringLength(name)) = builtin; } while (0)
|
|
|
|
FUNCTION1("sq", BigFloatPower(x, bigFloatConstants.two, &error));
|
|
FUNCTION1("sqrt", BigFloatPower(x, bigFloatConstants.half, &error));
|
|
FUNCTION1("ln", BigFloatLogarithm(x, &error));
|
|
FUNCTION1("sin", BigFloatSine(x, &error));
|
|
FUNCTION1("sinh", BigFloatHyperbolicSine(x, &error));
|
|
FUNCTION1("cos", BigFloatCosine(x, &error));
|
|
FUNCTION1("cosh", BigFloatHyperbolicCosine(x, &error));
|
|
FUNCTION1("tan", BigFloatTangent(x, &error));
|
|
FUNCTION1("tanh", BigFloatHyperbolicTangent(x, &error));
|
|
FUNCTION1("asin", BigFloatArcSine(x, &error));
|
|
FUNCTION1("asinh", BigFloatArcHyperbolicSine(x, &error));
|
|
FUNCTION1("acos", BigFloatArcCosine(x, &error));
|
|
FUNCTION1("acosh", BigFloatArcHyperbolicCosine(x, &error));
|
|
FUNCTION1("atan", BigFloatArcTangent(x, &error));
|
|
FUNCTION1("atanh", BigFloatArcHyperbolicTangent(x, &error));
|
|
FUNCTION2("mod", BigFloatModulo(x, y, &error));
|
|
FUNCTION2("rt", BigFloatPower(x, BigFloatDivide(bigFloatConstants.one, y, &error), &error));
|
|
FUNCTION2("log", BigFloatDivide(BigFloatLogarithm2(y, &error), BigFloatLogarithm2(x, &error), &error));
|
|
|
|
#undef FUNCTION1
|
|
#undef FUNCTION2
|
|
}
|
|
|
|
void Free() {
|
|
FreeAll();
|
|
builtins.Free();
|
|
}
|
|
};
|
|
};
|
|
|
|
Calculator::Parser calculator = {};
|
|
|
|
EsCalculationValue EsCalculateFromUserExpression(const char *expression) {
|
|
EsMessageMutexCheck();
|
|
|
|
BigFloatInitialise();
|
|
|
|
calculator.FreeAll();
|
|
calculator.position = expression;
|
|
Calculator::Node *node = calculator.ParseExpression();
|
|
Calculator::Value value = {};
|
|
if (calculator.NextToken().type != Calculator::TOKEN_EOF) node = nullptr;
|
|
if (node) value = calculator.Evaluate(node, nullptr);
|
|
|
|
EsCalculationValue result = {};
|
|
result.error = value.type == Calculator::VALUE_ERROR;
|
|
result.number = value.type == Calculator::VALUE_NUMBER ? value.number.ToDouble() : 0;
|
|
return result;
|
|
}
|
|
|
|
#endif
|