mirror of
https://github.com/dlang/phobos.git
synced 2025-04-28 06:00:35 +03:00
1369 lines
18 KiB
D
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;
|
|
}
|