phobos/math2.d
2007-09-10 02:16:36 +00:00

1369 lines
18 KiB
D

/*
* Copyright (c) 2002
* Pavel "EvilOne" Minayev
*
* Permission to use, copy, modify, distribute and sell this software
* and its documentation for any purpose is hereby granted without fee,
* provided that the above copyright notice appear in all copies and
* that both that copyright notice and this permission notice appear
* in supporting documentation. Author makes no representations about
* the suitability of this software for any purpose. It is provided
* "as is" without express or implied warranty.
*/
module math2;
import intrinsic;
private import math, string, c.stdlib, c.stdio;
//debug=math2;
/****************************************
* compare floats with given precision
*/
bit feq(real a, real b)
{
return feq(a, b, 0.000001);
}
bit feq(real a, real b, real eps)
{
return abs(a - b) <= eps;
}
/*********************************
* Modulus
*/
int abs(int n)
{
return n > 0 ? n : -n;
}
long abs(long n)
{
return n > 0 ? n : -n;
}
real abs(real n)
{
// just let the compiler handle it
return intrinsic.fabs(n);
}
/*********************************
* Square
*/
int sqr(int n)
{
return n * n;
}
long sqr(long n)
{
return n * n;
}
real sqr(real n)
{
return n * n;
}
unittest
{
assert(sqr(sqr(3)) == 81);
}
private ushort fp_cw_chop = 7999;
/*********************************
* Integer part
*/
real trunc(real n)
{
ushort cw;
asm
{
fstcw cw;
fldcw fp_cw_chop;
fld n;
frndint;
fldcw cw;
}
}
unittest
{
assert(feq(trunc(+123.456), +123.0L));
assert(feq(trunc(-123.456), -123.0L));
}
/*********************************
* Fractional part
*/
real frac(real n)
{
return n - trunc(n);
}
unittest
{
assert(feq(frac(+123.456), +0.456L));
assert(feq(frac(-123.456), -0.456L));
}
/*********************************
* Polynomial of X
*/
real poly(real x, real[] coefficients)
{
debug (math2) printf("poly(), coefficients.length = %d\n", coefficients.length);
if (!coefficients.length)
return 0;
real result = coefficients[coefficients.length - 1];
for (int i = coefficients.length - 2; i >= 0; i--)
result = result * x + coefficients[i];
return result;
}
unittest
{
debug (math2) printf("unittest.poly()\n");
static real[4] k = [ 4, 3, 2, 1 ];
assert(feq(poly(2, k), cast(real) 8 * 1 + 4 * 2 + 2 * 3 + 4));
}
/*********************************
* Sign
*/
int sign(int n)
{
return (n > 0 ? +1 : (n < 0 ? -1 : 0));
}
unittest
{
assert(sign(0) == 0);
assert(sign(+666) == +1);
assert(sign(-666) == -1);
}
int sign(long n)
{
return (n > 0 ? +1 : (n < 0 ? -1 : 0));
}
unittest
{
assert(sign(0) == 0);
assert(sign(+666L) == +1);
assert(sign(-666L) == -1);
}
int sign(real n)
{
return (n > 0 ? +1 : (n < 0 ? -1 : 0));
}
unittest
{
assert(sign(0.0L) == 0);
assert(sign(+123.456L) == +1);
assert(sign(-123.456L) == -1);
}
/**********************************************************
* Cycles <-> radians <-> grads <-> degrees conversions
*/
real cycle2deg(real c)
{
return c * 360;
}
real cycle2rad(real c)
{
return c * PI * 2;
}
real cycle2grad(real c)
{
return c * 400;
}
real deg2cycle(real d)
{
return d / 360;
}
real deg2rad(real d)
{
return d / 180 * PI;
}
real deg2grad(real d)
{
return d / 90 * 100;
}
real rad2deg(real r)
{
return r / PI * 180;
}
real rad2cycle(real r)
{
return r / (PI * 2);
}
real rad2grad(real r)
{
return r / PI * 200;
}
real grad2deg(real g)
{
return g / 100 * 90;
}
real grad2cycle(real g)
{
return g / 400;
}
real grad2rad(real g)
{
return g / 200 * PI;
}
unittest
{
assert(feq(cycle2deg(0.5), 180));
assert(feq(cycle2rad(0.5), PI));
assert(feq(cycle2grad(0.5), 200));
assert(feq(deg2cycle(180), 0.5));
assert(feq(deg2rad(180), PI));
assert(feq(deg2grad(180), 200));
assert(feq(rad2deg(PI), 180));
assert(feq(rad2cycle(PI), 0.5));
assert(feq(rad2grad(PI), 200));
assert(feq(grad2deg(200), 180));
assert(feq(grad2cycle(200), 0.5));
assert(feq(grad2rad(200), PI));
}
/************************************
* Arithmetic average of values
*/
real avg(real[] n)
{
real result = 0;
for (uint i = 0; i < n.length; i++)
result += n[i];
return result / n.length;
}
unittest
{
static real[4] n = [ 1, 2, 4, 5 ];
assert(feq(avg(n), 3));
}
/*************************************
* Sum of values
*/
int sum(int[] n)
{
long result = 0;
for (uint i = 0; i < n.length; i++)
result += n[i];
return result;
}
unittest
{
static int[3] n = [ 1, 2, 3 ];
assert(sum(n) == 6);
}
long sum(long[] n)
{
long result = 0;
for (uint i = 0; i < n.length; i++)
result += n[i];
return result;
}
unittest
{
static long[3] n = [ 1, 2, 3 ];
assert(sum(n) == 6);
}
real sum(real[] n)
{
real result = 0;
for (uint i = 0; i < n.length; i++)
result += n[i];
return result;
}
unittest
{
static real[3] n = [ 1, 2, 3 ];
assert(feq(sum(n), 6));
}
/*************************************
* The smallest value
*/
int min(int[] n)
{
int result = int.max;
for (uint i = 0; i < n.length; i++)
if (n[i] < result)
result = n[i];
return result;
}
unittest
{
static int[3] n = [ 2, -1, 0 ];
assert(min(n) == -1);
}
long min(long[] n)
{
long result = long.max;
for (uint i = 0; i < n.length; i++)
if (n[i] < result)
result = n[i];
return result;
}
unittest
{
static long[3] n = [ 2, -1, 0 ];
assert(min(n) == -1);
}
real min(real[] n)
{
real result = real.max;
for (uint i = 0; i < n.length; i++)
{
if (n[i] < result)
result = n[i];
}
return result;
}
unittest
{
static real[3] n = [ 2.0, -1.0, 0.0 ];
assert(feq(min(n), -1));
}
int min(int a, int b)
{
return a < b ? a : b;
}
unittest
{
assert(min(1, 2) == 1);
}
long min(long a, long b)
{
return a < b ? a : b;
}
unittest
{
assert(min(1L, 2L) == 1);
}
real min(real a, real b)
{
return a < b ? a : b;
}
unittest
{
assert(feq(min(1.0L, 2.0L), 1.0L));
}
/*************************************
* The largest value
*/
int max(int[] n)
{
int result = int.min;
for (uint i = 0; i < n.length; i++)
if (n[i] > result)
result = n[i];
return result;
}
unittest
{
static int[3] n = [ 0, 2, -1 ];
assert(max(n) == 2);
}
long max(long[] n)
{
long result = long.min;
for (uint i = 0; i < n.length; i++)
if (n[i] > result)
result = n[i];
return result;
}
unittest
{
static long[3] n = [ 0, 2, -1 ];
assert(max(n) == 2);
}
real max(real[] n)
{
real result = real.min;
for (uint i = 0; i < n.length; i++)
if (n[i] > result)
result = n[i];
return result;
}
unittest
{
static real[3] n = [ 0.0, 2.0, -1.0 ];
assert(feq(max(n), 2));
}
int max(int a, int b)
{
return a > b ? a : b;
}
unittest
{
assert(max(1, 2) == 2);
}
long max(long a, long b)
{
return a > b ? a : b;
}
unittest
{
assert(max(1L, 2L) == 2);
}
real max(real a, real b)
{
return a > b ? a : b;
}
unittest
{
assert(feq(max(1.0L, 2.0L), 2.0L));
}
/*************************************
* Arccosine
*/
real acos(real x)
{
return atan2(intrinsic.sqrt(1 - x * x), x);
}
unittest
{
assert(feq(acos(0.5), PI / 3));
}
/*************************************
* Arcsine
*/
real asin(real x)
{
return atan2(x, intrinsic.sqrt(1 - x * x));
}
unittest
{
assert(feq(asin(0.5), PI / 6));
}
/*************************************
* Arctangent
*/
real atan(real x)
{
asm
{
fld x;
fld1;
fpatan;
fwait;
}
}
unittest
{
assert(feq(atan(intrinsic.sqrt(3)), PI / 3));
}
/*************************************
* Arctangent y/x
*/
real atan2(real y, real x)
{
asm
{
fld y;
fld x;
fpatan;
fwait;
}
}
unittest
{
assert(feq(atan2(1, intrinsic.sqrt(3)), PI / 6));
}
/*************************************
* Arccotangent
*/
real acot(real x)
{
return tan(1.0 / x);
}
unittest
{
assert(feq(acot(cot(0.000001)), 0.000001));
}
/*************************************
* Arcsecant
*/
real asec(real x)
{
return intrinsic.cos(1.0 / x);
}
/*************************************
* Arccosecant
*/
real acosec(real x)
{
return intrinsic.sin(1.0 / x);
}
/*************************************
* Tangent
*/
/+
real tan(real x)
{
asm
{
fld x;
fptan;
fstp ST(0);
fwait;
}
}
unittest
{
assert(feq(tan(PI / 3), intrinsic.sqrt(3)));
}
+/
/*************************************
* Cotangent
*/
real cot(real x)
{
asm
{
fld x;
fptan;
fdivrp;
fwait;
}
}
unittest
{
assert(feq(cot(PI / 6), intrinsic.sqrt(3)));
}
/*************************************
* Secant
*/
real sec(real x)
{
asm
{
fld x;
fcos;
fld1;
fdivrp;
fwait;
}
}
/*************************************
* Cosecant
*/
real cosec(real x)
{
asm
{
fld x;
fsin;
fld1;
fdivrp;
fwait;
}
}
/*************************************
* Hypotenuse of right triangle
*/
/+
real hypot(real x, real y)
{
asm
{
fld y;
fabs;
fld x;
fabs;
fcom;
fnstsw AX;
test AH, 0x45;
jnz _1;
fxch ST(1);
_1: fldz;
fcomp;
fnstsw AX;
test AH, 0x40;
jz _2;
fstp ST(0);
jmp _3;
_2: fdiv ST, ST(1);
fmul ST, ST(0);
fld1;
fadd;
fsqrt;
fmul;
_3: fwait;
}
}
unittest
{
assert(feq(hypot(3, 4), 5));
}
+/
/*********************************************
* Extract mantissa and exponent from float
*/
real frexp(real x, out int exponent)
{
asm
{
fld x;
mov EDX, exponent;
mov dword ptr [EDX], 0;
ftst;
fstsw AX;
fwait;
sahf;
jz done;
fxtract;
fxch;
fistp dword ptr [EDX];
fld1;
fchs;
fxch;
fscale;
inc dword ptr [EDX];
fstp ST(1);
done:
fwait;
}
}
unittest
{
int exponent;
real mantissa = frexp(123.456, exponent);
assert(feq(mantissa * pow(2, exponent), 123.456));
}
/**********************************************
* Make a float out of mantissa and exponent
*/
real ldexp(real x, int exponent)
{
asm
{
fild exponent;
fld x;
fscale;
fstp ST(1);
fwait;
}
}
unittest
{
assert(feq(ldexp(666, 10), 666 * 1024));
}
/*************************************
* Round to nearest int > x
*/
long ceil(real x)
{
return frac(x) > 0 ? trunc(x) + 1 : trunc(x);
}
unittest
{
assert(ceil(+123.456) == +124);
assert(ceil(-123.456) == -123);
}
/*************************************
* Round to nearest int < x
*/
long floor(real x)
{
return frac(x) < 0 ? trunc(x) - 1 : trunc(x);
}
unittest
{
assert(floor(+123.456) == +123);
assert(floor(-123.456) == -124);
}
/*************************************
* Base 10 logarithm
*/
real log10(real x)
{
asm
{
fldlg2;
fld x;
fyl2x;
fwait;
}
}
unittest
{
assert(feq(log10(1000), 3));
}
/*************************************
* Base 2 logarithm
*/
real log2(real x)
{
asm
{
fld1;
fld x;
fyl2x;
fwait;
}
}
unittest
{
assert(feq(log2(1024), 10));
}
/*************************************
* Natural logarithm
*/
real log(real x)
{
asm
{
fldln2;
fld x;
fyl2x;
fwait;
}
}
unittest
{
assert(feq(log(E), 1));
}
/*************************************
* Natural logarithm of (x + 1)
*/
real log1p(real x)
{
asm
{
fldln2;
fld x;
fyl2xp1;
fwait;
}
}
unittest
{
assert(feq(log1p(E - 1), 1));
}
/*************************************
* Logarithm
*/
real log(real x, real base)
{
asm
{
fld1;
fld x;
fyl2x;
fld1;
fld base;
fyl2x;
fdiv;
fwait;
}
}
unittest
{
assert(feq(log(81, 3), 4));
}
/*************************************
* (base + 1) logarithm of x
*/
real log1p(real x, real base)
{
asm
{
fld1;
fld x;
fyl2x;
fld1;
fld base;
fyl2xp1;
fdiv;
fwait;
}
}
unittest
{
assert(feq(log1p(81, 3 - 1), 4));
}
/*************************************
* Exponent
*/
real exp(real x)
{
asm
{
fld x;
fldl2e;
fmul;
fld ST(0);
frndint;
fsub ST(1), ST;
fxch ST(1);
f2xm1;
fld1;
fadd;
fscale;
fstp ST(1);
fwait;
}
}
unittest
{
assert(feq(exp(3), E * E * E));
}
/*************************************
* Base to exponent
*/
real pow(real base, real exponent)
{
return exp(exponent * log(base));
}
unittest
{
assert(feq(pow(2, 10), 1024));
}
/*************************************
* Hyperbolic cosine
*/
real cosh(real x)
{
real z = exp(x) / 2;
return z + 0.25 / z;
}
unittest
{
assert(feq(cosh(1), (E + 1.0 / E) / 2));
}
/*************************************
* Hyperbolic sine
*/
real sinh(real x)
{
real z = exp(x) / 2;
return z - 0.25 / z;
}
unittest
{
assert(feq(sinh(1), (E - 1.0 / E) / 2));
}
/*************************************
* Hyperbolic tangent
*/
private real tanh_domain;
real tanh(real x)
{
if (x > tanh_domain)
return 1;
else if (x < -tanh_domain)
return -1;
else
{
real z = sqr(exp(x));
return (z - 1) / (z + 1);
}
}
unittest
{
assert(feq(tanh(1), sinh(1) / cosh(1)));
}
/*************************************
* Hyperbolic cotangent
*/
real coth(real x)
{
return 1 / tanh(x);
}
unittest
{
assert(feq(coth(1), cosh(1) / sinh(1)));
}
/*************************************
* Hyperbolic secant
*/
real sech(real x)
{
return 1 / cosh(x);
}
/*************************************
* Hyperbolic cosecant
*/
real cosech(real x)
{
return 1 / sinh(x);
}
/*************************************
* Hyperbolic arccosine
*/
real acosh(real x)
{
if (x <= 1)
return 0;
else if (x > 1.0e10)
return log(2) + log(x);
else
return log(x + intrinsic.sqrt((x - 1) * (x + 1)));
}
unittest
{
assert(acosh(0.5) == 0);
assert(feq(acosh(cosh(3)), 3));
}
/*************************************
* Hyperbolic arcsine
*/
real asinh(real x)
{
if (!x)
return 0;
else if (x > 1.0e10)
return log(2) + log(1.0e10);
else if (x < -1.0e10)
return -log(2) - log(1.0e10);
else
{
real z = x * x;
return x > 0 ?
log1p(x + z / (1.0 + intrinsic.sqrt(1.0 + z))) :
-log1p(-x + z / (1.0 + intrinsic.sqrt(1.0 + z)));
}
}
unittest
{
assert(asinh(0) == 0);
assert(feq(asinh(sinh(3)), 3));
}
/*************************************
* Hyperbolic arctangent
*/
real atanh(real x)
{
if (!x)
return 0;
else
{
if (x >= 1)
return real.max;
else if (x <= -1)
return -real.max;
else
return x > 0 ?
0.5 * log1p((2.0 * x) / (1.0 - x)) :
-0.5 * log1p((-2.0 * x) / (1.0 + x));
}
}
unittest
{
assert(atanh(0) == 0);
assert(feq(atanh(tanh(0.5)), 0.5));
}
/*************************************
* Hyperbolic arccotangent
*/
real acoth(real x)
{
return 1 / acot(x);
}
unittest
{
assert(feq(acoth(coth(0.01)), 100));
}
/*************************************
* Hyperbolic arcsecant
*/
real asech(real x)
{
return 1 / asec(x);
}
/*************************************
* Hyperbolic arccosecant
*/
real acosech(real x)
{
return 1 / acosec(x);
}
/*************************************
* Convert string to float
*/
real atof(char[] s)
{
if (!s.length)
return real.nan;
real result = 0;
uint i = 0;
while (s[i] == "\t" || s[i] == " ")
if (++i >= s.length)
return real.nan;
bit neg = false;
if (s[i] == "-")
{
neg = true;
i++;
}
else if (s[i] == "+")
i++;
if (i >= s.length)
return real.nan;
bit hex;
if (s[s.length - 1] == "h")
{
hex = true;
s.length = s.length - 1;
}
else if (i + 1 < s.length && s[i] == "0" && s[i+1] == "x")
{
hex = true;
i += 2;
if (i >= s.length)
return real.nan;
}
else
hex = false;
while (s[i] != ".")
{
if (hex)
{
if ((s[i] == "p" || s[i] == "P"))
break;
result *= 0x10;
}
else
{
if ((s[i] == "e" || s[i] == "E"))
break;
result *= 10;
}
if (s[i] >= "0" && s[i] <= "9")
result += s[i] - "0";
else if (hex)
{
if (s[i] >= "a" && s[i] <= "f")
result += s[i] - "a" + 10;
else if (s[i] >= "A" && s[i] <= "F")
result += s[i] - "A" + 10;
else
return real.nan;
}
else
return real.nan;
if (++i >= s.length)
goto done;
}
if (s[i] == ".")
{
if (++i >= s.length)
goto done;
ulong k = 1;
while (true)
{
if (hex)
{
if ((s[i] == "p" || s[i] == "P"))
break;
result *= 0x10;
}
else
{
if ((s[i] == "e" || s[i] == "E"))
break;
result *= 10;
}
k *= (hex ? 0x10 : 10);
if (s[i] >= "0" && s[i] <= "9")
result += s[i] - "0";
else if (hex)
{
if (s[i] >= "a" && s[i] <= "f")
result += s[i] - "a" + 10;
else if (s[i] >= "A" && s[i] <= "F")
result += s[i] - "A" + 10;
else
return real.nan;
}
else
return real.nan;
if (++i >= s.length)
{
result /= k;
goto done;
}
}
result /= k;
}
if (++i >= s.length)
return real.nan;
bit eneg = false;
if (s[i] == "-")
{
eneg = true;
i++;
}
else if (s[i] == "+")
i++;
if (i >= s.length)
return real.nan;
int e = 0;
while (i < s.length)
{
e *= 10;
if (s[i] >= "0" && s[i] <= "9")
e += s[i] - "0";
else
return real.nan;
i++;
}
if (eneg)
e = -e;
result *= pow(hex ? 2 : 10, e);
done:
return neg ? -result : result;
}
unittest
{
assert(feq(atof("123"), 123));
assert(feq(atof("+123"), +123));
assert(feq(atof("-123"), -123));
assert(feq(atof("123e2"), 12300));
assert(feq(atof("123e+2"), 12300));
assert(feq(atof("123e-2"), 1.23));
assert(feq(atof("123."), 123));
assert(feq(atof("123.E-2"), 1.23));
assert(feq(atof(".456"), .456));
assert(feq(atof("123.456"), 123.456));
assert(feq(atof("1.23456E+2"), 123.456));
//assert(feq(atof("1A2h"), 1A2h));
//assert(feq(atof("1a2h"), 1a2h));
assert(feq(atof("0x1A2"), 0x1A2));
assert(feq(atof("0x1a2p2"), 0x1a2p2));
assert(feq(atof("0x1a2p+2"), 0x1a2p+2));
assert(feq(atof("0x1a2p-2"), 0x1a2p-2));
assert(feq(atof("0x1A2.3B4"), 0x1A2.3B4p0));
assert(feq(atof("0x1a2.3b4P2"), 0x1a2.3b4P2));
}
/*************************************
* Convert float to string
*/
char[] toString(real x)
{
// BUG: this code is broken, returning a pointer into the stack
char[1024] buffer;
char* p = buffer;
uint psize = buffer.length;
int count;
while (true)
{
version(Win32)
{
count = _snprintf(p, psize, "%Lg", x);
if (count != -1)
break;
psize *= 2;
p = cast(char*) alloca(psize);
}
else version(linux)
{
count = snprintf(p, psize, "%Lg", x);
if (count == -1)
psize *= 2;
else if (count >= psize)
psize = count + 1;
else
break;
/+
if (p != buffer)
c.stdlib.free(p);
p = cast(char*) c.stdlib.malloc(psize);
+/
p = cast(char*) alloca(psize);
}
}
return p[0 .. count];
}
unittest
{
assert(!cmp(toString(123.456), "123.456"));
}
/*************************************
* Static constructor
*/
static this()
{
tanh_domain = log(real.max) / 2;
}