function isInf(x)
	return x + 1 == x
end

function isPosInf(x)
	return isInf(x) and x > 0
end

function isNegInf(x)
	return isInf(x) and x < 0
end

function isNaN(x)
	return not (x == x) and not isInf(x)
end
	

testCall(function() assert(tonumber("12345678e20") > 10e20, tonumber("12345678e20") .. " was not larger than " .. 10e20) end)

testCall(function() assert(tonumber("12345678e20") > 10e20) end)

testAssert(1 < 2, "1 must be less than 2")
testAssert(not (2 < 1), "2 must not be less than 1")

do
	local value1 = 1.234567
	local value2 = 1.234567
	value2 = value2 * 2
	value2 = value2 / 2

	testAssert(value1 == value2)
	testAssert(rawequal(value1, value2))
	
	local t = {}
	t[value1] = 1
	t[value2] = 2
	testAssert(t[value1] == 2)
	testAssert(t[value2] == 2)
end

function assertEquals(a, b)
	local errMsg = "expected " .. tostring(a) .. " = " .. tostring(b)
	local assert = assert
	local type = type
	assert(type(a) == type(b), "not same type")
	if (type(a) == "number") then
		local diff = math.abs(a - b)
		local max = math.max(a, b)
		assert((diff / max < 1e-8) or (diff < 1e-7), errMsg)
	else
		assert(a == b, errMsg)
	end
end

function testAssertEquals(a, b)
	testCall(function() assertEquals(a, b) end)
end

testAssertEquals(1, 1)
testAssertEquals(2.123, 2.123)

testAssertEquals(math.cos(1), 0.540302306)
testAssertEquals(math.cos(0.234), 0.972746698)


testAssertEquals(math.sin(0.234), 0.231870355)

do
   local x = 123
   testAssertEquals(-x, -123)
end

testAssertEquals(true, not not true)
testAssertEquals(true, not not 1)
testAssertEquals(false, not 0)


do
   local a = 1
   local b = 2
   local c = 0
   testAssert(a / c == b / c, "+inf broken")
   testAssert(-a / c == -b / c, "-inf broken")

   local s = tostring(c % c):lower()
   testAssert(s == "nan", "0 % 0 was: " .. s)

end

do
	testCall(function()
		for i = 1, 10, 0.1 do
			assertEquals(math.sqrt(i^2), i)
		end
	end)	
end

do
	testCall(function()
		for i = 1, 10, 0.1 do
			assertEquals(3^(-i), 1/(3^i))
		end
	end)	
	
	testAssert(isNaN((-123)^1.1))

	testCall(function()
		local mult = 1	
		for i = 0, 20 do
			local x = 1.123
			assertEquals(x ^ i, mult)
			mult = mult * x;
		end
	end)	
end

do
	testAssert(isPosInf(math.floor(1/0)))
	testAssert(isNegInf(math.floor(-1/0)))
	testAssert(isNaN(math.floor(0/0)))
	testAssert(isPosInf(math.ceil(1/0)))
	testAssert(isNegInf(math.ceil(-1/0)))
	testAssert(isNaN(math.ceil(0/0)))

	testCall(function()
		for i = -2, 2, 0.01 do
			local f = math.floor(i)
			local c = math.ceil(i)
			assert(c >= f)
			if c == f then
				assert(i == c)
				assert(i == f)
			end
		end
	end)
end

do	
	local a, b
	a, b = math.modf(1 / 0)
	testAssert(isPosInf(a))
	testAssert(b == 0)
	
	a, b = math.modf(-1 / 0)
	testAssert(isNegInf(a))
	testAssert(b == 0)

	a, b = math.modf(0 / 0)
	testAssert(isNaN(a))
	testAssert(isNaN(b))

	a, b = math.modf(-2.5)
	testAssert(a == -2)
	testAssert(b == -.5)

	testCall(function()
		for i = -2, 2, 0.01 do
			local ipart, fpart = math.modf(i)
			assert(ipart + fpart == i)
		end
	end)
end

local function assertInterval(value, low, high)
	assert(value >= low)
	assert(value <= high)
end

testCall(function()
	for i = -10, 10, 0.01 do
		assertInterval(math.cos(i), -1, 1)
		assertInterval(math.sin(i), -1, 1)
	end
end)

testCall(function()
	for i = -1, 1, 0.01 do
		assertInterval(math.acos(i), -math.pi, math.pi)
		assertInterval(math.asin(i), -math.pi, math.pi)
	end
end)
do
	local v
	v = math.acos(1.01) testAssert(isNaN(v), "expected NaN, got " .. v)
	v = math.acos(-1.01) testAssert(isNaN(v), "expected NaN, got " .. v)
	v = math.asin(1.01) testAssert(isNaN(v), "expected NaN, got " .. v)
	v = math.asin(-1.01) testAssert(isNaN(v), "expected NaN, got " .. v)
end

testCall(function()
	for i = -10, 10, 0.01 do
		local v1 = math.cos(i) ^ 2 + math.sin(i) ^ 2
		assertEquals(v1, 1)
	end
end)

testCall(function()
	for i = 0, math.pi, 0.01 do
		assertEquals(math.acos(math.cos(i)), i)
	end
end)

testCall(function()
	for i = -math.pi / 2, math.pi / 2, 0.01 do
		assertEquals(math.asin(math.sin(i)), i)
		assertEquals(math.atan(math.tan(i)), i)
	end
end)

