---------- ---------- ---------- ---------- ---------- 
-- Name: math.angleBetweenPoints( X1, Y1, X2, Y2 )
-- Desc: Angle between two points
---------- ---------- ---------- ---------- ---------- 
function math.angleBetweenPoints( X1, Y1, X2, Y2 )
	if not X1 then
		return nil
	end
	local dx = X2-X1
	local dy = Y2-Y1
	
	return math.atan2(dy, dx)
end
---------- ---------- ---------- ---------- ---------- 
-- Name: math.Distance( X1, Y1, X2, Y2 )
-- Desc: Distance between two points
---------- ---------- ---------- ---------- ---------- 
function math.distance( X1, Y1, X2, Y2 )
	if not X2 then
		return nil
	end
	local XD = X2 - X1
	local YD = Y2 - Y1
	
	return math.sqrt( XD*XD + YD*YD )
end

---------- ---------- ---------- ---------- ---------- 
-- Name: math.crossFade( )
-- Desc: returns a value going between 0 and 1, moving towards the boolen state at the speed specified
---------- ---------- ---------- ---------- ---------- 
function math.crossFade(state, current, speed)
	if state then
		if current < 1 then
			return math.min(1,current + speed)
		else
			return 1
		end
	else
		if current > 0 then
			return math.max(0,current - speed)	
		else
			return 0
		end
	end
end

---------- ---------- ---------- ---------- ---------- 
-- Name: math.distance2( X1, Y1, X2, Y2 )
-- Desc: Squared distance between two points (faster)
---------- ---------- ---------- ---------- ---------- 
function math.distance2( X1, Y1, X2, Y2 )
	local XD = X2 - X1
	local YD = Y2 - Y1
	
	return ( XD*XD + YD*YD )
end

---------- ---------- ---------- ---------- ---------- 
-- Name: math.Clamp( Value, Min, Max )
-- Desc: Clamps value between 2 values
---------- ---------- ---------- ---------- ---------- 
function math.clamp( Value, Min, Max )
	if ( Value < Min ) then return Min end
	if ( Value > Max ) then return Max end
	
	return Value
end

---------- ---------- ---------- ---------- ---------- 
-- Name: math.Rand( Low, High )
-- Desc: Random real number between low and high
---------- ---------- ---------- ---------- ---------- 
function math.rand( Low, High )
	print("Wtf, don't use this! Use math.random(a,b) instad")
	return Low + ( math.random() * ( High - Low ) )
end

---------- ---------- ---------- ---------- ---------- 
-- Name: math.Round( Value [, Digits] )
-- Desc: Rounds any real number to the nearest multiple of the variable 'digits'
-- Examples: 
-- 	math.Round(1.23, 0.1)
-- 	returns 1.2 (round to one decimal)
--
-- 	math.Round(345678, 1000)
-- 	returns 346000 (round to nearest thousand)
---------- ---------- ---------- ---------- ---------- 
function math.round( Value, Digits )
	Digits = Digits or 1
	local Remainder = Value % Digits
	
	if ( Remainder / Digits < 0.5 ) then
		return Value - Remainder
	else
		return Value - Remainder + Digits
	end
end

---------- ---------- ---------- ---------- ---------- 
-- Name: math.Approach( Cur, Target, Inc )
-- Desc: Increment Cur using number Inc until it reaches Target
---------- ---------- ---------- ---------- ---------- 
function math.approach( Cur, Target, Inc )
	Inc = math.abs( Inc )
	
	if ( Cur < Target ) then
		return math.clamp( Cur + Inc, Cur, Target )
	else
		return math.clamp( Cur - Inc, Target, Cur )
 	end
	
 	return Target
end

---------- ---------- ---------- ---------- ---------- 
-- Name: math.NormalizeAngle( Ang )
-- Desc: Converts an angle to -179 to 180 representation
---------- ---------- ---------- ---------- ---------- 
function math.normalizeAngle( Ang )
	while ( Ang < 0 ) do
		Ang = Ang + math.pi*2
	end
	
	while ( Ang >= math.pi*2 ) do
		Ang = Ang - math.pi*2
	end
	
	if ( Ang > math.pi) then
		return Ang - math.pi*2
	end
	
	return Ang
end

---------- ---------- ---------- ---------- ---------- 
-- Name: math.normalize( val, a, b )
-- Desc: Converts a value from a to b representation
---------- ---------- ---------- ---------- ---------- 
function math.normalize( val, a, b )
	return ((val-a) % (b-a+1)) + a
end

function math.difference(a,b)
	return math.abs(a-b)
