// Written in the D programming language

/* This module is obsolete and will be removed eventually */

/*
 * 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 std.math2;
private import std.math, std.string, std.c.stdlib, std.c.stdio;

//debug=math2;

/****************************************
 * compare floats with given precision
 */

bool feq(real a, real b)
{
	return feq(a, b, 0.000001);
} 
 
bool 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 std.math.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));
}

/*********************************
 * 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 (size_t i = 0; i < n.length; i++)
	    result += n[i];
	return cast(int)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));
}

/*************************************
 * 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 std.math.cos(1.0 / x);
}


/*************************************
 * Arccosecant
 */

real acosec(real x)
{
	return std.math.sin(1.0 / x);
}

/*************************************
 * Tangent
 */

/+
real tan(real x)
{
	asm
	{
		fld x;
		fptan;
		fstp ST(0);
		fwait;
	}
}

unittest
{
	assert(feq(tan(PI / 3), std.math.sqrt(3)));
}
+/

/*************************************
 * Cotangent
 */

real cot(real x)
{
	asm
	{
		fld x;
		fptan;
		fdivrp;
		fwait;
	}
}

unittest
{
	assert(feq(cot(PI / 6), std.math.sqrt(3.0L)));
}

/*************************************
 * Secant
 */

real sec(real x)
{
	asm
	{
		fld x;
		fcos;
		fld1;
		fdivrp;
		fwait;
	}
}


/*************************************
 * Cosecant
 */

real cosec(real x)
{
	asm
	{
		fld x;
		fsin;
		fld1;
		fdivrp;
		fwait;
	}
}

/*********************************************
 * 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.0L, cast(real)exponent), 123.456));
}
+/

/*************************************
 * 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 + std.math.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 + std.math.sqrt(1.0 + z))) :
			-log1p(-x + z / (1.0 + std.math.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(string 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;
	bool neg = false;
	if (s[i] == '-')
	{
		neg = true;
		i++;
	}
	else if (s[i] == '+')
		i++;
	if (i >= s.length)
		return real.nan;
	bool 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;
	bool 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.0L : 10.0L, cast(real)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));
}