testCall(function()
	for i = -100, 100, 0.5 do
		assertEquals(math.tan(math.atan(i)), i)
	end
end)

testAssert(math.atan(0) == 0)
testAssertEquals(math.atan(1 / 0), math.pi/2)
testAssertEquals(math.atan(-1 / 0), -math.pi/2)
testAssertEquals(math.atan2(1 / 0, 123), math.pi/2)
testAssertEquals(math.atan2(-1 / 0, 123), -math.pi/2)

testAssertEquals(math.atan2(2, 123), 0.01625872980513)
testAssertEquals(math.atan2(2, -123), 3.1253339237847)

testAssertEquals(math.atan2(2, 3), 0.58800260354757)
testAssertEquals(math.atan2(2, -3), 2.5535900500422)
testAssertEquals(math.atan2(-2, 3), -0.58800260354757)
testAssertEquals(math.atan2(-2, -3), -2.5535900500422)

testAssert(isNaN(math.sin(1 / 0)))
testAssert(isNaN(math.cos(1 / 0)))
testAssert(isNaN(math.tan(1 / 0)))

do
	local v = math.atan2(1, 0)
	testCall(function()
		for i = 0, 10 do
			assert(0 == math.atan2(0, i))
		end
	end)
	testCall(function()
		for i = 1, 20 do
			assertEquals(v, math.atan2(i, 0))
			assert(math.atan2(i, 2) == math.atan(i / 2))
		end
	end)
end

do
	testCall(function()
		for i = -10, 10, 0.1 do
			assertEquals(math.cosh(i)^2 - math.sinh(i)^2, 1)
			assertEquals(math.sinh(-i), -math.sinh(i))
			assertEquals(math.cosh(-i), math.cosh(i))
			assertEquals(math.tanh(i), math.sinh(i) / math.cosh(i))
		end
	end)
end

do
	testAssert(isNaN(math.rad(0/0)))
	testAssert(isNaN(math.deg(0/0)))
	testAssert(isPosInf(math.rad(1/0)))
	testAssert(isPosInf(math.deg(1/0)))
	testAssert(isNegInf(math.rad(-1/0)))
	testAssert(isNegInf(math.deg(-1/0)))
	
	testAssertEquals(math.pi, math.rad(180))
	testAssertEquals(360, math.deg(2 * math.pi))
	
	testCall(function()
		for i = 1, 100, 0.1 do
			assertEquals(i, math.rad(math.deg(i)))
			assertEquals(i, math.deg(math.rad(i)))
		end
	end)
end

do
	local a, b
	a, b = math.frexp(0/0)
	testAssert(isNaN(a) and b == 0)
	a, b = math.frexp(1/0)
	testAssert(isPosInf(a) and b == 0)
	a, b = math.frexp(-1/0)
	testAssert(isNegInf(a) and b == 0)

	testAssert(isPosInf(math.ldexp(1/0, 1)))
	testAssert(isNegInf(math.ldexp(-1/0, 1)))
	testAssert(isNaN(math.ldexp(0/0, 1)))

	testCall(function()
		for i = -10, 10, 0.1 do
			assertEquals(i, math.ldexp(math.frexp(i)))
			assertEquals(i, math.ldexp(i, 0/0))
			assertEquals(i, math.ldexp(i, 1/0))
			assertEquals(i, math.ldexp(i, -1/0))
		end
	end)
end

do
	testAssert(isNaN(math.fmod(0/0, 0/0)))
	testAssert(isNaN(math.fmod(0/0, 2)))
	testAssert(isNaN(math.fmod(2, 0/0)))
	testAssert(isNaN(math.fmod(1/0, 0/0)))
	testAssert(isNaN(math.fmod(1/0, 1/0)))
	testAssert(isNaN(math.fmod(-1/0, 0/0)))
	testAssert(isNaN(math.fmod(-1/0, 1/0)))
	
	testCall(function()
		for i = 1, 10 do
			assert(i == math.fmod(i, 1/0))
			assert(i == math.fmod(i, -1/0))
		end
	end)
	
	testCall(function()
		for i = 0.1, 10, 0.5 do
			assertEquals(math.fmod(i, 0.5), 0.1)
		end
	end)
	testCall(function()
		for i = -10.1, 0, 0.5 do
			assertEquals(math.fmod(i, 0.5), -0.1)
		end
	end)
	testCall(function()
		for i = 0.1, 10, 0.5 do
			assertEquals(math.fmod(i, -0.5), 0.1)
		end
	end)
	testCall(function()
		for i = -10.1, 0, 0.5 do
			assertEquals(math.fmod(i, -0.5), -0.1)
		end
	end)
end

-- test exp
do
	local e = math.exp(1)
	local x = 1
	for i = 1, 10 do
		x = x * e
		assertEquals(math.exp(i), x)
	end
end

-- test log
do
	testAssert(isNaN(math.log(-1)))
	testAssert(isNegInf(math.log(0)))
	testAssert(isPosInf(math.log(1/0)))
	
	local x = 1
	local e = math.exp(1)
	for i = 1, 10 do
		x = x * e
		assertEquals(math.log(x), i)
		assertEquals(math.log(1 / x), -i)
		
		assertEquals(math.log10(x), math.log(x) / math.log(10))
	end
end

testAssertEquals(math.pow(1.234, 10.170355), 8.48608917)