end
---------- ---------- ---------- ---------- ---------- 
-- Name: math.AngleDifference( Ang1, Ang2 )
-- Desc: Difference between two angles
---------- ---------- ---------- ---------- ---------- 
function math.angleDifference( Ang1, Ang2 )
	
	local Diff = math.normalizeAngle( Ang1 - Ang2 )
	
	if ( Diff < math.pi ) then
		return Diff
	end
	
	return Diff - math.pi*2
end


function math.angleDifferenceAbs( Ang1, Ang2 )
	local Diff = math.normalizeAngle( Ang1 - Ang2 )
	
	if ( Diff < math.pi ) then
		return  math.abs( Diff )
	end
	
	return math.abs(Diff - math.pi*2)
end

function math.flipAngle( angle )
	
	angle = math.normalizeAngle(angle + math.pi)
	
	if angle <= math.pi then
		return math.pi - angle
	else
		return 3 * math.pi - angle
	end
	
end


---------- ---------- ---------- ---------- ---------- 
-- Name: math.ApproachAngle( Cur, Target, Inc )
-- Desc: Increment angle Cur using number Inc until it reaches angle Target
---------- ---------- ---------- ---------- ---------- 
function math.approachAngle( Cur, Target, Inc )
	local Diff = math.angleDifference( Target, Cur )
	
	return math.approach( Cur, Cur + Diff, Inc )
end


---------- ---------- ---------- ---------- ---------- 
-- Name: math.safeRandom(smallest, largest)
-- Desc: Use this when the random value doesn't need to be synchronous (for example, in rendering)
---------- ---------- ---------- ---------- ---------- 
local safeSeed = os.time()
function math.safeRandom(a, b)

	safeSeed = 40692 * (safeSeed % 52774) - 3791 * math.floor(safeSeed / 52774)
	if safeSeed <= 0 then
		safeSeed = safeSeed + 2147483399
	end
	
	if a then
		if b then
			if  b > a then
				return a + (safeSeed % (b - a))
			else
				return a
			end
		else
			return 1 + (safeSeed % a)
		end
	else
		return (safeSeed % 2147483399) / 2147483399
	end
end

function math.safeGaussian()
	return (math.safeRandom(0,10000) - math.safeRandom(0,10000))/10000
end

function math.sign(x)
   if x<0 then
     return -1
   elseif x>0 then
     return 1
   else
     return 0
   end
end

--function math.random(v)
--	return 1
--end


---------------------------------------
---------------------------------------
function math.quantify(value, quantity)
	return(math.round((value/quantity))*quantity)
end

function math.snap(value, snap)
	return math.round(value/snap)*snap
end

---------- ---------- ---------- ---------- ---------- 
-- Name: math.randomAngle( )
-- Desc: random number between 0 and math.pi*2
---------- ---------- ---------- ---------- ---------- 
function math.randomAngle()
	return math.pi*math.random()*2
end

function math.randomGaussian()
	return math.random()-math.random()
end

---------- ---------- ---------- ---------- ---------- 
-- Name: math.retardingCurve( val, towards)
-- Desc: returns a curve that is fast at first and then slows down when close to the towards value
---------- ---------- ---------- ---------- ---------- 
function math.retardingCurve(val, towards)
	return ((towards or 1)-((towards or 1)-val) * ((towards or 1)-val))
end

function math.initRandom(seed)
	math.randomseed( seed or os.time() )
	math.random()
end

function math.bit(p)
	return 2 ^ (p - 1)  -- 1-based indexing
end

-- Typical call:  if hasbit(x, bit(3)) then ...
function math.hasBit(x, p)
	return x % (p + p) >= p       
end

function math.setBit(x, p)
	return math.hasBit(x, p) and x or x + p
end

function math.clearBit(x, p)
	return math.hasBit(x, p) and x - p or x
end




function math.lineIntersects(aX1, aY1, aX2, aY2, bX1, bY1, bX2, bY2)
	local outX, outY = nil,nil
	-- check vertical lines to avoid division by zero
	if (aX2 == aX1) then
		if (bX2 == bX1) then
			-- both lines are vertical
			if (bX1 == aX1) then
				-- both lines are vertical at exactly the same width, check if
				-- line 2's endpoints are between line one's
				outX = aX1
				if ((bY1 < aY1 and bY1 > aY2) or (bY1 > aY1 and bY1 < aY2)) then
					outY = bY1
					return outX, outY
				end
				if ((bY2 < aY1 and bY2 > aY2) or (bY2 > aY1 and bY2 < aY2)) then
					outY = bY2
					return outX, outY
				end
			end
			return nil, "spread"
		end
		if ((bX1 > aX1 and bX2 > aX1) or (bX1 < aX1 and bX2 < aX1)) then
			return nil, "off spread"
		end
		-- line 1 is vertical, so only check line 2
		local bK = ((bY2 - bY1) / (bX2 - bX1))
		local b1 = bY1 + bK * (aX1 - bX1)

		if ((b1 > aY1 and b1 > aY2) or (b1 < aY1 and b1 < aY2)) then
			return nil, "off spread 2"
		end

		outX = aX1
		outY = b1
		return outX, outY
	end
	if ((bX2 - bX1) == 0) then
		if ((aX1 > bX1 and aX2 > bX1) or (aX1 < bX1 and aX2 < bX1)) then
			return nil, "no x diff"
		end
		-- line 2 is vertical, so only check line A
		local aK = ((aY2 - aY1) / (aX2 - aX1))
		local a1 = aY1 + aK * (bX1 - aX1)

		if ((a1 > bY1 and a1 > bY2) or (a1 < bY1 and a1 < bY2)) then
			return nil, "no x diff 2"
		end

		outX = bX1
		outY = a1
		return outX, outY
	end

	-- check horizontal lines because of rounding errors in debug mode
	if (aY1 == aY2) then
		if (bY1 == bY2) then
			if (bY1 == aY1) then
				-- both lines are horizontal at exactly the same height, check if
				-- line 2's endpoints are between line one's
				outY = aY1
				if ((bX1 < aX1 and bX1 > aX2) or (bX1 > aX1 and bX1 < aX2)) then
					outX = bX1
					return outX, outY
				end
				if ((bX2 < aX1 and bX2 > aX2) or (bX2 > aX1 and bX2 < aX2)) then
					outX = bX2
					return outX, outY
				end
			end
			return nil, "no y Spread"
		end
		if ((bY1 > aY1 and bY2 > aY1) or (bY1 < aY1 and bY2 < aY1)) then
			return nil, "no y Spread 2"
		end
		-- line 1 is horizontal, so only check where line 2 crosses it
		local bK = ((bX2 - bX1) / (bY2 - bY1))
		local b1 = bX1 + bK * (aY1 - bY1)

		if ((b1 > aX1 and b1 > aX2) or (b1 < aX1 and b1 < aX2)) then
			return nil, "no y Spread 3"
		end

		outX = b1
		outY = aY1
		return outX, outY
	end
	if (bY1 == bY2) then
		if ((aY1 > bY1 and aY2 > bY1) or (aY1 < bY1 and aY2 < bY1)) then
			return nil, "same y"
		end
		-- line 2 is horizontal, so only cheak where line 1 crosses it
		local aK = ((aX2 - aX1) / (aY2 - aY1))
		local a1 = aX1 + aK * (bY1 - aY1)

		if ((a1 > bX1 and a1 > bX2) or (a1 < bX1 and a1 < bX2)) then
			return nil, "same y 2"
		end

		outX = a1
		outY = bY1
		return outX, outY
	end

	local aK = ((aY2 - aY1) / (aX2 - aX1))
	local bK = ((bY2 - bY1) / (bX2 - bX1))

	if (aK ~= bK) then
		local a1 = aY1 - aK * aX1
		local a2 = bY1 - bK * bX1

		local xi = - ((a1 - a2) / (aK - bK))
		local yi = a1 + aK * xi
		
		if ((((aX1 - xi) * (xi - aX2)) >= 0) and
			(((bX1 - xi) * (xi - bX2)) >= 0) and
			(((aY1 - yi) * (yi - aY2)) >= 0) and
			(((bY1 - yi) * (yi - bY2)) >= 0)) then
			
			outX = xi
			outY = yi
			return outX, outY
		end
	end
	-- lines are parallell or there is no intersection point
	return nil, "ended"
end



function math.interpolate(p, p1, p2, p3, p4)
	local ip = 1-p
	
	local avg1X= (p1*ip + p2*p) * ip + (p2*ip + p3*p) * p
	local avg2X= (p2*ip + p3*p) * ip + (p3*ip + p4*p) * p
	
	return avg1X*ip + avg2X*p
end

function math.cubicInterpolation(t, x0, x1, x2, x3)
	
	local A = x3-3*x2 +3*x1-x0
	local B = 3*x2-6*x1 + 3*x0
	local C = 3*x1-3*x0
	local D = x0

	local x = A*math.pow(t,3) + B*math.pow(t,2) + C*t + D

	return x
end
